Welcome, Guest. Please login or register.

Login with username, password and session length

 
Advanced search

1411615 Posts in 69390 Topics- by 58447 Members - Latest Member: sinsofsven

May 10, 2024, 06:31:46 AM

Need hosting? Check out Digital Ocean
(more details in this thread)
TIGSource ForumsDeveloperTechnical (Moderator: ThemsAllTook)OpenGL 2D textures problem
Pages: [1] 2
Print
Author Topic: OpenGL 2D textures problem  (Read 4942 times)
mechacrash
Level 1
*


It's almost like I'm actually being productive!


View Profile
« 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
Level 10
*****


Aquin is over here.


View Profile WWW
« 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!


View Profile
« 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
Level 4
****



View Profile WWW
« 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!


View Profile
« 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


View Profile WWW
« 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!


View Profile
« 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
Level 7
**



View Profile WWW
« 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
Level 4
****



View Profile WWW
« 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!


View Profile
« Reply #9 on: November 26, 2009, 01:33:02 PM »

Doesn't this enlarge the image though?
Logged

Saint
Level 3
***



View Profile WWW
« 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.txt

So 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;

Code:

// 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
Level 10
*****

λx.x


View Profile
« 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
Level 4
****



View Profile WWW
« 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
Level 10
*****


@iam2bam


View Profile WWW
« 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

Working on HeliBrawl
c-foo peng
Level 3
***


game chef


View Profile WWW
« 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
Level 10
*****


glitch


View Profile WWW
« 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

My projects:<br />Games: Jumpman Retro-futuristic platforming iJumpman iPhone version Drumcircle PC+smartphone music toy<br />More: RUN HELLO
Average Software
Level 10
*****

Fleeing all W'rkncacnter


View Profile WWW
« 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.txt

So 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;

Code:

// 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
Level 3
***



View Profile WWW
« 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
Level 10
*****

λx.x


View Profile
« Reply #18 on: November 27, 2009, 04:20:51 AM »

If you're working in c++ this might be useful:
Code:
//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
That's cool.
Level 10
*****


3D models are the best


View Profile WWW
« 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. Gentleman

(might do without the windows.h? i didn't actually look at the rest of the code)
Logged

Pages: [1] 2
Print
Jump to:  

Theme orange-lt created by panic