PDA

View Full Version : stencil backwards



zed
06-13-2005, 11:55 PM
just a comment
is it just me but stencil seems backwards

alphatest is logical
glAlphaFunc( GL_GREATER, 0.0 )
the fragment passes if its alpha value is greater than 0.0 ok simple

now i want the fragment to pass if the existing stencilvalue > 13, logically i would think i would write
glStencilFunc( GL_GREATER, 13, 0xff );
but ive gotta use
glStencilFunc( GL_LESS, 14, 0xff );
the spec is pretty unclear
"Accordingly, the stencil test passes never, always, and if the masked reference value is less than, less than or equal to, equal to,greater than or equal to, greater than, or not equal to the masked stored value in the stencil buffer"

my english aint that good, but after 20 reads im still scratching my head

dorbie
06-14-2005, 12:05 AM
It is the same (look at what happens to your color fragments).

The long list of values in that sentence refer to the GL_GREATER, GL_LESS etc. values, and the mask just refers to a bitmask of stencil bits, it doesn't apply directly to your perceived problem.

w.r.t. stencil pass or fail etc there are 3 possible outcomes for stencil ops.

Here's the key difference, you can apply a stencil op on stencil fail, on stencil_pass+z_fail or on stencil_pass+z_pass, so I think your perceived lack of symmetry comes from the stencil op on stencil test fail.

So to reword this, you're looking at the stencil operation on stencil fail and saying it's different, but the test isn't, it's just that after stencil pass you have a zbuffer test and two possible outcomes to give you more flexibility.

Once again, to make it absolutely clear:


void glStencilOp( GLenum fail, /* this is a stencil FAIL */
GLenum zfail, /* stencil PASS */
GLenum zpass /* stencil PASS */
)So you see there's no problem here, you have the option to specify KEEP on a stencil fail and whatever operation you like for z pass & fail and your test will be logically correct. The only difference is that stencil test allows you to perform an operation on a fail and a pass (after z test is also done).

dorbie
06-14-2005, 12:23 AM
This should make it clear than only a stencil pass and depth pass leads to a color fragment and depth write. For utility different stencil ops can be performed on stencil fail and depth fail in addition to depth pass.


/\ /\
/ \ / \
/ \ / \
/ \ / \
/ \ / \
/ \ PASS / \ PASS Color Write (blend)
pixel---->-<Stencil Test>--->--< Depth Test >-->-----Stencil Op (zpass)
\ / \ / Depth Write
\ / \ /
\ / \ /
\ / \ /
\ / \ /
\/ \/
| |
V FAIL V FAIL
| |
Stencil Op (fail) Stencil Op (zfail)

zed
06-14-2005, 12:53 PM
tahnks for the easy to understand explanation dorbie but
im gonna make myself look foolish but i still dont think its correct
NOTE i have no problems to do exactly what i want to do with the stencilbuffer.
i do agree with what youve said but it sorta wasnt what i wanted,
my question is how exactly does a fragment pass or fail the stencil test ie remove glStencilOp from consideration

say in the buffer the current stencil value is 10

now my new fragment comes along + i have
glStencilFunc( GL_GREATER, 13, 0xff );
my reasoning is
10 > 13 == false thus the stencil test has failed
so glStencilOp( this one, .. , .. ) gets done
but in reality the opposite happens

current stencil value == 10
i set glStencilFunc( GL_GREATER, 13, 0xff );
and draw a pixel, it passes the stencil test!
ie its telling me 10 > 13

dorbie
06-14-2005, 07:54 PM
I don't agree, but your issue is thinking too much about alpha ref. Consider instead the depth test.

There are two values in any compare, the source and destination.

With z you take the depth source and compare to the depth destination. When considering the stencil test you take the source stencil (which is a constant) and compare against the destination stencil.

With alpha ref there is no destination used in the test, so you take the source alpha and compare against the ref.

This is the key difference.

Stencil has no source and it's specified as a constant, but it definitely has a destination.

Alpha test on the other hand doesn't use a destination value (it's meaningless to do so) and instead uses a ref.

So all tests conceptually compare incomming fragment to destination fragment in a consistent way, but alpha test uses a constant ref as a compare vs incomming and stencil uses a constant ref for source (which it otherwise does not have).

It makes a lot of sense especially when you consider the depth test instead of the alpha test, and the availability of source and destination values.

zed
06-15-2005, 01:15 AM
ta, ive got it now u compare the reference value TO whatevers in the buffer. + not the buffer TO the reference value.

though aint this is a bit redundant
glStencilFunc( GL_GREATER, 13, 0xff );
surely if u felt the need for a mask u could do
glStencilFunc( GL_GREATER, 13&0xff );

anyways heres what ive done showing how much overdraw each pixel gets black=none,darkgreen=1,darkbue=3,darkred=5, ..white=20+

(didnt show up in post so these are the links)
http://uk.geocities.com/sloppyturds/nelson/2005_06_15A.jpg
http://uk.geocities.com/sloppyturds/nelson/2005_06_15B.jpg http://uk.geocities.com/sloppyturds/nelson/2005_06_15A.jpg
http://uk.geocities.com/sloppyturds/nelson/2005_06_15B.jpg
worthwhile implementing this (help found one bug in my code i didnt know existed)

Bob
06-15-2005, 07:43 AM
Originally posted by zed:

though aint this is a bit redundant
glStencilFunc( GL_GREATER, 13, 0xff );
surely if u felt the need for a mask u could do
glStencilFunc( GL_GREATER, 13&0xff );
Because the value in the stencil buffer is also affected by the mask, you cannot combine the two. You must keep them separate, otherwise it's not possible to determine what mask to use for the stencil buffer values.

dorbie
06-15-2005, 05:22 PM
Even considering your example at face value, it wouldn't work the same, think about an == operation. Masking just the value input doesn't cut it, not just for writes but for tests on the unmasked read value too.

I see you're using stencil increments to count depth complexity, cool, SGI's OpenGL Performer used to do that (still does), I think it's been described in "Real-Time Rendering" by Moller and Haines, they also suggest additive blend if you're missing a stencil buffer. With stencil you can support multiple modes that incremented on zfail or not thereby counting rejected pixels with two passes. Bonus points for reading back the buffers and counting the pixels you're drawing per frame (vs shading per frame).

P.S. incrementing on zfail and not zpass would also get you rejected fragments in a single pass, but z pass pixels and total pixels are two interesting metrics on any early z architecture.

zed
06-16-2005, 12:36 AM
Originally posted by dorbie:
Even considering your example at face value, it wouldn't work the same, think about an == operation. Masking just the value input doesn't cut it, not just for writes but for tests on the unmasked read value too.ok ok dont rub it in :) , i blame all the physics ive been doing recently, my head hurts

With stencil you can support multiple modes that incremented on zfail or not thereby counting rejected pixels with two passes. Bonus points for reading back the buffers and counting the pixels you're drawing per frame (vs shading per frame)yeah there are quite a few extra measurements i could add in. perhaps after the physics, im experimenting with a repulsion voxel like collision response system, quite fun, but hard.

T101
06-16-2005, 02:20 AM
Being used to left to right reading I would say specifying it as glStencilFunc(constant, GL_GREATER, etc) is maybe a bit more natural.

Zed, I like that screenshot by the way. Very atmospheric.

dorbie
06-22-2005, 10:37 AM
P.S. masks are just for writes, my bad.