Description
This is a series of tutorials developed for my blog that covers using dual-paraboloid mapping for reflections and shadows [link1 link2 link3]. It was developed in C# and XNA. The method uses paraboloid mapping for environment reflections and omnidirectional shadows for point lighting. It captures the world in two textures(instead of six as with cube mapping) using a paraboloid warp. This allows for dynamic scenes, but trades resolution for speed. More information on the camera animation can be found here.
Features
Dynamic reflections and shadows
Variance shadow mapping
Requires less memory than cube maps
Fast to update
Screen Shots
Video
Source Snippet
//////////////////////////////////////////////////////////////////////////////////////////// // // Texture & Shadow + Build the Dual-Paraboloid map // //////////////////////////////////////////////////////////////////////////////////////////// OutputVS_DP BuildDP_VS(float3 posL : POSITION0, float3 normalL : NORMAL0, float2 tex0: TEXCOORD0) { // Zero out our output. OutputVS_DP outVS = (OutputVS_DP)0; // Pass on texture coordinates to be interpolated in rasterization. outVS.tex0 = posL; outVS.pos = mul(float4(posL, 1.0f), World).xyz; //Render with the Dual-Paraboloid distortion // Transform to homogeneous clip space. outVS.posH = mul(float4(posL, 1.0f), WorldViewProj); outVS.posH.z = outVS.posH.z * Direction; float L = length( outVS.posH.xyz ); // determine the distance between (0,0,0) and the vertex outVS.posH = outVS.posH / L; // divide the vertex position by the distance outVS.z = outVS.posH.z; // remember which hemisphere the vertex is in outVS.posH.z = outVS.posH.z + 1; // add the reflected vector to find the normal vector outVS.posH.xy = outVS.posH.xy / outVS.posH.z; // divide xy coords by the new z-value outVS.posH.z = (L - NearPlane) / (FarPlane - NearPlane);// scale the depth to [0, 1] outVS.posH.w = 1; // set w to 1 so there is no w divide // Done--return the output. return outVS; } float4 BuildDP_PS(OutputVS_DP input) : COLOR { clip(input.z); float4 texColor = texCUBE(EnvTex, input.tex0); //transform to paraboloid basis float3 pos = mul(float4(input.pos, 1.0f), LightView); float L = length(pos); float alpha = .5f + pos.z / LightAttenuation; //calculate the mapping for the front and back hemispheres float3 P0 = CalcDPMDistortFront(pos, L, LightAttenuation); float3 P1 = CalcDPMDistortBack(pos, L, LightAttenuation); //Decide which hemisphere we're in based on alpha, and return the moments float3 moments = CalcDPMVSMMoments(ShadowFrontS, ShadowBackS, P0, P1, alpha); float shadow = CalcVSMShadow(moments.xy, moments.z); float lit_factor = (moments.z <= moments[0]); texColor.xyz *= max(lit_factor, shadow + .2f); return texColor; }
Downloads
Binary: XNA 3.0 Redist and .Net 3.5 Framework is required to be installed in order to run the demo.
Source: XNA Game Studio 3.0 and DirectX is required to be installed in order to build and run the source.