PDA

View Full Version : glGetTexImage yields slanted image with wrong content (no GL_(UN)PACK_ALIGNMENT!)



Brokenmind
05-30-2017, 10:25 AM
Hey guys,

render to texture, awesome. Beautiful stuff can be made with some Perlin Noise. Imagine a metal cube with a procedural texture:
http://fs5.directupload.net/images/user/170530/temp/cdqyjqju.jpg (http://www.directupload.net/file/u/46409/cdqyjqju_jpg.htm)

So now I want to extract the texture, with glTexImage2D or, in my case (openTK) it's GL.TexImage2D. Result looks like this:
http://fs5.directupload.net/images/user/170530/temp/cm5e5cyy.jpg (http://www.directupload.net/file/u/46409/cm5e5cyy_jpg.htm)

I've read through tons of posts, most of which resulted in the usage of glPixelStorei(GL_UNPACK_ALIGNMENT, 1). It was also mentioned that this is a common problem for textures that are no power of 2, but in my case, I can't extract the data no matter how big the texture is.

This is all the relevant code leading to the texture extraction:


int dimension = 100; // can be any number

// create render target
int screenRenderFBO = GL.GenFramebuffer();
GL.BindFramebuffer(FramebufferTarget.Framebuffer, screenRenderFBO);
DrawBuffersEnum renderTarget = DrawBuffersEnum.ColorAttachment0;

GL.ActiveTexture(TextureUnit.Texture0);
int createdTextures[i] = GL.GenTexture();
GL.BindTexture(TextureTarget.Texture2D, createdTexture);

GL.TexImage2D(TextureTarget.Texture2D, 0, PixelInternalFormat.Rgba8, dimension, dimension, 0, PixelFormat.Rgba, PixelType.Byte, IntPtr.Zero);
GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureMinFilter, (int)TextureMinFilter.Linear);
GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureMagFilter, (int)TextureMagFilter.Linear);

GL.FramebufferTexture(FramebufferTarget.Framebuffe r, FramebufferAttachment.ColorAttachment0, createdTexture, 0);
GL.BindFramebuffer(FramebufferTarget.Framebuffer, 0);




// bind render target
// assuming viewport stays the same
GL.BindFramebuffer(FramebufferTarget.Framebuffer, screenRenderFBO);
GL.DrawBuffers(1, renderTarget);

// do drawing code, I assume it works because the displayed texture is perfectly fine

// extract texture
byte[] pixels = new byte[dimension * dimension * 4];
IntPtr pixelAddress = Marshal.UnsafeAddrOfPinnedArrayElement(pixels, 0);

GL.ActiveTexture(TextureUnit.Texture0);
GL.BindTexture(TextureTarget.Texture2D, createdTexture);

GL.GetTexImage(TextureTarget.Texture2D, 0, PixelFormat.Bgra, PixelType.UnsignedByte, pixelAddress);

Bitmap bmp = new Bitmap(texture.Width, texture.Height, 8, PixelFormat.Format32bppRgb, pixelAddress);
bmp.Save(...)



I tried using every combination of glPixelStorei(GL_(UN)PACK_ALIGNMENT, 1|2|4|8) and it did not change a thing, but I have to say I don't really understand when exactly to use it. But with textures sized power of two, it should work without, right?
I also tried drawing the texture and then reading it with glReadPixels, the result was the exact same. Also, the colors are not only distorted in the Bitmap, but already before in the pixels array, so that should not be the problem.

Seeing the slanted image (the lines have a slope of exactly 0.5, in case that's important), I get the feeling that the mistake must be very obvious. I just hope the problem does not lie within openTK because in that case, good luck waiting for a fix...

If you have any idea what's going on here, please help! :)

Regards,
Brokenmind

GClements
05-30-2017, 04:06 PM
If you're retrieving RGBA data, an alignment of 4 (the default) is fine regardless of the texture width, as each pixel will be 4 bytes.

Are you sure that you aren't skewing the image in the rendering process? How are you determining fragment location? Interpolated vertex coordinates or gl_FragCoord?

Brokenmind
05-30-2017, 04:40 PM
I'm quite sure because the top screenshot was taken directly from the openGL window.. so I reckon it has to be right, right? Vertex shader looks like this


#version 330

layout(location = 0) in vec2 pos;
out vec2 coord;

void main()
{
coord = pos.xy;
gl_Position = vec4(pos, 0, 1);
}

with pos going from (-1,-1) around counterclockwise, and no other transformation is applied to out vec2 coord apart from going a bit left and right for that nice smoothing effect (in the noise algorithm only). Anything else I might have missed? :tired:

Brokenmind
06-01-2017, 04:06 AM
Also, the colors are not only distorted in the Bitmap, but already before in the pixels array, so that should not be the problem.

Assumptions can be so wrong... looks like I didn't see the error before. The pixel retrieval worked all along :doh:
Converting RGB data to a proper Bitmap (without SetPixel!) is a bit tricky, but if anyone ever has the same problem, here goes:


public static Image ExtractTexture(TextureWrapper texture)
{
byte[] pixels = new byte[texture.Width * texture.Height * 3];

texture.Bind();

// has to be BGR because weird color shift otherwise
GL.GetTexImage<byte>(TextureTarget.Texture2D, 0,
OpenTK.Graphics.OpenGL.PixelFormat.Bgr, PixelType.UnsignedByte, pixels);

Bitmap bmp = new Bitmap(texture.Width, texture.Height);
BitmapData data = bmp.LockBits(new Rectangle(0, 0, bmp.Width, bmp.Height),
System.Drawing.Imaging.ImageLockMode.WriteOnly, System.Drawing.Imaging.PixelFormat.Format24bppRgb) ;
IntPtr ptr = data.Scan0;

// same as memcpy
Marshal.Copy(pixels, 0, ptr, texture.Width * texture.Height * 3);

bmp.UnlockBits(data);
// flip due to different alignment
bmp.RotateFlip(RotateFlipType.RotateNoneFlipY);

return bmp;
}