Hi all,
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 for me 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 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 think the tileScale is applied in the view space, and tileBias is in the normalized device space (tile projection matrix).
After days experiments I still can't figure it out by myself.
Please give me some help!
I'm really confused.
Update:
I also found another method for deriving the tile frustum in the AMD Forward+ example.
1. calculate the four corner position X, Y on the near plane of the tile frustum in the normalized-device-space from the view port space position.
Note, we are using inverted 32-bit float depth for better precision, so near and far is reversed, and the z value in near plane is 1.f instead 0.f.
2. transform them back to the view space by multiply the inverse projection matrix.
3. build the tile frustum with the four corner and origin point in the view space, use the Geometric Approach described here in detail.
float4 frustumEqn[4];// construct frustum for this tile
// 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] );
It's much easy to understand, but a little bit slower.
Thank you very much. (Sorry for my poor English)