emulating stencil buffer

Started by
4 comments, last by vincoof 22 years, 2 months ago
As a weird topic, I won''t post any problem, but rather a *real* discussion. Don''t you guys (&gals?) think that the stencil buffer could be emulated in many ways ? We all know that *old* graphics card does NOT support hardware stenciling, and some recent cards does NOT support hardware stenciling UNLESS the stencil buffer uses bits from the depth buffer. My question is simple to understand (a bit more difficult to answer) : What stencil buffer emulations do you know ? By "emulation", I mean looking for stencil buffer effects without the stencil buffer.
Advertisement
I can't think of any super-fast methods off the top of my head, but one possibility would be to render the scene to be masked (not the base un-stenciled scene) to a texture, and render that texture on a quad over the entire scene (alpha masked as in NeHe's masking tutorial, with depth testing disabled). This may be limited to hardware that can support larger textures, and it's also speed limited, as renders to a texture have never been incredibly fast in my experience.


email

Something kinda sad about
the way that things have come to be.
Desensitized to everything.
What became of subtlety?
--Tool



Edited by - Lord Karnus on February 1, 2002 10:33:08 AM
[email=JValentine_13@hotmail.com]contact[/email]
okay, I''ll open the thread with the sample application of reflection.


In OpenGL, reflection is often done using the stencil buffer with the following algorithm :

draw reflection plane''s mask
flip the scene and draw it, only in the mask
draw the reflection plane (with blending)
draw the normal (non-flipped) scene

which is detailed as follow :

/* first of all : draw the mask */
activate_stencil_write();
deactivate_every_buffer_but_stencil(); /* set all color making to false, disable depth testing, etc */
draw_reflection_plane(); /* draw plane into stencil buffer only */

/* secondly : draw the reflected scene */
glPushMatrix();
reactivate_every_disabled_buffer(); /* set all color making to true, enable depth testing, etc */
flip_the_scene(); /* Something like glScalef(1.0f, -1.0f, 1.0f); */
set_light_positions();
set_clipping_planes(); /* optional */
activate_stencil_test();
draw_scene(); /* scene is drawn, flipped and clipped, using stencil for masking */
deactivate_stencil_test(); /* stop masking */
glPopMatrix();

/* thirdly : draw the reflection plane */
enable_blending();
draw_plane();
disable_blending(); /* note: this might be done only if blending was disabled before drawing the plane */

/* at last : draw the scene */
set_light_positions();
draw_scene(); /* scene is drawn, unflipped, without masking because stenciling is disabled */



In that case, stencil test is used to draw the reflected scene to lie within a window defined by the reflection plane.
(see Mark Kilgard''s screenshots there : Rendering Fast Reflections with OpenGL)

This usage of stencil buffer can be emulated by a technique posted by someone else (I don''t know his name : he''s an anonymous poster) which consists of using depth buffer :

/* first of all : draw the mask */
glClearDepth(0.0f);
glClear(GL_DEPTH_BUFFER_BIT); /* clear depth buffer with 0, meaning that all pixels are at the lowest depth in the frustum */
glEnable(GL_DEPTH_TEST);
glDepthFunc(GL_ALWAYS); /* don''t care of what the depth buffer contains : depth test always succesful */
glDepthRange(1.0f,1.0f); /* all depths will be at 1, meaning that pixels will have the highest depth */
deactivate_every_buffer_but_depth(); /* set all color making to false, disable lighting, etc */
draw_reflection_plane(); /* draw plane into depth buffer only */
glDepthRange(0.0f, 1.0f); /* Reset depth buffer range*/
glDepthFunc(GL_LEQUAL); /* Restore "normal" depth test function */


/* secondly : draw the reflected scene */
...
Oops, Lord Kranus replied before I did

Lord Kranus : your technique is not as slow as it might seems, since there exists fast copy from framebuffer to texture (see tutorial #37). The only limitation, IMO, is that the texture has to be a "power-of-two".

But instead of rendering into a texture, you could just render the mask to alpha buffer (red/green/blue buffers disabled). Something like :

/* Draw the mask */
glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_TRUE); /* enable alpha writing only */
glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
glClear(GL_COLOR_BUFFER_BIT); /* Clear alpha buffer */
glDisable(GL_LIGHTING); glDisable(GL_DEPTH_TEST); glDisable(GL_BLEND); glDisable(... /* disable everything possible, in order to draw fast */
glColor4f(0.0f, 0.0f, 0.0f, 1.0f); /* Set alpha to 1.0f */
drawMask(); /* draw mask into alpha buffer only. note that this draw is very fast due to above deactivations */
glEnable(GL_LIGHTING); glEnable(... /* enable again everything that was disabled three lines above */

/* Draw the scene in the mask */
glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_FALSE); /* enable rgb writing, disable alpha writing */
glEnable(GL_BLEND);
glBlendFunc(GL_DST_ALPHA, GL_ONE_MINUS_DST_ALPHA);
drawScene(); /* draw scene everywhere alpha buffer is 1, and only there, thanks to blending */

Edited by - vincoof on February 1, 2002 11:06:44 AM
The alpha masking would work, but you couldn''t draw any otherwise blended geometry (eg. transparency) in your reflections, since you need the blend function for your masking. And enabling blending reduces the fillrate by 50%.

OT: But you can use a similar alpha trick to do hardware accelerated shadow maps on any card, even old Voodoo2...
Oh, you want to blend the polygons that will be clipped by the mask ?
Obviously, this method does not work, but a little addition can do that :

/* draw the mask */
... /* copy the code of this section in previous post */

/* Prepare alpha buffer to use that missing blending */
glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_TRUE); /* enable alpha writing only */
glEnable(GL_BLEND);
glBlendFunc(GL_ZERO, GL_SRC_ALPHA)
drawScene(); /* draw the scene to alpha buffer */

/* draw the scene into the mask */
... /* copy the code of this section in previous post */


Please note that, in this case, glBlendFunc(GL_ZERO, GL_SRC_ALPHA) is equivalent to glBlendFunc(GL_DST_ALPHA, GL_ZERO).

The thing is, translucent polygons may not overlap.
But for simple cases, it works.

Also, I admit that the fillrate is overloaded ; but as you noticed, in old cards (where alpha buffer is hardware, not the stencil buffer) it will improve performance anyway.

This topic is closed to new replies.

Advertisement