I need a fresh pair of eyes. While working on rewriting my engine, I stumbled upon this issue while writing the Deferred Rendering path. The framebuffer displays only if I don't use a depth attachment, which means that the rendering is faulty, but if I do, all the outputs are blank. I wrote a lot of graphics handling classes but I broke down the code here:
Initialization:
glEnable(GL_DEPTH_TEST);
glDepthFunc(GL_LESS);
glEnable(GL_CULL_FACE);
glClearColor(0, 0, 0, 1);
glViewport(0, 0, 1024, 768);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
ShaderPreparation();
numBuffer = 3;
glGenFramebuffers(1, &fbo);
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, fbo);
numBuffers = numBuffer;
targetBuffer = 0;
textures = new unsigned int[numBuffers];
glGenTextures(numBuffers, textures);
This is done three times:
glBindTexture(GL_TEXTURE_2D, textures[targetBuffer]);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexImage2D(GL_TEXTURE_2D, 0, colorType, width, height, 0, colorFormat, colorDataType, NULL);
glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0 + targetBuffer, GL_TEXTURE_2D, textures[targetBuffer], 0);
targetBuffer++;
Then I create the Depth Attachment:
glGenTextures(1, &renderBuffer);
glBindTexture(GL_TEXTURE_2D, renderBuffer);
glTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT32F, width, height, 0, GL_DEPTH_COMPONENT, GL_FLOAT, NULL);
glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, renderBuffer, 0);
EDIT 2: Forgot the last bit of the FBO:
GLenum *DrawBuffers = new GLenum[numBuffers];
for (size_t i = 0; i < numBuffers; i++)
DrawBuffers[i] = GL_COLOR_ATTACHMENT0 + i;
glDrawBuffers(numBuffers, DrawBuffers);
// Report errors
GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER);
if (status != GL_FRAMEBUFFER_COMPLETE) {
fprintf(stderr, "Framebuffer Error. Status 0x%x\n", status);
}
// Unbind
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
Finally, my drawing (all calculations for geometry shader happens before this):
geometryShader->Use();
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, fbo);
graphicsWrapper->render(Geometry);
int val[3] = { 0,1,2 };
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
deferredShader->Use();
glActiveTexture(GL_TEXTURE0 + 0);
glBindTexture(GL_TEXTURE_2D, textures[0]);
glActiveTexture(GL_TEXTURE0 + 1);
glBindTexture(GL_TEXTURE_2D, textures[1]);
glActiveTexture(GL_TEXTURE0 + 2);
glBindTexture(GL_TEXTURE_2D, textures[2]);
// I use this system so it's compatible with Uniform Buffer
// Objects and the rendering code of other graphics languages
deferredShader->PassData(&val);
deferredShader->SetInteger();
deferredShader->SetInteger();
deferredShader->SetInteger();
glClear(GL_COLOR_BUFFER_BIT);
vaoQuad->Bind();
graphicsWrapper->DrawVertexArray(4);
vaoQuad->Unbind();
Note that my code is much more object oriented than this, and I had to take a lot of this code out of context. My question is, why does attaching the depth attachment to the framebuffer cause the framebuffer to blank, while removing it works, and how do I fix it?
EDIT: I know that it's not a renderbuffer, I just called the depth texture that because it USED to be a renderbuffer, and I forgot to change the name.
When you don't have a depth buffer, every fragment will automatically pass the depth test. Now after you add a depth buffer, the depth test will be effective (assuming you enabled it). However, yo forgot to clear your depth buffer:
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, fbo);
You clear the default framebuffer's color and depth buffer (or another FBO, it that is bound at that time), but not the depth buffer of fbo here...
There is a high chance that you depth texture is initially all zeros. With default depth conventions, that means it is already the nearest value, so the depth test will likely fail for anything you draw.
Related
I have three different framebuffer objects for three different steps:
GLuint fbo1; // Step1: draw initial scene
GLuint fbo2; // Step2: apply lens effect shader
GLuint fbo3; // Step3: final smoothing
I want to draw my initial scene as a texture attached to fbo1.
Then apply a lens-shader on that and save it as a texture in fbo2.
Then apply a smoothing-shader on fbo2 and save it to fbo3.
The code in my main render function, uses a few helper functions, and is as follows:
// Step1
useFrameBuffer(fbo1, fbo1); // src , dest
DrawInitialScene();
// Step2
LensShader.use(); // a glsl shader program class
useFrameBuffer(fbo1, fbo2); // src , dest
drawQuadOnFbo(fbo2Texture);
// Step3
SmoothingShader.use(); // a glsl shader program class
useFrameBuffer(fbo2, fbo3); // src , dest
drawQuadOnFbo(fbo3Texture);
// Final Copy Step to OpenGL default FBO
int width = self.frame.size.width;
int height = self.frame.size.height;
glBindFramebuffer(GL_READ_FRAMEBUFFER, fbo3);
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0); //destination
glBlitFramebuffer(0, 0, width, height, 0, 0, width, height, GL_COLOR_BUFFER_BIT, GL_LINEAR);
The helper function useFrameBuffer(src, dest), simply sets the source framebuffer to read from and destination write framebuffer:
void useFrameBuffer (GLuint fboSrc , GLuint fboDest)
{
glBindFramebuffer(GL_READ_FRAMEBUFFER, fboSrc);
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, fboDest);
GLenum drawbufs[] = {GL_COLOR_ATTACHMENT0};
glDrawBuffers(1, drawbufs);
glReadBuffer(GL_COLOR_ATTACHMENT0);
glViewport(0, 0, width, height); // width, height are private to class
}
To apply the shader on the output of fbo1 and fbo2, I am drawing a quad with a texture associated to the respective frame-buffers:
void drawQuadOnFbo(GLuint fboTexture) {
glClearColor(0.5f, 0.5f, 0.5f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); // We're not using stencil buffer now
glBlendFunc(GL_ONE, GL_ONE);
glBindVertexArray(quadVAO);
glDisable(GL_DEPTH_TEST);
glBindTexture(GL_TEXTURE_2D, fboTexture);
glDrawArrays(GL_TRIANGLES, 0, 6);
glBindVertexArray(0);
}
The Problem:
The problem is that output appears to be a combination of Step1 and Step3 only. Looks like the Step2 is completely missed out. You can see in the final copy step, I am copying the contents of fbo3 to the final OpenGL default fbo using:
glBindFramebuffer(GL_READ_FRAMEBUFFER, fbo3);
If I change this to fbo2, then I do see the results of applying Step2 (but then I don't have Step3).
So it looks like that both fbo2 and fbo3 are connected directly to the output of fbo1, but not connected to each other.
How can I fix that ??
Other Helper Functions:
I initialised my Framebuffers at the start of the application, outside the main render function using the method below:
GLuint initFrameBuffer( int width, int height, GLenum textureTarget) {
// 1. Create FBO
GLuint fbo;
glGenFramebuffers(1, &fbo);
// 2. Bind it
glBindFramebuffer(GL_FRAMEBUFFER, fbo);
// FrameBuffer Requirements
// (i). atleast 1 buffer (color, depth, stencil)
// (ii). atleast 1 color-attachment
// (iii). all attachments should be complete
// (iv). same no. of samples for each buffer
// 3. Texture Attachment
GLuint quadTex; // texture to be associated to this FBO
glActiveTexture(textureTarget);
glGenTextures(1, &quadTex);
glBindTexture(GL_TEXTURE_2D, quadTex);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, NULL);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
// attach it as COLOR-ATTACHMENT 0
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, quadTex, 0);
// 4. RenderBuffer attachement
GLuint rbo;
glGenRenderbuffers(1, &rbo);
glBindRenderbuffer(GL_RENDERBUFFER, rbo);
glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH24_STENCIL8, width, height);
glBindRenderbuffer(GL_RENDERBUFFER, 0);
// attach it
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_RENDERBUFFER, rbo);
// 5. Set Targets for fragment shader output
GLenum drawbufs[] = {GL_COLOR_ATTACHMENT0};
glDrawBuffers(1, drawbufs);
// 6. Check that the FrameBuffer is complete
if(glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE){
NSLog(#"ERROR::FRAMEBUFFER:: Framebuffer is not complete!");
return fboObj;
}
// 7. revert to Default
glBindFramebuffer(GL_FRAMEBUFFER, 0);
return fbo;
}
I am making a program with OpenGL that renders frames in the GPU, which I then transfer to memory so I can use them in another program. I have no need for a window or render to screen, so I am using GLFW but with a hidden window and context. Following opengl-tutorial.com I set up a Framebuffer with a texture and a depth renderbuffer so I can render to the texture and then read it's pixels. Just to check things I can make the window visible and I am rendering the texture back on the screen on a quad and using a passthrough shader.
My problem is that when I render to screen directly (with no Framebuffer or texture) the image looks great and smooth. However, when I render to texture and then render the texture to screen, it looks jagged. I don't think the problem is when rendering the texture to screen, because I am also saving the pixels I read into a .jpg and it looks jagged there too.
Both the window and texture are 512x512 pixels in size.
Here is the code where I set up the framebuffer:
FramebufferName = 0;
glGenFramebuffers(1, &FramebufferName);
glBindFramebuffer(GL_FRAMEBUFFER, FramebufferName);
//GLuint renderedTexture;
glGenTextures(1, &renderedTexture);
glBindTexture(GL_TEXTURE_2D, renderedTexture);
glTexImage2D(GL_TEXTURE_2D, 0, textureFormat, textureWidth, textureHeight, 0, textureFormat, GL_UNSIGNED_BYTE, 0);
numBytes = textureWidth * textureHeight * 3; // RGB
pixels = new unsigned char[numBytes]; // allocate image data into RAM
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
//GLuint depthrenderbuffer;
glGenRenderbuffers(1, &depthrenderbuffer);
glBindRenderbuffer(GL_RENDERBUFFER, depthrenderbuffer);
glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT, textureWidth, textureHeight);
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, depthrenderbuffer);
glFramebufferTexture(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, renderedTexture, 0);
DrawBuffers[0] = GL_COLOR_ATTACHMENT0;
glDrawBuffers(1, DrawBuffers); // "1" is the size of DrawBuffers
if(glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) {
std::cout << "Couldn't set up frame buffer" << std::endl;
}
g_quad_vertex_buffer_data.push_back(-1.0f);
g_quad_vertex_buffer_data.push_back(-1.0f);
g_quad_vertex_buffer_data.push_back(0.0f);
g_quad_vertex_buffer_data.push_back(1.0f);
g_quad_vertex_buffer_data.push_back(-1.0f);
g_quad_vertex_buffer_data.push_back(0.0f);
g_quad_vertex_buffer_data.push_back(-1.0f);
g_quad_vertex_buffer_data.push_back(1.0f);
g_quad_vertex_buffer_data.push_back(0.0f);
g_quad_vertex_buffer_data.push_back(-1.0f);
g_quad_vertex_buffer_data.push_back(1.0f);
g_quad_vertex_buffer_data.push_back(0.0f);
g_quad_vertex_buffer_data.push_back(1.0f);
g_quad_vertex_buffer_data.push_back(-1.0f);
g_quad_vertex_buffer_data.push_back(0.0f);
g_quad_vertex_buffer_data.push_back(1.0f);
g_quad_vertex_buffer_data.push_back(1.0f);
g_quad_vertex_buffer_data.push_back(0.0f);
//GLuint quad_vertexbuffer;
glGenBuffers(1, &quad_vertexbuffer);
glBindBuffer(GL_ARRAY_BUFFER, quad_vertexbuffer);
glBufferData(GL_ARRAY_BUFFER, g_quad_vertex_buffer_data.size() * sizeof(GLfloat), &g_quad_vertex_buffer_data[0], GL_STATIC_DRAW);
// PBOs
glGenBuffers(cantPBOs, pboIds);
for(int i = 0; i < cantPBOs; ++i) {
glBindBuffer(GL_PIXEL_PACK_BUFFER, pboIds[i]);
glBufferData(GL_PIXEL_PACK_BUFFER, numBytes, 0, GL_DYNAMIC_READ);
}
glBindBuffer(GL_PIXEL_PACK_BUFFER, 0);
index = 0;
nextIndex = 0;
Here is the code where I render to the texture:
glBindFramebuffer(GL_FRAMEBUFFER, FramebufferName);
glViewport(0,0,textureWidth,textureHeight); // 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);
for(int i = 0; i < geometriesToDraw.size(); ++i) {
geometriesToDraw[i]->draw(program);
}
Where draw(ShaderProgram) is the function that calls glDrawArrays. And here is the code where I render the texture to screen:
// 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,textureWidth,textureHeight);
// Clear the screen
glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glUseProgram(shaderTexToScreen.getProgramID());
// 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(shaderTexToScreen.getUniformLocation("renderedTexture"), 0);
// 1rst attribute buffer : vertices
glEnableVertexAttribArray(0);
glBindBuffer(GL_ARRAY_BUFFER, quad_vertexbuffer);
glVertexAttribPointer(
0,
3,
GL_FLOAT,
GL_FALSE,
0,
(void*)0
);
glDrawArrays(GL_TRIANGLES, 0, 6);
glDisableVertexAttribArray(0);
This is what I get when rendering the scene to screen directly:
And this is what I get when rendering the scene to texture:
I can include the code for the vertex and fragment shaders used in rendering the texture to screen, but as I am reading the pixel data straight from the texture and writing it to a file and that still looks jagged, I don't think that is the problem. If there is anything else you want me to include, let me know!
I thought maybe it could be that there is some hidden scaling when doing the rendering to texture and so GL_NEAREST makes it look bad, but if it really is pixel to pixel (both windows and texture are the same size) there shouldn't be a problem there right?
As pointed out by genpfault and Frischer Hering, there is no antialiasing when rendering to a normal texture. However, you can render to a Multisample texture, which will hold information for as many samples as you request. To render this to screen you need to sample the texture to get one color for each pixel, which can be done by calling glBlitFramebuffer. According to the OpenGL reference on glBlitFramebuffer:
If SAMPLE_BUFFERS for the read framebuffer is greater than zero and SAMPLE_BUFFERS for the draw framebuffer is zero, the samples corresponding to each pixel location in the source are converted to a single sample before being written to the destination.
These two links were very helpful too:
http://www.learnopengl.com/#!Advanced-OpenGL/Anti-aliasing
http://ake.in.th/2013/04/02/offscreening-and-multisampling-with-opengl/
Here is my solution, creation of objects:
/// FRAMEBUFFER MULTISAMPLE
framebufferMS = 0;
glGenFramebuffers(1, &framebufferMS);
glBindFramebuffer(GL_FRAMEBUFFER, framebufferMS);
glGenTextures(1, &renderedTextureMS);
glBindTexture(GL_TEXTURE_2D_MULTISAMPLE, renderedTextureMS);
glTexImage2DMultisample(GL_TEXTURE_2D_MULTISAMPLE, SAMPLES, textureFormat, textureWidth, textureHeight, GL_TRUE);
glGenRenderbuffers(1, &depthrenderbufferMS);
glBindRenderbuffer(GL_RENDERBUFFER, depthrenderbufferMS);
glRenderbufferStorageMultisample(GL_RENDERBUFFER, SAMPLES, GL_DEPTH24_STENCIL8, textureWidth, textureHeight);
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, depthrenderbufferMS);
glFramebufferTexture(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, renderedTextureMS, 0);
DrawBuffersMS[0] = GL_COLOR_ATTACHMENT0;
glDrawBuffers(1, DrawBuffersMS);
if(glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) {
std::cout << "Couldn't set up frame buffer" << std::endl;
}
/// FRAMEBUFFER SIMPLE
framebuffer = 0;
glGenFramebuffers(1, &framebuffer);
glBindFramebuffer(GL_FRAMEBUFFER, framebuffer);
glGenTextures(1, &renderedTexture);
glBindTexture(GL_TEXTURE_2D, renderedTexture);
glTexImage2D(GL_TEXTURE_2D, 0, textureFormat, textureWidth, textureHeight, 0, textureFormat, GL_UNSIGNED_BYTE, 0);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glFramebufferTexture(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, renderedTexture, 0);
DrawBuffers[0] = GL_COLOR_ATTACHMENT0;
glDrawBuffers(1, DrawBuffers); // "1" is the size of DrawBuffers
if(glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) {
std::cout << "Couldn't set up frame buffer" << std::endl;
}
And the rendering process:
// Render to framebuffer multisample
glBindFramebuffer(GL_FRAMEBUFFER, framebufferMS);
glViewport(0,0,textureWidth,textureHeight);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
for(int i = 0; i < geometriesToDraw.size(); ++i) {
geometriesToDraw[i]->draw(program);
}
// Sample to normal texture
glBindFramebuffer(GL_READ_FRAMEBUFFER, framebufferMS);
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, framebuffer);
glBlitFramebuffer(0, 0, textureWidth, textureHeight, 0, 0, textureWidth, textureHeight, GL_COLOR_BUFFER_BIT, GL_NEAREST);
You can also find more questions on the subject here on stackoverflow which I missed because I searches for terms like "jagged" instead of multisampling :P
Thanks a lot for your help!
I ran the framebuffers example in this page -original code- (using glfw3 and glew in xcode 4.6 in osx 10.8), it worked fine, then I wanted to add multisampling (to avoid jagged edges on cube edges and on the floor, glfwWindowHint (GLFW_SAMPLES, 4) was enough when rendering directly to the back-buffer), found some answers directing to opengl.org, tried to use glTexImage2DMultisample but it displayed nothing (black screen). The framebuffer settings and rendering loop is:
// Create frame buffer
GLuint frameBuffer;
glGenFramebuffers(1, &frameBuffer);
glBindFramebuffer(GL_FRAMEBUFFER, frameBuffer);
// Create texture to hold color buffer
GLuint texColorBuffer;
glGenTextures(1, &texColorBuffer);
glBindTexture(GL_TEXTURE_2D_MULTISAMPLE, texColorBuffer);
//glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, NULL);
glTexImage2DMultisample(GL_TEXTURE_2D_MULTISAMPLE, 4, GL_RGB, width, height, GL_FALSE);
/*
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
*/
//glFramebufferTexture2DEXT(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, texColorBuffer, 0);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D_MULTISAMPLE, texColorBuffer, 0);
// Create Renderbuffer Object to hold depth and stencil buffers
GLuint rboDepthStencil;
glGenRenderbuffers(1, &rboDepthStencil);
glBindRenderbuffer(GL_RENDERBUFFER, rboDepthStencil);
//glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH24_STENCIL8, width, height);
glRenderbufferStorageMultisample (GL_RENDERBUFFER, 4, GL_DEPTH24_STENCIL8, width, height);
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_RENDERBUFFER, rboDepthStencil);
// ...
while (!window->shouldClose()) {
static int rot = 0;
// Bind our framebuffer and draw 3D scene (spinning cube)
glBindFramebuffer(GL_FRAMEBUFFER, frameBuffer);
auto err_res = glCheckFramebufferStatus(GL_FRAMEBUFFER);
if(err_res != GL_FRAMEBUFFER_COMPLETE) {
ERR("Incomplete frameBuffer:%X!", err_res);
goto end;
}
glBindVertexArray(vaoCube);
glEnable(GL_DEPTH_TEST);
glUseProgram(sceneShaderProgram);
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, texKitten);
glActiveTexture(GL_TEXTURE1);
glBindTexture(GL_TEXTURE_2D, texPuppy);
// Clear the screen to white
glClearColor(1.0f, 1.0f, 1.0f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glUniformMatrix4fv(uniModel, 1, GL_FALSE, glm::value_ptr(model));
glUniformMatrix4fv(uniView, 1, GL_FALSE, glm::value_ptr(view));
glUniformMatrix4fv(uniProj, 1, GL_FALSE, glm::value_ptr(proj));
// Draw cube
glEnable(GL_MULTISAMPLE);
glDrawArrays(GL_TRIANGLES, 0, 36);
glEnable(GL_STENCIL_TEST);
// Draw floor
glStencilFunc(GL_ALWAYS, 1, 0xFF);
glStencilOp(GL_KEEP, GL_KEEP, GL_REPLACE);
glStencilMask(0xFF);
glDepthMask(GL_FALSE);
glClear(GL_STENCIL_BUFFER_BIT);
glDrawArrays(GL_TRIANGLES, 36, 6);
// Draw cube reflection
glStencilFunc(GL_EQUAL, 1, 0xFF);
glStencilMask(0x00);
glDepthMask(GL_TRUE);
model = glm::scale(glm::translate(model, glm::vec3(0, 0, -1)), glm::vec3(1, 1, -1));
glUniformMatrix4fv(uniModel, 1, GL_FALSE, glm::value_ptr(model));
glUniform3f(uniColor, 0.3f, 0.3f, 0.3f);
glDrawArrays(GL_TRIANGLES, 0, 36);
glUniform3f(uniColor, 1.0f, 1.0f, 1.0f);
glDisable(GL_STENCIL_TEST);
/*
// Bind default framebuffer and draw contents of our framebuffer
glBindFramebufferEXT(GL_FRAMEBUFFER, 0);
glBindVertexArray(vaoQuad);
glDisable(GL_DEPTH_TEST);
glUseProgram(screenShaderProgram);
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, texColorBuffer);
glDrawArrays(GL_TRIANGLES, 0, 6);
*/
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0); // Make sure no FBO is set as the draw framebuffer
glBindFramebuffer(GL_READ_FRAMEBUFFER, frameBuffer); // Make sure your multisampled FBO is the read framebuffer
glDrawBuffer(GL_BACK); // Set the back buffer as the draw buffer
glBlitFramebuffer(0, 0, width, height, 0, 0, width, height, GL_COLOR_BUFFER_BIT, GL_LINEAR);
// Swap buffers
glfwSwapBuffers(window->getHandle());
glfwPollEvents();
}
glVersion: 3.2 NVIDIA-8.10.44 304.10.65f03
glRenderer: NVIDIA GeForce 9400M OpenGL Engine
The 'EXT' additions are probably unnecessary but I also tried to run without them before and the result was the same. What am I doing wrong?
EDIT: Now binding GL_TEXTURE_2D_MULTISAMPLE and getting GL_FRAMEBUFFER_INCOMPLETE_MULTISAMPLE error!
If you checked your framebuffer object for completeness, you would probably have caught this by now... your depth/stencil buffer needs to be multisampled as well.
A framebuffer is considered multisample incomplete by both core and the EXT FBO extension if one attachment has a different number of samples than any other attachment. In your case, you have a color buffer attachment with 4 samples and a depth/stencil attachment with 1 sample.
Name
glCheckFramebufferStatus — check the completeness status of a framebuffer
Description
glCheckFramebufferStatus queries the completeness status of the framebuffer object currently bound to target. target must be GL_DRAW_FRAMEBUFFER, GL_READ_FRAMEBUFFER or GL_FRAMEBUFFER. GL_FRAMEBUFFER is equivalent to GL_DRAW_FRAMEBUFFER.
The return value is GL_FRAMEBUFFER_COMPLETE if the framebuffer bound to target is complete. Otherwise, the return value is determined as follows:
[...]
GL_FRAMEBUFFER_INCOMPLETE_MULTISAMPLE is returned if the value of GL_RENDERBUFFER_SAMPLES is not the same for all attached renderbuffers; if the value of GL_TEXTURE_SAMPLES is the not same for all attached textures; or, if the attached images are a mix of renderbuffers and textures, the value of GL_RENDERBUFFER_SAMPLES does not match the value of GL_TEXTURE_SAMPLES.
To fix this, you need to allocate a multisampled depth/stencil attachment with 4 samples:
glRenderbufferStorageMultisample (GL_RENDERBUFFER, 4, GL_DEPTH24_STENCIL8, width, height);
By the way, since your implementation is >= 3.0, you do not need the EXT suffix on anything. All of the constants defined by the EXT extension are identical to ARB / core FBOs, but some of the EXT functions (such as glCheckFramebufferStatusEXT) have more restrictive behavior (requiring each attachment to have the same image dimensions, for instance).
This is the lovely image I keep seeing gDebugger as I attempt to implement shadow maps. For reference I'm using the tutorial at http://fabiensanglard.net/shadowmapping/index.php. I've tried a couple different things with varying results, mostly either an FBO that has flat depth values(no slight variance indicating weird depth ranges) or this awesome guy. I've tried a huge amount of different things but alas, sans complete FBO failure, I mostly come back to this. For reference, here is what gDebugger spits out as my actual depth buffer:
So, with all that in mind, here's my code. Shader code is withheld because I have yet to get something in my FBO that's worth processing.
Here's how I initialize the FBO/Depth tex:
int shadow_width = 1024;
int shadow_height = 1024;
// Try to use a texture depth component
glGenTextures(1, &depth_tex);
glBindTexture(GL_TEXTURE_2D, depth_tex);
// GL_LINEAR does not make sense for depth texture. However, next tutorial shows usage of GL_LINEAR and PCF
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
// Remove artefact on the edges of the shadowmap
glTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP );
glTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP );
// No need to force GL_DEPTH_COMPONENT24, drivers usually give you the max precision if available
glTexImage2D( GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT24,
shadow_width, shadow_height, 0, GL_DEPTH_COMPONENT,
GL_FLOAT, 0);
// attach the texture to FBO depth attachment point
glBindTexture(GL_TEXTURE_2D, 0);
// create a framebuffer object
glGenFramebuffers(1, &fbo_id);
glBindFramebuffer(GL_FRAMEBUFFER, fbo_id);
// Instruct openGL that we won't bind a color texture with the currently binded FBO
glDrawBuffer(GL_NONE);
glReadBuffer(GL_NONE);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT,GL_TEXTURE_2D, depth_tex, 0);
// switch back to window-system-provided framebuffer
glBindFramebuffer(GL_FRAMEBUFFER, 0);
And here's my render code:
glLoadIdentity();
float lpos[4] = {0,0,0,1};
float lpos3[4] = {5000,-500,5000,1};
glBindTexture(GL_TEXTURE_2D,0);
glBindFramebuffer(GL_FRAMEBUFFER,fbo_id);
glViewport(0, 0, 1024, 1024);
glClear(GL_DEPTH_BUFFER_BIT);
glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE);
setup_mats(lpos[0],lpos[1],lpos[2],lpos3[0],lpos3[1],lpos3[2]);
glCullFace(GL_FRONT);
mdlman.render_models(R_STENCIL);
mdlman.render_model(R_STENCIL,1);
set_tex_mat();
glBindFramebuffer(GL_FRAMEBUFFER,0);
glViewport( 0, 0, (GLsizei)800, (GLsizei)600 );
glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
this->r_cam->return_LookAt();
gluPerspective(45,800/600,0.5f,2000.0f);
glMatrixMode(GL_PROJECTION);
glLoadMatrixf(&r_cam->getProjectionMatrix()[0][0]);
glMatrixMode(GL_MODELVIEW);
glLoadMatrixf(&r_cam->getViewMatrix()[0][0]);
glCullFace(GL_BACK);
mdlman.render_models(R_NORM);
And here's how I draw objects for the FBO:
glEnableClientState(GL_NORMAL_ARRAY);
glEnableClientState(GL_VERTEX_ARRAY);
glBindBuffer(GL_ARRAY_BUFFER,models.at(mdlid).t_buff);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER,models.at(mdlid).i_buff);
glNormalPointer(GL_FLOAT,sizeof(vert),BUFFER_OFFSET(sizeof(GLfloat)*2));
glVertexPointer(3, GL_FLOAT,sizeof(vert),BUFFER_OFFSET(sizeof(GLfloat)*5));
glDrawElements(GL_TRIANGLES,(models.at(mdlid).f_index.size())*3,GL_UNSIGNED_INT,0);
glBindBuffer(GL_ARRAY_BUFFER, 0);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
glDisableClientState(GL_VERTEX_ARRAY);
glDisableClientState(GL_NORMAL_ARRAY);
Some closing notes: the depth buffer image from gDebugger IS from the lights POV. My camera is quaternion based and uses matrices aligned with OGL for it's lookAt function(which is used to translate to light position/view.) I can provide the code for that if need be. Everything else is pretty much carbon-copied from the tutorial to make sure that I'm not doing or setting up something stupid.
I am trying to get into screen a video frame in a 2d texture covering the screen, I must use framebuffers, because eventually I wish to do the ping-pong rendering technique. But for the time being I want first to achieve just to render on the screen by using framebuffers.
This is my setup code:
// Texture setup
int[] text = new int[1];
glGenTextures(1, text, 0);
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, text[0]);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GLES20.GL_LINEAR);
glTexImage2D(GL_TEXTURE_2D, 0, GL_LUMINANCE, 512, 512, 0, GL_LUMINANCE, GL_UNSIGNED_BYTE, null);
// Frambuffer setup
int[] fbo = new int[1];
glGenFramebuffers(1, fbo, 0);
glBindFramebuffer(GL_FRAMEBUFFER, fbo[0]);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, text[0], 0);
glBindFramebuffer(GL_FRAMEBUFFER, 0);
Till here all ok, I didn't write here the testing code to keep it small, but right after I check if the frame buffer was created correctly, and all is fine.
Now in my Render loop I do next:
// Use the GLSL program
glUseProgram(programHandle);
// Swap to my FBO
glBindTexture(GL_TEXTURE_2D, 0);
glBindFramebuffer(GL_FRAMEBUFFER, fbo[0]);
glViewport(0, 0, 512, 512);
// Pass the new image data to the program so the fragment shader processes it.
// Using glTexSubImage2D to speed up
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, text[0]);
glUniform1i(glGetUniformLocation(programHandle, "u_texture"), 0);
glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 512, 512, GL_LUMINANCE, GL_UNSIGNED_BYTE, NewImageData);
// Draw the quad
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
// Swap back to the default screen frambuffer
glBindFramebuffer(GL_FRAMEBUFFER, 0);
glBindTexture(GL_TEXTURE_2D, 0);
At this point I get a black screen, and on the log I can see a GL_INVALID_FRAMEBUFFER_OPERATION 1286.
I tried putting the glDrawArrays after the glBindFramebuffer call, but then the application crashes.
Any thoughts? Thanks in advance.
LUMINANCE textures are not renderable, this means you can't render to them using a FBO. This problem is solved with the GL_ARB_texture_rg extension, which introduces one and two channel texture formats that are renderable.