Recent from talks
Nothing was collected or created yet.
Stencil buffer
View on WikipediaThis article has multiple issues. Please help improve it or discuss these issues on the talk page. (Learn how and when to remove these messages)
|
A stencil buffer is an extra data buffer, in addition to the color buffer and Z-buffer, found on modern graphics hardware. The buffer is per pixel and works on integer values, usually with a depth of one byte per pixel. The Z-buffer and stencil buffer often share the same area in the RAM of the graphics hardware.
In the simplest case, the stencil buffer is used to limit the area of rendering (stenciling). More advanced usage of the stencil buffer makes use of the strong connection between the Z-buffer and the stencil buffer in the rendering pipeline. For example, stencil values can be automatically increased/decreased for every pixel that fails or passes the depth test.
The simple combination of depth test and stencil modifiers make a vast number of effects possible (such as stencil shadow volumes, Two-Sided Stencil,[1] compositing, decaling, dissolves, fades, swipes, silhouettes, outline drawing, or highlighting of intersections between complex primitives) though they often require several rendering passes and, therefore, can put a heavy load on the graphics hardware.
The most typical application is still to add shadows to 3D applications. It is also used for planar reflections.
Other rendering techniques, such as portal rendering, use the stencil buffer in other ways; for example, it can be used to find the area of the screen obscured by a portal and re-render those pixels correctly.
The stencil buffer and its modifiers can be accessed in computer graphics by using APIs like OpenGL, Direct3D, Vulkan or Metal.
Architecture
[edit]The stencil buffer typically shares the same memory space as the Z-buffer, and typically the ratio is 24 bits for Z-buffer + 8 bits for stencil buffer or, in the past, 15 bits for Z-buffer + 1 bit for stencil buffer. Another variant is 4 + 24, where 28 of the 32 bits are used and 4 ignored. Stencil and Z-buffers are part of the frame buffer, coupled to the color buffer. The first chip available to a wider market was 3Dlabs' Permedia II, which supported a one-bit stencil buffer.
The bits allocated to the stencil buffer can be used to represent numerical values in the range [0, 2n-1], and also as a Boolean matrix (n is the number of allocated bits), each of which may be used to control the particular part of the scene. Any combination of these two ways of using the available memory is also possible.
Stencil test
[edit]Stencil test or stenciling is among the operations on the pixels/fragments (Per-pixel operations), located after the alpha test, and before the depth test. The stencil test ensures undesired pixels do not reach the depth test. This saves processing time for the scene. Similarly, the alpha test can prevent corresponding pixels to reach the stencil test.
The test itself is carried out over the stencil buffer to some value in it, or altered or used it, and carried out through the so-called stencil function and stencil operations. The stencil function is a function by which the stencil value of a certain pixel is compared to a given reference value. If this comparison is logically true, the stencil test passes. Otherwise not.
In doing so, the possible reaction caused by the result of comparing three different state-depth and stencil buffer:
- Stencil test is not passed
- Stencil test is passed but not the depth test
- Both tests are passed (or stencil test is passed, and the depth is not enabled)
For each of these cases can be set different operations over the examined pixel. In the OpenGL stencil functions, the reference value and mask, respectively, define the function glStencilFunc. In Direct3D each of these components is adjusted individually using methods SetRenderState devices currently in control. This method expects two parameters, the first of which is a condition that is set and the other its value. In the order that was used above, these conditions are called D3DRS_STENCILFUNC, D3DRS_STENCILREF, and D3DRS_STENCILMASK.
Stencil operations in OpenGL adjust glStencilOp function that expects three values. In Direct3D, again, each state sets a specific method SetRenderState. The three states that can be assigned to surgery are called D3DRS_STENCILFAIL, D3DRENDERSTATE_STENCILZFAIL, and D3DRENDERSTATE_STENCILPASS.
Z-fighting
[edit]Due to the lack of precision in the Z-buffer, coplanar polygons that are short-range, or overlapping, can be portrayed as a single plane with a multitude of irregular cross-sections. These sections can vary depending on the camera position and other parameters and are rapidly changing. This is called Z-fighting. There exist multiple solutions to this issue:
- Bring the far plane closer to restrict the scene's depth, thus increasing the accuracy of the Z-buffer, or reducing the distance at which objects are visible in the scene.
- Increase the number of bits allocated to the Z-buffer, which is possible at the expense of memory for the stencil buffer.
- Move polygons farther apart from one another, which restricts the possibilities for the artist to create an elaborate scene.
All of these approaches to the problem can only reduce the likelihood that the polygons will experience Z-fighting, and do not guarantee a definitive solution in the general case.
A solution that includes the stencil buffer is based on the knowledge of which polygon should be in front of the others. The silhouette of the front polygon is drawn into the stencil buffer. After that, the rest of the scene can be rendered only where the silhouette is negative, and so will not clash with the front polygon.
Shadow volume
[edit]Shadow volume is a technique used in 3D computer graphics to add shadows to a rendered scene. They were first proposed by Frank Crow in 1977[2] as the geometry describing the 3D shape of the region occluded from a light source. A shadow volume divides the virtual world in two: areas that are in shadow and areas that are not.
The stencil buffer implementation of shadow volumes is generally considered among the most practical general-purpose real-time shadowing techniques for use on modern 3D graphics hardware. It has been popularised by the video game Doom 3, and a particular variation of the technique used in this game has become known as Carmack's Reverse.
Reflections
[edit]Reflection of a scene is drawn as the scene itself transformed and reflected relative to the "mirror" plane, which requires multiple render passes and using of stencil buffer to restrict areas where the current render pass works:
- Draw the scene excluding mirror areas – for each mirror lock the Z-buffer and color buffer
- Render visible part of the mirror
- Depth test is set up so that each pixel is passed to enter the maximum value and always passes
- for each mirror:
- Depth test is set so that it passes only if the distance of a pixel is less than the current (default behavior)
- The matrix transformation is changed to reflect the scene relative to the mirror plane
- Unlock the Z-buffer and color buffer
- Draw the scene, but only the part of it that lies between the mirror plane and the camera. In other words, a mirror plane is also a clipping plane
- Again locks color buffer, depth test is set so that it always passes, reset stencil for the next mirror.
Planar Shadows
[edit]While drawing a plane of shadows, there are two dominant problems: The first concerns the problem of deep struggle in case the flat geometry is not awarded on the part covered with the shadow of shadows and outside. See the section that relates to this. Another problem relates to the extent of the shadows outside the area where the plane there.
Another problem, which may or may not appear, depending on the technique, the design of more polygons in one part of the shadow, resulting in darker and lighter parts of the same shade. All three problems can be solved geometrically, but because of the possibility that hardware acceleration is directly used, it is a far more elegant implementation using the stencil buffer: 1. Enable lights and the lights 2. Draw a scene without any polygon that should be projected shadows 3. Draw all polygons which should be projected shadows, but without lights. In doing so, the stencil buffer, the pixel of each polygon to be assigned to a specific value for the ground to which they belong. The distance between these values should be at least two, because for each plane to be used two values for two states: in the shadows and bright. 4. Disable any global illumination (to ensure that the next steps will affect only individual selected light) For each plane: For each light: 1. Edit a stencil buffer and only the pixels that carry a specific value for the selected level. Increase the value of all the pixels that are projected objects between the date of a given level and bright. 2. Allow only selected light for him to draw level at which part of her specific value was not changed.
Spatial shadows
[edit]Stencil buffer implementation of spatial drawing shadows is any shadow of a geometric body that its volume includes part of the scene that is in it. If any part of the scene belongs to this volume, light is not illuminated given, otherwise it is. This problem is compounded by the increase in the number of lights but does not address the number of areas on which the shadows fall. There are several solutions to the problem, but we followed the following algorithm: 1. Draw a scene without light 2. Lock the Z-buffer and color buffer, so that the two can not make changes For each light 1. Using in-depth information about the scene (Z-buffer) to fill the stencil buffer only on parts of the scene where volume shadow does not exist or are not visible from the existing buildings. 2. Unlock buffer for color, and adjust the function of the Z-buffer to allow amendments only where the depth value equal to an existing 3. Draw the scene illuminated only by this light, but only for part of the scene passing the stencil test
Each of these passages implies that a clean stencil buffer can be used.
As for the shadows, this technique can be used to illuminate parts of space that are under strong light. For example, the brightness of the spotlight in a dark room with a large presence of dust in the air could be seen illuminating the appropriate volume of space.
Other applications
[edit]A further example is the so-called soft shadow, in which the transition between the illuminated and shadowed part of the scene is out of focus. Specifically, one way to achieve this effect stencil buffer is to multiply the volume of the shadow, and that as the copies, respectively are scaled according to a geometric series with a low magnification, e.g.,. 1.04. The Center of scaling can be the center of gravity of the polygon that represents the top volume. This in itself will give a series of composite shadows that give the desired effect.
Another implementation includes the field of visualization during the modeling technique solids Constructive Solid Geometry (CSG), wherein stencil buffer, together with the Z-buffer, can successfully solve the problems of the Boolean operations of the SOLiD .
OpenGL
[edit]glEnable(GL_STENCIL_TEST); // by default not enabled
glStencilMask(stencilMask); // allow writing to stencil buffer, by default (0xFF) no mask.
glClearStencil(clearStencilValue); // clear stencil value, by default = 0
glStencilFunc(func, ref, mask); // by default GL_ALWAYS, 0, 0xFF, always pass stencil test
glStencilOp(fail,zfail,zpass); // by default GL_KEEP, GL_KEEP, GL_KEEP, do not change stencil buffer
glClear(GL_STENCIL_BUFFER_BIT); // clear stencil buffer, fill with (clearStencilValue & stencilMask)
Test: ( ref & mask ) func (stencilValue & mask)
Depending on the three possible conditions of the stencil function/depth function.
1. Stencil Test Function fails:
If say func is GL_NEVER, the stencil test will always fail. Neither Color/Z-buffers are modified. The stencil buffer is modified as per glStencilOp fail. If say glStencilOp(GL_REPLACE, GL_KEEP, GL_KEEP) then GL_REPLACE takes place and stencilValue = (ref & stencilMask) // will become ref
2. Stencil Test Function passes/Depth Test Function fails:
If say func is GL_ALWAYS, the stencil test will always pass, but depth test may fail. Neither Color/Z-buffer are modified. The stencil buffer is modified as per glStencilOp zfail. If say glStencilOp(GL_KEEP, GL_INCR, GL_KEEP) then GL_INCR takes place and stencilValue = (stencilValue+1) // will become 1
3. Stencil Function passes/Depth Function passes:
If say func is GL_ALWAYS, the stencil test will always pass. If the depth test also passes. Both Color/Z-buffer are modified. The stencil buffer is modified as per glStencilOp zpass. If say, glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP) then Stencil values are not changed, only Color and Z-buffers are modified.
Typically Stencil buffer is initialized by setting Z-buffer and color buffer masks to false. and then setting appropriate ref value to stencil buffer by failing the stencil test every time.
// disable color and Z-buffers
glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE);
glDepthMask(GL_FALSE);
glStencilFunc(GL_NEVER, 1, 0xFF); // never pass stencil test
glStencilOp(GL_REPLACE, GL_KEEP, GL_KEEP); // replace stencil buffer values to ref=1
glStencilMask(0xFF); // stencil buffer free to write
glClear(GL_STENCIL_BUFFER_BIT); // first clear stencil buffer by writing default stencil value (0) to all of stencil buffer.
draw_stencil_shape(); // at stencil shape pixel locations in stencil buffer replace stencil buffer values to ref = 1
Now use the initialized stencil buffer and stencil test to write only in the locations where the stencil value is 1:
// enable color and Z-buffers.
glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
glDepthMask(GL_TRUE);
// no more modifying of stencil buffer on stencil and depth pass.
glStencilMask(0x00);
// can also be achieved by glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);
// stencil test: only pass stencil test at stencilValue == 1 (Assuming depth test would pass.)
// and write actual content to depth and color buffer only at stencil shape locations.
glStencilFunc(GL_EQUAL, 1, 0xFF);
draw_actual_content();
See also
[edit]- Z-buffering (depth buffer)
- Shadow volume (a stencil buffer technique creating shadows)
References
[edit]- ^ "Stencil Buffer Techniques (Direct3D 9) - Win32 apps".
- ^ Crow, Franklin C: "Shadow Algorithms for Computer Graphics", Computer Graphics (SIGGRAPH '77 Proceedings), vol. 11, no. 2, 242-248.
Stencil buffer
View on GrokipediaFundamentals
Definition and Purpose
The stencil buffer is an auxiliary per-pixel data buffer in graphics rendering hardware, typically allocated 8 bits per pixel, that operates alongside the color buffer and depth buffer to selectively mask or restrict rendering operations on a pixel-by-pixel basis.[7] It stores unsigned integer values, usually ranging from 0 to 255 in an 8-bit configuration, which serve as a reference for comparison tests during the rendering process.[7] This buffer enables precise control over which fragments contribute to the final image, functioning as a mask to enable or disable drawing in specific regions without altering the underlying geometry or color data.[7] The primary purpose of the stencil buffer is to facilitate complex visual effects and optimizations in real-time rendering, such as shadows, reflections, and scene partitioning, by defining pixel visibility through conditional testing and updating of stored values.[8] For instance, it allows developers to restrict rendering to designated areas, like mirroring only visible portions of a scene or isolating geometric volumes for compositing, thereby enhancing efficiency in multipass rendering pipelines.[8] These capabilities stem from its role in per-pixel decision-making, which goes beyond the depth buffer's occlusion handling to support arbitrary masking logic tailored to application needs.[7] Historically, hardware support for the stencil buffer gained prominence with the 3Dlabs Permedia II graphics chip in 1997, which introduced a 1-bit stencil buffer for mass-market 3D acceleration, marking an early step toward widespread adoption in professional and consumer GPUs.[8] By the late 1990s, implementations expanded to 8-bit depths in chips like NVIDIA's RIVA TNT, solidifying its integration into standard graphics APIs. Today, the stencil buffer is a core feature in modern GPUs across APIs like OpenGL and Vulkan, essential for real-time rendering in games and simulations.[7] In a general workflow, during the rasterization stage of the graphics pipeline, incoming fragments' stencil values are compared against the buffer's contents using programmable functions, with successful tests allowing updates to the buffer or proceeding to subsequent operations like depth testing, thereby defining the boundaries of renderable regions.[7] This process integrates seamlessly with the fragment processing phase, where the buffer's state influences whether pixels are written to the color or depth buffers.[7]Architecture
The stencil buffer is typically allocated in shared memory with the Z-buffer to optimize storage in graphics hardware, forming a combined depth-stencil buffer. Common modern formats pack 24 bits for depth precision and 8 bits for the stencil component into a 32-bit per-pixel structure, as standardized in OpenGL extensions like GL_OES_packed_depth_stencil.[9] Historically, earlier formats included 15 bits for the Z-buffer and 1 bit for the stencil, as seen in initial DirectX implementations such as D15S1. This packing reduces memory footprint while enabling efficient access during rendering. Data in the stencil buffer is represented as unsigned integer values per pixel, ranging from 0 to , where is the bit depth allocated to the stencil (commonly 8 bits today). These values support operations like masking and counting in per-pixel tests. Hardware supports packed formats for seamless integration, such as the 32-bit combined Z-stencil structure, which allows atomic updates to both buffers in a single memory transaction. The hardware evolution of the stencil buffer began with Silicon Graphics Incorporated (SGI) in the late 1980s and early 1990s, where it was first introduced as per-pixel stencil testing in their IRIS GL library on systems like the VGX workstation.[10] Early SGI implementations varied in depth, with the Indigo 2 Extreme providing 4 stencil bits and the Octane MXI expanding to 8 bits, influencing the transition to OpenGL. By the late 1990s and 2000s, consumer GPUs from NVIDIA (e.g., Riva TNT with 8-bit stencil) and AMD adopted the 8-bit standard, embedding it in fixed-function pipelines without support for floating-point stencil values in rasterization stages.[11] Performance considerations for the stencil buffer center on memory bandwidth, as reads and writes to the buffer occur per fragment, potentially increasing latency in high-resolution rendering. Optimizations in fixed-function pipelines, such as early stencil rejection, discard fragments before expensive shading computations if the stencil test fails, reducing unnecessary buffer accesses and improving throughput.[12] Stencil operations are particularly efficient on 8-bit surfaces compared to deeper buffers, minimizing bandwidth overhead in scenarios like masking.[13]Stencil Test
The stencil test is a per-fragment operation in the graphics rendering pipeline that can be performed early, before the fragment shader if early fragment tests are supported and the shader does not modify gl_FragDepth, or late, after the fragment shader but before the depth test otherwise.[14] It compares the current value stored in the stencil buffer at the fragment's location against a programmable reference value to determine whether the fragment proceeds to subsequent pipeline stages. This test enables selective rendering by discarding fragments that fail the comparison, facilitating techniques such as masking and multipass effects.[15] The comparison is configured using functions likeGL_ALWAYS (always passes), GL_NEVER (always fails), GL_LESS (passes if reference < stencil value), GL_LEQUAL, GL_GREATER, GL_GEQUAL, GL_EQUAL, or GL_NOTEQUAL, applied after bitwise ANDing both values with a read mask to select relevant bits.[15] The reference value is clamped to the range [0, 2^n - 1], where n is the number of stencil bits (typically 8), and the read mask (set via glStencilFunc) controls which bits participate in the comparison, allowing per-bit granularity.[15] If the test passes, the fragment advances; otherwise, it is discarded, and an associated stencil operation is applied to update the buffer.[15]
Upon test outcome, stencil buffer updates are determined by three programmable operations: one for stencil fail (sfail), one for stencil pass but depth test fail (dpfail), and one for both passing (dppass).[7] Available operations include GL_KEEP (retain current value), GL_ZERO (set to 0), GL_REPLACE (set to reference value), GL_INCR (increment, saturating at maximum), GL_INCR_WRAP (increment with wraparound), GL_DECR (decrement, saturating at 0), GL_DECR_WRAP (decrement with wraparound), and GL_INVERT (bitwise invert).[7] These are masked by a write mask (set via glStencilMask), which bitwise ANDs the new value before storing, protecting specified bits from modification.[16] If the depth test is disabled, the dpfail operation is skipped, and dppass applies directly on stencil pass.[7]
The logic can be expressed in pseudocode as follows:
if (stencil_func(stencil_value & read_mask, reference & read_mask, func)) {
if (depth_test_passes()) {
stencil_value = apply_operation(stencil_value, dppass, write_mask);
} else {
stencil_value = apply_operation(stencil_value, dpfail, write_mask);
}
} else {
stencil_value = apply_operation(stencil_value, sfail, write_mask);
}
if (stencil_func(stencil_value & read_mask, reference & read_mask, func)) {
if (depth_test_passes()) {
stencil_value = apply_operation(stencil_value, dppass, write_mask);
} else {
stencil_value = apply_operation(stencil_value, dpfail, write_mask);
}
} else {
stencil_value = apply_operation(stencil_value, sfail, write_mask);
}
Integration in Rendering Pipeline
Relation to Other Buffers
In the graphics rendering pipeline, the stencil buffer operates during the fragment processing stage, specifically after fragment shading. In OpenGL, it occurs before the depth test, enabling early fragment rejection if the stencil test fails and avoiding unnecessary depth computations and blend operations, thereby improving fill rate efficiency on the GPU. In Direct3D, the depth test precedes the stencil test.[17][18] The stencil buffer exhibits strong synergy with the depth buffer (Z-buffer), as stencil operations can be configured to depend on depth test outcomes, such as incrementing or decrementing stencil values only when the depth test passes or fails. This interdependence allows the stencil to conditionally enable or disable depth writes, facilitating complex visibility determinations. Furthermore, depth and stencil data are frequently packed into a single buffer format, like D24S8 (24 bits for depth and 8 bits for stencil), which optimizes memory usage by sharing the same per-pixel allocation rather than requiring separate buffers.[19][20] Interaction with the color buffer occurs post-testing: fragments that pass both stencil and depth tests proceed to update the color buffer, while failures mask these writes, preventing unintended overwrites. This masking is essential for multi-pass techniques, where the stencil buffer protects specific screen regions during subsequent renders, ensuring precise control over final pixel colors without affecting non-target areas.[21] In advanced rendering contexts, the stencil buffer enhances integration in deferred pipelines by flagging pixels with material IDs during the geometry pass, enabling targeted shading in later passes to process only relevant G-buffer regions and reduce overdraw. Similarly, in forward rendering, it partitions geometry by marking visibility zones, allowing efficient separation of scene elements for layered effects or optimizations.[22][23]Z-Fighting Mitigation
Z-fighting is a visual artifact in 3D rendering characterized by shimmering or flickering pixels, arising from insufficient sub-pixel precision in the depth buffer when rendering nearly coplanar polygons.[3] This occurs because the depth values of the polygons are so close that rounding errors during depth testing cause alternating visibility between surfaces across frames, particularly noticeable on large, flat areas like terrain or walls.[3] The stencil buffer mitigates Z-fighting in such scenes by marking pixels covered by an occluding surface (e.g., a base polygon or mesh), allowing subsequent coplanar geometry (e.g., overlays or details) to render only in those masked regions without relying on the depth buffer, thus avoiding precision conflicts.[24] This approach leverages the stencil buffer's per-pixel integer values to create a precise mask of covered areas, ensuring the secondary geometry adheres exactly to the primary surface's contours, such as in decal application or multi-layered coplanar meshes.[25] The technique typically involves a two-pass rendering process. In the first pass, occluders are rendered with the depth test enabled to establish depth values, while the stencil buffer is configured to replace its value with 1 only for front-facing polygons passing the depth test, effectively marking the covered regions (e.g., set to 1).[24] In the second pass, occludees are rendered with the depth test disabled and the stencil test set to draw only where the stencil value equals 1, ensuring they appear coplanar without Z-fighting artifacts.[25] This method assumes consistent polygon winding order to correctly identify front-facing surfaces for stencil updates.[24] Limitations include its inapplicability to transparent surfaces, where depth testing is necessary for proper alpha blending and order-independent transparency, potentially introducing overdraw or incorrect compositing.[3] Additionally, the approach requires careful state management across passes and may increase bandwidth usage due to multiple renders of the same geometry.[25]Shadow Rendering Techniques
Shadow Volumes
Shadow volumes are a technique for generating real-time dynamic shadows in 3D rendering by extruding the silhouettes of occluder objects from a light source to form infinite pyramidal volumes that delineate shadowed regions.[26] The stencil buffer is employed to perform a per-pixel point-in-volume test, marking pixels inside the volume as shadowed by incrementing or decrementing stencil values based on the winding order of the volume's faces relative to the viewer.[27] The integration of shadow volumes with the stencil buffer was advanced by Mark Heidmann in 1991, who proposed rendering the front-facing caps of the shadow volume to increment the stencil buffer where the depth test passes (Z-pass), followed by rendering the back-facing caps to decrement it, effectively counting the number of volume boundaries enclosing each pixel.[28] This approach enables efficient hardware-accelerated shadow computation by leveraging the stencil test to restrict subsequent scene rendering to lit areas only.[29] Carmack's Reverse, introduced by John Carmack for Doom 3 in 2004, is a Z-fail variant of the stencil shadow volume method that addresses artifacts from near-plane clipping by inverting the depth test behavior.[30] In this two-pass technique, shadow volume front faces are rendered with stencil increment on depth fail, and back faces with decrement on depth fail; the final scene pass then renders only where the stencil value equals zero, indicating lit pixels outside the volume.[29] The stencil value in Z-fail represents the net number of front-minus-back faces whose projections enclose the pixel, providing a robust count even when the viewpoint lies inside the shadow volume.[31] Optimizations for shadow volume generation often employ binary space partitioning (BSP) trees to efficiently extract occluder silhouettes and construct volumes per light source, reducing geometry complexity in dynamic scenes.[32] Hardware support for stencil operations in GPUs, widely available since the early 2000s with architectures like NVIDIA GeForce 3 and ATI Radeon 8500, has enabled real-time performance by accelerating the stencil updates and two-sided rendering.[27]Planar Shadows
Planar shadows are a rendering technique that projects the outline of occluding objects onto a flat receiver surface, such as the ground plane, using the light source as the center of projection, with the stencil buffer employed to confine the shadow to that surface and mitigate rendering artifacts. This approach is particularly suited for scenes where shadows fall onto planar geometry, enabling efficient real-time computation without requiring complex volumetric structures. By transforming the occluder's vertices onto the plane via a specialized projection matrix, the technique produces geometrically accurate shadows that align precisely with the light's perspective.[33] The core method involves calculating a 4×4 shadow matrix based on the light position and the receiver plane equation , where are the plane coefficients with normal . Let . The matrix has its fourth row as [0, 0, 0, 1]. For the first three rows (indices for x, y, z), the elements are for columns , where is the Kronecker delta ( for point lights and 0 for directional lights). This implements a homography that maps 3D points to their projections on the plane. The resulting polygon may extend infinitely for directional lights but is typically clipped to the view frustum to bound computation. The stencil buffer plays a crucial role by first marking the receiver plane's pixels, ensuring the projected shadow is drawn only within those bounds and avoiding depth conflicts with other geometry.[33][34] To implement planar shadows, the rendering pipeline follows these steps: clear the stencil buffer to 0 and render the scene normally, including the lit receiver plane while setting the stencil value to 1 for pixels where the depth test passes; then, for each occluder, compute the shadow matrix, multiply it with the current modelview matrix, and render the transformed geometry using alpha blending (e.g., source alpha and one-minus-source alpha) to darken the area, with the stencil test requiring equality to 1 and an operation to increment or replace the value to 2 upon passing, preventing subsequent projections from over-darkening the same pixel; finally, apply a small polygon offset to the shadow rendering to resolve z-fighting with the plane. This stencil masking restricts shadows to the receiver surface, handling cases where projections overlap or extend beyond the plane's visible area by leveraging frustum clipping.[33][34][35] Compared to naive projective rendering without stenciling, this method offers key advantages, including the elimination of double blending artifacts—where multiple shadow layers darken pixels excessively—through the stencil's one-time-write mechanism, and improved handling of self-shadowing by confining projections to the plane without interfering with the occluder's own depth-tested rendering. Additionally, by using the stencil to mask the infinite or unbounded nature of projections (e.g., from point lights), it ensures shadows remain clipped to the viewable receiver extent, reducing unnecessary fill rate and avoiding visual inconsistencies like "shadow swimming" from unmasked extensions during camera movement. These benefits make the technique performant on hardware supporting stencil operations, with minimal overhead when depth testing is enabled in 32-bit modes.[34][33][35]Spatial Shadows
Spatial shadows extend traditional shadow volume techniques to handle area or spatial light sources, producing soft-edged shadows with umbra and penumbra regions by scaling and extruding multiple silhouettes around the light source.[36] This approach approximates the varying light blockage from extended light sources, where the penumbra represents partial occlusion transitioning from full shadow to illuminated areas.[37] Originally inspired by hard shadow volumes, these methods modernize the extrusion process to generate wedge-shaped volumes that capture penumbral geometry without requiring full volumetric sampling. In stencil-based implementations, spatial shadows rely on multi-pass accumulation to build umbra and penumbra masks. Front-facing and back-facing wedges of the extruded volumes are rendered sequentially into the stencil buffer (or a dedicated light intensity buffer), incrementing or decrementing values to count ray traversals through the shadow geometry.[36] Overlap counts in the buffer then determine shadow intensity during a final lighting pass, where higher counts indicate deeper umbra and lower counts yield softer penumbral blending via alpha modulation or additive operations.[37] This enables percentage-closer-like filtering adapted for volumes, smoothing edges based on silhouette depth variations.[36] Key techniques include extruded silhouette volumes, first conceptualized by Crow in 1977 and modernized with penumbra wedges for real-time approximation.[36] Each silhouette edge generates a wedge defined by four planes—two for the umbra boundary and two for the penumbra—rasterized efficiently to avoid overdraw in complex scenes.[37] For efficiency in intricate environments, spatial data structures like hashing can accelerate silhouette extraction and culling, reducing the computational cost of volume construction.[38] Modern adaptations integrate these stencil techniques with deferred shading pipelines, where shadow volumes are rendered into the stencil during geometry passes to mask light contributions from area sources.[39] Layered stencil updates support multiple area lights by accumulating per-light masks in sequence, allowing blended occlusion without full scene re-rendering per source.[40] This combination maintains real-time performance while providing physically plausible soft shadows in dynamic scenes.[39]Additional Rendering Applications
Reflections
The stencil buffer facilitates the rendering of realistic reflections on planar surfaces, such as mirrors or calm water, by precisely masking the reflected scene to the boundaries of the reflective polygon. This technique involves identifying the mirror polygon and rendering it into the stencil buffer, setting stencil values to 1 within its bounds while maintaining 0 elsewhere, which clips subsequent rendering to the mirror area. The scene geometry is then reflected across the mirror plane by applying a transformation matrix to the vertex coordinates, ensuring points on the plane remain fixed while others are mirrored symmetrically. This approach, introduced in early hardware-accelerated rendering pipelines, leverages the stencil test to avoid rendering reflections outside the designated surface. The process unfolds in a multi-pass rendering sequence to integrate reflections seamlessly into the scene. In the first pass, the opaque geometry of the scene is rendered with depth testing enabled, but the mirror polygon is drawn solely to the stencil buffer (with color and depth writes disabled) to establish the mask. The second pass renders the transformed reflected geometry only where the stencil value equals 1, typically with depth testing disabled to prevent conflicts with the original scene's depth buffer and avoid overwriting visible elements. A third pass then renders the mirror surface itself, blending it over the reflection using alpha transparency or depth testing to simulate the reflective material's properties. This method ensures the reflection appears confined and correctly positioned without artifacts from overdraw. Edge cases in reflection rendering are managed through targeted optimizations and constraints inherent to the stencil buffer. For the reflection view, portal-like culling is applied to the reflected frustum, excluding geometry outside the mirror's visible bounds to reduce computational overhead and prevent unnecessary rendering of distant or occluded objects. Recursive reflections, where a reflected scene includes further mirrors, extend the technique by iteratively applying the stencil mask and transformations; however, this is limited primarily by performance degradation from excessive pass counts, typically to a small number of levels (e.g., 3 to 5 in practice). To mitigate recursion costs, implementations often cap depth or use simplified geometry for deeper levels. While environment mapping with cube maps offers efficient approximations for dynamic or curved reflections by precomputing or updating a 360-degree texture, the stencil buffer enables precise, geometry-based planar reflections that accurately duplicate the scene without approximation errors. This exactness comes at the cost of additional geometry passes but provides superior fidelity for flat, local reflectors in real-time applications.Decals and Compositing
The stencil buffer plays a crucial role in decal projection by masking rendering to specific surface intersections, preventing artifacts like z-fighting when overlaying textures onto base geometry. In this technique, a depth-prepass renders the base surface while writing a unique stencil value to affected pixels, ensuring subsequent decal geometry—such as bullet holes or graffiti projected onto walls—only updates pixels where the stencil matches the predefined value. This approach disables depth testing during decal rendering to allow precise blending without interference from the base's depth values.[41][19] For compositing, the stencil buffer enables layered effects by marking targeted areas for multi-pass blending, such as applying blood splatter or environmental decals to dynamic hit locations in games without affecting surrounding geometry. A common workflow involves an initial pass to set stencil values for intersection regions via ray-casting or collision detection, followed by restricted rendering passes that blend the effect only where the stencil test passes, using operations like stencil replace or increment to handle multiple layers. This method supports dynamic markings, like vehicle damage in simulations, by isolating updates to precise pixel regions.[42][19] Advanced techniques leverage the stencil buffer in screen-space decals, where projected textures are applied post-deferred shading; a stencil mask derived from depth bounds rejects fragments outside surface intersections, ensuring decals conform to geometry without additional geometry submission. In real-time constructive solid geometry (CSG)-like operations, the stencil buffer facilitates boolean unions by tracking polygon parity per pixel—toggling bits to count intersecting primitives and rendering only visible results after parity evaluation—enabling efficient blending of overlaid volumes like modular terrain or destructible environments.[43][44] Performance benefits arise from early stencil rejection, which culls overdraw in multi-pass rendering; for instance, masking dynamic objects in screen-space decals avoids unnecessary fragment processing, maintaining frame rates above 30 FPS in complex scenes with heavy decal usage, as demonstrated in production rendering pipelines. This rejection occurs at the hardware level before shading, reducing computational load compared to unmasked blending approaches.[43][41]Portal and Occlusion Culling
The portal technique in rendering leverages the stencil buffer to enable efficient visualization of interconnected scenes divided into cells, such as indoor environments. Portal polygons are defined as planar boundaries connecting adjacent cells in a spatial partitioning structure like a binary space partitioning (BSP) tree, allowing visibility determination between regions. To render through a portal, the polygon is first rasterized into the stencil buffer with a specific value (e.g., 1), enabling a mask that restricts subsequent drawing operations to the portal's projected screen area via the stencil test. This masking prevents geometry from outside the portal from appearing incorrectly, while backface culling discards non-visible surfaces during recursive traversal of connected cells. The process repeats depth-first for nested portals, incrementing or tracking stencil values to manage recursion levels and avoid infinite loops.[45][46] In occlusion culling, the stencil buffer facilitates hardware-accelerated determination of hidden geometry by counting visible pixels of potential occludees against pre-rendered occluders. Occluders—typically large, conservative bounding volumes or silhouettes—are rendered first with depth testing enabled but color writes disabled, incrementing the stencil buffer value for each passing pixel to quantify screen coverage. For candidate occludees, a low-polygon proxy (e.g., bounding box) is then rendered similarly, and an occlusion query retrieves the pixel count that survives both depth and stencil tests; if this count falls below a threshold (often 0 or a small fraction of screen resolution), the full geometry is culled to skip unnecessary draw calls. This image-space approach provides conservative but fast culling, integrating hierarchical depth buffers (hi-Z) for early rejection and precise frustum clipping.[47][45] Such stencil-based methods integrate seamlessly with BSP tree partitioning, as pioneered in games like Quake, where portals define cell connectivity for initial frustum culling, and the stencil buffer refines per-pixel visibility in hardware rendering passes. This combination optimizes large, complex indoor scenes by minimizing overdraw and vertex processing, potentially reducing draw calls by orders of magnitude in visibility-limited environments. Modern GPUs enhance these techniques through extensions for conditional rendering tied to occlusion queries, allowing dynamic skipping of occluded subtrees without CPU intervention.[45][48]API Implementations
OpenGL
In OpenGL, the stencil buffer is controlled through a set of functions in the fixed-function pipeline, enabling per-fragment testing and updates during rasterization.[15] The stencil test is activated by callingglEnable(GL_STENCIL_TEST), which applies the test to incoming fragments before they affect the framebuffer.[49] Once enabled, the comparison function, reference value, and mask are specified using glStencilFunc(func, ref, mask), where func determines the test (e.g., GL_ALWAYS, GL_EQUAL), ref is the reference value clamped to the stencil buffer's bit depth, and mask bitwise-ANDs with both the reference and the fragment's stencil value for comparison.[15] The outcomes of the stencil test—stencil fail, depth fail (if depth testing is enabled), and depth pass—are handled by glStencilOp(sfail, dpfail, dpass), which specify operations like GL_KEEP, GL_INCR, GL_DECR, or GL_ZERO to update the stencil buffer.[7] Additionally, glStencilMask(mask) controls which bits of the stencil buffer can be written, with the mask applying to both front- and back-facing primitives unless specified otherwise.[16]
To support two-sided rendering, such as in shadow volumes where front and back faces require different stencil operations, OpenGL provides separate control for front- and back-facing polygons via the glStencilFuncSeparate(face, func, ref, mask) and glStencilOpSeparate(face, sfail, dpfail, dpass) functions, along with glStencilMaskSeparate(face, mask).[50][51] These were introduced as the EXT extension in 2003 (building on earlier vendor extensions like ATI_separate_stencil) and incorporated into the OpenGL 2.0 core specification.[52] The face parameter accepts GL_FRONT, GL_BACK, or GL_FRONT_AND_BACK to target specific winding directions, allowing asymmetric stencil behavior without state changes between draw calls.[50]
In modern OpenGL versions (3.3 and later), the stencil buffer functionality is retained primarily in the compatibility profile to support legacy fixed-function pipelines, while the core profile emphasizes programmable shaders but still includes the stencil test as part of the fragment processing pipeline.[14] Direct interaction with the stencil buffer from shaders is limited and typically requires extensions; for instance, the rare ARB_shader_stencil_export extension (approved 2010) allows fragment shaders to export a per-fragment stencil reference value via the gl_FragStencilRefARB output variable, influencing the test or write operations without reading the buffer contents.[53] Reading the stencil buffer in shaders is not natively supported and generally requires off-line queries like glReadPixels.
The following example demonstrates enabling the stencil test and configuring it to increment the stencil value on depth pass for front faces (common in shadow volume extrusion), using separate functions for clarity:
glEnable(GL_STENCIL_TEST); // Enable stencil testing[](https://registry.khronos.org/OpenGL-Refpages/gl4/html/glEnable.xhtml)
glStencilFuncSeparate(GL_FRONT, GL_ALWAYS, 0, 0xFF); // Always pass for front faces, full mask[](https://registry.khronos.org/OpenGL-Refpages/gl4/html/glStencilFuncSeparate.xhtml)
glStencilOpSeparate(GL_FRONT, GL_KEEP, GL_KEEP, GL_INCR); // Increment on depth pass for front[](https://registry.khronos.org/OpenGL-Refpages/gl4/html/glStencilOpSeparate.xhtml)
glStencilMask(0xFF); // Allow writes to all bits[](https://registry.khronos.org/OpenGL-Refpages/gl4/html/glStencilMask.xhtml)
glEnable(GL_STENCIL_TEST); // Enable stencil testing[](https://registry.khronos.org/OpenGL-Refpages/gl4/html/glEnable.xhtml)
glStencilFuncSeparate(GL_FRONT, GL_ALWAYS, 0, 0xFF); // Always pass for front faces, full mask[](https://registry.khronos.org/OpenGL-Refpages/gl4/html/glStencilFuncSeparate.xhtml)
glStencilOpSeparate(GL_FRONT, GL_KEEP, GL_KEEP, GL_INCR); // Increment on depth pass for front[](https://registry.khronos.org/OpenGL-Refpages/gl4/html/glStencilOpSeparate.xhtml)
glStencilMask(0xFF); // Allow writes to all bits[](https://registry.khronos.org/OpenGL-Refpages/gl4/html/glStencilMask.xhtml)