I want to be able to clip my postprocessing image passes to specific regions so that effects such as a blur would only affect theses regions
In order to do that i use the stencil buffer and my pipeline is as follows :
Render some objects to the stencil buffer only, writing 1s
Render some objects where the stencil value equals 1 (this works)
Render some objects whatever there is in the stencil buffer
Run postprocess passes (by drawing a quad with the image resulting of the 3 previous step as a bound texture) where the stencil value equals 1 (or always, depending on an attribute of my effects)
The results i get :
Black image when postprocess involves stencil buffer
'Good' image when it does not
An image with non null values only outside the masks when i change `glStencilFunc(GL_EQUAL, 1, 0xFF);` to `glStencilFunc(GL_NOTEQUAL, 1, 0xFF);`
What strikes me is the fact that the image obtained with glStencilFunc(GL_ALWAYS, 1, 0xFF); is not even equal to the union of the other two : the one with glStencilFunc(GL_EQUAL, 1, 0xFF); is all black.
What is wrong with this code ?
gl->glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, objectsTex, 0);
gl->glClear(GL_COLOR_BUFFER_BIT);
// ================= Masks ===================
gl->glEnable(GL_STENCIL_TEST);
gl->glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE); // Disable color buffer writing
gl->glStencilFunc(GL_ALWAYS, 1, MASKSBITPLANE);
gl->glStencilOp(GL_KEEP, GL_KEEP, GL_REPLACE);
gl->glStencilMask(MASKSBITPLANE); // Write values as is in the stencil buffer
gl->glClear(GL_STENCIL_BUFFER_BIT);
for (const auto& scobjptr : renderGroup->getRenderGroupObjects().getMaskObjects()){
renderBlankSceneObject(scobjptr, gl, glext);
}
// ================= Masked ===================
gl->glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); // Enable color buffer writing
gl->glStencilFunc(GL_EQUAL, 1, MASKSBITPLANE);
gl->glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);
gl->glStencilMask(0x00); // Disable writing to the stencil buffer
for (const auto& scobjptr : renderGroup->getRenderGroupObjects().getMaskedObjects()){
renderSceneObject(scobjptr, gl, glext);
}
// ================= Raw objects ===================
gl->glDisable(GL_STENCIL_TEST);
for (const auto& scobjptr : renderGroup->getRenderGroupObjects().getRawObjects()){
renderSceneObject(scobjptr, gl, glext);
}
// ================= Postprocess ===================
auto& shaderEffects(renderGroup->shaderEffects());
if (renderGroup->areShaderEffectsMasked()){
gl->glEnable(GL_STENCIL_TEST);
gl->glStencilFunc(GL_EQUAL, 1, MASKSBITPLANE);
gl->glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);
gl->glStencilMask(0x00); // Disable writing to the stencil buffer
}
for (auto it(shaderEffects.begin()); it != shaderEffects.end(); ++it)
{
gl->glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, currentImageTex, 0);
gl->glClear(GL_COLOR_BUFFER_BIT);
// postprocess
gl->glUseProgram(shaderEffect->program().programId());
gl->glUniform1f(shaderEffect->m_timeLocation, m_time.elapsed());
gl->glActiveTexture(GL_TEXTURE0 + GLShaderEffect::PROCESSED_IMAGE_TEXTURE);
gl->glBindTexture(GL_TEXTURE_2D, processedTexture);
// some glUniform* calls
updateUniforms(gl, shaderEffect->ressourceClientsCollection());
// some glActiveTexture + glBindTexture calls
bindTextures(gl, shaderEffect->ressourceClientsCollection());
glext->glBindVertexArray(shaderEffect->vao());
gl->glDrawElements(GL_TRIANGLES, shaderEffect->elementsCount(), GL_UNSIGNED_INT, nullptr);
swap(currentImageTex, objectsTex);
}
The answer : I didn't restore the context after drawing, e.g. disable stencil testing at the end of my render pass.
I use Qt to blit my framebuffer into a widget and the stencil test was still active with another stencil buffer attached : that is why the screen got black.
Conclusion : Always restore the context to its previous state when you use a framework
Related
I draw two triangles. Before draw the first, I set the contents of the stencil buffer to make sure the stencil test will fail. So the first triangle won't be rendered on the screen. At the same time, I call the func "glStencilOp(GL_REPLACE, GL_KEEP, GL_KEEP)" to replace the contents of the stencil buffer with new value ref. Then I draw the second triangle. Theoreticaly, the stencil test in drawing this triangle should pass and the second triangle should be rendered on the screen. While in fact, the second stencil test failed again! I can't find the reason. Can anyone help me ? Thanks.
glClearColor(0.2f, 0.3f, 0.3f, 1.0f);
glStencilOp(GL_REPLACE, GL_KEEP, GL_KEEP); // replace stencil buffer when test failed
glStencilMask(10); // set mask 10
glClearStencil(13); // set clear value 13
// since mask is 10 and value is 13, so the contents of stencil buffer is 8
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
glStencilMask(0xff); // enable the writing of stencil buffer
glStencilFunc(GL_EQUAL, 9, 0xf); // (0xf & 8) != (0xf & 9),stencil test failed
// this triangle won't rendered
glUseProgram(shaderProgramOrange);
glBindVertexArray(VAOs[0]);
glDrawArrays(GL_TRIANGLES, 0, 3);
glStencilFunc(GL_EQUAL, 9, 0xf); // now ref is 9,theoreticaly, the stencil test should pass
// in fact, the test still failed! I didn't know why.
// this triangle should have been rendered
glUseProgram(shaderProgramYellow);
glBindVertexArray(VAOs[1]);
glDrawArrays(GL_TRIANGLES, 0, 3);
glBindVertexArray(0);
// Swap the screen buffers
glfwSwapBuffers(window);
glStencilMask(0xff);
glClearStencil(0);
glClear(GL_STENCIL_BUFFER_BIT);
Well, I’m trying to draw tiles into a stencil buffer, but while drawing obviously happens something that I don’t understand and during the drawing disappearing (not drawn) part of the tiles.
So, how I draw:
// Enable blending.
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
// Enable testing.
glEnable(GL_STENCIL_TEST);
// Disable depth test.
glDisable(GL_DEPTH_TEST);
// Set stencil buff to 0.
glClearStencil(0);
glClear(GL_STENCIL_BUFFER_BIT);
// Here I got all visible tiles by a camera.
auto const visible_tiles = camera.visible_tiles();
// Draw tiles into a stencil.
glStencilOp(GL_KEEP, GL_KEEP, GL_REPLACE);
glStencilMask(0xFF);
// Don't output the color.
glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE);
// ...
// Here I had a loop where I go through each tile from all which was visible for the camera.
auto tile_id = 1;
visible_tiles.for_each_tile([this, &tile_id](auto const& tile_position) {
// Output tile ID into stencil buffer. I assume there will never be more than
// 255 tiles.
glStencilFunc(GL_ALWAYS, tile_id, 0xFF);
mesh_->draw(); // << Here I draw.
++tile_id;
});
// Disable testing.
glDisable(GL_STENCIL_TEST);
However, how my problem looks like?
]1
If I disable stencil testing at all, everything is OK.
And we draw it correctly.
UPD: With the help of the debug, I managed to narrow the circle of suspects )))
And I realized that the problem was somewhere in glEnable(GL_STENCIL_TEST), namely when I repeatedly call my method “draw(…)”.
So, I just did the following:
static bool flag = false;
if (!flag) {
glEnable(GL_STENCIL_TEST);
flag = true;
}
So, now I call glEnable(GL_STENCIL_TEST) only once, at first call of draw, and didn’t call glDisable(GL_STENCIL_TEST);
And it looks as if everything works correctly, at least now I didn’t see any defects.
But why this works?
I am trying to write a fluid simulator that requires solving iteratively some differential equations (Lattice-Boltzmann Method). I want it to be a real-time graphical visualisation using OpenGL. I ran into a problem. I use a shader to perform relevant calculations on GPU. What I what is to pass the texture describing the state of the system at time t into the shader, shader performs the calculation and returns the state of the system at time t+dt, I render the texture on a quad and then pass the texture back into the shader. However, I found that I can not read and write to the same texture at the same time. But I am sure I have seen implementations of such calculations on GPU. How do they work around it? I think I saw a few discussion on a different way of working around the fact that OpenGL can read and write the same texture, but I could not quite understand them and adapt them to my case. To render to texture I use: glFramebufferTexture(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, renderedTexture, 0);
Here is my rendering routine:
do{
//count frames
frame_counter++;
// Render to our framebuffer
glBindFramebuffer(GL_FRAMEBUFFER, FramebufferName);
glViewport(0,0,windowWidth,windowHeight); // Render on the whole framebuffer, complete from the lower left corner to the upper right
// Clear the screen
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
// Use our shader
glUseProgram(programID);
// Bind our texture in Texture Unit 0
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, renderTexture);
glUniform1i(TextureID, 0);
printf("Inv Width: %f", (float)1.0/windowWidth);
//Pass inverse widths (put outside of the cycle in future)
glUniform1f(invWidthID, (float)1.0/windowWidth);
glUniform1f(invHeightID, (float)1.0/windowHeight);
// 1rst attribute buffer : vertices
glEnableVertexAttribArray(0);
glBindBuffer(GL_ARRAY_BUFFER, quad_vertexbuffer);
glVertexAttribPointer(
0, // attribute 0. No particular reason for 0, but must match the layout in the shader.
3, // size
GL_FLOAT, // type
GL_FALSE, // normalized?
0, // stride
(void*)0 // array buffer offset
);
// Draw the triangles !
glDrawArrays(GL_TRIANGLES, 0, 6); // 2*3 indices starting at 0 -> 2 triangles
glDisableVertexAttribArray(0);
// Render to the screen
glBindFramebuffer(GL_FRAMEBUFFER, 0);
// Render on the whole framebuffer, complete from the lower left corner to the upper right
glViewport(0,0,windowWidth,windowHeight);
// Clear the screen
glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
// Use our shader
glUseProgram(quad_programID);
// Bind our texture in Texture Unit 0
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, renderedTexture);
// Set our "renderedTexture" sampler to user Texture Unit 0
glUniform1i(texID, 0);
glUniform1f(timeID, (float)(glfwGetTime()*10.0f) );
// 1rst attribute buffer : vertices
glEnableVertexAttribArray(0);
glBindBuffer(GL_ARRAY_BUFFER, quad_vertexbuffer);
glVertexAttribPointer(
0, // attribute 0. No particular reason for 0, but must match the layout in the shader.
3, // size
GL_FLOAT, // type
GL_FALSE, // normalized?
0, // stride
(void*)0 // array buffer offset
);
// Draw the triangles !
glDrawArrays(GL_TRIANGLES, 0, 6); // 2*3 indices starting at 0 -> 2 triangles
glDisableVertexAttribArray(0);
glReadBuffer(GL_BACK);
glBindTexture(GL_TEXTURE_2D, sourceTexture);
glCopyTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, 0, 0, windowWidth, windowHeight, 0);
// Swap buffers
glfwSwapBuffers(window);
glfwPollEvents();
}
What happens now, is that when I render to the framebuffer, I the texture I get as an input is empty, I think. But when I render the same texture on screen, it renders succesfully what I excpect.
Okay, I think I've managed to figure something out. Instead of rendering to a framebuffer what I can do is to use glCopyTexImage2D to copy whatever got rendered on the screen to a texture. Now, however, I have another issue: I can't understand if glCopyTexImage2D will work with a frame buffer. It works with onscreen rendering, but I am failing to get it to work when I am rendering to a framebuffer. Not sure if this is even possible in the first place. Made a separate question on this:
Does glCopyTexImage2D work when rendering offscreen?
I have set up my operations to draw to the stencil buffer, similar to the following:
void onDisplay() {
glClear(GL_DEPTH_BUFFER_BIT);
glEnable(GL_STENCIL_TEST);
glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE);
glDepthMask(GL_FALSE);
glStencilFunc(GL_NEVER, 1, 0xFF);
glStencilOp(GL_REPLACE, GL_KEEP, GL_KEEP);
// Draw to stencil buffer
glStencilMask(0xFF);
glClear(GL_STENCIL_BUFFER_BIT); // needs mask=0xFF
draw_circle();
glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
glDepthMask(GL_TRUE);
glStencilMask(0x00);
// draw where stencil's value is 0
glStencilFunc(GL_EQUAL, 0, 0xFF);
/* (nothing to draw) */
// draw only where stencil's value is 1
glStencilFunc(GL_EQUAL, 1, 0xFF);
draw_scene();
glDisable(GL_STENCIL_TEST);
}
Now, if I have the following fragment shader enabled when I call draw_circle() (above):
void main() {
gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0);
}
How will the values of the stencil buffer differ from if I were to use the following fragment shader?
void main() {
gl_FragColor = vec4(0.0, 1.0, 0.0, 1.0);
}
In other words, I'm wondering how the output of the fragment shader affects the stencil buffer when drawing to the stencil buffer.
Edit:
The point of my question is to correct some misunderstandings I know I have regarding the stencil buffer. One example I have that I think explains the stencil buffer fairly well is [1]. Here, the following is mentioned:
The glColorMask function allows you to specify which data is written to the color buffer during a drawing operation. In this case you would want to disable all color channels (red, green, blue, alpha). Writing to the depth buffer needs to be disabled separately as well with glDepthMask, so that cube drawing operation won't be affected by leftover depth values of the rectangle. This is cleaner than simply clearing the depth buffer again later.
So, it seems from this page, that, in order to write to the stencil buffer, one needs to enable/disable the appropriate modes (i.e. color and depth), and then go through the entire rasterization process, which will only write to the stencil buffer. Since the rasterization process includes the fragment shader, is the output of the fragment shader (i.e. gl_FragColor) simply ignored? How can I tell GL what to write to the stencil buffer position (x, y)?
[1] : https://open.gl/depthstencils
Unless you have access to AMD/ARB_shader_stencil_export, the fragment shader cannot directly affect the output to the stencil buffer. The only exception to this is discarding the fragment.
And according to this database, only AMD cards provide this extension. Also, that extension exposes an output specifically for the stencil. It modifies the stencil value of the fragment; the color values of the fragment never affect the fragment's stencil value.
I'm trying to achieve the following effect in OpenGL as depicted in the given link http://commons.wikimedia.org/wiki/File:OpenGL_Tutorial_Stencil.png, code snippet is also below, this is the official openGL wikipedia tutorial.
Although it has code and comment out explanations I still find it hard to understand the logic of stencil buffer operation in openGL. I kindly remind that before starting to stenciling I read most of the instructions in the red book, so I know and I'm familiar with, how glStencilFunc, glStencilOp and glStencilMask etc.. works, but there are still some obscure and unclarified things in my mind related to the stenciling.
Here is my walkthrough on how I interpret the code :
With glStencilFunc(GL_NEVER, 1, 0xFF); glStencilOp(GL_REPLACE, GL_KEEP, GL_KEEP); I believe that stencil test will always pass and failing the stencil test will result in 1s filled in stencil buffer due to the instructions set in glStencilOp
If the previous code blocks (#1) sets the all pixels in stencil buffer what we achieve with glStencilMask(0xFF); procedure call. Do we guarantee that all the 1s are written into stencil buffer successfully or what actually?
We draw the circle then switch on the masks but this time glStencilMask(0x00), here is one of the confusion, I think this deals with preventing stencil buffer values to be overriden by new values but code comments says that this simply fills the stencil buffer with 0s.
Finally, if you we don't change any value (due to glStencilMask(0x00) ) in stencil buffer what is the sense of having the
// fill 0s
glStencilFunc(GL_EQUAL, 0, 0xFF);
/* (nothing to draw) */
// fill 1s
glStencilFunc(GL_EQUAL, 1, 0xFF);
Main program body :
void onDisplay() {
glClearColor(0.1, 0.1, 0.1, 1.0);
glClear(GL_COLOR_BUFFER_BIT);
glClear(GL_DEPTH_BUFFER_BIT);
glEnable(GL_STENCIL_TEST);
glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE);
glDepthMask(GL_FALSE);
glStencilFunc(GL_NEVER, 1, 0xFF);
glStencilOp(GL_REPLACE, GL_KEEP, GL_KEEP); // draw 1s on test fail (always)
// draw stencil pattern
glStencilMask(0xFF);
glClear(GL_STENCIL_BUFFER_BIT); // needs mask=0xFF
draw_circle();
glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
glDepthMask(GL_TRUE);
glStencilMask(0x00);
// fill 0s
glStencilFunc(GL_EQUAL, 0, 0xFF);
/* (nothing to draw) */
// fill 1s
glStencilFunc(GL_EQUAL, 1, 0xFF);
draw_scene();
glDisable(GL_STENCIL_TEST);
glutSwapBuffers();
}
glStencilMask(0xFF) is analogous to the glColorMask(true, true, true, true) or glDepthMask(true) functions - 0xFF is a mask that is bitwise ANDed with any stencil data before it is written to the stencil buffer. Therefore, via glStencilMask(0xFF), we are ensuring that any value that OpenGL attempts to write to the stencil buffer is successfully written.
glStencilMask(0x00): when 0x00 is bitwise ANDed with any byte, the output will always be 0. So, any time bytes are attempted to be written to the stencil buffer, the byte written in will be 0. Essentially, at this point, it simply ensures that the stencil buffer does NOT have any new data written to it.
As per my interpretation, the section with the /* (nothing to draw) */ comment is where you would draw materials intended to be outside the stencil tested circle. 0 is the default value in the stencil buffer; therefore, since the area in the circle is marked 1, pixels that are outside the circle will pass, and pixels inside will be discarded. It's simply a placeholder.
I believe that when the comments say // fill 0s, what they mean is that "anything drawn here will fill the part of the stencil buffer that is set to 0. Considering the call to glStencilFunc with GL_EQUAL and 0 as the arguments, this is what will happen. I also am sure that when the comments say // fill 1s they mean the same but with the part of the stencil buffer set to 1 (i.e. the area where the circle has been drawn to the stencil buffer.)