95.4002 Comp 4002 Wilf LaLonde ©2009 Implementing “Mirrors” • Many problems to resolve (we’ll consider each as we encounter them). Reflected O1 is visible objects already behind should not appear in front O1 O2 Comp 4002 Reflected O2 should not be visible Wilf LaLonde ©2009 Example: The Back of the Mirror Comp 4002 Wilf LaLonde ©2009 Example: The Front of the Mirror Comp 4002 Wilf LaLonde ©2009 Implementing “Mirrors” • Strategy: Draw all objects twice. • Once normally. • Once reflected and clipped to mirror region. Use reflection transformation R O2 is not visible O1 O2 Comp 4002 Warning: We Will Find Out That We Have To Do It In The Other Order Wilf LaLonde ©2009 Recall: W versus W-1 • Vehicle’s W maps from local to world. • Vehicle’s W-1 maps back from world to local. W Mirror z-axis points behind mirror W-1 Treat mirror like a vehicle… Comp 4002 Wilf LaLonde ©2009 To “Reflect” an Object About The Mirror • Replace the object’s local to world transformation W by WR where R = WM-1 Sz=-1 WM WM is the mirror’s local to world transf.. Sz=-1is the transf.. that scales z by -1; i.e., scale by x = 1.0, y = 1.0, z = -1.0. Explanation on next page. Comp 4002 Wilf LaLonde ©2009 Explaining the “Reflect” Transformation • Why replacing W by WR mirrors the object. R = WM-1 Sz=-1 WM W WWM-1 WWM-1Sz=-1 WWM-1Sz=-1WM object in world coordinates. object in mirror’s local coordinates makes front go behind and vice versa (flips sign of z). back to world coordinates. IF WM converts local to world, THEN WM-1 converts world to local Comp 4002 Wilf LaLonde ©2009 A Side-Effect of the “Reflect” Transformation • A front-facing face becomes back facing (and vice-versa). 2 3 1 1 4 4 2 Will this be a front or back face? 3 This must be counteracted. Comp 4002 Wilf LaLonde ©2009 Solution: Flip Face Visibility • Two possible solutions: • Flip which faces must be culled using glCullFace (GL_BACK); //or GL_FRONT • Flip polygon vertex ordering; i.e., clockwise to anti-clockwise (or vice versa) glFrontFace (GL_CW); //or GL_CCW Comp 4002 Wilf LaLonde ©2009 Reflected Back Objects Become Visible Too? • Must eliminate those objects that WERE behind (after the reflection, they are in front). Before behind Comp 4002 After in front (not wanted) Wilf LaLonde ©2009 Solution: Clip Undesired Objects • Approach: • Use a clipping plane Positive side draws Negative side disappears glEnable (GL_CLIP_PLANE0); glClipPlane (GL_CLIP_PLANE0, plane); • But be careful PLANES BEHAVE LIKE LIGHTS. They are transformed from local to camera coordinates and then stay UNCHANGED relative to the camera EVEN IF THE CAMERA IS MOVED. Comp 4002 Wilf LaLonde ©2009 Let’s Look At A Top View To See Where The Plane is Relative To The Camera Where is the plane NORMAL relative to the mirror? negative z a mirror is like a vehicle clipping plane Mirror with M and M-1 Camera with C and C-1 Comp 4002 Where is the plane relative to the camera? pointing toward camera Wilf LaLonde ©2009 Clipping Plane Details Plane = [Nx,Ny,Nz,-N.P0] • Plane at mirror center P0=[0,0,0] relative to mirror local coordinates P = {0,0,1,0} , -N.P0=0 • Plane in world coordinates P*M • Plane as seen by camera P*MC-1 Mirror z-axis points behind mirror Mirror with M and M-1 Camera with C and C-1 Comp 4002 Wilf LaLonde ©2009 Tell Renderer To Clip Undesired Objects • Solution: Specify plane in local coordinates • Set up stack to contain MC-1 (M is mirror transformation, C is camera). glPushMatrix (); //Assuming C-1 is already on stack glMultMatrix (mirrorLocalToWorld); //M Gldouble localPlane = {0.0, 0.0, 1.0, 0.0}; //Normal is -ve z-axis (to keep what mirror sees). glClipPlane (GL_CLIP_PLANE0, localPlane); glPopMatrix (); //restore Clipping plane points in direction that is KEPT.. Comp 4002 Wilf LaLonde ©2009 Another Problem: Won’t Z-Buffering Screw up? • We need to render objects twice, once normally and once reflected-and-with-back-objects-clipped. • What about objects obscured by mirror? Isn’t O3 closer than reflected O1? O2 O1 Reflected O1 Comp 4002 O3 Solution: Draw INSIDE first and OUTSIDE last… Wilf LaLonde ©2009 Partial solution: I Call It Hardening The Mirror • IN MIRROR ONLY, PASS1: Render all objects reflected and back clipped (exclude mirror; reflected O1 drawn but not O2 or O3). Get just . • Render mirror but disable coloring to harden it O2 (z-buffer works; everything in mirror get mirror’s z-depth). • PASS2: Render all O1 O3 objects normally (exclude mirror; O1, O2 and O3 will be drawn properly z-buffered). Get both (O1+O2) + Comp 4002 Wilf LaLonde ©2009 Rendering The Mirror • Disable coloring but permit z-buffering. glColorMask (0, 0, 0, 0); //Disable coloring; 0 for false. • Render the mirror as if there were something to draw. … render mirror polygon ... • Enable coloring once again. glColorMask (1, 1, 1, 1); //Enable coloring; 1 for true. Comp 4002 Wilf LaLonde ©2009 One Remaining Problem (Drawing only INSIDE) • How do we draw ONLY inside the mirror. If not done, reflected O2 will be visible! O1 O2 Reflected O2 should not be visible • Solution should handle arbitrary shaped mirrors. Comp 4002 Wilf LaLonde ©2009 How to Clip To Mirror Interior • Need to mask area by rendering the mirror first without coloring or z-buffering (to handle arbitrary mirror shapes). • Then need to render reflected objects only inside the masked area. should not be visible O1 O2 Comp 4002 Wilf LaLonde ©2009 Using Stencil Functions/Operations • The stencil buffer has available a fixed number of bits per screen pixel; e.g., 8 bits. • These bits can all be set to any integer value; e.g. 0 or 255. • Some of these bits can be ignored with suitable masks; e.g., 0x0F only looks at the right 4 bits. Comp 4002 Wilf LaLonde ©2009 Using Stencil Functions/Operations • How Stencil Buffer is Used (Done in Hardware) • If stencil test passes, drawing is allowed. • Stencil buffer changed according to operation. • glStencilFunc (operation, data, mask) QUERY: Do masked stencil buffer bits at point being drawn satisfy “data operation bits”; i.e., 5 < bits. ORDER IS BACKWARD Example: glStencilFunc (GL_LESS, 5, 0x0F); • glStencilOp (failRule, passZfailRule, passZpassRule) MODIFICATION: bits := rule (bits) Example rule: GL_INCR, GL_ZERO. Comp 4002 Wilf LaLonde ©2009 Sample Stencil Functions/Operations • glStencilFunc (function, data, mask) GL_NEVER, GL_ALWAYS, GL_LESS, GL_LEQUAL, GL_EQUAL, GL_GEQUAL, GL_GREATER, GL_NOTEQUAL TEST: Do masked stencil bits satisfy “5 < stencil bits” Example: glStencilFunc (GL_LESS, 5, 0x0F); • glStencilOp (iffails, ifpassesButZfails, ifpassesAndZpasses) GL_KEEP, GL_ZERO, GL_INCR, GL_DECR, GL_REPLACE (by data), GL_INVERT MODIFICATION: bits := rule (bits)Except for replace Comp 4002 Wilf LaLonde ©2009 Sample Stencil Functions/Operations • glStencilMask (mask) Controls which bits are changed by glStencilOp. Default is: all bits; i.e., mask = 0xffffffff ~0 equivalent to 0xffffffff Not needed here but other applications might. Comp 4002 Wilf LaLonde ©2009 Creating a Mask (4 steps: first 2) • Enable the stencil buffer glEnable (GL_STENCIL_TEST); glClearStencil (0); //The fill value to use. glClear (GL_STENCIL_BUFFER_BIT); //Now fill. • Disable coloring and z-buffering glColorMask (0, 0, 0, 0); //Disable coloring; 0 for false. glDisable (GL_DEPTH_TEST); The stencil buffer can perform masking. Comp 4002 Wilf LaLonde ©2009 Creating a Mask (4 steps: last 2) • Ensure that any drawn location replaces stencil bits (currently 0) by 1. glStencilFunc (GL_NEVER, 1 “variable”, ~0 “mask”); //On anything, fail the test (so nothing draws). glStencilOp (GL_REPLACE, GL_REPLACE, GL_REPLACE); //ifF, ifPButZfails, ifPAndZpasse //Always replace bits that fail by 1. • Render mirror as if it were a normal object … render mirror ... All mirror bits NOW 1. Comp 4002 Wilf LaLonde ©2009 Rendering Inside the Mask • Re-enable coloring and depth buffering. glColorMask (1, 1, 1, 1); //Enable coloring; 1 for true. glEnable (GL_DEPTH_TEST); • Don’t allow stencil bits to change anymore. glStencilFunc (GL_EQUAL, 1, ~0); //On 1 pass the test and keep bits unchanged. glStencilOp (GL_KEEP, GL_KEEP, GL_KEEP); • Render reflected objects as discussed earlier … render reflected objects … Comp 4002 Wilf LaLonde ©2009 Implementing “Mirrors” • So many steps (perhaps a summary is needed). Reflected O1 is visible objects already behind O1 O2 Comp 4002 Reflected O2 is not visible Wilf LaLonde ©2009 Implementing “Mirrors” (Summary) • Create stencil mask from mirror face Disable coloring and z-buffering. Enable stencil buffering to set to 1 when drawing. Set up mirror and render it to make mirror bits 1. • In mirror, render reflected world (no mirror) Enable coloring and z-buffering (stencil still enabled). Set up stencil buffer to not change bits. Set up reflection matrix and back clipping plane. Flip front/back facing test. Render whole world (mirror excluded). Disable stencil buffering and clipping plane. Restore front/back facing test. Comp 4002 Wilf LaLonde ©2009 Implementing “Mirrors” (Summary) • Render mirror to “harden it” Disable coloring but NOT z-buffering. Set up mirror and render it. Alternative: use blending and draw mostly transparent but slightly colored mirror. • Outside mirror, render world (no mirror) Enable coloring. Render whole world (mirror excluded). Comp 4002 Wilf LaLonde ©2009 95.4002 Comp 4002 Wilf LaLonde ©2009 Clipping Plane Approach • Advantages: Not all cards support stencil buffers (today, they probably all do…). Better reason: stencil buffer also useful for shadows (conflict). • Disadvantages: • Can’t handle unusual shapes since number of clipping planes is limited (currently, 6 is limit and 1 is needed for clipping out back side objects; so really only have 5). • Recursively clipped mirrors can end up with many more sides than 4… (must approximate with 5). See next slide… Comp 4002 Wilf LaLonde ©2009 Recursive Mirrors More Difficult • When more than 5 planes are needed to represent sub-mirrors, there are artifacts. Imagine that PART of the yellow mirror is visible inside mirror1. 6 planes needed to bound it (one more than allowed)… mirror2 seen inside mirror1 has 6 sides AFTER CLIPPING mirror1 Comp 4002 Wilf LaLonde ©2009 95.4002 Comp 4002 Wilf LaLonde ©2009 Recursive Mirrors • Main idea • Use a counter incrementing as you go deeper/decrementing as you come back. First mirror at 1, inside ones at 2, inside those at 3, … alternating front/back facing order… • For the stencil buffer approach, make mask that matches counter. • For planes approach, compute new planes pushing old ones onto a stack (activate only top INNERMOST ones). Comp 4002 Wilf LaLonde ©2009 Interactions with Visibility • Consider reflected frustum. visibility camera position mirror Region 1 portal virtual (reflected) camera Region 4 Region 2 portal camera Comp 4002 Region 3 Wilf LaLonde ©2009 Visibility in Recursive Mirrors • Should adjust the frustum by narrowing onto mirror (need not be exact… JUST contain). • Reflect the narrowed frustum. • When drawing world in mirror, take reflected frustum visibility into account. and visibility camera position (a point in front of mirror; not the camera position nor the reflected camera position) if you are computing region visibility. Why: Because The Region You Are In Must Be Where The Front of the Mirror is Located Comp 4002 Wilf LaLonde ©2009 Recursive Mirrors: Reflections • Camera must be reflected in such a way that routines that make use of the camera for drawing special effects do not know it was done (e.g., coronas, lens flares, sun flares, sprites, etc make use of camera position) • When camera is asked for position, it returns it’s virtual reflected position (not visibility position). • Lights on player get reflected; so they must be recomputed… Comp 4002 Wilf LaLonde ©2009 95.4002 Comp 4002 Wilf LaLonde ©2009 Much Simpler Than A Mirror • Switch to the remote camera. Display Normal camera • • • • Remote camera Render world normally. Copy what you drew onto a texture. Switch back to the normal camera. Draw the mirror’s “face” using the new texture bits. Comp 4002 Wilf LaLonde ©2009 Drawing into Restricted Area Of Color Buffer glPushAttrib (GL_VIEWPORT_BIT); //draw glEnable (GL_SCISSOR_TEST); //into glScissor (0, 0, 64, 64); //top-left glViewport (0, 0, 64, 64); //part glClearColor (0.0f, 0.0f, 0.0f, 1.0f); glClear (GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); glPushMatrix(); Setup new camera matrix, etc. Draw whatever you want to draw. glPopMatrix(); glDisable (GL_SCISSOR_TEST); glPopAttrib(); Comp 4002 Wilf LaLonde ©2009 Moving Color Buffer To Your Texture Select/activate a texture handle to draw into. Then … glCopyTexImage2D (GL_TEXTURE_2D, 0, GL_RGB, 0, 0, 64, 64, 0); Problem: It’s very slow to copy from a back buffer to a texture. Can draw directly into a second back buffer that is simultaneously a texture New cards support frame buffers (back buffers built from textures that can be drawn with). Comp 4002 Wilf LaLonde ©2009