I'm just learning the tile-based deferred shading, and found the great article by Andrew Lauritzen. And I also checked the source code, it's really helpful. But i still have some confusion about the code for tile frustum construction:
// Work out scale/bias from [0, 1]
float2 tileScale = float2(mFramebufferDimensions.xy) * rcp(float(2 * COMPUTE_SHADER_TILE_GROUP_DIM));
float2 tileBias = tileScale - float2(groupId.xy);
// Now work out composite projection matrix
// Relevant matrix columns for this tile frusta
float4 c1 = float4(mCameraProj._11 * tileScale.x, 0.0f, tileBias.x, 0.0f);
float4 c2 = float4(0.0f, -mCameraProj._22 * tileScale.y, tileBias.y, 0.0f);
float4 c4 = float4(0.0f, 0.0f, 1.0f, 0.0f);
// Derive frustum planes
float4 frustumPlanes[6];
// Sides
frustumPlanes[0] = c4 - c1;
frustumPlanes[1] = c4 + c1;
frustumPlanes[2] = c4 - c2;
frustumPlanes[3] = c4 + c2;
// Near/far
frustumPlanes[4] = float4(0.0f, 0.0f, 1.0f, -minTileZ);
frustumPlanes[5] = float4(0.0f, 0.0f, -1.0f, maxTileZ);
// Normalize frustum planes (near/far already normalized)
[unroll] for (uint i = 0; i < 4; ++i) {
frustumPlanes[i] *= rcp(length(frustumPlanes[i].xyz));
}
I'm sure it's the Clip Space Approach for derive the Frustum, explained here in detail.
The really confusion part is the code for building the tile project matrix.
// Work out scale/bias from [0, 1]
float2 tileScale = float2(mFramebufferDimensions.xy) * rcp(float(2 * COMPUTE_SHADER_TILE_GROUP_DIM));
float2 tileBias = tileScale - float2(groupId.xy);
// Now work out composite projection matrix
// Relevant matrix columns for this tile frusta
float4 c1 = float4(mCameraProj._11 * tileScale.x, 0.0f, tileBias.x, 0.0f);
float4 c2 = float4(0.0f, -mCameraProj._22 * tileScale.y, tileBias.y, 0.0f);
float4 c4 = float4(0.0f, 0.0f, 1.0f, 0.0f);
mFramebufferDimensions is the current view port dimension. COMPUTE_SHADER_TILE_GROUP_DIM is the tile dimension.
I guess they did the transformation in the NDS space, but i can't figure out the derivation.
and i think the correct scale factor should be:
float2 tileScale = float2(mFramebufferDimensions.xy) * rcp(float(COMPUTE_SHADER_TILE_GROUP_DIM));
// instead of
// float2 tileScale = float2(mFramebufferDimensions.xy) * rcp(float(2 * COMPUTE_SHADER_TILE_GROUP_DIM));
And I can't figure out the exact mean of tileBias.
I'm really confused.
PS:
I also found another method for derive the tile frustum in the AMD Forward+: [http://developer.amd.com/tools-and-sdks/graphics-development/amd-radeon-sdk/] example.
calculate the four corner in near plane of the new tile frustum in Normalized-device-space.
transform them back to the view space with the inverse projection matrix.
build the tile frustum with the four corner and origin point use the Geometric Approach described here: [http://www.lighthouse3d.com/tutorials/view-frustum-culling/geometric-approach-extracting-the-planes/] in detail.
float4 frustumEqn[4];
// construct frustum for this tile
uint pxm = TILE_RES*groupIdx.x;
uint pym = TILE_RES*groupIdx.y;
uint pxp = TILE_RES*(groupIdx.x+1);
uint pyp = TILE_RES*(groupIdx.y+1);
uint uWindowWidthEvenlyDivisibleByTileRes = TILE_RES*GetNumTilesX();
uint uWindowHeightEvenlyDivisibleByTileRes = TILE_RES*GetNumTilesY();
// four corners of the tile, clockwise from top-left
float4 frustum[4];
frustum[0] = ConvertProjToView( float4( pxm/(float)uWindowWidthEvenlyDivisibleByTileRes*2.f-1.f, (uWindowHeightEvenlyDivisibleByTileRes-pym)/(float)uWindowHeightEvenlyDivisibleByTileRes*2.f-1.f,1.f,1.f) );
frustum[1] = ConvertProjToView( float4( pxp/(float)uWindowWidthEvenlyDivisibleByTileRes*2.f-1.f, (uWindowHeightEvenlyDivisibleByTileRes-pym)/(float)uWindowHeightEvenlyDivisibleByTileRes*2.f-1.f,1.f,1.f) );
frustum[2] = ConvertProjToView( float4( pxp/(float)uWindowWidthEvenlyDivisibleByTileRes*2.f-1.f, (uWindowHeightEvenlyDivisibleByTileRes-pyp)/(float)uWindowHeightEvenlyDivisibleByTileRes*2.f-1.f,1.f,1.f) );
frustum[3] = ConvertProjToView( float4( pxm/(float)uWindowWidthEvenlyDivisibleByTileRes*2.f-1.f, (uWindowHeightEvenlyDivisibleByTileRes-pyp)/(float)uWindowHeightEvenlyDivisibleByTileRes*2.f-1.f,1.f,1.f) );
// create plane equations for the four sides of the frustum,
// with the positive half-space outside the frustum (and remember,
// view space is left handed, so use the left-hand rule to determine
// cross product direction)
for(uint i=0; i<4; i++)
frustumEqn[i] = CreatePlaneEquation( frustum[i], frustum[(i+1)&3] );
Hi all,
I have got another problem about the code for reconstruct view space position from depth.
// Compute screen/clip-space position and neighbour positions
// NOTE: Mind DX11 viewport transform and pixel center!
// NOTE: This offset can actually be precomputed on the CPU but it's actually slower to read it from
// a constant buffer than to just recompute it.
float2 screenPixelOffset = float2(2.0f, -2.0f) / gbufferDim;
float2 positionScreen = (float2(positionViewport.xy) + 0.5f) * screenPixelOffset.xy + float2(-1.0f, 1.0f);
float2 positionScreenX = positionScreen + float2(screenPixelOffset.x, 0.0f);
float2 positionScreenY = positionScreen + float2(0.0f, screenPixelOffset.y);
positionViewport is the SV_Position.
I known it's means transform from viewport space [0 - ViewportDimension] to the clip-space [-1 - 1]. But why need add 0.5f offset to the positionViewport before the transformation?
Thank you very much.