Further Improvements in Imposter Transitioning

One particular problem in transitioning between imposters (billboards) and full meshes is the lack of lighting detail in the imposter. Consider that a model mesh has a Normal (a vector perpendicular to the surface of the triangle) for every point on the mesh from which to calculate the effect of the lighting, this means that the model has subtle differences in reflected lighting intensity across the model, giving a stronger 3D effect. An imposter however only has four points making up its quad. When its painted the light interacts with the four Normals to create a completely flat reflection and light intensity across the whole billboard. The transition between a flat billboard and a fully lit model is very obvious.

How do you light an imposter when its only got four possible normals ?

A nice way to avoid this is to record two different kinds of billboard – a colour based one, and one which encodes the Normal for each pixel into the RGB values of another texture. With multi-surface rendering you can actually create both these in a single pass. The Normal imposter sheet essentially encodes the direction that the pixel was “facing” when the model was rendered.

SmartTree.Texture.Imposter ImposterNormal
Colour based imposter sheet Normal based imposter sheet

The HLSL to write the normals is simple;

// Normal map pixel shader
PixelToSurface normalMapPixelShader(VertexToPixel ps_input)
    PixelToSurface output;
    float4 textureColor = tex2D(TextureSampler0, ps_input.TextureUV);
    clip(textureColor.a - Param_AlphaClip);

    // Translate from -1 to 1 to 0->1
    float3 normal = normalize(ps_input.Normal.xyz);
    normal /= 2; // Range of =-0.5 => 0.5
    normal += 0.5f;
    output.PixelColor = float4(normal,1);
    return output;


When rendering the imposter, pass both the imposter colour sheet and the imposter normal sheet as separate textures, then for each pixel to be rendered render the colour picked from the colour sheet, then use pick the normal from the normal sheet. Use standard colour/light/normal calculations to render the pixel with the appropriate reflection+strength.

Remember when picking the normal from the normal sheet to reverse the above calculation before using it for lighting;

float3 normal = (2 * (normalSample)) – 1.0f;

The results are;

TreeModel TreeBillboardWithNormals TreeBillboardWithoutNormals
Fully lit model Lit Imposter Unlit imposter

For a whole landscape this allows imposters and models to be lit correctly to provide a more seamless interface between the two.