540 likes | 561 Views
Z-buffer. Kurt Akeley CS248 Lecture 13 6 November 2007 http://graphics.stanford.edu/courses/cs248-07/. Painter’s algorithm. Initial OpenGL behavior is “painter’s algorithm” Fragment color values overwrite sample color values Sequence is guaranteed
E N D
Z-buffer Kurt Akeley CS248 Lecture 13 6 November 2007 http://graphics.stanford.edu/courses/cs248-07/
Painter’s algorithm Initial OpenGL behavior is “painter’s algorithm” • Fragment color values overwrite sample color values • Sequence is guaranteed • Result at each sample is the color of the last fragment Painter’s algorithm (ordered rendering) is useful: • 2-D rendering (especially pre-filter triangle antialiasing) • Volumetric rendering (future lecture) But painter’s algorithm puts a huge burden on the application for general 3-D rendering • Want “nearest” geometry to be visible • But sorting in the application is difficult, sometimes very difficult …
Sorting difficulties Topological Interpenetrating
Z-buffer Vertex operations • Project ze to zc Primitive operations • Homogenize • Z viewport (glDepthRange) generates zw Rasterization • Assign a zw value to each fragment sample Fragment / framebuffer operations • Conditionally replace pixel sample values (color and depth) with fragment sample values • Depth comparison specified by glDepthFunc • Typically replace if fragment is nearer to viewer than pixel
Naming conventions Depth buffer and Z-buffer have the same meaning OpenGL uses “depth” rather than “z” • Emphasized notion of depth attribute rather than 3-D window coordinates • Resulted in nice name syntax I’ve reverted to “z” • I prefer to think of window coordinates as a 3-D system • Z-buffer is in more common usage (maybe)
Outline Z-buffering example Culling Other approaches to the hidden surface problem Numerical issues Selected topics
Walk through the vertex pipeline Application Z-buffering enabled Multi-sample antialiasing enabled This is simple to setup: Vertex assembly Vertex operations Primitive assembly glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGBA | GLUT_DEPTH | GLUT_MULTISAMPLE); … glEnable(GL_DEPTH_TEST);glEnable(GL_MULTISAMPLE); Primitive operations Rasterization Fragment operations Framebuffer Display
Vertex operations Application Vertex assembly Vertex operations Primitive assembly Primitive operations Rasterization Fragment operations Framebuffer Display
Perspective projection Specify a frustum: n and f are distances, not coordinates. Both must be positive. glMatrixMode(GL_PROJECTION); glLoadIdentity(); glFrustum(l,r,b,t,n,f);
The frustum ye zc is projected xe Assuming we is 1.0 ze
Primitive operations Application Vertex assembly Vertex operations Primitive assembly Primitive operations Rasterization Fragment operations Framebuffer Display
Homogenization of Z ye xe ze
Viewport transformation Transform the [-1,1] range device coordinates to window coordinates: glViewport(int l, int b, int w, int h); glDepthRange(double n, double f); glViewport(0, 0, 10, 6); glDepthRange(0.0, 1.0); One pixel
Z arithmetic This will be a problem!
Rasterization Application Vertex assembly Vertex operations Primitive assembly Primitive operations Rasterization Fragment operations Framebuffer Display
a2 a0 a1 Perspective-correct attribute evaluation (x1 y1 w1 f1) (x y f ) All w’s are wc’s (x0 y0 w0 f0) (x2 y2 w2 f2)
a2 a0 a1 Perspective-correct zw evaluation (x1 y1 zw1) zw was projected during homogenization (x y zw) (x0 y0 zw0) (x2 y2 zw2)
Perspective-correct zw evaluation zw is not linearly related to ze • We’ll plot the relationship later when considering precision But this doesn’t matter for hidden-surface calculations • zw has been projected • So it interpolates linearly • So comparison yields the nearest fragment sample • We don’t care about the actual distances
Mask generation Mask bits Two triangles, first red, second green: 7 6 5 4 3 2 1 0 struct { short x,y; bool mask[n]; // 01110110 float depth[n]; // as required float r,g,b,a; // 1,0,0,1} fragment; struct { short x,y; bool mask[n]; // 00110110 float depth[n]; // as required float r,g,b,a; // 0,1,0,1} fragment; Single Pixel
Fragment / pixel operations Application Vertex assembly Vertex operations Primitive assembly Primitive operations Rasterization Fragment operations Framebuffer Display
Fragment / pixel operations All samples initially black, farthest: 7 6 5 4 3 2 1 0 struct { short x,y; bool mask[n]; // 01110110 float depth[n]; // as required float r,g,b,a; // 1,0,0,1} fragment; Resolved color5/8, 0, 0, 1 struct { short x,y; bool mask[n]; // 00110110 float depth[n]; // as required float r,g,b,a; // 0,1,0,1} fragment; Resolved color3/8, 1/4, 0, 1
Multi-sample antialiasing Requires a lot of state: • Fragment • Mask • Full depth value for each sample (but this can be optimized) • Pixel • Full color and depth values for each sample • Additional front and back resolved color values But it works really well: • Handles arbitrary fragment sequences • Handles interpenetrating triangles • Satisfies rule 1 (no frame-to-frame discontinuities) • Has no unpredictable or ill-mannered behaviors Other less expensive (and more clever) antialiasing approaches are not so well behaved …
Culling Culling: elimination of primitives or fragments that will not affect the final image Andrew discussed culling last week • Frustum culling • Portal culling (a special case of occlusion culling) Basic idea: eliminate work as early in the sequence as possible • But with a reasonable pay-off for the effort
Culling Application Frustum culling Occlusion culling Portal culling Vertex assembly Vertex operations Primitive assembly Back-face culling Primitive operations HierarchicalZ culling Rasterization Fragment operations Framebuffer Display
Occlusion culling Active research topic • Difficult for sparse scenes (e.g., oil refinery) Introduces notion of “depth complexity” • Average number of fragments generated per pixel Portal culling is a special case of occlusion culling OpenGL includes acceleration support • “reduction” query (any pixel samples modified?) glBeginQuery(GL_SAMPLES_PASSED, id); // render proxy object (e.g., a bounding cuboid) glEndQuery(GL_SAMPLES_PASSED, id); glGetQueryObjectiv(id, GL_QUERY_RESULT, *params);
Back-face culling Application Eliminate triangles that face away from the point of projection • Such triangles are never visible if they compose proper solids How to identify? • OpenGL has no facet normals • So cannot compute dot product with eye-to-vertex vector • Could compute plane equation in eye coordinates and substitute (0, 0, 0, 1) • But no other area computations are done in eye coordinates • Compute signed area in window coordinates • Shares math with rasterization setup • Compute in order of vertex specification Vertex assembly Vertex operations Primitive assembly Primitive operations Rasterization Fragment operations Framebuffer Display
Signed area calculation (x1 y1) (x0 y0) (x2 y2) Usual rule: CCW vertex rotation on outside of solid volume
OpenGL triangle facing commands glFrontFace(GL_CW | GL_CCW); glEnable(GL_CULL_FACE);glDisable(GL_CULL_FACE);glCullFace(GL_FRONT | GL_BACK | GL_FRONT_AND_BACK); glMaterialfv(GL_FRONT | GL_BACK | GL_FRONT_AND_BACK, …);glLightModeli(GL_LIGHT_MODEL_TWO_SIDE, GL_TRUE | GL_FALSE);
Hierarchical Z culling Application Fragment ops build a multi-resolution, conservative z-buffer • Much like a MIPmap • But max value rather than filtered value Entire primitives are culled against this multi-resolution z-buffer Optimal use requires front-to-back rendering order • Opposite of painter’s algorithm Will be covered again in Dave Oldcorn’s Tuning and Debugging lecture Vertex assembly Vertex operations Primitive assembly Primitive operations HierarchicalZ culling Rasterization Fragment operations Framebuffer Display
Z-buffer as a back stop Ideal goal: • z-buffer rejects no samples Practical goal: • z-buffer sees a minimal depth complexity • e.g., 2-3 Z-buffer back stop allows efficient, conservative culling
Other hidden-surface algorithms Application level • Binary space partition (BSP) tree • Various analytical approaches Application/OpenGL • Convex solids • Sort at application level using plane equations • Render each solid with back-facing triangles culled • Hidden-line rendering • Future lecture
Z-buffering is a brute-force technique And this is good! Never solves the “hidden-surface” problem Instead resolves visibility one sample (ray) at a time
Z-buffer punch through When surfaces are too close, the z-buffer may not sort them accurately Here’s an example of the resulting “punch through” for near-parallel red and green surfaces:
Z-buffer mapping (eye-to-window coords) zw Normalized to Zfar = 1 ze
Bad Normalized to Zfar = 1 Good Zfar = 50 milesZnear = 26 feet Difference between adjacent zw representations near the specified ze, mapped back to eye coordinates ze
Resolution loss Rules of thumb: • log2(f/n) bits are lost in the far field • There are roughly 216 inches in a mile Solutions: • Minimize f/n ! • Push n as far away as possible (draw vehicle separately) • Pull f in (e.g., fog) • Adjust n and f dynamically • Use a different • ze to zw mapping and/or • zw representation
Bad 16-bit integerz-buffer Good Zfar = 50 milesZnear = 26 feet 16-bit reverse mapped, floating-point z-buffer 24-bit integerz-buffer ze
Z-buffer resolution summary This is a rich topic • We’ve just scratched the surface • May return to this topic in one of the last lectures Good practice: • Minimize f/n ! • Use back-face cull and model LOD to avoid tiny triangle-to-triangle separations • If possible use reversed-mapped (complementary) floating-point z-buffer • There are problems with this in OpenGL • zc is clipped to +/- wc, rather than 0 <= zc <= wc • I believe it works well in Direct3D 10
Transparent surfaces and the z-buffer Z-buffer finds the nearest surface • It cannot sort front-to-back or back-to-front for blending • A-buffer can do this, but is expensive to implement • I know of no hardware that does so Two good solutions: • Depth peeling • Multi-pass sort, renders one transparent surface per pass • Can use occlusion reduction to terminate the loop • Or just render a fixed number of passes • Pre-filtered point and line antialiasing algorithm • Render all opaque triangles first • Builds opaque z-buffer • Then render all transparent triangles in arbitrary order • Disable z-buffer write so all tests are of the opaque z-buffer
Fade model LOD Switching suddenly from one model LOD to another violates rule 1 • Viewers will see a jarring transition Need to fade between model LODs over several frames But blending and z-buffering don’t cooperate in this case • Cannot render in-place in the scene • Need to resolve visibility within each LOD model • But blend between the resulting “images” Solutions: • Render separate off-screen images, then composite to scene • Morph geometrically between two models • Fade “spatially” rather than blending …
OpenGL sample-coverage fade Use standard z-buffer semantics for each sample Allocate a fraction of the samples to one model, and the remaining fraction to the other Fade by transitioning the allocation frame-to-frame Works best with multi-sample antialiasing: glSampleCoverage(value, invert); glEnable(GL_SAMPLE_COVERAGE);
Sample coverage example Mask bits 7 6 5 4 3 2 1 0 glSampleCoverage(0.25, GL_FALSE); // possible mask is 00100100 glSampleCoverage(0.25, GL_TRUE); // corresponding mask is 11011011 Single Pixel