Related
I have been trying to solve this visual bug for a few days without any success, so I'm asking this question to see if somebody can help me understand what is happening.
First I will describe the problem without any code, and then I will present some code. Here is the situation:
My OpenGL application renders this image to a multisample framebuffer:
I then blit that multisample framebuffer into a regular framebuffer (not a multisample one).
I then read the RGB data from that regular framebuffer into an array of unsigned bytes using glReadPixels.
Finally, I call stbi_write_png with the array of unsigned bytes. This is the result:
To me it looks like the first line of bytes is shifted to the right, which causes all the other lines to be shifted, resulting in a diagonal shape.
Here is my code:
To create the multisample framebuffer:
int width = 450;
int height = 450;
int numOfSamples = 1;
// Create the multisample framebuffer
glGenFramebuffers(1, &mMultisampleFBO);
glBindFramebuffer(GL_FRAMEBUFFER, mMultisampleFBO);
// Create a multisample texture and use it as a color attachment
glGenTextures(1, &mMultisampleTexture);
glBindTexture(GL_TEXTURE_2D_MULTISAMPLE, mMultisampleTexture);
glTexImage2DMultisample(GL_TEXTURE_2D_MULTISAMPLE, numOfSamples, GL_RGB, width, height, GL_TRUE);
glBindTexture(GL_TEXTURE_2D_MULTISAMPLE, 0);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D_MULTISAMPLE, mMultisampleTexture, 0);
// Create a multisample renderbuffer object and use it as a depth attachment
glGenRenderbuffers(1, &mMultisampleRBO);
glBindRenderbuffer(GL_RENDERBUFFER, mMultisampleRBO);
glRenderbufferStorageMultisample(GL_RENDERBUFFER, numOfSamples, GL_DEPTH_COMPONENT, width, height);
glBindRenderbuffer(GL_RENDERBUFFER, 0);
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, mMultisampleRBO);
To create the regular framebuffer:
// Create the regular framebuffer
glGenFramebuffers(1, &mRegularFBO);
glBindFramebuffer(GL_FRAMEBUFFER, mRegularFBO);
// Create a texture and use it as a color attachment
glGenTextures(1, &mRegularTexture);
glBindTexture(GL_TEXTURE_2D, mRegularTexture);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, NULL);
glBindTexture(GL_TEXTURE_2D, 0);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, mRegularTexture, 0);
Note that both framebuffers are reported as complete.
To blit the multisample framebuffer into the regular one, read from the regular one and write the PNG image:
int width = 450;
int height = 450;
static GLubyte* data = new GLubyte[3 * 450 * 450];
memset(data, 0, 3 * width * height);
// Blit the multisample framebuffer into the regular framebuffer
glBindFramebuffer(GL_READ_FRAMEBUFFER, mMultisampleFBO);
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, mRegularFBO);
glBlitFramebuffer(0, 0, width, height, 0, 0, width, height, GL_COLOR_BUFFER_BIT, GL_NEAREST);
// Read from the regular framebuffer into the data array
glBindFramebuffer(GL_FRAMEBUFFER, mRegularFBO);
glReadPixels(0, 0, width, height, GL_RGB, GL_UNSIGNED_BYTE, data);
// Write the PNG image
int numOfComponents = 3; // RGB
int strideInBytes = width * 3;
stbi_write_png(imgName.c_str(), width, height, 3, data, width * 3);
Note that glGetError reports no errors.
I haven't been able to figure out what is wrong. Thank you for any help!
The issue is cause be the alignment of a row, when the image is read by glReadPixels. By default the alignment of the start of each row of the image is assumed to be 4.
Since the width of the image is 450, which is not divisible by 4 (450/4 = 112.5) and the format is RGB (3 bytes), the alignment has to be changed.
Change the GL_PACK_ALIGNMENT (glPixelStore) before reading the image data:
glPixelStorei(GL_PACK_ALIGNMENT, 1);
glReadPixels(0, 0, width, height, GL_RGB, GL_UNSIGNED_BYTE, data);
I have been working with VR recently and encountered some OpenGL related problem.
The API i use for VR capture a video stream and write it to a texture, I, then, want to submit this texture to a headset. But there is an incompatibility in the API : the texture I get from the stream has an undefined internal format and cannot be submitted to the headset directly.
I am working on a workaround, for now, I have used a GPU -> CPU -> GPU transfer :
I read the first texture pixel (with glReadPixels) and write them into a buffer, then I use this buffer to create a texture with the correct format. This works fine but has some latencies due to data transfers.
I have been trying to do a direct GPU copy but failed :
I tried using PBO but have problems with invalid operation (following http://www.songho.ca/opengl/gl_pbo.html), here is the code with a invalid operation on glReadPixels
// Initialization
glGenBuffers(1, &pbo);
glGenTextures(1, &dstTexture);
glBindTexture(GL_TEXTURE_2D, dstTexture);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL);
glBindTexture(GL_TEXTURE_2D, 0);
// Copy to GPU
glBindBuffer(GL_PIXEL_PACK_BUFFER, pbo);
glBufferData(GL_PIXEL_PACK_BUFFER, bufferSize, NULL, GL_DYNAMIC_DRAW);
glBindTexture(GL_TEXTURE_2D, id); // texture given by the API
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glReadPixels(0, 0, width, height, GL_RGBA, GL_UNSIGNED_BYTE, NULL);
glBindTexture(GL_TEXTURE_2D, 0);
glBindBuffer(GL_PIXEL_UNPACK_BUFFER, pbo);
glBufferData(GL_PIXEL_UNPACK_BUFFER, bufferSize, NULL, GL_DYNAMIC_READ);
glBindTexture(GL_TEXTURE_2D, dstTexture);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL);
I tried using FBO but encountered pointer exceptions.
glCopyImageSubData does not work because the first texture internal format is not recognised.
What are the steps to do a direct GPU copy?
I'm trying to use glReadPixels to map a texture in memory to be able to use openGL's immutable storage feature.
I'm using deferred shading so I prepare my G-Buffers as following:
glGenFramebuffers(1, &_gBuffer);
glBindFramebuffer(GL_FRAMEBUFFER, _gBuffer);
glGenTextures(1, &_pos3dTex);
glBindTexture(GL_TEXTURE_2D, _pos3dTex);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA32F, width, height, 0, GL_RGBA, GL_FLOAT, NULL);
GLuint layout = glGetFragDataLocation(_progID, "positionTexture");
...
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0 + layout, GL_TEXTURE_2D, _pos3dTex, 0);
...
unsigned int attachments[1] = { GL_COLOR_ATTACHMENT0 + layout };
glDrawBuffers(1, attachments);
glGenRenderbuffers(1, &_rboDepth);
glBindRenderbuffer(GL_RENDERBUFFER, _rboDepth);
glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT, width, height);
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, _rboDepth);
And then I want to map my texture in memory so I can use immutable storage:
//Bind Data
glGenBuffers(1, &_pos3dPbo);
glBindBuffer(GL_PIXEL_PACK_BUFFER, _pos3dPbo);
//w*h*4 (Channels)*4(size of float)
glBufferData(GL_PIXEL_PACK_BUFFER,width*height*4* 4, 0, GL_STREAM_READ);
glBindBuffer(GL_PIXEL_PACK_BUFFER, 0);
glBindFramebuffer(GL_FRAMEBUFFER, _gBuffer);
glReadBuffer(GL_COLOR_ATTACHMENT0 + layout);
glPixelStorei(GL_PACK_ALIGNMENT, 4);//4 channels
glReadPixels(0, 0, width, height, GL_RGBA,GL_FLOAT,NULL);
glBindBuffer(GL_PIXEL_PACK_BUFFER, 0);
//Reading
glBindBuffer(GL_PIXEL_PACK_BUFFER, _pos3dPbo);
void* outPos3d = glMapBuffer(GL_PIXEL_PACK_BUFFER, GL_READ_ONLY);
glBindBuffer(GL_PIXEL_PACK_BUFFER, 0);
//Releasing
glBindBuffer(GL_PIXEL_PACK_BUFFER, _pos3dPbo);
glUnmapBuffer(GL_PIXEL_PACK_BUFFER);
glBindBuffer(GL_PIXEL_PACK_BUFFER, 0);
I'm getting an "Access Violation" just after the glReadPixels function.
What Am I doing wrong plz?
You are unbinding your GL_PIXEL_PACK_BUFFER before the glReadPixels, so the NULL pointer will be interpreted relative to your client memory address space, and trying to copy the data to there will almost certainly result in a crash.
I been trying to wrap my head around trasnfering data from my FBO to a PBO to a texture to render it to a QUAD:
glGenFramebuffers(1, &framebuffer);
glBindFramebuffer(GL_FRAMEBUFFER, framebuffer);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, colorAttachment0, 0);
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, depthAttachment);
To my PBO:
glGenBuffers(1, &bufferObj);
glBindBuffer(GL_PIXEL_UNPACK_BUFFER, bufferObj);
glBufferData(GL_PIXEL_UNPACK_BUFFER, 800 * 800 * 4, NULL, GL_DYNAMIC_DRAW);
But I try to transfer it like this: (*which is where the ISSUE IS *) .. my guess
glReadBuffer(GL_COLOR_ATTACHMENT0);
glBindBuffer(GL_PIXEL_PACK_BUFFER, bufferObj);
glReadPixels(0, 0, WIDTH, HEIGHT, GL_RGBA, GL_UNSIGNED_BYTE, 0);
glBindBuffer(GL_PIXEL_PACK_BUFFER, 0);
glReadBuffer(GL_NONE);
I have code that renders out a PBO from CUDA .. transfers it to a texture, and it displays it correctly
And I can render out my FBO by rendering its texture to a quad as well
The issue is the transferring between the FBO to the PBO it seems to to function correctly, since when I replace the render FBO->PBO code it does not work correctly
Some things to try:
Make sure that your FBO is still bound when you do the ReadPixels.
Check that glGetError isn't reporting any errors.
Try reading to a buffer (regular C array) instead of binding the PBO and see if it seems to be getting filled in with correct values.
I'm trying to copy a PBO into a Texture with automipmapping enabled, but it seems only the top level texture is generated (in other words, no mipmapping is occuring).
I'm building a PBO using
//Generate a buffer ID called a PBO (Pixel Buffer Object)
glGenBuffers(1, pbo);
//Make this the current UNPACK buffer
glBindBuffer(GL_PIXEL_UNPACK_BUFFER, *pbo);
//Allocate data for the buffer. 4-channel 8-bit image
glBufferData(GL_PIXEL_UNPACK_BUFFER, size_tex_data, NULL, GL_DYNAMIC_COPY);
cudaGLRegisterBufferObject(*pbo);
and I'm buildilng a texture using
// Enable Texturing
glEnable(GL_TEXTURE_2D);
// Generate a texture identifier
glGenTextures(1,textureID);
// Make this the current texture (remember that GL is state-based)
glBindTexture( GL_TEXTURE_2D, *textureID);
// Allocate the texture memory. The last parameter is NULL since we only
// want to allocate memory, not initialize it
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA_FLOAT32_ATI, size_x, size_y, 0,
GL_RGBA, GL_FLOAT, NULL);
// Must set the filter mode, GL_LINEAR enables interpolation when scaling
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR_MIPMAP_LINEAR);
glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_GENERATE_MIPMAP, GL_TRUE);
Later in a kernel I modify the PBO using something like:
float4* aryPtr = NULL;
cudaGLMapBufferObject((void**)&aryPtr, *pbo);
//Pixel* gpuPixelsRawPtr = thrust::raw_pointer_cast(&gpuPixels[0]);
//... do some cuda stuff to aryPtr ...
//If we don't unmap the PBO then OpenGL won't be able to use it:
cudaGLUnmapBufferObject(*pbo);
Now, before I draw to the screen using the texture generated above I call:
(note that rtMipmapTex = *textureID above and rtResultPBO = *pbo above)
glEnable(GL_DEPTH);
glEnable(GL_TEXTURE_2D);
glBindTexture(GL_TEXTURE_2D, rtMipmapTex);
glBindBuffer(GL_PIXEL_UNPACK_BUFFER, rtResultPBO);
glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, canvasSize.x, canvasSize.y, GL_RGBA, GL_FLOAT, NULL);
This all works fine and correctly displays the texture. But, if I change that last line to
glTexSubImage2D(GL_TEXTURE_2D, 1, 0, 0, canvasSize.x, canvasSize.y, GL_RGBA, GL_FLOAT, NULL);
which, as far as I understand, should show me the first level instead of the zeroth level texture in the texture pyramid, I just get a blank white texture.
How do I copy the texture from the PBO in such a way that the auto-mipmapping is triggered?
Thanks.
I was being an idiot. The above code works perfectly, the problem was that
glTexSubImage2D(GL_TEXTURE_2D, 1, 0, 0, canvasSize.x, canvasSize.y, GL_RGBA, GL_FLOAT, NULL);
doesn't select the mipmap level from the texture, it selects it from the pbo, which is not mipmapped. Instead you can display the particular mipmapped level with:
glTexEnvi(GL_TEXTURE_FILTER_CONTROL,GL_TEXTURE_LOD_BIAS, 4);