Shadow Mapping Help

Hello Unity community. I’m in the process of trying to implement shadow mapping for my class’s senior project, which we will be presenting at GDC San Francisco. Things have been going well up to this point but I’m having a bit of trouble now. I’ve done a lot of searching and experimenting but I’m still not getting any shadows to show up.

What I’ve done so far:

  • Using -force-opengl mode (a project decision)
  • Overriding the terrain shader (FirstPass.shader). Since there’s no way to access the terrain material inspector I set the shadow map as a global shader property with Shader.SetGlobalTexture(“_ShadowMap”, renderTexture);
  • Using an orthographic camera for the light cam (directional light)
  • Using a surface-vertex shader combo with a custom lighting model based off the default Lambert so I can use the Alpha component of SurfaceOutput to feed in the shadow coefficient and include that in the lighting calculation
  • I have the following script attached to the light cam that constructs the lightVP matrix and sets it as a global shader property
void Start () {
	texMatrix = Matrix4x4.identity;
	texMatrix[0,0] = 0.5f;
	texMatrix[1,1] = 0.5f;
	texMatrix[2,2] = 0.5f;
	texMatrix[0,3] = 0.5f;
	texMatrix[1,3] = 0.5f;
	texMatrix[2,3] = 0.5f;
}
	
void LateUpdate()
{
	lightVPMatrix  = texMatrix;
	lightVPMatrix *= this.camera.projectionMatrix;
	lightVPMatrix *= this.camera.worldToCameraMatrix;
	Shader.SetGlobalMatrix("_LightVPMatrix", lightVPMatrix);
}

And here is the Cg portion of the terrain shader code (nothing outside of this section has been added to or changed):

CGPROGRAM
#pragma surface surf ShadowMapping vertex:vert
#include "UnityCG.cginc"

inline fixed4 LightingShadowMapping (SurfaceOutput s, fixed3 lightDir, fixed atten)
{
	fixed shadowCoeff = s.Alpha;												//NEW
	fixed diff = max (0, dot (s.Normal, lightDir));
	
	fixed4 c;
	c.rgb = s.Albedo * _LightColor0.rgb * shadowCoeff * (diff * atten * 2);		//ALTERED
	c.a = 1.0f;
	return c;
}


struct Input {
	float2 uv_Control : TEXCOORD0;
	float2 uv_Splat0 : TEXCOORD1;
	float2 uv_Splat1 : TEXCOORD2;
	float2 uv_Splat2 : TEXCOORD3;
	float2 uv_Splat3 : TEXCOORD4;
	float4 projTex;								//NEW
};

sampler2D _Control;
sampler2D _Splat0,_Splat1,_Splat2,_Splat3;
sampler2D _ShadowMap;							//NEW
float4x4  _LightVPMatrix;						//NEW


void vert(inout appdata_full v, out Input o) {
	float4x4 LightMVPMatrix = mul( _Object2World, _LightVPMatrix );		//NEW
	o.projTex = mul( v.vertex, LightMVPMatrix );						//NEW
}


void surf (Input IN, inout SurfaceOutput o) {
	float shadowCoeff = tex2Dproj(_ShadowMap, UNITY_PROJ_COORD(IN.projTex)).r;	//NEW
	
	half4 splat_control = tex2D (_Control, IN.uv_Control);
	half3 col;
	col  = splat_control.r * tex2D (_Splat0, IN.uv_Splat0).rgb;
	col += splat_control.g * tex2D (_Splat1, IN.uv_Splat1).rgb;
	col += splat_control.b * tex2D (_Splat2, IN.uv_Splat2).rgb;
	col += splat_control.a * tex2D (_Splat3, IN.uv_Splat3).rgb;
	o.Albedo = col;
	o.Alpha = shadowCoeff;														//NEW
}
ENDCG

I’m not clueless in the area of shadow mapping but I’m a rookie when it comes to shaders. Any help would be greatly appreciated! Let me know if you need more info.

A couple sources I have been using:
Cg Tutorial: Projective Texturing and Shadow Mapping
Relevant Unity Forum Thread

817535--30263--$ShadowMap.PNG
817535--30264--$LightView.PNG

Is there anyone that can provide at least a little assistance?

Okay, I’m trying a different route for now. I’m now using a vert-frag shader instead of a surface shader and on a plane instead of terrain. I’m getting the shadow map to render to the plane but in incorrect positioning and the tex2Dproj function isn’t doing the comparison: it simply draws the shadow map to the plane (as you can see in the screenshot, the whole terrain is being darkened). The light cam is facing straight down so the shadows should appear directly underneath. If I manually set the shadowCoeff to 0.0f the whole plane appears completely in shadow and if I set it to 1.0f the whole plane is completely lit. So that logic works. Any ideas why the comparison isn’t working?

Shader "ShadowReceiver2" {
	Properties {
		_MainTex ("Base (RGB)", 2D) = "white" {}
		_ShadowMap ("Shadow Texture", 2D) = "white" {}
	}
	SubShader {
	Pass {
	
		CGPROGRAM
// Upgrade NOTE: excluded shader from Xbox360; has structs without semantics (struct v2f members projTex)
#pragma exclude_renderers xbox360
		#pragma vertex vert
		#pragma fragment frag
		
		#include "UnityCG.cginc"

		sampler2D _MainTex;
		sampler2D _ShadowMap;
		float4x4  _LightVPMatrix;
		
		float4 _MainTex_ST;

		struct v2f {
			float4 pos      : SV_POSITION;
			float2 uv       : TEXCOORD0;
			float4 projTex;
		};

		v2f vert (appdata_base v) {
			v2f o;
			o.pos = mul(UNITY_MATRIX_MVP, v.vertex);
			float4x4 LightMVPMatrix = mul (_Object2World, _LightVPMatrix);
			o.projTex = mul(LightMVPMatrix, v.vertex);
			
			o.uv = TRANSFORM_TEX(v.texcoord, _MainTex);
			return o;
		}
		
		half4 frag (v2f i) : COLOR
		{
			half4 c = tex2D(_MainTex, i.uv);
			
			//i.projTex.xy /= i.projTex.w;
			//float depth = i.projTex.z / i.projTex.w;
			float4 shadowCoeff = tex2Dproj(_ShadowMap, UNITY_PROJ_COORD(i.projTex));
			//float shadowCoeff = (tex2D(_ShadowMap, i.projTex.xy).r < depth) ? 0.0f : 1.0f;
			//float shadowCoeff = (DecodeFloatRGBA(_ShadowMap, i.projTex.xy) < depth) ? 0.0f : 1.0f;
			c *= shadowCoeff;
			
			return c;
		}
		ENDCG
	}
	}
}

840364--31317--$screen4.PNG
840364--31318--$screen2.PNG
840364--31319--$screen1.PNG
840364--31320--$screen3.PNG

My guess is tex2Dproj isn’t detecting that the texture is a depth texture - the NVidia article I read states that it switches behavior depending on whether it’s a standard color map or a depth map.
I’m actually running into this issue, because I cannot use the standard functionality of tex2Dproj (I’m trying to set up a shadowmap as a colormap instead of a depth texture so that it can run on mobile hardware without depth texture support such as Tegra - mainly for learning purposes)
If I could figure out what the distance is from the current fragment to the light source, and then compare that to the depth retrieved from tex2Dproj, I could do a simple greater than comparison to determine whether the fragment should be in shadow. The question is how to calculate that distance correctly.
Anybody have any ideas?