PDA

View Full Version : Question with glReadPixels ?



HanWu
04-07-2009, 07:19 AM
I am going to read a block of pixel from the screen and then bind with a texture. At first, I round width and height to the nearest 2*n integer( the width and height will be used by the glReadPixel and glTexImage2D). The image of the screen is nicely capture with this method. Later, I would like to reduce the workload of glReadPixels by assigning the exact screen width and screen height (not a 2*n integer) to the glReadPixels, and follow by the glTexImage2D (the width and height still rounded to the nearest 2*n) but this time the image that capture with this method is distorted. The code is as follow:

First Method

Gl.glPixelStorei(Gl.GL_UNPACK_ALIGNMENT, 1);
Gl.glPixelStorei(Gl.GL_PACK_ALIGNMENT, 1);
Gl.glReadBuffer(Gl.GL_FRONT);

if (view.width <= 256 && view.height <= 256)
{
saizView = 256;
}
else if (winWidth <= 512 && winHeight <= 512)
{
saizView = 512;
}
else if (winWidth <= 1024 && winHeight <= 1024)
{
saizView = 1024;
}
else
{
// saizView=1024;
saizView = 1024 * 2;
}

int[] sizeint = new int[1];
Gl.glGetIntegerv(Gl.GL_MAX_TEXTURE_SIZE, sizeint);

byte[] colorSave = new byte[3 * saizView * saizView];

Gl.glReadPixels(0, 0, saizView, saizView, Gl.GL_RGB, Gl.GL_UNSIGNED_BYTE, colorSave);

Gl.glBindTexture(Gl.GL_TEXTURE_2D, saveImage[0]);
Gl.glTexParameteri(Gl.GL_TEXTURE_2D, Gl.GL_TEXTURE_WRAP_S, Gl.GL_REPEAT);
Gl.glTexParameteri(Gl.GL_TEXTURE_2D, Gl.GL_TEXTURE_WRAP_T, Gl.GL_REPEAT);
Gl.glTexParameteri(Gl.GL_TEXTURE_2D, Gl.GL_TEXTURE_MAG_FILTER, Gl.GL_NEAREST);
Gl.glTexParameteri(Gl.GL_TEXTURE_2D, Gl.GL_TEXTURE_MIN_FILTER, Gl.GL_NEAREST);
Gl.glTexImage2D(Gl.GL_TEXTURE_2D, 0, Gl.GL_RGB, saizView, saizView, 0, Gl.GL_RGB, Gl.GL_UNSIGNED_BYTE, colorSave);
colorSave = null;

Second Method

Gl.glPixelStorei(Gl.GL_UNPACK_ALIGNMENT, 1);
Gl.glPixelStorei(Gl.GL_PACK_ALIGNMENT, 1);
Gl.glReadBuffer(Gl.GL_FRONT);
// Gl.glReadBuffer(Gl.GL_BACK);

if (view.width <= 256 && view.height <= 256)
{
saizView = 256;
}
else if (winWidth <= 512 && winHeight <= 512)
{
saizView = 512;
}
else if (winWidth <= 1024 && winHeight <= 1024)
{
saizView = 1024;
}
else
{
// saizView=1024;
saizView = 1024 * 2;
}

int[] sizeint = new int[1];
Gl.glGetIntegerv(Gl.GL_MAX_TEXTURE_SIZE, sizeint);

byte[] colorSave = new byte[3 * saizView * saizView];

for (int i = 0; i < colorSave.Length; i++)
{
colorSave[i] = 0;
}

Gl.glReadPixels(0, 0, view.width, view.height, Gl.GL_RGB, Gl.GL_UNSIGNED_BYTE, colorSave);

Gl.glBindTexture(Gl.GL_TEXTURE_2D, saveImage[0]);
Gl.glTexParameteri(Gl.GL_TEXTURE_2D, Gl.GL_TEXTURE_WRAP_S, Gl.GL_REPEAT);
Gl.glTexParameteri(Gl.GL_TEXTURE_2D, Gl.GL_TEXTURE_WRAP_T, Gl.GL_REPEAT);
Gl.glTexParameteri(Gl.GL_TEXTURE_2D, Gl.GL_TEXTURE_MAG_FILTER, Gl.GL_NEAREST);
Gl.glTexParameteri(Gl.GL_TEXTURE_2D, Gl.GL_TEXTURE_MIN_FILTER, Gl.GL_NEAREST);
Gl.glTexImage2D(Gl.GL_TEXTURE_2D, 0, Gl.GL_RGB, saizView, saizView, 0, Gl.GL_RGB, Gl.GL_UNSIGNED_BYTE, colorSave);
colorSave = null;

Can someone tell me where I am wrong? Thanks in advance.

overlay
04-07-2009, 09:03 AM
This is not a direct answer to your question, but a couple of
remarks:

1. You mean 2^n, not 2*n, right? If at runtime the GL_VERSION query returns something >=2.0 or if you have extension GL_ARB_texture_non_power_of_two in the string returned by the GL_EXTENSIONS query, textures already support any type of size, not just power-of-two.

2. If your goal is to transfer data from the framebuffer to a texture, you'd better look at glCopyTexImage2D() or glCopyTexSubImage2D().

* With glReadPixel()/glTexImage2D() you're doing VRAM->RAM (slow), RAM->VRAM (slow)
* With glCopyTexImage2D(), your doing VRAM->VRAM (fast)

HanWu
04-07-2009, 06:56 PM
Thanks for reminding me about the glCopyTexImage2D function, it is far more faster than my previous method.

k_szczech
04-08-2009, 05:16 AM
Actually, use glCopyTexSubImage2D, because glCopyTexImage2D creates new texture every time, while the 'Sub' version only updates existing texture. Shouldn't make much difference in performance, but it's the right way to do it.
Also, you should consider using Framebuffer Object extension if available to render directly to texture (without any copying).