mechacrash
Level 1
It's almost like I'm actually being productive!
|
|
« on: November 26, 2009, 11:55:19 AM » |
|
I'm currently having trouble creating and using a texture that is not the power of 2 in size in openGL. I have looked into NPOT and heard about using rectangles but want to know if there is anyone with better knowledge of this or a better alternative.
|
|
|
Logged
|
|
|
|
Aquin
|
|
« Reply #1 on: November 26, 2009, 11:58:09 AM » |
|
Personally I just stick with powers of two in the image data *anyway*. It's not as if it eats up more memory (if you're using a png format at least.)
Another alternative is to use a library like SOIL or SFML that will take image data of any size and make it work with OpenGL. I'm not sure if you're using a library to load your images or if you're doing it on your own.
|
|
|
Logged
|
I'd write a devlog about my current game, but I'm too busy making it.
|
|
|
mechacrash
Level 1
It's almost like I'm actually being productive!
|
|
« Reply #2 on: November 26, 2009, 12:11:26 PM » |
|
How do you get a random sized image into a power of 2 texture?
|
|
|
Logged
|
|
|
|
powly
|
|
« Reply #3 on: November 26, 2009, 12:15:04 PM » |
|
If your target platform isn't anything ancient or very hand-heldily, you should be able to use non-power-of-two textures without any problems. So what is this trouble of yours, texture doesn't show up, is distorted or what?
Like Aquin, I like to just use 2^n -size textures.. They work everywhere without any hassle.
And for a random sized image, you can just find the next power of two from its width and height (say, 18*68 -> 32*128) and in the glTexCoord() calculate the places (corners will be at 0, 0, 18/32, 68/128)
|
|
|
Logged
|
|
|
|
mechacrash
Level 1
It's almost like I'm actually being productive!
|
|
« Reply #4 on: November 26, 2009, 12:34:39 PM » |
|
My problem with NPOT is that the computers at my school cant handle it so I want some method that can be used on every computer.
In reply to your method, wouldn't that stretch the image into the texture size? how would you fix that?
|
|
|
Logged
|
|
|
|
Draknek
Level 6
"Alan Hazelden" for short
|
|
« Reply #5 on: November 26, 2009, 12:41:11 PM » |
|
What you want to do is add padding to the image and modify your texture coordinates. So if your image used to be 100x100 and your new image with padding is 128x128, the coordinates would be from 0 to 100.0f/128.0f instead of from 0 to 1. If your target platform isn't anything ancient or very hand-heldily, you should be able to use non-power-of-two textures without any problems.
Not with basic OpenGL. There are extensions to handle NPOT textures which should be available pretty much everywhere, but they aren't guaranteed and they aren't part of OpenGL itself. I always just use power-of-two textures. Why go to the hassle of rewriting my image loading code when it works fine as it is?
|
|
|
Logged
|
|
|
|
mechacrash
Level 1
It's almost like I'm actually being productive!
|
|
« Reply #6 on: November 26, 2009, 12:48:23 PM » |
|
What you want to do is add padding to the image and modify your texture coordinates.
How do I add padding?
|
|
|
Logged
|
|
|
|
brog
|
|
« Reply #7 on: November 26, 2009, 01:02:32 PM » |
|
What you want to do is add padding to the image and modify your texture coordinates.
How do I add padding? Paste your image in the top left corner of a larger image.
|
|
|
Logged
|
|
|
|
powly
|
|
« Reply #8 on: November 26, 2009, 01:09:57 PM » |
|
pretty much everywhere
Yes, that's what I meant when I wrote about ancient computers.. And yes, now I also see why you can't use non-power-of-two directly. I also do stuff with school computers occasionally. Well, just pad your images correctly and it'll work just fine. (extra points if you just draw pictures to the 2^n*2^n -format, it works fine in most cases)
|
|
|
Logged
|
|
|
|
mechacrash
Level 1
It's almost like I'm actually being productive!
|
|
« Reply #9 on: November 26, 2009, 01:33:02 PM » |
|
Doesn't this enlarge the image though?
|
|
|
Logged
|
|
|
|
Saint
|
|
« Reply #10 on: November 26, 2009, 01:38:26 PM » |
|
NPOT textures are in the GL standard since 1.5, but for windows you have to do some workarounds. There are essentially two extensions you can check for, GL_ARB_texture_non_power_of_two and GL_ARB_texture_rectangle. GL_ARB_texture_non_power_of_two means you can use NPOT textures just as you can ordinary textures, and it works implicitly which is why you usually don't have to worry about it. GL_ARB_texture_rectangle means you can use them with the following restrictions; - You have to use the texture target GL_TEXTURE_RECTANGLE_ARB (0x84F5) instead of GL_TEXTURE_2D - You have to use GL_CLAMP or GL_EDGE_CLAMP for texture wrapping (that is, no tiling) - You cannot have mipmaps (and thus must use GL_NEAREST or GL_LINEAR for filter modes) - Texture coordinates are not normalized (that is, instead of ranging from 0 to 1 the range is from 0 to the actual width/height of the image) NVidia cards and drivers have supported GL_ARB_texture_non_power_of_two for the last 5-7 years, ATI/AMD started supporting it for their earlier cards about a year ago and only supported GL_ARB_texture_rectangle a long time before that (although there was some hack in their drivers - I am not sure how long back this goes - that allowed you to use GL_TEXTURE_2D in NPOT sizes if you conformed to the rules except for the last one). http://www.opengl.org/registry/specs/ARB/texture_rectangle.txtSo the simplest way would be to check for the texture_non_power_of_two extension in the extension string ( glGetString(GL_EXTENSIONS) ). If you cannot find it, look for the texture_rectangle extension and make sure all your NPOT textures are clamped and do not have mipmaps if you can find it. If you have neither, the simplest way is to scale up all your textures to nearest power-of-two- size so you do not have to change parameters - this will make them look a little ugly on older computers (and if they are that old there is a slight risk the increased texture size will become an issue), but unless you want to cater specifically to people with old computers that is the easiest solution. I would recommend trying to only use POT textures as they are generally faster to process, and seeing as some platforms cannot even handle NPOT textures it is a good habit to have. However, here is some (not very pretty, mind you) code to resample a given texture to nearest POT size; // uint - unsigned int // uint8 - unsigned char // fp32 - float
// m_bForcePOT - flag whether Power-of-Two texture dimensions are to be forced or not // UseWidth - Width of input image, in pixels // UseHeight - Height of input image, in pixels // nCh = texture channels in image (3 for RGB, 4 for RGBA and so on) // _pData - input data, uint8*
if( m_bForcePOT ) { uint Wid = 1; uint Hei = 1; while( Wid < UseWidth ) Wid <<= 1; while( Hei < UseHeight ) Hei <<= 1; if( Wid != UseWidth || Hei != UseHeight ) { pData = new uint8[nCh * Wid * Hei]; fp32 SclX = (fp32)UseWidth / (fp32)Wid; fp32 SclY = (fp32)UseHeight / (fp32)Hei;
for(uint i = 0;i < Wid;i++) { fp32 RealX = (fp32)i * SclX; uint XPos = (uint)RealX; fp32 FracX = (RealX - XPos); uint nNxt = (i < Wid - 1) ? nCh : 0;
for(uint j = 0;j < Hei;j++) { fp32 RealY = (fp32)j * SclY; uint YPos = (uint)RealY; fp32 FracY = (RealY - YPos);
uint8 * pDst = pData + (i + j * Wid) * nCh; const uint8 * pSrc = _pData + (XPos + YPos * UseWidth) * nCh; const uint8 * pSrc2 = (j < Hei - 1) ? (pSrc + (UseWidth * nCh)) : pSrc; for(uint k = 0;k < nCh;k++) { fp32 Cl0,Cl1; Cl0 = (pSrc[k] * (1.0f - FracX)) + (pSrc[k+nNxt] * FracX); Cl1 = (pSrc2[k] * (1.0f - FracX)) + (pSrc2[k+nNxt] * FracX); fp32 Cl = Clamp( (Cl0 * (1.0f - FracY)) + (Cl1 * FracY), 0.0f,255.5f ); pDst[k] = (uint8)Cl; } } }
_pData = pData; UseWidth = Wid; UseHeight = Hei; } }
//Don't forget to free the pData memory you allocated!
|
|
« Last Edit: November 26, 2009, 01:44:02 PM by Saint »
|
Logged
|
|
|
|
Tycho Brahe
|
|
« Reply #11 on: November 26, 2009, 02:12:33 PM » |
|
I can help you with this, as I needed exactly this and worked out how to do it. As I recall I used devil image loading library, but I need to be at my computer to give you code, I'll try to dig out some within about 16 hours...
|
|
|
Logged
|
|
|
|
powly
|
|
« Reply #12 on: November 26, 2009, 02:32:20 PM » |
|
Doesn't this enlarge the image though?
Yes, it will. The point is, when using OpenGL textures you can specify which part of the texture use for your triangle/quad using glTexCoordxx(). Just use the part you need and you're all set.
|
|
|
Logged
|
|
|
|
Martin 2BAM
|
|
« Reply #13 on: November 26, 2009, 02:50:35 PM » |
|
As a rule of thumb always use Power of 2 textures.
I have an ATI Radeon 9800 Pro (I think it's old enough now, but NOT ARCAIC) and it supports non-pow-2 textures but it does so in SOFTWARE RENDERING!...dropping the framerate to 2% (really)
Check out the code of SFML (Open source), or if you're doing just 2D... switch to SFML, it kicks ass.
|
|
|
Logged
|
|
|
|
c-foo peng
|
|
« Reply #14 on: November 26, 2009, 04:07:03 PM » |
|
I have nightmares about my games not working on random systems X for random hardware reason Y. So to be on the safe side, I try to make nothing dependent on specific configurations.
Power of twos for great safety!
|
|
|
Logged
|
|
|
|
mcc
|
|
« Reply #15 on: November 26, 2009, 06:05:59 PM » |
|
mechacrash, one common thing you should probably be aware of is the idea of a "texture atlas". What many people do is take several textures they will be using at about the same time, each of which is probably not a power of two image, and fit them jigsaw-style into a single large image which is a power of two. (After packing your images in there will be space left over, which is filled either with some sort of neutral color, sometimes or "extra" texture pixels so that you don't get images "bleeding" into each other.) There are a number of free programs around for packing images into atlases. What you can do once you've made an atlas is just bind your atlas texture once, and then switch between the different textures within the atlas by changing your glTexCoordinates.
I would recommend staying away from NPOT textures even if some of your target systems allow it. It will make your program harder to port later and gain you very little.
|
|
|
Logged
|
|
|
|
Average Software
|
|
« Reply #16 on: November 26, 2009, 06:27:43 PM » |
|
NPOT textures are in the GL standard since 1.5, but for windows you have to do some workarounds. There are essentially two extensions you can check for, GL_ARB_texture_non_power_of_two and GL_ARB_texture_rectangle. GL_ARB_texture_non_power_of_two means you can use NPOT textures just as you can ordinary textures, and it works implicitly which is why you usually don't have to worry about it. GL_ARB_texture_rectangle means you can use them with the following restrictions; - You have to use the texture target GL_TEXTURE_RECTANGLE_ARB (0x84F5) instead of GL_TEXTURE_2D - You have to use GL_CLAMP or GL_EDGE_CLAMP for texture wrapping (that is, no tiling) - You cannot have mipmaps (and thus must use GL_NEAREST or GL_LINEAR for filter modes) - Texture coordinates are not normalized (that is, instead of ranging from 0 to 1 the range is from 0 to the actual width/height of the image) NVidia cards and drivers have supported GL_ARB_texture_non_power_of_two for the last 5-7 years, ATI/AMD started supporting it for their earlier cards about a year ago and only supported GL_ARB_texture_rectangle a long time before that (although there was some hack in their drivers - I am not sure how long back this goes - that allowed you to use GL_TEXTURE_2D in NPOT sizes if you conformed to the rules except for the last one). http://www.opengl.org/registry/specs/ARB/texture_rectangle.txtSo the simplest way would be to check for the texture_non_power_of_two extension in the extension string ( glGetString(GL_EXTENSIONS) ). If you cannot find it, look for the texture_rectangle extension and make sure all your NPOT textures are clamped and do not have mipmaps if you can find it. If you have neither, the simplest way is to scale up all your textures to nearest power-of-two- size so you do not have to change parameters - this will make them look a little ugly on older computers (and if they are that old there is a slight risk the increased texture size will become an issue), but unless you want to cater specifically to people with old computers that is the easiest solution. I would recommend trying to only use POT textures as they are generally faster to process, and seeing as some platforms cannot even handle NPOT textures it is a good habit to have. However, here is some (not very pretty, mind you) code to resample a given texture to nearest POT size; // uint - unsigned int // uint8 - unsigned char // fp32 - float
// m_bForcePOT - flag whether Power-of-Two texture dimensions are to be forced or not // UseWidth - Width of input image, in pixels // UseHeight - Height of input image, in pixels // nCh = texture channels in image (3 for RGB, 4 for RGBA and so on) // _pData - input data, uint8*
if( m_bForcePOT ) { uint Wid = 1; uint Hei = 1; while( Wid < UseWidth ) Wid <<= 1; while( Hei < UseHeight ) Hei <<= 1; if( Wid != UseWidth || Hei != UseHeight ) { pData = new uint8[nCh * Wid * Hei]; fp32 SclX = (fp32)UseWidth / (fp32)Wid; fp32 SclY = (fp32)UseHeight / (fp32)Hei;
for(uint i = 0;i < Wid;i++) { fp32 RealX = (fp32)i * SclX; uint XPos = (uint)RealX; fp32 FracX = (RealX - XPos); uint nNxt = (i < Wid - 1) ? nCh : 0;
for(uint j = 0;j < Hei;j++) { fp32 RealY = (fp32)j * SclY; uint YPos = (uint)RealY; fp32 FracY = (RealY - YPos);
uint8 * pDst = pData + (i + j * Wid) * nCh; const uint8 * pSrc = _pData + (XPos + YPos * UseWidth) * nCh; const uint8 * pSrc2 = (j < Hei - 1) ? (pSrc + (UseWidth * nCh)) : pSrc; for(uint k = 0;k < nCh;k++) { fp32 Cl0,Cl1; Cl0 = (pSrc[k] * (1.0f - FracX)) + (pSrc[k+nNxt] * FracX); Cl1 = (pSrc2[k] * (1.0f - FracX)) + (pSrc2[k+nNxt] * FracX); fp32 Cl = Clamp( (Cl0 * (1.0f - FracY)) + (Cl1 * FracY), 0.0f,255.5f ); pDst[k] = (uint8)Cl; } } }
_pData = pData; UseWidth = Wid; UseHeight = Hei; } }
//Don't forget to free the pData memory you allocated!
I used to do crap like this, but then I discovered gluScaleImage. Just use that to scale the image data to power of 2 sizes before creating your texture.
|
|
|
Logged
|
What would John Carmack do?
|
|
|
Saint
|
|
« Reply #17 on: November 27, 2009, 12:51:33 AM » |
|
I used to do crap like this, but then I discovered gluScaleImage. Just use that to scale the image data to power of 2 sizes before creating your texture.
Yes, that is slightly more flexible since you can also change the image format, although the result is essentially the same in this case. I just tend to assume people aren't using GLU...
|
|
|
Logged
|
|
|
|
Tycho Brahe
|
|
« Reply #18 on: November 27, 2009, 04:20:51 AM » |
|
If you're working in c++ this might be useful: //DevIL #include "IL\il.h" #include "IL\ilu.h" #include "IL\ilut.h" //FOR GENERAL WINDOWS STUFF #include <windows.h> //FOR TCHAR TYPE #include <tchar.h> //STRING/CHAR/TCHAR CONVERSION #include <strsafe.h>
//String / tchar conversion //string --> tchar TCHAR* StringToTCHAR(string& s) { tstring tstr; const char* all = s.c_str(); int len = 1 + strlen(all); wchar_t* t = new wchar_t[len]; if (NULL == t) throw std::bad_alloc(); mbstowcs(t, all, len); return (TCHAR*)t; } //Tchar --> String std::string TCHARToString(const TCHAR* ptsz) { int len = wcslen((wchar_t*)ptsz); char* psz = new char[2*len + 1]; wcstombs(psz, (wchar_t*)ptsz, 2*len + 1); std::string s = psz; delete [] psz; return s; } //Main function and game loop GLuint LoadImageRGBA(string IMG) { //setting up the loading folder stringstream LOCATION; LOCATION<<directory<<"\\Assets\\"; string asDir = LOCATION.str(); _chdir(asDir.c_str());
//now load //the bool for checking for success ILboolean success; //returning texture GLuint RETURN = NULL; //the texture for DevIL ILuint texid; //generate the texture ilGenImages(1, &texid); /* Generation of one image name */ //bind that stuff ilBindImage(texid); /* Binding of image name */ //load the image success = ilLoadImage((wchar_t*)IMG.c_str()); /* Loading of image the image (IMG.c_str()) */ if (success) /* If no error occured: */ { //convert to RGB mode success = ilConvertImage(IL_RGBA, IL_UNSIGNED_BYTE); if (!success) { /* Error occured */ SDL_Quit(); } glGenTextures(1, &RETURN); /* Texture name generation */ glBindTexture(GL_TEXTURE_2D, RETURN); /* Binding of texture name */ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); /* We will use linear interpolation for magnification filter */ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); /* We will use linear interpolation for minifying filter */ glTexImage2D(GL_TEXTURE_2D, 0, ilGetInteger(IL_IMAGE_BPP), ilGetInteger(IL_IMAGE_WIDTH), ilGetInteger(IL_IMAGE_HEIGHT), 0, ilGetInteger(IL_IMAGE_FORMAT), GL_UNSIGNED_BYTE, ilGetData()); /* Texture specification */
} else { /* Error occured */ SDL_Quit(); } ilDeleteImages(1, &texid); return RETURN; } It works for me and I've yet to find a machine it doesnt work on. However, this may just be due to limited testing...
|
|
|
Logged
|
|
|
|
Hideous
|
|
« Reply #19 on: November 27, 2009, 05:10:43 AM » |
|
I know loads of machines that won't work on. They don't run Windows though, so you probably don't care. (might do without the windows.h? i didn't actually look at the rest of the code)
|
|
|
Logged
|
|
|
|
|