Kyle Hayward's


Impostor Rendering - Reflections and Refractions


I have had multiple projects that have dealt with impostor rendering. The first is a tutorial developed in C# and XNA that I wrote for my blog [link]. This post focused on teaching the reader how to intersect a ray with a quad and to use this for accurately reflecting nearby objects. Using impostors for nearby reflections produces much more visually accurate results compared to standard environment mapping. Depth impostor reflections is an extension of billboard impostor. Instead of just using the color information of the impostor, depth information is also used which allows motion parallax in the reflection and correct intersection of the impostor with the reflector.

I worked on this project in conjunction with professor Popescu and graduate student, Paul Rosen from the graphics lab. Non-pinhole depth impostors make up for limitations in simple depth impostors. That is, depth impostors can only see samples captured from the reflector's point of view. But when looking at the reflector, some reflected rays may actually intersect an object but we won't register the intersection because a standard depth map doesn't contain this information. Single pole occlusion camera ( SPOC, a type of Non-pinhole ) depth maps capture silhouette samples that a pinhole camera would not see, allowing for near view independent reflections of nearby objects. Impostor construction allows for interactive dynamic scenes and does not need to be updated when the viewpoint moves. Other non-pinhole cameras, such as the Graph Camera can be used to construct environment impostors. Refracted rays were computed using an image-space refraction algorithm developed by Chris Wyman [link]. The non-pinhole impostors research project was developed in C++ and OpenGl/Cg. Source code samples for the project can be found in the samples section.

You can find the paper here:

The following images demonstrate the geometry captured by pinhole and non-pinhole cameras and the resulting reflections:

Planar Pinhole Depth Impostor                  SPOC Depth Impostor


  • Accurate, detailed reflections

  • Dynamic scenes

  • View independence

  • Motion parallax in reflection

  • Sample areas missed in pinhole depth maps

  • Ray type independence, handles reflected or refracted rays, just intersects a depth map

Screen Shots


Source Snippet

//Billboard Impostor reflections effect shader
float4 ImpostorReflectPS(VertexShaderOutput input) : COLOR0
    input.NormalW = normalize(input.NormalW);
    float3 fromEyeW = normalize(input.PosW - EyePos);
    //find the relflected ray by reflecting the fromEyeW vector across the normal
    float3 reflectedRay = reflect(fromEyeW, input.NormalW);
    float4 finalColor = texCUBE(EnvTex, reflectedRay);
    for(int i = 0; i < NumImpostors; i++)
        //take the dot product of the reflected ray and the normal of the impostor
        //quad to see how orthogonal the ray is to the quad
        //if a = 0, then the ray is parallel to the quad
        //if a = 1, then it is orthogonal to the quad
        float a = dot(-reflectedRay, Impostors[i].Normal);

        //if less than this, the ray is nearly or entirely parallel to the plane
        if(a > 0.001f)
            //we construct the vector from a point on the quad to the pixel position    
            float3 vec = Impostors[i].Vertex - input.PosW;
            //the signed distance from the pixel position to the quad is given by the negative
            //dot product of the quad normal and vec
            float b = -dot(vec, Impostors[i].Normal);

            //divide b by a to get our distance from the world pixel position
            float r = b / a;

            if(r >= 0)
                //Using the Ray equation: P(t) = S + tV. Where S is the orgin, V is the direction,
                //and t is the distance along V
                //We find the intersection by starting at the origin (PosW), and walk to the end
                //of the ray by multiplying the reflectedRay by r - the distance to the quad
                //and adding it to the orgin
                float3 intersection = input.PosW + r * reflectedRay;

                float2 texC;
                //project the intersection point with the WVP matrix used to render the quad
                float4 projIntersect = mul(float4(intersection, 1.0), Impostors[i].WVP);

                //perform the perspective divide and transform to NDC coordinates [0, 1]
                texC = projIntersect.xy / projIntersect.w * .5 + .5;

                //make sure the intersection is in the bounds of the image [0, 1]
                if((texC.x <= 1 && texC.y <= 1) &&
                   (texC.x >= 0 && texC.y >= 0))
                    float4 color = tex2D(ImpostorSamplers[i], float2(texC.x, 1-texC.y));
                    //blend based on the alpha of the sampled color
                    finalColor = lerp(finalColor, color, color.a);
    finalColor.rgb *= MaterialColor;
    return finalColor;


XNA demo for billboard impostors

XNA Game Studio 3.0 and DirectX is required to be installed in order to build and run the demo.