Access depth-stencil texture in a shader program - opengl

It seems to be difficult to find information about how to access depth and stencil buffers in shaders of successive render passes.
In a first render pass, I do not only render color and depth information but also make use of stencil operations to count objects. I use a multi render target FBO for this, with color buffers and a combined depth stencil buffer attached. All of them are in the form of textures (no render buffer objects involved).
In a second render pass (when rendering to the screen), I want to access the previously computed stencil index on a per-pixel basis (but not necessarily the same pixel I'm drawing then), similar like you would like to access the previously rendered color buffer to apply some post processing effect.
But I fail to bind the depth stencil texture in the second pass to my shader program as a uniform. At least only black values are read from it, so I guess it's not bound correctly.
Is it possible to bind a depth stencil texture to a texture unit for use in a shader program? Is it impossible to access depth and stencil textures using "normal" samplers? Is it possible with some "special" sampler? Does it depend on the interpolation mode set on the texture or a similar setting?
If not, what is the best (fastest) way to copy the stencil information into a separate color texture between these two render passes? Maybe involving a third render pass which draws a single color using stencil test (I only need a binary version of the stencil buffer in the final render pass, to be precise I need to test if the value is zero).
The setup for the textures being used by the intermediate FBO is as follows:
// The textures for color information (GL_COLOR_ATTACHMENT*):
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, w, h, 0, GL_RGBA, GL_UNSIGNED_BYTE, 0);
// The texture for depth and stencil information (GL_DEPTH_STENCIL_ATTACHMENT*):
glTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH24_STENCIL8, w, h, 0, GL_DEPTH_STENCIL, GL_UNSIGNED_INT_24_8, 0);
In the second render pass, I currently only try to "debug" the contents of all textures. Therefore I setup the shader with these values:
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, <texture>);
glUniform1i(texLocation, 0);
and let the shader program simply copy the texture to the screen:
uniform sampler2D tex;
in vec2 texCoord;
out vec4 fragColor;
void main() {
fragColor = texture2D(tex, texCoord);
}
The results are as followed:
When <texture> above refers to one of my color textures, I see the color output rendered in the first render pass, which is what I expect.
When <texture> above refers to the depth stencil texture, the shader doesn't do anything (I see the color with which I clear the screen).
When copying the depth stencil texture to the CPU and examine it, I see both the depth and stencil information in the packed 24 + 8 bit data as expected.

I have no experience with using stencil as a texture, but you may want to take a look at the following extension :
http://www.opengl.org/registry/specs/ARB/stencil_texturing.txt
Another option could be to create a view of the texture using
http://www.opengl.org/registry/specs/ARB/texture_view.txt
Or you could count objects without the stencil buffer, perhaps using MRT and additive blending on second render target using :
http://www.opengl.org/registry/specs/EXT/draw_buffers2.txt
But I'm afraid those options are not included in pure GL3.3...

Related

OpenGL glDrawBuffer and glBindTexture

I'm still new on opengl3 and I'm trying to create a multipass rendering.
In order to do that, I created FBO, generated several textures and attached them to it with
unsigned index_col = 0;
for (index_col = 0; index_col < nbr_textures; ++index_col)
glFramebufferTexture(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0 + index_col, texture_colors[index_col], 0);
It works well (I try to believe that I'm doing good here!).
My comprehension problem occurs after, when I try to render in the first texture offscreen, then in the second texture, then render on the screen.
To render to a particular texture, I'm using :
FBO.bind();
glDrawBuffer(GL_COLOR_ATTACHMENT0);
glBindTexture(GL_TEXTURE_2D, FBO.getColorTextures(0)); //getColorTextures(0) is texture_colors[0]
Then I draw, using my shader, and after I would like to do :
glDrawBuffer(GL_COLOR_ATTACHMENT1);
glBindTexture(GL_TEXTURE_2D, FBO.getColorTextures(1));
and after all
glBindFramebuffer(GL_FRAMEBUFFER, 0);
RenderToScreen(); // assuming this function render to screen with a quad
My question is :
What is the difference between glDrawBuffer and glBindTexture? Is it necessary to call both? Aren't textures attached to buffer? (I can't test it actually, because I'm trying to make it works...)
Thanks!
glBindTexture is connecting a texture with a texture sampler unit for reading. glDrawBuffer selects the destination for drawing writes. If you want to select a texture as rendering target use glDrawBuffer on the color attachment the texture is attached to; and make sure that none of the texture sampler units it is currently bound to is used as a shader input! The result of creating a feedback loop is undefined.
glDrawBuffer selects the color buffer (in this case of the framebuffer object) that you will write to:
When colors are written to the frame buffer, they are written into the color buffers specified by glDrawBuffer
If you wanted to draw to multiple color buffers you would have written
GLuint attachments[2] = { GL_COLOR_ATTACHMENT0, GL_COLOR_ATTACHMENT1 };
glDrawBuffers(2, attachments);
while glBindTexture binds a texture to a texture unit.
They serve different purposes - remember that OpenGL and its current rendering context behave as a state machine.

OpenGL implementing skybox in a deferred renderer

I am trying to figure out how to render a skybox in a deferred renderer so that it can be included in post processing effects, However my Geometry stage is in view space and unfortunately the skybox in this stage will be effected by it's position relative to the light as any object would (it behaves like large box located very far from the light source and shows up very dark).
my setup without trying to incorporate the skybox in post processing is as follows:
1:(bind FBO) Render Geometry to color, normal, position FBO texture attachments (unbind FBO).
2:(bind FBO) Render the scene and calculate lighting in screen space.(unbind FBO)
3:(bind FBO) apply post processing effects (unbind FBO)
4: blit the Geometry FBO's depth buffer to the default frame buffer
5: render skybox.
I've tried to switch step 5 with 3
like this:
2:(bind FBO) Render the scene and calculate lighting in screen space.
5: render skybox
(unbind FBO)
3:(bind FBO) apply post processing effects (unbind FBO)
4: blit the Geometry FBO's depth buffer to the default frame buffer
but obviously the skybox has no depth information about the scene and renders on top of the lighting stage. And if I try to do any depth blitting between 2 and 5, I believe I am making invalid GL calls because I'm already bound to an FBO while calling
GL30.glBindFramebuffer(GL30.GL_READ_FRAMEBUFFER, DeferredFBO.fbo_handle);
GL30.glBindFramebuffer(GL30.GL_DRAW_FRAMEBUFFER, 0); // Write to default
// framebuffer or a skybox framebuffer
GL30.glBlitFramebuffer(0, 0, DisplayManager.Width,
DisplayManager.Height, 0, 0, DisplayManager.Width,
DisplayManager.Height, GL11.GL_DEPTH_BUFFER_BIT,
GL11.GL_NEAREST);
So I came up with a really easy hacky solution to this problem without having to incorporate any texture barriers or messing with the depth or color buffers.
I actually render the Skybox Geometry in the Geometry pass of the Deferred Rendering process, I render the skybox and set a flag in the fragment shader to color my skybox, remembering to modify the view matrix to remove the translation with another uniform flag in the vertex Shader. In the fragment shader I set the skybox color as such. Here is a basic summary without pasting all of the code.
layout (binding = 4) uniform samplerCube cubeMap;
uniform float SkyRender;
void main(){
if(SkyRender){
vec4 SkyColor = texture(cubeMap, skyTexCoords);
gAlbedoSpec.rgb = SkyColor.rgb;
gAlbedoSpec.a = -1;
}else{
gAlbedoSpec.rgb = texture(DiffuseTexture, TexCoords);
gAlbedoSpec.a = texture(SpecularTexture, TexCoords).r;
}
I set the alpha component of my skybox in the Color buffer as a flag for my Lighting pass. Here I set it to to -1.
In my lighting pass I simply choose to color my box with Diffuse Only instead of adding lighting calculations if my gAlbedoSpec alpha value is -1.
if(Diffuse.a > -1){
FragColor = SphereNormal * vec4(Dlighting, 1.0)+vec4(Slighting, 1.0);
}else{
FragColor = Diffuse ;
}
It's fairly simple and doesn't require much code and gets the job done.
Then give it the depth information it lacks.
When you rendered your scene in step 1, you used a depth buffer. So when you draw your skybox, you need an FBO that uses that same depth buffer. But this FBO also needs to use the color image that you rendered to in step 2.
Now, this FBO cannot be the same FBO you used in step 2. Why?
Because that would be undefined behavior. Presumably, step 2 reads from your depth buffer to reconstruct the position (if this is not the case, then you can just attach the depth buffer to the FBO from step 2. But then again, you're also wasting tons of performance). But that depth buffer is also attached to the FBO. And that makes it undefined behavior. Even if you're not writing to the depth, it is still undefined under OpenGL.
So you will need another FBO, which has the depth buffer from step 1 with the color buffer from step 2.
Unless you have access to OpenGL 4.5/ARB_texture_barrier/NV_texture_barrier. With that feature, it becomes defined behavior if you use write masks to turn off writes to the depth buffer. All you need to do is issue a glTextureBarrier before performing step 2. So you don't need another FBO if you have that.
In either case, keep the depth test enabled when rendering your skybox, but turn off depth writing. This will allow fragments behind your actual world to be culled, but the depth of the skybox fragments will be infinitely far away.

GL_TEXTURE_3D color and stencil FBO attachments

I am doing a layered rendering to an offscreen FBO using OpenGL 4.3.I used GL_TEXTURE_3D with several layers as COLOR attachment.Then I use geometry shader to index into different layers when writing the output.It works ok.Now I need also stencil attachment for stencil test I am performing during the rendering.First I tried just to attach a render buffer as in the case with 2D attachments.
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT,
GL_RENDERBUFFER, _stencilBuffer)
In this case,when checking FBO for completeness I am getting frame buffer error:
GL_FRAMEBUFFER_INCOMPLETE_LAYER_TARGETS_ARB
Then I assumed that if the color attachment is 3D so the stencil attachment also must be 3D.And because there is no 3D render buffer I tried to attach a 3D texture for depth stencil slot of the FBO.
glTexImage3D(GL_TEXTURE_3D, 0, GL_DEPTH24_STENCIL8, width, height, depth,
0, GL_DEPTH_STENCIL, GL_UNSIGNED_INT_24_8);
Where width - texture width,height-texture height,depth-number of layers inside texture 3D.
//Attach to FBO:
glFramebufferTexture(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, texId, 0));
Doing it this way I am getting:
GL_FRAMEBUFFER_INCOMPLETE
INVALID_OPERATION
I have searched any possible example to find how such a setup should be done,but found nothing.I also tried to use GL_TEXTURE_2D_ARRAY instead,but same problem. (for some reason this actually fixed the problem which persisted in my earlier tests)
UPDATE
My fault as got confused with some of my findings during the debug.Basically half of what I wrote above can be discarded.But because other people may get into the same issues I will explain what happened.
At first,when I attached a 3d texture to COLOR attachment of FBO I created a render buffer for GL_DEPTH_STENCIL attachment.And yes,on completeness check I got:
GL_FRAMEBUFFER_INCOMPLETE_LAYER_TARGETS_ARB
Next,I tried instead:
glTexImage3D(GL_TEXTURE_3D, 0, GL_DEPTH24_STENCIL8, width, height, depth,
0, GL_DEPTH_STENCIL, GL_UNSIGNED_INT_24_8);
which thrown:
INVALID_OPERATION
Now,instead of GL_TEXTURE_3D target I tried GL_TEXTURE_2D_ARRAY which finally caused the FBO to be complete.So,while I would still like to understand why GL_TEXTURE_3D causes INVALID_OPERATION(feel free to post an answer),this change has solved the problem.
Based on the spec, GL_FRAMEBUFFER_INCOMPLETE_LAYER_TARGETS means (quoted from OpenGL 4.5 spec):
If any framebuffer attachment is layered, all populated attachments must be layered. Additionally, all populated color attachments must be from textures of the same target (three-dimensional, one- or two-dimensional array, cube map, or cube map array textures).
Based on the first part of this, your initial attempt of using a single layer stencil attachment with a layered 3D texture color attachment was clearly illegal.
The second part sounds somewhat unclear to me. Since it only talks about "color attachments", it suggests that using a GL_TEXTURE_3D color attachment and a GL_TEXTURE_2D_ARRAY stencil attachment would be legal. But I'm not convinced that this is actually the intention. Unfortunately I couldn't find additional confirmation of this in the rest of the spec.
Using GL_TEXTURE_3D for a stencil or depth/stencil texture is a non-starter. There's no such thing as a 3D stencil texture. From the 4.5 spec, pages 191-192 in section "8.5 Texture Image Specification"
Textures with a base internal format of DEPTH_COMPONENT, DEPTH_STENCIL, or STENCIL_INDEX are supported by texture image speciļ¬cation commands only if target is TEXTURE_1D, TEXTURE_2D, TEXTURE_2D_MULTISAMPLE, TEXTURE_1D_ARRAY, TEXTURE_2D_ARRAY, TEXTURE_2D_MULTISAMPLE_ARRAY, TEXTURE_RECTANGLE, TEXTURE_CUBE_MAP, TEXTURE_CUBE_MAP_ARRAY, PROXY_TEXTURE_1D, PROXY_TEXTURE_2D, PROXY_TEXTURE_2D_MULTISAMPLE, PROXY_TEXTURE_1D_ARRAY, PROXY_TEXTURE_2D_ARRAY, PROXY_TEXTURE_2D_MULTISAMPLE_ARRAY, PROXY_TEXTURE_RECTANGLE, PROXY_TEXTURE_CUBE_MAP, or PROXY_TEXTURE_CUBE_MAP_ARRAY.
That's a long list, but TEXTURE_3D is not in it.
Based on this, I believe that what you found to be working is the only option. You need to use textures with target GL_TEXTURE_2D_ARRAY for both the color and stencil attachment.

Deferred renderer with stencil buffer FBO

In my deferred renderer I create a FBO for my geometry pass where I store also a depth + stencil buffer into a texture with these parameters:
Attachment: GL_DEPTH_STENCIL_ATTACHMENT
Format: GL_DEPTH_STENCIL
Internal format: GL_DEPTH32F_STENCIL8
Data type: GL_UNSIGNED_INT_24_8
and I can successfully populate this texture with depth and stencil.
The problem I'm facing now is how to use the stencil buffer created in my geometry pass in order to use it somewhere else, like when I do a directional light pass I'd like to process only the pixels covered by the geometry visible on my geometry pass.
So, how can I use the stencil buffer stored during my geometry pass for further use?
You can of course always use the stencil buffer for stencil testing. To illustrate how to use stencil testing on a basic level, here are the critical calls for an example where you draw in two passes, where in the second pass we will exclude all pixels that were not covered in the first pass:
When you clear the framebuffer, make sure that you include the bit for clearing the stencil buffer:
glClear(... | GL_STENCIL_BUFFER_BIT);
Enable the stencil test:
glEnable(GL_STENCIL_TEST);
Set up the stencil function/operation to set the value in the stencil buffer to 1 for all rendered pixels:
glStencilFunc(GL_ALWAYS, 1, 1);
glStencilOp(GL_REPLACE, GL_REPLACE, GL_REPLACE);
GL_ALWAYS specifies that all pixels pass the stencil test (i.e. no pixels are rejected). GL_REPLACE specifies that the value in the stencil buffer is replaced by the reference value, which is the second argument to glStencilFunc(), for all rendered pixels.
Draw the geometry for the first pass.
Change the stencil function/operation to only render pixels where the value in the stencil buffer is 1:
glStencilFunc(GL_EQUAL, 1, 1);
glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);
GL_EQUAL specifies that only pixels at positions where the stencil buffer value is equal to the reference value 1 pass the stencil test, and all other pixels are rejected. GL_KEEP specifies that the current values in the stencil buffer are not modified in this pass.
Draw the geometry for the second pass.
But the way I read your question, you're looking for something different. You want to sample the stencil buffer from one rendering pass in a later rendering pass, similar to the way you can sample depth textures.
This is supported in OpenGL 4.3 and later. With texId the name of your depth/stencil texture, the key part is this texture parameter setting:
glBindTexture(GL_TEXTURE_2D, texId);
glTexParamteri(GL_TEXTURE_2D, GL_DEPTH_STENCIL_TEXTURE_MODE, GL_STENCIL_INDEX);
This specifies that the stencil part of the depth/stencil texture should be used for sampling. Only nearest sampling is supported for stencil textures, so make sure that you also have these values:
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
Not surprisingly based on the nature of the value, stencil textures are treated as unsigned integer textures. This means that the sampler type in the shader code needs to be usampler2D:
uniform usampler2D MyStencilTexture;
And when you sample the texture, the result will also be of type unsigned:
uvec4 stencilValue = texture(MyStencilTexture, textureCoordinates);
The stencil value will then be in stencilValue.r.

OpenGL, how to use depthbuffer from framebuffer as usual depth buffer

I have frame buffer, with depth component and 4 color attachments with 4 textures
I draw some stuff into it and unbind the buffer after, using 4 textures for fragment shader (deferred lighting).
Later i want to draw some more stuff on the screen, using the depth buffer from my framebuffer, is it possible?
I tried binding the framebuffer again and specifying glDrawBuffer(GL_FRONT), but it does not work.
Like Nicol already said, you cannot use an FBOs depth buffer as the default framebuffer's depth buffer directly.
But you can copy the FBO's depth buffer over to the default framebuffer using the EXT_framebuffer_blit extension (which should be core since GL 3):
glBindFramebuffer(GL_READ_FRAMEBUFFER, fbo);
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
glBlitFramebuffer(0, 0, width, height, 0, 0, width, height,
GL_DEPTH_BUFFER_BIT, GL_NEAREST);
If this extension is not supported (which I doubt when you already have FBOs), you can use a depth texture for the FBO's depth attachment and render this to the default framebuffer using a textured quad and a simple pass through fragment shader that writes into gl_FragDepth. Though this might be slower than just blitting it over.
I just experienced that copying a depth buffer from a renderbuffer to the main (context-provided) depth buffer is highly unreliable when using glBlitFramebuffer. Just because you cannot guarantee the format does match. Using GL_DEPTH_COMPONENT24 as my internal depth-texture-format just didn't work on my AMD Radeon 6950 (latest driver) because Windows (or the driver) decided to use the equivalent to GL_DEPTH24_STENCIL8 as the depth-format for my front/backbuffer, although i did not request any stencil precision (stencil-bits set to 0 in the pixel format descriptor). When using GL_DEPTH24_STENCIL8 for my framebuffer's depth-texture the Blitting worked as expected, but I had other issues with this format. The first attempt worked fine on NVIDIA cards, so I'm pretty sure I did not mess things up.
What works best (in my experience) is copying via shader:
The Fragment-Program (aka Pixel-Shader) [GLSL]
#version 150
uniform sampler2D depthTexture;
in vec2 texCoords; //texture coordinates from vertex-shader
void main( void )
{
gl_FragDepth = texture(depthTexture, texCoords).r;
}
The C++ code for copying looks like this:
glDepthMask(GL_TRUE);
glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE);
glEnable(GL_DEPTH_TEST); //has to be enabled for some reason
glBindFramebuffer(GL_FRAMEBUFFER, 0);
depthCopyShader->Enable();
DrawFullscreenQuad(depthTextureIndex);
I know the thread is old, but it was one of my first results when googeling my issue, so I want to keep it as consistent as possible.
You cannot attach images (color or depth) to the default framebuffer. Similarly, you can't take images from the default framebuffer and attach them to an FBO.