I am try to Limit OpenGL draw to part of the window. In order to do it I am trying to use GL_STENCIL_TEST option. Here is my code:
// Enable stencil and clear the stencil buffer
glClear(GL_STENCIL_BUFFER_BIT);
glEnable(GL_STENCIL_TEST);
// Enable func and Op so drawing will effect the stencil buffer
glStencilFunc(GL_NEVER, 0x0, 0x0);
glStencilOp(GL_INCR, GL_INCR, GL_INCR);
// Draw a rect to the stencil
DrawSolidRect(rectDrawArea);
// Enable func and Op so drawing will not effect the stencil buffer
// but will only effect places where the stencil was drawn to in the previous step.
glStencilFunc(GL_EQUAL, 0x1, 0x1);
glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);
The problem is that glClear(GL_STENCIL_BUFFER_BIT) function is very time consuming, and since I am drawing with 25 fps, it really slows down the application.
I tried to remove this function - this solves the slowness problem but it causes flickering when I initialize the application - For about 10 seconds. Than it disappears and works ok.
Can someone suggest why the flickering is or what other function may I use that not so time-consuming?
If you're using rectangular clip regions, it's probably MUCH easier and faster to use glScissor. http://www.opengl.org/sdk/docs/man/xhtml/glScissor.xml
As noted in the link above, also call glEnable(GL_SCISSOR_TEST) to enable scissor testing.
Thanks to #datenwolf
Related
The stencil buffer is not working at all. It is as if it is not turned on. With the below code, I expect to see nothing on the screen as my test is GL_NEVER which will always fail. But the rectangle reliably show up on the screen.
glEnable(GL_STENCIL_TEST);
glStencilMask(0xff)
glClear(GL_STENCIL_BUFFER_BIT)
glStencilFunc(GL_NEVER, 1, 0xFF);
glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);
glBegin(GL_QUADS)
glVertex3fv(...); glVertex3fv(...)
glVertex3fv(...); glVertex3fv(...)
glEnd()
Another thing worth noting is that I am using pygame+ pyOpenGL.
pygame.display.set_mode(display, DOUBLEBUF|OPENGL)
I saw references with GLUT saying that a stencil buffer needs to be allocated on initialization, but I am not sure how to with pygame.
This took me some time to figure out. So I am going to put it here in case someone else runs into this too.
pygame.display.gl_set_attribute(GL_STENCIL_SIZE, 8)
pygame.display.set_mode(display, DOUBLEBUF|OPENGL)
I would like it so that when mesh A (the character), is behind mesh B (a wall), it is still rendered but with a solid gray color.
I'm beginning opengles 2.0 and I'm still unsure as to go about this. From what I understand the depth buffer allows meshes to fight out who will be seen in the fragments they encompass, also there are various blend functions that could possibly involved in this, finally the stencil buffer looks like it would also have this desirable functionality.
So is there a way to output different colors through the shader based on a failed depth test? Is there a way to do this through blending? Or must I use the stencil buffer some how?
And what is this technique called for future reference? I've seen it used in a lot of video games.
This can be done using the stencil buffer. The stencil buffer gives each pixel some additional bits which can be used as a bitmask or a counter. In your case you'd configure the stencil test unit to set a specific bitmask when the depth test for the character fails (because it's obstructed by the well). Then you switch the stencil test mode operation to pass the stencil test for this specific bitmask, and render a full viewport, solid quad in the desired color, with depth testing and depth writes disabled.
Code
I strongly recommend you dive deep into the documentation for the stencil test unit. It's a very powerful mechanism, often overlooked. Your particular problem would be solved by the following. I stuggest you take this example code, read it in parallel to the stencil test functions references glStencilFunc, glStencilOp.
You must add a stencil buffer to your frame buffer's pixel format – how you do that is platform dependent. For example, if you're using GLUT, then you'd add |GLUT_STENCIL to the format bitmask of glutInitDisplayMode; on iOS you'd set a property on your GLKView; etc. Once you've added a stencil buffer, you should clear it along with your other render buffers by adding |GL_STENCIL_BUFFER_BIT to the initial glClear call of each drawing.
GLint const silouhette_stencil_mask = 0x1;
void display()
{
/* ... */
glEnable(GL_DEPTH_TEST);
glDepthMask(GL_TRUE);
glDepthFunc(GL_LESS);
glDisable(GL_STENCIL_TEST);
/* The following two are not necessary according to specification.
* But drivers can be buggy and this makes sure we don't run into
* issues caused by not wanting to change the stencil buffer, but
* it happening anyway due to a buggy driver.
*/
glStencilFunc(GL_NEVER, 0, 0);
glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);
draw_the_wall();
glEnable(GL_STENCIL_TEST);
glStencilFunc(GL_ALWAYS, silouhette_stencil_mask, 0xffffffff);
glStencilOp(GL_KEEP, GL_REPLACE, GL_KEEP);
draw_the_character();
glStencilFunc(GL_EQUAL, silouhette_stencil_mask, 0xffffffff);
glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);
glDisable(GL_DEPTH_TEST);
glDepthMask(GL_FALSE);
draw_full_viewport_solid_color();
/* ... */
}
I was trying to configure my stencil buffer so that, when enabled, it would set when the pixel drawn is not transparent (thus creating a map of pixels that light can collide with). What I've done is:
glClearStencil(0); //clear stencil
glStencilFunc(GL_EQUAL, 0xFF, 0x000000FF); //only where alpha (mask : 0x000000FF) is 0xFF (opaque)
glStencilOp(GL_INCR, GL_KEEP, GL_KEEP); //increment if passes (if it is opaque)
render(); //withing this method I sometimes disable the whole thing to draw the floor, for example
Then, i use the following code to test:
/* TURN OFF STENCIL */
glEnable(GL_STENCIL_TEST); //re-enable
glStencilFunc(GL_EQUAL, 0, 1); //if the test is equal to 1
glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP); //do not change stencil buffer
ImageInfo.drawColorSquare(0, 0, Configurations.SCREEN_WIDTH, Configurations.GAME_HEIGHT, Color.BLUE); //drwa blue square
glDisable(GL_STENCIL_TEST); //disable
However, there are two problems:
It doesn't seem to be ignoring transparent pixels, as it should;
If a region overlaps with another, then it reverses - for example, it sets to one, then another region is drawn in the same area, and it reset it to 0 again.
I don't know why that is happening. Probably something wrong with my mask, I guess - I wasn't absolutely sure how many pixels OpneGL used in the Color Buffer.
Also, GL_INCR should add up to the max, and not go back, according to the documentation. Since my stencil buffer size is one bit, it should set to one, try to increase again, fail, and keep on one (instead of reseting).
The Stencil Test is independent of what happens in the color buffer. Setting glStencilFunc
( http://www.opengl.org/sdk/docs/man/xhtml/glStencilFunc.xml )
you can specify how the Stencil Test interacts with what is already stored in the Stencil Buffer.
Setting glStencilOp ( http://www.opengl.org/sdk/docs/man/xhtml/glStencilOp.xml ) gives you the possibility of using the result of the depth test to perform the Stencil Test.
A good tutorial that explain the Stencil Test and a very instructive algorithm based upon it can be found here http://ogldev.atspace.co.uk/www/tutorial37/tutorial37.html
The stencil buffer is usually an 8-bit buffer that reserves a small portion of the memory normally used for the depth buffer and is used for advanced rejection of fragments; things like masking to arbitrary shapes rather than using rectangular scissor boxes. It has nothing to do with your color buffer, and to make sure that fragments that have a specific alpha value do not affect the pixels on screen you would use something called an alpha test.
In core OpenGL 3, the fixed-function alpha test is no longer supported, so you would have to implement it in a fragment shader and then discard if it failed to meet your condition.
I want to set up multiple stencils in OpenGL and then draw into multiple combinations of them.
For example:
I have two rectangular stencils like this:
Then I want to draw into:
anywhere
left rectangle (blue+purple)
right rectangle (purple+red)
middle rectangle (purple)
whole colored area (blue+purple+red)
I have found that it is possible to declare multiple stencils in different bits of stencil buffer but I don't know how.
How do I setup glStencilOp and glStencilFunc for this to work?
Or can I (should I) use glScissor for that?
I don't currently know, if it's possible to configure the stencil buffer for being able to do all the above 5 steps without making any changes to the stencil buffer between them. It would be easy if glStencilOp provided bitwise OR, but it doesn't and with just using increment or decrement you would have to draw the rectangles multiple times.
But if the regions are always rectangles, why not just use the scissor test? So the first 3 steps (or actually 2 and 3) can be done by just setting the rectangle's region with glScissor and enabling the scissor test (glEnable(GL_SCISSOR_TEST)).
For the middle one (step 4) you either compute the purple intersection rectangle yourself and use the scissor test again, or you use the stencil test:
glEnable(GL_STENCIL_TEST);
glStencilFunc(/*whatever*/);
glStencilOp(GL_INCR, GL_INCR, GL_INCR); //increase the stencil value
//draw both rectangles
glStencilFunc(GL_EQUAL, 2, 0xFFFFFFFF); //only draw where both rectangles are
glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP); //don't change the stencil buffer
//draw things
So we first draw both rectangles and increase the stencil value everywhere they are drawn. Then we draw our things everywhere the stencil value is 2, meaning both rectangles were drawn.
For the 5th step you use the same stencil buffer, but with
glStencilFunc(GL_LEQUAL, 1, 0xFFFFFFFF);
for the second pass. This way you draw something everywhere the stencil buffer is at least 1, meaning at least one rectangle was drawn.
For more than two rectangles it can get more complicated and you need to play around a bit to find the most optimal way.
I try to use the stencil buffer of a FBO in OpenGL, but I can't get it to work. I bound a depth24_stencil8 texture to the FBO both for the depth and stencil targets. As a simple test, I tried:
/* Enable FBO */
glEnable(GL_STENCIL_TEST);
glStencilFunc(GL_NEVER, 1, 0xff);
glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);
glBegin(GL_TRIANGLE);
/* Draw some triangles */
glEnd();
glDisable(GL_STENCIL_TEST);
/* Disable FBO and render it on screen as a texture. */
As I use GL_NEVER, nothing should be rendered at all, but I can see the triangles. This is like if there were no stencil at all, but I cannot understand why. Trying this code without FBOs works, so I think I use the stencil functions correctly. I don't have any idea how to solve this problem. Did anyone already use a stencil with FBOs?
My bad, I was not attaching correctly the stencil buffer to my FBO. Strange thing is that my fbo status was not indicating any error or bad attachement, so I was persuaded it was OK... this problem has driven me crazy but now it seems to work.