glReadPixels
Copies pixels from the read framebuffer back to the CPU (or into a pixel buffer object).
Signature
void glReadPixels(GLint x, GLint y, GLsizei width, GLsizei height, GLenum format, GLenum type, void *data);
How it works
Reads a rectangle from the read buffer of the framebuffer bound to GL_READ_FRAMEBUFFER: screenshots, or the object ID under the mouse in picking.
Two things to know. The origin is the bottom-left corner, but window coordinates start at the top, so flip y: height − 1 − mouseY. And a plain call stalls: the CPU waits until the GPU has finished every pending draw. To avoid that, bind a buffer to GL_PIXEL_PACK_BUFFER first; data then becomes a byte offset into it, the call returns at once, and you map the buffer a frame later (or after a fence signals).
Parameters
Bottom-left corner of the rectangle.
Size of the rectangle (1, 1 for a single pixel).
Channels to return.
Common values
- GL_RGBA / GL_RGB
- Colour, for screenshots.
- GL_RED_INTEGER
- One integer channel, for ID buffers (GL_R32UI).
- GL_DEPTH_COMPONENT
- The depth value.
Type of each value: GL_UNSIGNED_BYTE, GL_FLOAT, GL_UNSIGNED_INT…
Destination memory, or a byte offset when a GL_PIXEL_PACK_BUFFER is bound.
Tips & pitfalls
Rows are padded to GL_PACK_ALIGNMENT (4 bytes by default): reading GL_RGB rows of odd width needs
glPixelStorei(GL_PACK_ALIGNMENT, 1) or a bigger buffer.Reading a whole 1080p frame synchronously costs milliseconds; one pixel through a PBO costs almost nothing.
Errors
When the call is invalid it does nothing and sets an error, which glGetError returns:
- GL_INVALID_OPERATION
- format/type do not suit the read buffer (e.g. GL_RGBA with an integer texture), or the pack buffer is too small.
- GL_INVALID_FRAMEBUFFER_OPERATION
- The read framebuffer is incomplete.
Example
1// picking: the object ID under the mouse, without stalling
2glBindFramebuffer(GL_READ_FRAMEBUFFER, idFbo);
3glBindBuffer(GL_PIXEL_PACK_BUFFER, pbo);
4glReadPixels(mx, height - 1 - my, 1, 1, GL_RED_INTEGER, GL_UNSIGNED_INT, nullptr);
5GLsync fence = glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0);