OpenGL ping pong works with one pass, not with two - c++

This might be a more basic OpenGL mistake than the title suggests.
I am doing segmentation using fragment shaders in OpenGL, which require multiple rendering passes to do successive operations (eg. gaussian blur + edge detection + segmentation).
As far as I understood, there is this common technique called ping pong which takes two frame buffers (FBO) and simply renders to one FBO using the other as input.
The thing is, one pass--shader_0 outputting stuff to FBO_1 using FBO_0 as input--works, but when I try to use shader_1 with FBO_0 as input and render into FBO_1, I get a completely transparent image.
I checked both shaders and they do work individually, yet together they produce this transparent output.
Here is the set of calls I do for each pass, with segmentationBuffers containing the two FBOs, respectively used as input and output for this pass:
glBindFramebuffer(
GL_FRAMEBUFFER,
segmentationBuffers[lastSegmentationFboRenderedTo]->FramebufferName
);
glViewport(0, 0, windowWidth, windowHeight);
glEnable(GL_DEPTH_TEST);
glEnable(GL_CULL_FACE);
glCullFace(GL_BACK);
currentStepShader->UseProgram();
glClearColor(0, 0, 0, 0.0f);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
// Enable blending
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
lastSegmentationFboRenderedTo = (lastSegmentationFboRenderedTo + 1) % 2;
glActiveTexture(GL_TEXTURE0);
glBindTexture(
GL_TEXTURE_2D,
segmentationBuffers[lastSegmentationFboRenderedTo]->renderedTexture
);
glUniform1i(glGetUniformLocation(shader->shaderPtr, "inputTexture"), 0);
glUniform2fv(
glGetUniformLocation(shader->shaderPtr, "texCoordOffsets"),
25,
texCoordOffsets
);
quad->Draw(GL_TRIANGLES, shader,
orthographicProjection,
glm::mat4(1.0f),
getOverlayModelMatrix()
);
And as stated above, doing one pass yields correct intermediary results, but doing two in a row gives a transparent frame. I suspect this is a more basic OpenGL mistake than it seems, but any help is appreciated!

I solved the issue by removing the call to glEnable(GL_DEPTH_TEST);.
I suspect that by enabling depth testing, OpenGL was discarding fragments from subsequent computation steps since they had the same depth value.

Related

OpenGL multisample: the results are the same as that when multisample not used

Use OpenGL (version 330) multisample, in QT framework.
The rendering image is like a star shape.
I use fragment shader to render the shape intensity on the black canvas.
I do not use OpenGL primitives.
When multisample is not used, and when the rendering output canvas has a smaller resolution (say 400x400 pixels), I can see aliasing effects along star shape edges.
If I increase the resolution, say 1500x1500 pixels, then the aliasing effects are much less obvious. So I think mutlisampling should be able to improve the result.
Now, in order to improve speed, I do not increase the resolution of the render buffer. Instead, I decide to try to use multisampling to reduce aliasing effects.
int num_samples = 2; // 4; // I guess the maximum for most graphic cards are 8
GLuint tex;
glGenTextures(1, &tex);
glBindTexture(GL_TEXTURE_2D_MULTISAMPLE, tex);
glTexImage2DMultisample( GL_TEXTURE_2D_MULTISAMPLE, num_samples, GL_R11F_G11F_B10F, width, height, true );
GLuint fbo;
glGenFramebuffers( 1, &fbo );
glBindFramebuffer( GL_FRAMEBUFFER, fbo );
glFramebufferTexture2D( GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D_MULTISAMPLE, tex, 0 );
glViewport(0,0, width, height);
glEnable(GL_MULTISAMPLE);
// ... some code
// draw a rectangle, as it is 2D image processing
// OpenGL render program release
// now convert multisample frame buffer fbo to a regular frame buffer qopenglFramebufferOjbectP
// qopenglFramebufferOjbectP is QOpenGLFramebufferObject
glBindFramebuffer(GL_READ_FRAMEBUFFER, fbo);
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, qopenglFramebufferOjbectP->handle());
glBlitFramebuffer(0, 0, width, height, 0, 0, width, height, GL_COLOR_BUFFER_BIT, GL_LINEAR);
The whole code seems not to be totally wrong, since the output is the desired shape, except the anti aliasing effect.
The problem is:
Either I use multisample (with different sample numbers as 2 4, or 8), or I do not use multisample, the results are the same. I specially wrote the results out to images, and compared them side by side.
But if multisampling takes effect, the results should be expected to have less aliasing effects than that when multismaple is not used.
I use fragment shader to render the shape intensity on the black canvas. I do not use OpenGL primitives.
The basic idea of multisampling is that you're doing the same number of fragment shader invocations as non-multisampling, but a particular fragment only writes the outputs to specific samples in each pixel based on the geometry of the primitives you render. You are rendering what I presume is a quad; any apparent geometry is a fiction created by the fragment shader. Hence you have gained no benefit from the technique.
Imposter-based techniques don't usually benefit from multisampling.
There are ways to handle this, of course. The most obvious is to turn on per-sample shading, but this also effectively turns multisampling into super-sampling. That is, it isn't cheap.
A better idea would be to explicitly output a coverage mask with gl_SampleMask. It's not easy and it depends on how you generate your geometry. The idea is to, for each sample that a fragment covers, detect if that sample is within the imposter-generated geometry. If so, set that sample's mask to 1; if not, set it to 0. Thus, you generate 1 output value, and it is broadcast to the non-zero samples.
Both this and per-sample shading require GL 4.0+ (or ARB_sample_shading).

OpenGL - Is there an easier way to fill window with a texture, instead using VBO,etc?

My OpenGL window is drawn like this:
glClearColor(0.3f, 0.4f, 0.3f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT);
I want to use a texture to fill up the window.
Is there an easier way to do that, instead of creating another VBO, EBO besides the one I'm already using for my triangles?
Since there is the glClearColor that fills the background..
The most direct and generally most efficient way to draw a texture to the window is by using glBlitFramebuffer().
To use this, you need to create an FBO, and attach your texture texId to it:
GLuint fboId = 0;
glGenFramebuffers(1, &fboId);
glBindFramebuffer(GL_READ_FRAMEBUFFER, fboId);
glFramebufferTexture2D(GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
GL_TEXTURE_2D, texId, 0);
Note that the code above bound GL_READ_FRAMEBUFFER, since we want to use this as the source of the blit.
Then, to copy the content:
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0); // if not already bound
glBlitFramebuffer(0, 0, width, height, 0, 0, width, height,
GL_COLOR_BUFFER_BIT, GL_NEAREST);
This is for the case where texture and window have the same size. Otherwise, you can specify different sizes in the first 8 arguments, and may want to use GL_LINEAR for the last parameter.
Using glBlitFramebuffer() has a few advantages over drawing a window sized textured quad:
It needs fewer API calls.
You don't need to write a shader for the copy operation.
You don't need to bind a different shader program, which can reduce overhead.
The driver may have a more optimized code path for the operation, compared to using an app provided shader and draw call.
Many GPUs have dedicated units for blitting data, which can be more efficient than the programmable shader units. They can also potentially run in parallel to the general purpose programmable part of the GPU, allowing the copy to be executed in parallel with rendering. If that applies, the performance gain can be very substantial.
In one word: No.
Well in legacy OpenGL there'd be glDrawPixels but this function never was very well supported and dead slow on most implementation. You better forget that I told you about it. Also it's been removed from modern OpenGL and never existed in OpenGL-ES.
There are already some answers to this question, but I want to add some more alternatives, for completeness:
1. attributeless rendering
With modern GL, you can render completely without vertex attributes. You can put the 4 2d coordiantes of the full screen rect directly as a const array into the vertex shader and access them via gl_VertexID:
// VERTEX SHADER
#version 150 core
out vec2 v_tex;
const vec2 pos[4]=vec2[4](vec2(-1.0, 1.0),
vec2(-1.0,-1.0),
vec2( 1.0, 1.0),
vec2( 1.0,-1.0));
void main()
{
v_tex=0.5*pos[gl_VertexID] + vec2(0.5);
gl_Position=vec4(pos[gl_VertexID], 0.0, 1.0)
}
// FRAGMENT SHADER
#version 150 core
in vec2 v_tex;
uniform sampler2D texSampler;
out vec4 color;
void main()"
{
color=texture(texSampler, v_tex);
}
If your texture exactly matches the resolution of your viewport (so you are not scaling the texture at all), you can completely remove the v_tex varying and use color=texelFetch(texSampler, ivec2(gl_FragCoord.xy)) in the FS, as #datenwolf suggested in his comment.
In any case, you still need some VAO bound, even if no attributes are enabled in it. So this method requires you to do the following once during intialization:
Create and compile the shaders and link them to the program
Create a new VAO name by a glGenVertexArrays() call
And for drawing, you have to:
Bind the texture you want to draw
Use the program
Bind the (still empty) VAO
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4)
You might also be able to simply re-use the currently bound VAO. As the shader does not access any attributes, it does not matter what data your VBOs provide, and which attributes are enabled currently.
This method requires you to switch the shader, which isn't exactly cheap either, so it might be better to just switch the buffer bindigs and keep the current shader.. But you might need to switch the shader anyway.
2. nvidia-specifc extension
NVidia provides a specific extension for the task of drawing a texture to the screen: NV_draw_texture. This introduces the glDrawTextureNV() function which allows drawing a texture without setting changing anything on the GL state. Quoting from the overview section of the extension spec:
While this functionality can be obtained in unextended OpenGL by drawing a
rectangle and using a fragment shader to do a texture lookup,
DrawTextureNV() is likely to have better power efficiency on
implementations supporting this extension. Additionally, use of this
extension frees the application developer from having to set up
specialized shaders, transformation matrices, vertex attributes, and
various other state in order to render the rectangle.
The drawback of this method is of course that it is nvidia-specific, so it is probably of less practical use in a general GL application.
You can render your texture to a fullscreen quad using an ortographic projection:
glEnable(GL_TEXTURE_2D);
glBindTexture(GL_TEXTURE_2D, texture);
glDisable(GL_LIGHTING);
// Set up ortographic projection
glMatrixMode(GL_PROJECTION);
glPushMatrix();
glLoadIdentity();
glOrtho(0, width, 0, height, -1, 1);
// Render a quad
glBegin(GL_QUADS);
glTexCoord2f(0,0); glVertex2f(0,0);
glTexCoord2f(0,1); glVertex2f(0,width);
glTexCoord2f(1,1); glVertex2f(height, width);
glTexCoord2f(1,0); glVertex2f(height,0);
glEnd();
// Reset Projection Matrix
glPopMatrix();
glDisable(GL_TEXTURE_2D);
glEnable(GL_LIGHTING);
Render this into your framebuffer instead of glClearColor.

Draw the contents of the render buffer Object

Do not quite understand the operation render buffer object. For example if I want to show what is in the render buffer, I must necessarily do the render to texture?
GLuint fbo,color_rbo,depth_rbo;
glGenFramebuffers(1,&fbo);
glBindFramebuffer(GL_FRAMEBUFFER,fbo);
glGenRenderbuffersEXT(1, &color_rb);
glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, color_rb);
glRenderbufferStorageEXT(GL_RENDERBUFFER_EXT, GL_RGBA8, 256, 256);
glFramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT,GL_RENDERBUFFER_EXT, color_rb);
glGenRenderbuffersEXT(1, &depth_rb);
glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, depth_rb);
glRenderbufferStorageEXT(GL_RENDERBUFFER_EXT, GL_DEPTH_COMPONENT24, 256, 256);
glFramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT_EXT,GL_RENDERBUFFER_EXT, depth_rb);
if(glCheckFramebufferStatusEXT(GL_FRAMEBUFFER_EXT)!=GL_FRAMEBUFFER_COMPLETE_EXT)return 1;
glBindFramebuffer(GL_FRAMEBUFFER,0);
//main loop
//This does not work :-(
glBindFramebuffer(GL_FRAMEBUFFER,fbo);
glClearColor(0.0,0.0,0.0,1.0);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
drawCube();
glBindFramebuffer(GL_FRAMEBUFFER,0);
any idea?
You are not going to see anything when you draw to an FBO instead of the default framebuffer, that is part of the point of FBOs.
Your options are:
Blit the renderbuffer into another framebuffer (in this case it would probably be GL_BACK for the default backbuffer)
Draw into a texture attachment and then draw texture-mapped primitives (e.g. triangles / quad) if you want to see the results.
Since 2 is pretty self-explanatory, I will explain option 1 in greater detail:
/* We are going to blit into the window (default framebuffer) */
glBindFramebuffer (GL_DRAW_FRAMEBUFFER, 0);
glDrawBuffer (GL_BACK); /* Use backbuffer as color dst. */
/* Read from your FBO */
glBindFramebuffer (GL_READ_FRAMEBUFFER, fbo);
glReadBuffer (GL_COLOR_ATTACHMENT0); /* Use Color Attachment 0 as color src. */
/* Copy the color and depth buffer from your FBO to the default framebuffer */
glBlitFramebuffer (0,0, width,height,
0,0, width,height,
GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT,
GL_NEAREST);
There are a couple of things worth mentioning here:
First, blitting from one framebuffer to another is often measurably slower than drawing two textured triangles that fill the entire viewport. Second, you cannot use linear filtering when you blit a depth or stencil image... but you can if you take the texture mapping approach (this only truly matters if the resolution of your source and destination buffers differ when blitting).
Overall, drawing a textured primitive is the more flexible solution. Blitting is most useful if you need to do Multisample Anti-Aliasing, because you would have to implement that in a shader otherwise and multisample texturing was added after Framebuffer Objects; some older hardware/drivers support FBOs but not multisample color (requires DX10 hardware) or depth (requires DX10.1 hardware) textures.

Aliasing issue with SDL + OpenGL masking

I've been trying to make Worms style destructible terrain, and so far it's been going pretty well...
Snapshot1
I have rigged it so that the following image is masked onto the "chocolate" texture.
CircleMask.png
However, as can be seen on Snapshot 1, the "edges" of the CircleMask are still visible (overlapping each other). I'm fairly certain it has something to do with aliasing, as mask image is being stretched before being applied (that, and the SquareMask.png does not have this issue). This is my problem.
My masking code is as follows:
void MaskedSprite::draw(Point pos)
{
glEnable(GL_BLEND);
// Our masks should NOT affect the buffers color, only alpha.
glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_TRUE);
glBlendFunc(GL_ONE_MINUS_DST_ALPHA,GL_DST_ALPHA);
// Draw all holes in the texture first.
for (unsigned i = 0; i < masks.size(); i++)
if (masks.at(i).mask) masks.at(i).mask->draw(masks.at(i).pos, masks.at(i).size);
// But our image SHOULD affect the buffers color.
glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
// Now draw the actual sprite.
Sprite::draw(pos);
glDisable(GL_BLEND);
}
The draw() function draws a quad with the texture on it to the screen. It has no blend functions.
If you invert the alpha channel on your mask image so that the inside of the circle has alpha 0.0, You can use the following blending mode:
glClearColor(0,0,0,1);
// ...
glBlendFunc(GL_DST_ALPHA, GL_ZERO);
This means, when the screen is cleared, each pixel will be set to alpha 1.0. Each time the mask is rendered with blending enabled, it will multiply the mask's alpha value with the current alpha at that pixel, so the alpha value will never increase.
Note that using this technique, any alpha channel in the sprite texture will be ignored. Also, if you are rendering a background before the terrain, you will need to change the blend function before rendering the final sprite image. Something like glBlendFunc(GL_DST_ALPHA, GL_ONE_MINUS_DST_ALPHA) would work.
Another solution would be to use your blending mode but set the mask texture's interpolation mode to nearest-neighbor to ensure that each value sampled from the mask is either 0.0 or 1.0:
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
My last bit of advice is this: the hard part about destructible 2D terrain is not getting it to render correctly, it's doing collision detection with it. If you haven't given thought to how you plan to tackle it, you might want to.

render transparent textures

I have one texture that has some portions which are transparent transparent I want to apply over an object whose faces are some opaque material (or colour if it's simpler) but the final object gets transparent. I want the final object to be totally opaque.
Here is my code:
First I set the material:
glDisable(GL_COLOR_MATERIAL);
glColorMaterial(GL_FRONT_AND_BACK, GL_AMBIENT);
glColor4f(0.00, 0.00, 0.00, 1.00);
glColorMaterial(GL_FRONT_AND_BACK, GL_DIFFUSE);
glColor4f(0.80, 0.80, 0.80, 1.00);
glColorMaterial(GL_FRONT_AND_BACK, GL_SPECULAR);
glColor4f(0.01, 0.01, 0.01, 1.00);
glEnable(GL_COLOR_MATERIAL);
Then I setup the VBOs
glBindTexture(GL_TEXTURE_2D, object->texture);
glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
glBindBuffer(GL_ARRAY_BUFFER, object->object);
glVertexPointer(3, GL_FLOAT, sizeof(Vertex), ver_offset);
glTexCoordPointer(2, GL_FLOAT, sizeof(Vertex), tex_offset);
glNormalPointer(GL_FLOAT, sizeof(Vertex), nor_offset);
And finally I draw the object
glEnable(GL_BLEND);
glDisable(GL_DEPTH_TEST);
glDisable(GL_TEXTURE_2D);
glBlendFunc(GL_ONE, GL_ZERO);
glDrawArrays(GL_TRIANGLES, 0, object->num_faces);
glEnable(GL_TEXTURE_2D);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
glDrawArrays(GL_TRIANGLES, 0, object->num_faces);
glDisableClientState(GL_VERTEX_ARRAY);
glDisableClientState(GL_TEXTURE_COORD_ARRAY);
glDisable(GL_BLEND);
glEnable(GL_DEPTH_TEST);
I tried passing different arguments to glBlendFunc() with no prevail. I've uploaded the source here: http://dpaste.com/83559/
UPDATE
I get this, but I want this (or without texture this).
The 2nd and the 3rd picture are produces with glm. I studied the sources, but since my knowledge of OpenGL is limited I didn't understand much.
If you're trying to apply two textures to your object you really want to set two textures and use multitexturing to achieve this look. Your method is drawing the geometry twice which is a huge waste of performance.
Multitexturing will just sample from two texture units while only drawing the geometry once. You can do this with shaders (the way things really should be done) or you can still used the fixed function pipeline (see: http://bluevoid.com/opengl/sig00/advanced00/notes/node62.html)
AFAIK the blend function takes fragment colors (opposed to texture colors). So if you draw the object a second time with blending, the triangles become transparent.
What you want to accomplish could be done using multitexturing.
This is just a wild guess, as you have failed to provide any screenshots of what the actual problem is, but why do you disable the depth test? Surely you want to enable depth testing on the first pass with a standard GL_LESS and then do the second pass with GL_EQUAL?
Edit:
ie
glEnable(GL_BLEND);
glEnable(GL_DEPTH_TEST); // ie do not disable
glDepthFunc( GL_LESS ); // only pass polys have a z value less than ones already in the z-buffer (ie are in front of any previous pixels)
glDisable(GL_TEXTURE_2D);
glBlendFunc(GL_ONE, GL_ZERO);
glDrawArrays(GL_TRIANGLES, 0, object->num_faces);
// for the second pass we only want to blend pixels where they occupy the same position
// as in the previous pass. Therefore set to equal and only pixels that match the
// previous pass will be blended together.
glDepthFunc( GL_EQUAL );
glEnable(GL_TEXTURE_2D);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
glDrawArrays(GL_TRIANGLES, 0, object->num_faces);
glDisableClientState(GL_VERTEX_ARRAY);
glDisableClientState(GL_TEXTURE_COORD_ARRAY);
glDisable(GL_BLEND);
Try disabling blending and drawing a single pass with your texture function set to GL_DECAL instead of GL_MODULATE. This will blend between the vertex color and the texture color based on the texture’s alpha channel, but leave the alpha channel set to the vertex color.
Note that this will ignore any lighting applied to the vertex color anywhere the texture was opaque, but this sounds like the intended effect, based on your description.
It will be much simpler with pixel shaders. otherwise I think you need multi-passes rendering or more than one texture.
You can find comprehensive details here :http://www.opengl.org/resources/faq/technical/transparency.htm