- Home /
A shader that uses lighting but ignores it's own mesh/normals for lighting.
I have two games that have a dynamic lighting system that involes a setting and rising sun/moon so I can't lightmap this. They have custom built terrains, and currently all grasses and trees need tinted via SetColor to match the lighting/ambience. They also do not respond to where they are (grass in cave is as bright as grass on a hilltop). This is because I had to set them to "unlit" or use emission, as when they poster to the camera, they would change colour (as their orientation to the light changed) if they used unity's normal lighting. Is it possible to build shaders that compute lighting based on the location of the object without using the geometry within? this way a grass could be in shadow because it's behind a tree, but lit equally no matter where it faces.
Thanks, James :)
P.S:
Here's a sample of what I'm currently having to use, I set the color in my day/night managent script to be an average of the sun colour and the ambient colour.
Shader "gamevial/Tree" { Properties { _Color ("Main Color", Color) = (1,1,1,1) _MainTex ("Base (RGB) Trans (A)", 2D) = "white" {} }
SubShader {
Tags {"Queue"="Transparent" "IgnoreProjector"="True" }
Lighting Off
Cull Off
Alphatest Greater 0.5
Pass{
//Alphatest Greater 0.5
Tags{ "LightMode"="ShadowCaster"}
SetTexture [_MainTex] {}
}
Material{
}
Pass {
//Blend SrcAlpha OneMinusSrcAlpha
ZWrite On
Tags{ "LightMode"="Always"}
SetTexture [_MainTex] {
constantColor [_Color]
Combine texture * constant DOUBLE
}
}
}
}
Yes, you can definitely, for example, send one normal as a parameter (like _Color, but a Vector3.) Then redo lighting to always use that normal.
But I'd guess that ShaderLab (your code snippet) may not allow that much fiddling. Probably a surface shader, with o.normal=myFakeNormal
somewhere?
Hi Owen, and thanks for replying. I still can't get the shader to ignore the geometry normals for lighting. I post my attempt (you'll see other attempts in there too).
Shader "gamevial/NewGrass" {
Properties {
_$$anonymous$$ainTex ("Base (RGB)", 2D) = "white" {}
_$$anonymous$$yColor ("Some Color", Color) = (0,1,0,1)
}
SubShader {
Tags { "RenderType" = "Transparent" }
Lighting On
//Alphatest Greater 0
//Blend SrcAlpha One$$anonymous$$inusSrcAlpha
//
LOD 100
CGPROGRA$$anonymous$$
#pragma surface surf Lambert addshadow
struct Input {
float2 uv_$$anonymous$$ainTex;
float3 worldNormal;
INTERNAL_DATA
};
sampler2D _$$anonymous$$ainTex;
fixed4 _$$anonymous$$yColor;
void surf (Input IN, inout SurfaceOutput o) {
half4 c = tex2D (_$$anonymous$$ainTex, IN.uv_$$anonymous$$ainTex);
//
o.Albedo = c.rgb;
o.Alpha = c.a;
//o.Emission=_LightColor0;
o.Normal=(1,1,1,1);//WorldNormalVector(IN, o.Normal);
o.Specular=0;
}
ENDCG
}
}
The object is still lit as based on it's orientation etc. $$anonymous$$y knowledge of cg shaders is poor, so I don't know how stupid my workings are, appologies in advance.
It's been awhile since I've worked with CG shaders, but I'd suggest starting with the simplest possible shader, like those outlined here: http://docs.unity3d.com/Documentation/Components/SL-SurfaceShaderExamples.html
For example, the first textured shader on that page doesn't use normals at all in the definition - maybe that would solve your problem?
FWIW, I tested this shader, and it doesn't seem to have the issue you're describing where view angle causes the lighting to change. However, it might be missing other features that you need for the shader in your game:
Shader "HVS_WIP/Surface"
{
Properties
{
_$$anonymous$$ainTex ("Texture", 2D) = "white" {}
_Color ("Color (RGB)", Color) = (0.5, 0.5, 0.5, 1.0)
}
// Surface Shader
SubShader
{
Tags { "RenderType" = "Opaque" }
CGPROGRA$$anonymous$$
#pragma surface surf Lambert
// Add these as an optimization
//noforwardadd nolightmap nodirlightmap approxview halfasview
uniform sampler2D_half _$$anonymous$$ainTex;
uniform fixed4 _Color;
struct Input
{
float2 uv_$$anonymous$$ainTex;
};
void surf(Input i, inout SurfaceOutput o)
{
o.Albedo = tex2D(_$$anonymous$$ainTex, i.uv_$$anonymous$$ainTex).rgb;
}
ENDCG
}
}
Hope it helps, but again, I'm unfortunately a bit out of practice with writing shaders :-\
Thanks $$anonymous$$romenak, this shader kind-of worked. I needed to turn shadows on as shadow maps from objects behind were overwriting in front of all instances of the objects, the effect is now amazing, but I suspect that a surface shader is overkill here. All I theoretically need to do is compute what kind of light is being received, and multiply it by the texture. Really bad pseudo-code follows...
$$anonymous$$aterial{
Emission(_AmountoflightHittingSaidPoint)
}
Pass {
SetTexture [_$$anonymous$$ainTex] {
Combine texture * primary
}
}
"_AmountOfLightHittingSaidPoint" would be a mix of the ambient lighting and a sum of the lights (and ideally but not necessarily the shadows) affecting the model.
I'm doing grass here, so having a shader that does as little work as possible is the ideal here.
I also include the edited version of your shader below, $$anonymous$$romenak, as it is awesome. Thanks! (note: I added a fallback simpler shader for older cards)
Shader "HVS_WIP/Surface"
{
Properties {
_$$anonymous$$ainTex ("Texture", 2D) = "white" {}
_Cutoff ("Cutoff", float) = 0.5
_Colour ("Fallback Colour",Color)=(1,1,1,1)
}
// Surface Shader
SubShader {
Tags { "RenderType" = "GrassBillboardShader" }
ZWrite On
CGPROGRA$$anonymous$$
#pragma surface surf Lambert alphatest:_Cutoff addshadow halfasview
// Add these as an optimization
uniform sampler2D_half _$$anonymous$$ainTex;
//fixed _Cutoff;
struct Input{
float2 uv_$$anonymous$$ainTex;
fixed _Cutoff;
};
void surf(Input IN, inout SurfaceOutput o) {
fixed4 c=tex2D(_$$anonymous$$ainTex, IN.uv_$$anonymous$$ainTex);
o.Albedo = c.rgb;
o.Alpha =tex2D(_$$anonymous$$ainTex, IN.uv_$$anonymous$$ainTex).a;
}
ENDCG
}
SubShader {
Tags { "RenderType" = "GrassBillboardShader" }
ZWrite On
Lighting On
Cull Back
AlphaTest Greater [_Cutoff]
Blend SrcAlpha One$$anonymous$$inusSrcAlpha
$$anonymous$$aterial{
Ambient(1,1,1,1)
Diffuse[_Colour]
//Emission[_Colour]
}
Pass {
SetTexture [_$$anonymous$$ainTex] {
constantColor [_Colour]
Combine texture * primary
}
}
}
}
I never found a simpler shader solution for this than the above, but I found a Hack, which sometimes does the trick. Ins$$anonymous$$d of building a shader that overwrites or ignores the normals you can edit the meshes to point all normals in one direction which then allows you to create roughly the same illusion regardless of shader (so you can use dirt cheap ones :) ). An example is given below.
using UnityEngine;
#if UNITY_EDITOR
using UnityEditor;
#endif
using System.Collections;
public class leavesNormals : $$anonymous$$onoBehaviour {
public bool DoIt;
public string pName;
// Use this for initialization
void Start () {
}
// Update is called once per frame
void Update () {
}
void OnDrawGizmosSelected(){
if(DoIt){
DoIt=false;
$$anonymous$$eshFilter v$$anonymous$$eshFilter=GetComponent<$$anonymous$$eshFilter>();
$$anonymous$$esh v$$anonymous$$esh=v$$anonymous$$eshFilter.mesh;
if(pName==""){
pName=v$$anonymous$$esh.name;
}
Vector3[] vNormals=v$$anonymous$$esh.normals;
for (int i=0;i<vNormals.Length;i++){
vNormals[i]=Vector3.up;
}
v$$anonymous$$esh.normals=vNormals;
#if UNITY_EDITOR
AssetDatabase.CreateAsset( v$$anonymous$$esh, "Assets/$$anonymous$$yAssets/TreeFabs/"+pName+".asset");
AssetDatabase.SaveAssets();
#endif
}
}
}
Click the boolean on the inpector to make a "flattened" mesh copy saved to the folder "Assetts/$$anonymous$$yAssets/Treefabs" in your project folder. I'd still love to know how to do the above, but hope this helps others in my spot.