GLART week 10: Stencil Buffer Techniques
Reading: Red Book, Chapter 10, The Framebuffer, Buffers and Their Uses: pgs 432-447 Examples: GLART_10_Stencil.java -- Use the stencil buffer to mask an area of the screen also needed: Flag_of_the_United_States.png -- flag image NOTES: ================================================================= Stenciling is a technique to mask out areas of the screen and restrict rendering only to these masked areas. To create a stencil in the physical world you could cut a hole out of a piece of cardboard. If you held the cardboard against a wall and sprayed paint over it the paint will hit the wall only in the empty area that was cut out. To make a mask like this in OpenGL we use the Stencil Buffer and the Stencil Test, which can allow or prevent certain pixels from being drawn to the screen. Stencil techniques are often used to control lighting, shadowing and reflections, and other special effects such as blurs. However stencils can be used any time you want to precisely restrict rendering to the screen. A stencil mask works like the cardboard mask, but the "hole" is made by drawing a shape into the stencil buffer. Every pixel that makes up that shape is given a number, let's say 1. For example a "hole" mask might look something like this in the stencil buffer: 00000000000 00001110000 00001110000 00000000000 The stencil test can be set to draw to the screen only where the stencil value is 1, so like the spray paint in our cardboard mask, rendering will appear on screen only where pixels have a 1 value in the stencil buffer. The OpenGL commands to control stenciling: // turn stencil testing on gl.glEnable(GL.GL_STENCIL_TEST); // define how to write into stencil buffer gl.glStencilOp(GL.GL_REPLACE, GL.GL_REPLACE, GL.GL_REPLACE); // define how to test stencil buffer gl.glStencilFunc(GL.GL_EQUALS, maskvalue, 0xFFFFFFFF); I have created functions in JOApp to create masks in the stencil buffer: To create a mask: clearMask() - clear the stencil buffer (set to 0) beginMask(1) - prepare to draw a mask of 1s to the stencil buffer (turn off color buffer) (draw something) - draw some geometry, the shape will be drawn into the stencil buffer only endMask() - return to normal drawing To restrict drawing to the masked shaped activateMask(1) - stencil test will pass only pixels that have a 1 in the stencil buffer (draw something) - rendering will appear only inside the masked shape disableMask() - turn off stencil test Buffers ------------------------ We've briefly discussed the two buffers before: the Color buffer and Depth buffer. Together with the Stencil buffer these three data structures manage how pixels are drawn onto the screen, and keep track of all the pixels that are actually on the screen. Buffers are like large arrays that hold information about each pixel on the screen. The most basic buffer is the Color buffer, which holds the RGBA color value for each pixel. Other buffers hold depth information, and stencil masks. Think of the buffers as arrays that hold a numerical value for each pixel on the screen. Each buffer has a "test" associated with it. Before a pixel is drawn into the color buffer it is tested to see if it should be drawn or not. For instance, let's say that shape A is behind shape B, so that A is completely hidden from view. If the depth test is enabled, OpenGL will test the pixels that make up shape A before it draws them to the screen. It will compare the depth value of those pixels to the depth values stored in the depth buffer at the same screen location, and if the depth buffer values are closer to the front, OpenGL will discard the pixels of shape A. Shape A fails the depth test (something was drawn to the screen that is closer to the viewpoint than shape A, so shape A is not visible), and so will not be drawn into the color buffer and so will not be visible on screen. The three buffers are: Color buffer holds an RGB color value and alpha value for each pixel often referred to as the "frame buffer" similar to the pixels array in Processing the color buffer is always "on" (unlike Depth and Stencil buffers, which can be disabled) Depth buffer holds a depth value (a float from 0 to 1) for each pixel describes what object is closest to the viewpoint at each pixel Stencil buffer holds a mask value for each pixel (an integer usually 0-255) can be used to restrict drawing to specific areas of the screen Buffer Tests: Each buffer has a corresponding test that can be turned on or off. The tests restrict what pixels are drawn to the screen. For example the depth test prevents an object from being drawn to the screen if it is behind another object. To be more precise, a pixel is drawn into the color buffer only if that pixel passes these tests. Tests can be enabled or disabled by calling glEnable() and glDisable() with one of these parameters: GL_ALPHA_TEST test an incoming pixel alpha value before drawing to screen for example: discard transparent pixels from a gif texture image Color buffer is always "on", but alpha testing can be turned on/off by default it's "off". GL_DEPTH_TEST test an incoming pixel depth value before drawing to screen for example: don't draw objects that are hidden behind other objects by default it's "off" GL_STENCIL_TEST test the mask at a pixel position before drawing to screen for example: only draw pixels if they fall inside a mask area by default it's "off" You can change how tests will occur: Each buffer has a function that can change the rule for how that buffer test will behave. glAlphaFunc() glDepthFunc() glStencilFunc() For example the command glDepthFunc(GL_LEQUAL) tells the depth test to pass pixels only if the depth value for that pixel is less than or equal to the depth value that is already in the depth buffer. With this setting when two objects overlap the object closer to the camera will pass the test and be drawn into the color buffer, while objects that are behind other objects (their depth value is greater than what's in the depth buffer) will fail the test and be ignored. You can make the buffers read-only: To fine-tune how a buffer test behaves, you may want to preserve the contents of the buffer by making it read-only. This prevents OpenGL from writing any more values to the buffer but preserves the current values in the buffer. This is not the same as disabling the test, which will completely turn the test and its buffer off. When the buffer is made read-only the test for that buffer is still active. glColorMask() glDepthMask() glStencilMask() Additional functions: Two tests can be fine-tuned with additional functions that control what to do when a value passes or fails a test. glBlendFunc() specifies how to blend an incoming pixel into the color buffer ie. glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA) will average the pixels together must glEnable(GL_BLEND) for alpha blending to work glStencilOp() specifies what to do when: 1) the stencil test fails, or 2) the stencil test passes but depth test fails or 3) the stencil test passes and depth test passes Summary Buffer Enable/Disable How to test incoming Make buffer Additional testing on this pixel value against read-only buffer what's in the buffer -------------- --------------------- ---------------------- --------------------- -------------- Color GL_ALPHA_TEST glAlphaFunc glColorMask glBlendFunc Depth GL_DEPTH_TEST glDepthFunc glDepthMask Stencil GL_STENCIL_TEST glStencilFunc glStencilMask glStencilOp Scissor Test --------------------------- The "Scissor Test" clips all drawing to a specified rectangle. Like other tests it can be enabled or disabled, and when enabled, it will restrict what pixels are rendered. Unlike other tests, it is not related to a specific buffer. The Scissor test restricts rendering to a given rectangular area of the display, but does not specifically alter the behavior of the color, depth or stencil buffers. glEnable(GL_SCISSOR_TEST) turn on scissor test (by default it's off) glScissor(lowerX, lowerY, width, height) specify the rectangle where rendering can occur