glTexSubImage2D not working with GL_RGB or GL_LUMINANCE textures [SOLVED]

Started by
6 comments, last by zaneski13 12 years, 1 month ago
Ok, so I'm having a problem with the glTexSubImage2D function. The function only works with GL_RGBA textures, but when I use GL_RGB or GL_LUMINANCE textures, glTexSubImage2D acts funny.

I've created a class called TextureEditable
Bellow are the 2 functions I made. SetData, and SetSubData(). formatType can be TEX_RGB, TEX_RGBA, or TEX_ALPHA


bool TextureEditable::SetData(int width_, int height_, unsigned char *pixels, int formatType)
{
if ( !math::IsPower2(width_) || !math::IsPower2(height_) )
return false;
if ( !pixels )
return false;
if ( formatType != TEX_RGB && formatType != TEX_RGBA && formatType != TEX_ALPHA )
return false;
//ok, so the data that was passed in is good, only error that can possibly happen is if the data has less elements that width*height*bpp
format = formatType;
width = width_;
height = height_;
if ( data )
delete [] data;
int bpp = 1;
if ( format == TEX_RGB )
bpp = 3;
else if ( format == TEX_RGBA )
bpp = 4;
cout << "bpp = " << bpp << endl;
data = new unsigned char[width*height*bpp];

for ( int i=0; i < width*height*bpp; i++ )
data = pixels;
//now we have the data
if ( texID == 0 ) //then we haven't generated a gl texture yet, so generate one
{
Texture::EnableGLTextures();
glGenTextures(1, &texID);
glBindTexture(GL_TEXTURE_2D, texID);
Texture::lastBoundTexture = texID;
cout << "creating tex " << texID << endl;
}
Texture::EnableGLTextures();
glBindTexture(GL_TEXTURE_2D, texID);
Texture::lastBoundTexture = texID;
glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST );
glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST );
if ( format == TEX_RGB )
{
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, data);
}
else if ( format == TEX_RGBA )
{
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, data);
}
else if ( format == TEX_ALPHA )
{
glTexImage2D(GL_TEXTURE_2D, 0, GL_LUMINANCE, width, height, 0, GL_LUMINANCE , GL_UNSIGNED_BYTE, data);
}



return true;
}
bool TextureEditable::SetSubData(int x, int y, int w, int h, unsigned char *pixels)
{
if ( !pixels )
return false;
if ( x < 0 || x >= width || y < 0 || y >= height )
return false;

Texture::EnableGLTextures();
glBindTexture(GL_TEXTURE_2D, texID);
Texture::lastBoundTexture = texID;
if (format == TEX_RGBA )
{
glTexSubImage2D(GL_TEXTURE_2D, 0, x, y, w, h, GL_RGBA, GL_UNSIGNED_BYTE, pixels);
}
else if ( format == TEX_RGB )
{
glTexSubImage2D(GL_TEXTURE_2D, 0, x, y, w, h, GL_RGB, GL_UNSIGNED_BYTE, pixels);
}
else if ( format == TEX_ALPHA )
glTexSubImage2D(GL_TEXTURE_2D, 0, x, y, w, h, GL_LUMINANCE, GL_UNSIGNED_BYTE, pixels);

}




OK, so those are the main functions I'm working with in that class.

in my main program I do this...


tex2 = new TextureEditable("");

unsigned char pixels[4*4] = {100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100};
tex2->SetData(4,4,pixels,TEX_ALPHA);

unsigned char sub[2*2] = {255,255,255,255};
tex2->SetSubData(1,1,2,2,sub);



I render a quad with the texture mapped on it. What I should see is a grey square with a white square in the middle
Instead I see a grey square with a square in the middle that is shaded white in the first 2 texels and shaded different shades of grey for the other 2 texels

Note, the desired effect works when I use a texture that has GL_RGBA data. I think whats happening is when I call glTexSubImage2D its taking the pixel data i send it assumes its a RGBA texture when its a greyscale texture. But I'm not sure. Any help would be greatly appreciated.
Advertisement
Your code looks correct on the first view, your problem might be somewhere else.
You could try this code: http://nehe.gamedev...._mapping/12038/ and compare it step by step to yours to see where the problem is.

the_visualist
Look up glPixelStore and GL_UNPACK_ALIGNMENT. The parameter determines the byte alignment of each row of image data, and thus how much passing is present in the source data. The default value if 4, which means that each row of the image must start on a byte-offset that is a multiple of 4. Your luminance sub-image does not obey that alignment, since the second row is only two bytes away from the start address, so OpenGL skips two bytes before starting to read the second row, and at that time you are outside the sub-image data. Either pad the image data to meed the alignment requirement, or set the alignment to 1 byte to imply no padding.
Thanks, that sounds like the function I should be using. Just a question. I checked the reference guide for glPixelStore and the parameter GL_UNPACK_ALIGNMENT, and appariently the only valid values you can pass to the function is 1,2,4, or 8. According to http://www.talisman.org/opengl-1.1/Reference/glPixelStore.html

So when the pixel data I'm passing to the texture is GL_LUMIANCE I'd call glPixelStore(GL_UNPACK_ALIGNMENT, 1); before calling glTexSubImage2D()
But, when the pixel data I'm passing to the texture is GL_RGB I'd call glPixelStore(GL_UNPACK_ALIGNMENT, 3) before calling glTexSubImage2D() but '3' isn't a valid value to pass to glPixelStore(GL_UNPACK_ALIGNMENT, n); According to the link above, only 1,2,4, and 8 are valid values. 1 being (L,L,L,1) 2 being (L,L,L,A) ??? 4 being (R,G,B,A) 8 being (R,G,B,A,ummmmmmmmmm idk)
Heres what I have for code at the moment


bool TextureEditable::SetSubData(int x, int y, int w, int h, unsigned char *pixels)
{
if ( !pixels )
return false;
if ( x < 0 || x >= width || y < 0 || y >= height )
return false;


Texture::EnableGLTextures();
glBindTexture(GL_TEXTURE_2D, texID);
Texture::lastBoundTexture = texID;
if (format == TEX_RGBA )
{
glPixelStorei( GL_UNPACK_ALIGNMENT, 4);
glTexSubImage2D(GL_TEXTURE_2D, 0, x, y, w, h, GL_RGBA, GL_UNSIGNED_BYTE, pixels);
}
else if ( format == TEX_RGB )
{
glTexSubImage2D(GL_TEXTURE_2D, 0, x, y, w, h, GL_RGB, GL_UNSIGNED_BYTE, pixels);
}
else if ( format == TEX_ALPHA )
{
glPixelStorei( GL_UNPACK_ALIGNMENT, 1);
glTexSubImage2D(GL_TEXTURE_2D, 0, x, y, w, h, GL_LUMINANCE, GL_UNSIGNED_BYTE, &pixels[0]);
}

}


Right now TEX_RGBA and TEX_ALPHA formats are working. idk what to do about TEX_RGB since '3' isnt a valid value to pass to glPixelStore
The byte alignment has nothing to do with how many color components you have. All it means is that each row of the image must start on an address that is a multiple of the alignment.

Consider a 5 pixel wide RGB image with one byte per channel; each row of the image is then 15 bytes. So where does the second row of pixels start? Is it 15 bytes from the start of the first row? The default alignment is 4, which means that OpenGL assumes that each row starts on an offset that that is a multiple of 4 bytes from the previous row. Thus, in the example, the 15-byte row has to be padded with an empty byte before the next one starts. If there is no padding, OpenGL won't read the image correctly, since the image doesn't contain any padding but OpenGL assumes it does. That is, rows are not necessarily stored tightly packed in memory.

Consider instead that you have a 5 pixel wide luminance image. Each row is now 5 bytes, which still isn't a multiple of 4. The next multiple of 4 is 8, so there must be 3 bytes of padding at the end of each row before the next row starts.

Consider an RGBA image this time; each row is now 20 bytes, which is a multiple of 4. No padding is necessary for this image, since each row is already a multiple of 4 bytes long.

It has nothing to do with what format you use, but how long each row is in bytes. The byte-length of each row must be a multiple of the alignment, or you have to insert padding after each row to ensure that the alignment is fulfilled.
If you have strict non-padded data, then just set the alignment to 1 and don't bother with it anymore. Every image is on a 1-byte alignment, so there won't be any problems, at least no read to the image properly.
Thanks for clarifying! smile.png
I can move onto my editor now

This topic is closed to new replies.

Advertisement