Description
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: http://www.cs.purdue.edu/cgvlab/papers/popescu/popescuNPI_CGA11.pdf
The following images demonstrate the geometry
captured by pinhole and non-pinhole cameras and the resulting
reflections:
Planar Pinhole Depth Impostor
SPOC Depth Impostor
Features
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
Video
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; }
Downloads
XNA Game Studio 3.0 and DirectX is required to be installed in order to build and run the demo.