I'm implementing single-pass surface voxelization (via Geometry Shader) for Voxel Cone Tracing (VCT),
and after some debugging I've discovered that I have to insert out-of-bounds checks into the pixel shader to avoid voxelizing geometry which is outside the voxel grid:
void main_PS_VoxelTerrain_DLoD( VSOutput pixelInput )
{
const float3 posInVoxelGrid =
(pixelInput.position_world - g_vxgi_voxel_radiance_grid_min_corner_world) * g_vxgi_inverse_voxel_size_world;
const uint color_encoded = packR8G8B8A8( float4( posInVoxelGrid, 1 ) );
const int3 writecoord = (int3) floor( posInVoxelGrid );
const uint writeIndex1D = flattenIndex3D( (uint3)writecoord, (uint3)g_vxgi_voxel_radiance_grid_resolution_int );
// HACK:
bool inBounds =
writecoord.x >= 0 && writecoord.x < g_vxgi_voxel_radiance_grid_resolution_int &&
writecoord.y >= 0 && writecoord.y < g_vxgi_voxel_radiance_grid_resolution_int &&
writecoord.z >= 0 && writecoord.z < g_vxgi_voxel_radiance_grid_resolution_int ;
if( inBounds )
{
rwsb_voxelGrid[writeIndex1D] = color_encoded;
}
else
{
rwsb_voxelGrid[writeIndex1D] = 0xFF0000FF; //RED, ALPHA
}
}
Why is this check needed, and how can I avoid it? Shouldn't Direct3D automatically clip the pixels falling outside the viewport? (I tried to ensure that out-of-bounds pixels are clipped in the geometry shader and I also enable depthClip in rasterizer, but it doesn't work.)
Here's a picture illustrating the problem (extraneous voxels are highlighted with red):
And here the full HLSL code of the voxelization shader:
int maxIndex3( in float3 v )
{
return ( v.x > v.y )
? ( ( v.x > v.z ) ? 0 : 2 )
: ( ( v.y > v.z ) ? 1 : 2 );
}
cbuffer Uniforms
{
row_major float4x4 u_local_to_world_space;
};
RWStructuredBuffer< uint > rwsb_voxelGrid;
//-----------------------------------------------------------------------------
struct VSOutput
{
float3 position_world : Position; // world-space position
float3 normalWS : Normal0; // world-space normal
};
VSOutput main_VS_VoxelTerrain_DLoD( in Vertex_DLOD vertexInput )
{
VSOutput vertexOutput;
vertexOutput.position_world = mul( u_local_to_world_space, float4( vertexInput.localPosition, 1.0f ) ).xyz;
vertexOutput.normalWS = Dir_Local_To_World( vertexInput.localNormal );
return vertexOutput;
}
//-----------------------------------------------------------------------------
struct GSOutput
{
float4 positionNDC : SV_Position; // clip-space position
float3 position_world : Position; // world-space position
float3 normalWS : Normal0; // world-space normal
};
[maxvertexcount(3)]
void main_GS(
triangle VSOutput input[3],
inout TriangleStream< GSOutput > outputStream
)
{
// Calculate the dominant direction of the surface normal.
const int axis = maxIndex3( abs( input[0].normalWS + input[1].normalWS + input[2].normalWS ) );
// Project the triangle in the dominant direction for rasterization,
// but not for lighting.
[unroll]
for( uint i = 0; i < 3; i++ )
{
GSOutput output;
output.position_world = input[i].position_world;
const float3 position01_in_voxel_grid =
((input[i].position_world - g_vxgi_voxel_radiance_grid_min_corner_world) * g_vxgi_inverse_voxel_size_world) * g_vxgi_voxel_radiance_grid_inverse_resolution;
output.positionNDC.xyz = position01_in_voxel_grid * 2 - 1;
[flatten]
switch (axis) {
case 0:
output.positionNDC.xy = output.positionNDC.yz;
break;
case 1:
output.positionNDC.xy = output.positionNDC.zx;
break;
default:
break;
}
output.positionNDC.zw = 1;
output.normalWS = input[i].normalWS;
outputStream.Append(output);
}
outputStream.RestartStrip();
}
//-----------------------------------------------------------------------------
void main_PS_VoxelTerrain_DLoD( VSOutput pixelInput )
{
const float3 posInVoxelGrid =
(pixelInput.position_world - g_vxgi_voxel_radiance_grid_min_corner_world) * g_vxgi_inverse_voxel_size_world;
const uint color_encoded = packR8G8B8A8( float4( posInVoxelGrid, 1 ) );
const int3 writecoord = (int3) floor( posInVoxelGrid );
const uint writeIndex1D = flattenIndex3D( (uint3)writecoord, (uint3)g_vxgi_voxel_radiance_grid_resolution_int );
// HACK:
bool inBounds =
writecoord.x >= 0 && writecoord.x < g_vxgi_voxel_radiance_grid_resolution_int &&
writecoord.y >= 0 && writecoord.y < g_vxgi_voxel_radiance_grid_resolution_int &&
writecoord.z >= 0 && writecoord.z < g_vxgi_voxel_radiance_grid_resolution_int ;
if( inBounds )
{
rwsb_voxelGrid[writeIndex1D] = color_encoded;
}
else
{
rwsb_voxelGrid[writeIndex1D] = 0xFF0000FF; //RED, ALPHA
}
}