IV. ANÁLISIS DE CONTEXTO Y DE LA SITUACIÓN ACTUAL
IV.1. CONTEXTO
IV.1.1. Cuestiones Internas y Externas
For face partitioning we compute a partition index or mask using a cube map with a single texel in each face. In the texel of each face we store the index or mask of the partition corresponding to that face. We perform the cube map lookup in the camera’s post-perspective
space using the method described by Kozlov (2004). We do not store the shadow maps directly in a cube map because cube maps do not currently support non-square textures of differing resolution or the necessary logarithmic transformation. We also store a flag in each face of the cube map indicating whether the face corresponds to a side face or an end face. This flag is needed in order to determine whether or not to apply the logarithmic transformation.
If side face splitting is used to handle shearing, we store the partition indices for both halves of the face in the cube map. The second index remains unused for unsplit faces. To choose the proper index we test the world space position of the fragment against the splitting plane for the face. We adopt the convention that the first index corresponds to the negative side of the plane. For faces without a split we use the plane equation (0,0,0,−1) which is guaranteed to always give a negative result. To save computation in the fragment program we compute the distance of vertex positions from the split planes in a vertex program and interpolate. A code fragment using this technique is shown in Figure 6.2.
6.7
Logarithmic rasterization.
The LogPSM parameterization requires logarithmic rasterization. Logarithmic rasterization causes planar primitives to become curved. The logarithmic transformation could be com- puted on vertices in a vertex program, but because current GPUs only support projective transformations, the primitives would still be decomposed into planar triangles when ren- dered. When thefe/ne ratio is large, a very fine tessellation of the scene would be required in order to avoid error with this approach. We currently simulate logarithmic rasterization by performing brute-force rasterization in a fragment program. We first render the scene usingMlwith the viewport set to the range [0,0]×[1,1] and readback clipped triangles using the OpenGL feedback mechanism. This gives us the triangles transformed by Fp(u, v). We compute the edge equations and depth interpolation equation for each triangle. We then transform the vertices of each triangle by the logarithmic part of Flp and render a bounding quad. We use the fragment program in Figure 6.3 to invert the logarithmic portion of Flp,t on each fragment position. We then compare this position against the linear triangle edge equations and discard fragments that fall outside the triangle. The equation for the inverse
float computeFacePartition(
uniform float4 lPosPP, // location of light (post-perspective)
uniform float3 invD0, // 1/(d0,d1,d2) dn is distance from lPos to uniform float3 invD1, // 1/(d3,d4,d5) cube face n
varying float4 posPP, // location of fragment (post-perspective) varying float4 splitDist,// distance to split planes for side faces samplerCUBE faceTex;
{
// Compute texture coordinate for cube map lookup. // From "PSM Care and Feeding" [Kozlov04]
float3 cubeTexCoord;
float3 v = posPP.xyz / posPP.w; float3 p = lPosPP;
float3 dir = v - p;
float3 maxTmp = max(dir * invD0, dir * invD1); float a = max( max(maxTmp.x, maxTmp.y), maxTmp.z); cubeTexCoord = p + dir/a;
// compute partition from data stored in cube map face float4 faceData = texCUBE( faceTex, cubeTexCoord ); float4 mask0_3 = unpack_4ubyte(faceData.r);
float4 faceIndex = faceData.g; return faceIndex.g
+ dot( float4(splitDist > 0), mask0_3 ) * 4; }
Figure 6.2: CG code used for face partitioning. The side faces are assigned indices 0–3. If a face n is split, its other half is assigned index n+ 4. The end faces are assigned indices 6 and 7. The cube texture stores the index of each face and a mask corresponding to the face. All channels of the mask are 0 for end faces. Note that light positions at infinity in post-perspective space must be approximated as a distant point light for Kozlov’s cube map texture lookup code (Kozlov, 2004) to work properly.
of the logarithmic portionFlp,t(v) is given by:
v=d0ed1t+d2 (6.2) d0 = 1 c1(y1−y0) d1 = 1 c0 d2 =− c1y0+c2 c1(y1−y0) .
void logRasterize(
varying float3 stzPos, // 2D shadow map position (s,t) + depth (z) varying float3 edgeEq0,
varying float3 edgeEq1, varying float3 edgeEq2, varying float3 depthEq, uniform float d[3], out float depth : DEPTH ) {
// invert transform float3 p = stzPos;
p.y = d[0]*exp( d[1]*p.y )+d[2];
// test against edges
if( dot( p, edgeEq0 ) < 0 || dot( p, edgeEq1 ) < 0 || dot( p, edgeEq2 ) < 0 ) discard;
// compute depth
depth = dot(p, depthEq); }
Figure 6.3: CG code used to simulate logarithmic rasterization in a fragment program.
With our unoptimized simulator we observe frame rates of 2–3 frames per second on a PC with a GeForce 6800GT graphics card and a 2.8 GHz processor. Most of that time is spent in computing the bounding quads. GPUs that support geometry shaders can probably be used to create the bounding quads for the warped triangles much more efficiently, but we have not yet implemented this. Even with a geometry shader, however, logarithmic rasterization would likely be considerably slower than linear shadow map rasterization. GPUs have a number of optimizations which are disabled when a fragment program outputs depth information, such as z-culling and higher rasterization rates for depth-only rendering.
In the following chapter we show how logarithmic rasterization can be implemented on graphics hardware in order to render LogPSMs at rates comparable to other algorithms based on linear rasterization.
1
s
t
w
m
Figure 6.4: Procedural grid shader. The heavy lines represent texel boundaries in texture space. A pointm lies on a grid line if the fractional components of its sand tcoordinates are less than the grid line widthw.