Skip to main content
gamedev.net
🔒 Locked

Shadow Map Problem

Started by ayush3017 6 replies 1.8k views
Original Post
ayush3017
ayush3017
Hello,
Ok, what my problem is that I have developed deferred rendering engine for my game. Now I want to apply shadows to it. So first I am trying with simple PCF shadows. I thought rather constructing position from depth to apply shadows in the deferred pass why not do shadow calculations when drawing the model as there is only 1 light for which I would be drawing shadows. But I am having strange results:
Image1
Image2

Here is shader for storing depth when drawn from light position:
float4x4 lightWorldViewProjection;void shadowMapVS(float3 posL : POSITION0,                      out float4 posH : POSITION0,                      out float2 depth : TEXCOORD0){	posH = mul(float4(posL, 1.0f), lightWorldViewProjection);		depth = posH.zw;}float4 shadowMapPS(float2 depth : TEXCOORD0) : COLOR{	return depth.x / depth.y; // z / w}technique shadowMapTechnique{	pass P0	{		vertexShader = compile vs_2_0 shadowMapVS();        pixelShader  = compile ps_2_0 shadowMapPS();	}}


And here is the code when applying shadow map:

float4x4 world;float4x4 view;float4x4 projection;float3   eyePos;bool lod;bool leaves;float Time;bool tree;bool light;float4x4 InverseTransposeView;float SMAP_SIZE;float4x4 lightWVP;static const float SHADOW_EPSILON = 0.0001f;float3 lightDir = float3(0.0f, 10000.0f, 2000.0f);float4 lightColor = float4(1.0f, 1.0f, 1.0f, 1.0f);float4 materialAmbient = float4(0.8f, 0.8f, 0.8f, 1.0f);float4 materialDiffuse = float4(0.8f, 0.8f, 0.8f, 1.0f);//-----------------------------------------------------------------------------// Textures.//-----------------------------------------------------------------------------texture shadowMap;sampler shadowMapSampler = sampler_state{	Texture = <shadowMap>;	MinFilter = POINT;	MagFilter = POINT;	MipFilter = POINT;	AddressU  = CLAMP;     AddressV  = CLAMP;};texture colorMap;sampler colorMapSampler = sampler_state{	Texture = <colorMap>;    MinFilter = Anisotropic;	MagFilter = Linear;    MipFilter = Point;    MaxAnisotropy = 16;};struct OutputVS{	float4	vPos			: POSITION0;    float2  vTex0           : TEXCOORD0;	float3	vWorldPos		: TEXCOORD1;	float3	vWorldNrm		: TEXCOORD2;	float3  eyeVec			: TEXCOORD3;	float4  vPos1			: TEXCOORD4;		};struct OutputPS{	float4	vMaterial		: COLOR0;	float4	vWorldNrm		: COLOR1;	float4	vWorldPosXY		: COLOR2;	};OutputVS VS_Lambert(float3 posL      : POSITION0,                      float3 tangentL  : TANGENT0,                     float3 binormalL : BINORMAL0,                     float3 normalL   : NORMAL0,                      float2 tex0      : TEXCOORD0){    	OutputVS outVS = (OutputVS)0;		float4 vertex = mul(float4(posL, 1), world);	/*if(leaves)	{		//float amplitude = magnitude * (vertex.y + 20);		float4 wave = 0.06 * float4(1.5 * sin(Time + vertex.x),			0,			1.5 * cos(Time + vertex.z),			0);							vertex = vertex + wave;	}*/	float4x4 ViewSpace = mul(world, view);	outVS.vWorldPos = mul(float4(posL, 1), ViewSpace);	float3 vWorldPos = mul(float4(posL, 1), ViewSpace);	outVS.eyeVec = vertex.xyz - eyePos;	float4x4 viewProjection = mul(view, projection);	outVS.vPos = mul(float4(outVS.vWorldPos, 1), projection);		outVS.vPos1 = mul(float4(posL, 1), lightWVP);	outVS.vTex0 = tex0;		outVS.vWorldNrm = normalize(mul(float4(normalL, 0), (float3x3) InverseTransposeView));		return outVS;	}//-----------------------------------------------------------------------------// Pixel shader.//-----------------------------------------------------------------------------OutputPS PS_Lambert(OutputVS i) : COLOR{	OutputPS o;				float3 texColor = tex2D(colorMapSampler, i.vTex0);				o.vWorldNrm.xyz = 0.5f * (i.vWorldNrm  + 1.0f);	o.vWorldNrm.w = 0.0;		o.vWorldPosXY = float4(i.vWorldPos.xyz, 0);		float4 projTex = i.vPos1;	projTex.xy /= projTex.w;            	projTex.x =  0.5f * (projTex.x  + 1.0f); 	projTex.y = 0.5f * (projTex.y  + 1.0f); 	projTex.y = 1 - projTex.y;		float depth = projTex.z / projTex.w; 	float2 texelpos = SMAP_SIZE * projTex.xy;                               float2 lerps = frac( texelpos );			float dx = 1 / SMAP_SIZE;		float s0 = (tex2D(shadowMapSampler, projTex.xy).r + SHADOW_EPSILON < depth) ? 0.2f : 1.0f;	float s1 = (tex2D(shadowMapSampler, projTex.xy + float2(dx, 0.0f)).r + SHADOW_EPSILON < depth) ? 0.2f : 1.0f;	float s2 = (tex2D(shadowMapSampler, projTex.xy + float2(0.0f, dx)).r + SHADOW_EPSILON < depth) ? 0.2f : 1.0f;	float s3 = (tex2D(shadowMapSampler, projTex.xy + float2(dx, dx)).r   + SHADOW_EPSILON < depth) ? 0.2f : 1.0f;		float shadowCoeff = lerp(lerp(s0, s1, lerps.x), lerp(s2, s3, lerps.x), lerps.y);		o.vMaterial.rgb = texColor*shadowCoeff;	o.vMaterial.a = tex2D(colorMapSampler, i.vTex0).a;		return o;}technique Lambert{	pass	{		VertexShader = compile vs_3_0 VS_Lambert();		PixelShader = compile ps_3_0 PS_Lambert();		AlphaTestEnable = true;		AlphaFunc = GreaterEqual;		AlphaRef = 224;		CullMode = None;			}}


What could be wrong?? Thanks.
programci_84
programci_84
There's a strange thing I've seen in your shadow-map filler shader:
//in shadowMapVS()posH = mul(float4(posL, 1.0f), lightWorldViewProjection);	


If "lightWorldViewProjection" matrix has been made up by light's world, view and projection, your shadow map will be filled wrong. It must be product of light's view and projection.

Here's the corrected version of yours:
float4x4 lightViewProjection;float4x4 modelWorld;void shadowMapVS(float3 posL : POSITION0,                      out float4 posH : POSITION0,                      out float2 depth : TEXCOORD0){	float4x4 lightWorldViewProjection = mul (modelWorld, lightViewProjection);	posH = mul(float4(posL, 1.0f), lightWorldViewProjection);		depth = posH.zw;}float4 shadowMapPS(float2 depth : TEXCOORD0) : COLOR{	return depth.x / depth.y; // z / w}


hth.
-R

[Edited by - programci_84 on September 6, 2010 1:19:27 AM]
There's no "hard", and "the impossible" takes just a little time.
ayush3017
ayush3017
No no lightWorldViewProjection is actually the product of modelWorld and lightViewProjection.
Also If I change my shadow map size the shadow map projection also changes dramatically.
ayush3017
ayush3017
ALso here is my c++ code for setting render targets and clearing the buffer:
//positionZMap corresponds to the depth map from the lightPosition        .......        .......device->Clear(0, 0, D3DCLEAR_TARGET | D3DCLEAR_ZBUFFER | D3DCLEAR_STENCIL, 0x00000000, 1.f, 0);	device->BeginScene();		ClearTexture(materialMap, device, 0x00000000);	ClearTexture(normalMap, device, 0x00000000);	ClearTexture(positionXYMap, device, 0x00000000);	ClearTexture(positionZMap, device, 0x00000000);	SetRenderTarget(0, positionZMap, device);	DrawShadowedGeometry(device);	device->SetRenderTarget(0, NULL);	device->Clear(0, 0, D3DCLEAR_ZBUFFER | D3DCLEAR_STENCIL, 0x00000000, 1.f, 0);		SetRenderTarget(0, materialMap, device);	SetRenderTarget(1, normalMap, device);	SetRenderTarget(2, positionXYMap, device);		DrawDefferedGeometry(device, V);	IDirect3DSurface9* backBufferSurface;	device->GetBackBuffer(0, 0, D3DBACKBUFFER_TYPE_MONO, &backBufferSurface);	device->SetRenderTarget(0, backBufferSurface);	backBufferSurface->Release();	device->SetRenderTarget(1, NULL);	device->SetRenderTarget(2, NULL);        //Perform light calculations, ambientOcclusion and draw full screen quad        .......        .......void ClearTexture(LPDIRECT3DTEXTURE9 renderTargetTexture, LPDIRECT3DDEVICE9 device, D3DCOLOR xColor){	LPDIRECT3DSURFACE9 d3dSurface;	renderTargetTexture->GetSurfaceLevel(0, &d3dSurface);	device->ColorFill(d3dSurface, NULL, xColor);	d3dSurface->Release();}void SetRenderTarget(int renderTargetIndex, LPDIRECT3DTEXTURE9 renderTargetTexture, LPDIRECT3DDEVICE9 device){	LPDIRECT3DSURFACE9 d3dSurface;	renderTargetTexture->GetSurfaceLevel(0, &d3dSurface);	device->SetRenderTarget(renderTargetIndex, d3dSurface);	d3dSurface->Release();}
ayush3017
ayush3017
Ahh! There was a problem in world matrix. Now working fine.
Also now I have another problem:
I want shadows of alpha clipped objects like tree leaves, grills, etc. How do I do that???
Thanks.
programci_84
programci_84
I didn't try anything like that but I can give you two ideas:


First option is using Alpha Testing:
Before your shadow map filling shader run, you must play with some render states:
You must set,
* Both D3DRS_ALPHATESTENABLE and D3DRS_ALPHABLENDENABLE to TRUE,
* D3DRS_ALPHAFUNC to D3DCMP_GREATEREQUAL
* D3DRS_ALPHAREF to a value that can be found by try'n error method. That value can be obtained by a FloatToDWORD(f) macro:
#define FloatToDWORD(f, d)   d = *((DWORD*)&f)

For example, if you want the pixels that have an alpha value less than 0.6f to be killed, just get the correct value for D3DRS_ALPHAREF with a code-snippet like that;
float myAlphaKiller = 0.6f; DWORD killerDWValue;FloatToDWORD (myAlphaKiller, killerDWValue);yourD3DDevice->SetRenderState(D3DRS_ALPHAREF, killerDWValue);


Then begin your shader:
Pass your diffuse map textures to your shadow map filler shader. In pixel shader, sample that texture and take the .a (alpha) component. Then just output the same z/w value with that alpha value (e.g. return float4 (z/w, z/w, z/w, diffuseMapAlpha)).

And finally, don't forget to restore the render states above to their last values.

Another option is directly returning "1" from shadow map filler shader:
Sample diffuse texture with corresponding texture coordinates and take the .a (alpha) component. If that value is less than a floating point value that you determined (e.g. 0.6f like above), just return "1". Else, return the same z/w thing. This is the simplest, but not the cheapest way.

I cannot try it now but you can ;) Please tell me the results. If succeeds, I can improve my shadow mapping with that technique :D

hth.
-R
There's no "hard", and "the impossible" takes just a little time.
belfegor
belfegor
Quote:

I want shadows of alpha clipped objects like tree leaves, grills, etc. How do I do that???


in your depth pass ps
...float tAlpha = tex2D(colormap_samp, IN.TexCoord0).a;if(tAlpha < 1.0f){	discard;}return depth.x / depth.y;...
ayush3017
ayush3017
Thanks belfegor & programci_84, It worked like a charm and with talpha less than 0.6f in my alpha clipped objects.
@programci_84 : I tried your second method which is similar to belfegor's one. But however rather than passing "1" it is better to discard the pixel as pointed by belfegor. Because consider this image:

If I return 1 i.e. max depth then the opaque object will fail to cast shdaows as first the light samples the alpha clipped object.
AS now I have learnt basic shadow mapping and there is a lot of aliasing its right time for me to switch to VSM.

Topic Locked

This topic has been locked by a moderator. New replies are not allowed.

Sign in to reply to this topic.