Datriot's picture

Loading Textures

I'm pretty new to OpenGL, as I've been a DirectX use for the most part. I must say that texture loading and usage is quite different in OpenGL and I'm not sure how to go about loading and displaying one ( preferably on a quad ).
There's some tutorials out there, but one catered for OpenTK would be helpful so help would be appreciated. :)


Comments

Comment viewing options

Select your preferred way to display the comments and click "Save settings" to activate your changes.
Datriot's picture

Well that's good then, at least I know I won't have to re-do what I've so far for a more effective method.

objarni's picture

These kind of things would be wonderful to have in the OpenTK.Utilities.dll !

Just imagine:

Texture tex = OpenTK.Utilities.Texture.Load("c:/mypic.png");

.. where Load() sets some sane defaults for the tex object.

the Fiddler's picture

Inertia has written a .dds loader which works almost exactly like this. I'm doing the same for .wav and .ogg files, so this functionality will appear in OpenTK.Utilities sooner or later. ;)

Inertia's picture

There's a GDI+ loader aswell http://www.opentk.com/node/253

The idea of the utilities.dll is to draw a clean line between bindings and higher level functions. Some people might only want to use OpenTK.dll and deal with asset/content loading on their own, since the loaders are not optimized for speed (and will probably never be). No matter how much one would optimize the .dds loader there will always be the need to flip the image upside down (DirectX vs. OpenGL texture matrix) and we cannot make it faster than the user can: by creating his custom file format and save/load (the already flipped image which the utilities.dll gave you) directly.

However unless you're loading 100MB+ of textures the OpenTK loaders will serve you well.

jeske's picture

The code sample in the beginning of this is using the old depreciated Glu stuff, here is an updated version of the code, for other folks who land on this from a search result like I did..

if (!System.IO.File.Exists(texFilename)) {
	throw new Exception("missing texture file : " + texFilename);
}
 
//make a bitmap out of the file on the disk
Bitmap TextureBitmap = new Bitmap(texFilename);
 
//get the data out of the bitmap
System.Drawing.Imaging.BitmapData TextureData = 
      TextureBitmap.LockBits(
                 new System.Drawing.Rectangle(0,0,TextureBitmap.Width,TextureBitmap.Height),
                 System.Drawing.Imaging.ImageLockMode.ReadOnly,
                 System.Drawing.Imaging.PixelFormat.Format24bppRgb
      );
 
//Code to get the data to the OpenGL Driver
 
//generate one texture and put its ID number into the "Texture" variable
GL.GenTextures(1,out Texture);
//tell OpenGL that this is a 2D texture
GL.BindTexture(TextureTarget.Texture2D,Texture);
 
//the following code sets certian parameters for the texture
GL.TexEnv(TextureEnvTarget.TextureEnv, 
       TextureEnvParameter.TextureEnvMode, (float)TextureEnvMode.Modulate);
GL.TexParameter(TextureTarget.Texture2D, 
       TextureParameterName.TextureMinFilter, (float)TextureMinFilter.LinearMipmapLinear);
GL.TexParameter(TextureTarget.Texture2D, 
       TextureParameterName.TextureMagFilter, (float)TextureMagFilter.Linear);
 
// tell OpenGL to build mipmaps out of the bitmap data
GL.TexParameter(TextureTarget.Texture2D, 
       TextureParameterName.GenerateMipmap, (float)1.0f);
 
// load the texture
GL.TexImage2D(
    TextureTarget.Texture2D,
    0, // level
    PixelInternalFormat.Three,
    TextureBitmap.Width, TextureBitmap.Height,
    0, // border
    PixelFormat.Rgb,
    PixelType.UnsignedByte,
    TextureData.Scan0
    );
 
//free the bitmap data (we dont need it anymore because it has been passed to the OpenGL driver
TextureBitmap.UnlockBits(TextureData);
Kintar's picture

Thanks for all the example code, everyone. I've got a UV coordinates question that seems relevant here.

I'm just getting started with OpenGL+OpenTK, and have a simple app that draws a textured quad on the screen. My texture isn't mapping the way I expected, however, and I can't tell if I'm misunderstanding the load operation or the UV coordinate system, or something completely unrelated.

I have the following texture:
Original Texture

I load it without flipping, like this:

GlHandle = GL.GenTexture();
GL.BindTexture(TextureTarget.Texture2D, GlHandle);
GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureMagFilter, (float)TextureMagFilter.Linear);
GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureMinFilter, (float)TextureMinFilter.Linear);
GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.GenerateMipmap, 1);
 
using (var bmp = (Bitmap)Image.FromFile(@"Textures\" + name + ".png"))
{
	var bits = bmp.LockBits(new Rectangle(0, 0, bmp.Width, bmp.Height), ImageLockMode.ReadOnly,
							System.Drawing.Imaging.PixelFormat.Format32bppArgb);
 
	GL.TexImage2D(TextureTarget.Texture2D, 0, PixelInternalFormat.Rgba, bits.Width, bits.Height, 0,
				  PixelFormat.Bgra, PixelType.UnsignedByte, bits.Scan0);
 
	bmp.UnlockBits(bits);
}

I'm drawing a textured quad using the following code in a GameWindow:

protected override void OnRenderFrame(FrameEventArgs e)
{
	GL.Clear(ClearBufferMask.ColorBufferBit | ClearBufferMask.DepthBufferBit);
 
	GL.MatrixMode(MatrixMode.Modelview);
	GL.LoadIdentity();
 
	var qtrWidth = Width/4;
	var qtrHeight = Height/4;
 
	GL.Enable(EnableCap.Texture2D);
 
	// tex is an instance of a class that wraps the loading and 
	// deleting code, and gives me an easy way to bind the texture
	tex.Bind();
 
	GL.Begin(BeginMode.Quads);
	GL.Color3(Color.White);
 
	GL.Vertex2(qtrWidth, qtrHeight * 2);
	GL.TexCoord2(0, 0);
 
	GL.Vertex2(qtrWidth * 2, qtrHeight * 2);
	GL.TexCoord2(1, 0);
 
	GL.Vertex2(qtrWidth * 2, qtrHeight);
	GL.TexCoord2(1, 1);
 
	GL.Vertex2(qtrWidth, qtrHeight);
	GL.TexCoord2(0, 1);
 
	GL.End();
 
	SwapBuffers();
}

So my quad is defined by four vertices running CCW; bottom-left, , bottom-right, top-right, top-left, with the corresponding UV coordinates {0,0},{1,0},{1,1},{0,1}. This is the way I understood the UV system to work, with the origin in the bottom-left;

However, this is the image I get:
Rendered Quad

I was expecting an image flipped on the Y-axis, since Bitmaps origins are upper-left, but in order to correct this mis-mapping I have to rotate the texture 90 degrees CW, then flip it along the X axis.

Can anyone tell me what part of all these texture coordinates I'm misunderstanding?

Kintar's picture

Well...it appears what I don't understand is the GL.Ortho projection. :) Typing out my question helped me in thinking about it, so here's what I've discovered so far.

My original setup code looked like this:

protected override void OnResize(EventArgs e)
{
	GL.MatrixMode(MatrixMode.Projection);
	GL.LoadIdentity();
	GL.Ortho(ClientRectangle.Left, ClientRectangle.Right, ClientRectangle.Bottom, ClientRectangle.Top,
			 -1.0, 1.0);
	GL.Viewport(ClientRectangle.Size);
}

I thought this would give me a viewport where {0,0} was the top-left of the viewport and {width,height} was the bottom-right. Apparently that isn't so. Changing my setup code to this:

GL.MatrixMode(MatrixMode.Projection);
GL.LoadIdentity();
GL.Ortho(-1, -1, 1, 1,
		 -1.0, 1.0);
GL.Viewport(ClientRectangle.Size);

And my render code to this:

GL.Begin(BeginMode.Quads);
GL.Color3(Color.White);
 
GL.TexCoord2(0, 1);
GL.Vertex2(-.5, -.5);
 
GL.TexCoord2(1, 1);
GL.Vertex2(.5, -.5);
 
GL.TexCoord2(1, 0);
GL.Vertex2(.5, .5);
 
GL.TexCoord2(0, 0);
GL.Vertex2(-.5, .5);
 
GL.End();

Results in the proper image:
New Output

So my new question is: Can someone explain why the original viewport setup code produces an (apparently) rotated viewport?

Kintar's picture

I feel like a right moron. Sorry for the spam. The issue is this:

Setup code:

GL.Ortho(ClientRectangle.Left, ClientRectangle.Right, ClientRectangle.Bottom, ClientRectangle.Top,  -1.0, 1.0);

This was based on the intellisense code completion for GL.Ortho, which says the parameters are: left, right, bottom, top, zNear, zFar.

Changing the setup code to:

GL.Ortho(ClientRectangle.Left, ClientRectangle.Top, ClientRectangle.Right, ClientRectangle.Bottom, -1, 1);

Resolves the issue, because it's not talking about viewport coordinates, it's talking about clipping plane coordinates.

Sorry for the spam. I hope my thrashing helps someone else one day.

iabstract's picture

The coordinate system is throwing me off a bit. @Kintar ... looks like a MineCraft block sheet to me :).

So I am trying to figure out the coordinate system and how to scale a texture (from .png). I am going to have a single texture as a background and multiple images drawn on top of it - think of the way a Windows Form is drawn.

How do we understand the coordinate system when we are drawing objects that will be interacted with by the user - buttons, text boxes, etc.

the Fiddler's picture

To match the WinForms coordinate system (origin at top-left, +x +y going to the bottom-right), you can use a projection like:

GL.Ortho(0, ClientRectangle.Width, ClientRectangle.Height, 0,  -1.0, 1.0);

Note that this only affects vertex coordinates. Texture coordinates are always between (0, 1), with the origin at the bottom-left and +x, +y going to the top-right.