In this example, I am rendering each circle as a simple quadrilateral. The real magic happens in the pixel shader.
Let's take a look at the shader code.
cbuffer shapes
{
float2 square[] =
{
float2(-1.0f, -1.0f),
float2(-1.0f, 1.0f),
float2(1.0f, -1.0f),
float2(1.0f, 1.0f)
};
};
PS_INPUT VS(VS_INPUT input)
{
PS_INPUT output;
float3 vpos = float3(0.4f * square[input.vertid], 0.0f) + 2.6f * input.position;
output.position = mul(float4(vpos, 1.0f), mul(World, Projection));
output.color = input.color;
output.qpos = 1.1f * square[input.vertid];
return output;
}
float4 PS(PS_INPUT input) : SV_Target
{
float dist = length(input.qpos);
float fw = 0.8f * fwidth(dist);
float circ = smoothstep(fw, -fw, dist - 1.0f);
return float4(input.color, circ);
}
The utility of imposter shapes shows through when looking at the vertex shader. The vertex shader need only run 4 times per circle. As you can see, I am not only transforming the square's vertices, I am also passing the raw vertices over to the pixel shader. The pixel shader uses this to measure the distance between the center of the square and each fragment in order to determine which fragments should be visible and which should be blended away.
Why use imposters? Consider a visualization application which needs to render thousands of circles very quickly. This method allows for the rendering of efficient, high-quality circles. In a future post, I will show how this method can be adapted to rendering spheres as quadrilaterals.
No comments:
Post a Comment