vK 3 1 RON
vK 3 1 RON

Reputation: 115

Shadow Mapping - Space Transformations are going bad

I am currently studying shadow mapping, and my biggest issue right now is the transformations between spaces. This is my current working theory/steps.

Pass 1:

Pass 2:

Shader Code

Pass1:

PS_INPUT vs(VS_INPUT input) {
  output.pos = mul(input.vPos, mvp);
  output.cameraDepth = output.pos.zw;
  ..
  float4 vPosInLight = mul(input.vPos, m);
  vPosInLight = mul(vPosInLight, light.viewProj);
  output.lightDepth = vPosInLight.zw;
}

PS_OUTPUT ps(PS_INPUT input){

  float cameraDepth = input.cameraDepth.x / input.cameraDepth.y;
  //Bundle cameraDepth in alpha channel of a normal map.
  output.normal = float4(input.normal, cameraDepth);
  
  //4 Lights in total -- although only 1 is active right now. Going to use r/g/b/a for each light depth. 
  output.lightDepths.r = input.lightDepth.x / input.lightDepth.y;
}

Pass 2 (Screen Quad):

float4 ps(PS_INPUT input) : SV_TARGET{
  float4 pixelPosView = depthToViewSpace(input.texCoord);
  ..
  float4 pixelPosWorld = mul(pixelPosView, invV);
  float4 pixelPosLight = mul(pixelPosWorld, light.viewProj);
  float shadow = shadowCalc(pixelPosLight);

  //For testing / visualisation
  return float4(shadow,shadow,shadow,1);
}

float4 depthToViewSpace(float2 xy) {
  //Get pixel depth from camera by sampling current texcoord.
  //Extract the alpha channel as this holds the depth value.
  //Then, transform from [0..1] to [-1..1]
  float z = (_normal.Sample(_sampler, xy).a) * 2 - 1;
  float x = xy.x * 2 - 1;
  float y = (1 - xy.y) * 2 - 1;
  float4 vProjPos = float4(x, y, z, 1.0f);
  float4 vPositionVS = mul(vProjPos, invP);
  vPositionVS = float4(vPositionVS.xyz / vPositionVS.w,1);
  return vPositionVS;
}

float shadowCalc(float4 pixelPosL) {
  //Transform pixelPosLight from [-1..1] to [0..1]
  float3 projCoords = (pixelPosL.xyz / pixelPosL.w) * 0.5 + 0.5;
  float closestDepth = _lightDepths.Sample(_sampler, projCoords.xy).r;   
  float currentDepth = projCoords.z;
  return currentDepth > closestDepth; //Supposed to have bias, but for now I just want shadows working haha
}

CPP Matrices

// (Position, LookAtPos, UpDir)
auto lightView = XMMatrixLookAtLH(XMLoadFloat4(&pos4), XMVectorSet(0,0,0,1), XMVectorSet(0,1,0,0));

// (FOV, AspectRatio (1000/680), NEAR, FAR)
auto lightProj = XMMatrixPerspectiveFovLH(1.57f , 1.47f, 0.01f, 10.0f);
XMStoreFloat4x4(&_cLightBuffer.light.viewProj, XMMatrixTranspose(XMMatrixMultiply(lightView, lightProj)));

Current Outputs

White signifies that a shadow should be projected there. Black indicates no shadow.

CameraPos (0, 2.5, -2) CameraLookAt (0, 0, 0) CameraFOV (1.57) CameraNear (0.01) CameraFar (10.0)

LightPos (0, 2.5, -2) LightLookAt (0, 0, 0) LightFOV (1.57) LightNear (0.01) LightFar (10.0)

If I change the CameraPosition to be (0, 2.5, 2), basically just flipped on the Z axis, this is the result.

Obviously a shadow shouldn't change its projection depending on where the observer is, so I think I'm making a mistake with the invV. But I really don't know for sure. I've debugged the light's projView matrix, and the values seem correct - going from CPU to GPU. It's also entirely possible I've misunderstood some theory along the way because this is quite a tricky technique for me.

Upvotes: 1

Views: 155

Answers (1)

vK 3 1 RON
vK 3 1 RON

Reputation: 115

Aha! Found my problem. It was a silly mistake, I was calculating the depth of pixels from each light, but storing them in a texture that was based on the view of the camera. The following image should explain my mistake better than I can with words.

enter image description here

For future reference, the solution I decided was to scrap my idea for storing light depths in texture channels. Instead, I basically make a new pass for each light, and bind a unique depth-stencil texture to render the geometry to. When I want to do light calculations, I bind each of the depth textures to a shader resource slot and go from there. Obviously this doesn't scale well with many lights, but for my student project where I'm only required to have 2 shadow casters, it suffices.

_context->DrawIndexed(indexCount, 0, 0); //Draw to regular render target 
_sunlight->use(1, _context); //Use sunlight shader (basically just runs a Vertex Shader & Null Pixel shader so depth can be written to depth map)
_sunlight->bindDSVSetNullRenderTarget(_context);
_context->DrawIndexed(indexCount, 0, 0); //Draw to sunlight depth target

bindDSVSetNullRenderTarget(ctx){
    ID3D11RenderTargetView* nullrv = { nullptr };
    ctx->OMSetRenderTargets(1, &nullrv, _sunlightDepthStencilView);
}
//The purpose of setting a null render target before doing the draw call is 
//that a draw call with only a depth target bound is much faster. 
//(At least I believe so, from my reading online)

Upvotes: 2

Related Questions