1
\$\begingroup\$

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.

  1. calculate the four corner in near plane of the new tile frustum in Normalized-device-space.

  2. transform them back to the view space with the inverse projection matrix.

  3. 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.

\$\endgroup\$
3
  • \$\begingroup\$ You have a lot of questions about little details in the math, like why there is a factor of 2 here or a 0.5 offset there. I think it would help if you sit down with some graph paper and a calculator and work out some of the numbers yourself, going through each line of the code, and drawing diagrams to help visualize what's going on. For instance, take a simple case like a 90 degree wide, 1:1 overall frustum and try to calculate the frustum for tile (0, 0). \$\endgroup\$ Commented Oct 16, 2013 at 4:48
  • \$\begingroup\$ Hi, thank you for your reply. I already did some experiment as you suggest before asking, and I known how to calculate the frustum from the projection matrix. I also mentioned another method, it's more straightforward and easier to understand. The first method do the calculate in the NDS space, and is not so easy. The tileScale should be 'float2(mFramebufferDimensions.xy) * rcp(float(COMPUTE_SHADER_TILE_GROUP_DIM))', just give some tips. \$\endgroup\$
    – dongsaturn
    Commented Oct 17, 2013 at 1:37
  • \$\begingroup\$ About my second question it's actually not a little math issuse. It's relevance to the DX11 rasterization rule. Please check here for detail. \$\endgroup\$
    – dongsaturn
    Commented Oct 17, 2013 at 1:47

0

You must log in to answer this question.

Browse other questions tagged .