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.
float2 square =
PS_INPUT VS(VS_INPUT input)
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];
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.