Java-Gaming.org    
Featured games (91)
games approved by the League of Dukes
Games in Showcase (576)
games submitted by our members
Games in WIP (497)
games currently in development
News: Read the Java Gaming Resources, or peek at the official Java tutorials
 
    Home     Help   Search   Login   Register   
Pages: [1]
  ignore  |  Print  
  LWJGL Tutorial Series - Textures  (Read 8629 times)
0 Members and 1 Guest are viewing this topic.
Offline SHC
« Posted 2013-09-07 13:22:01 »

LWJGL Tutorial Series - Tutorial 4


Welcome to the part 4 of LWJGL Tutorial Series. We write the code again in this Tutorial and not copy them since you may not completely understand code if it was copied. Try extending the Game class we made and set-up OpenGL with an orthographic camera on your own to see if you can achieve that without any other reference. If you got it, continue with this part or else it is better to start from tutorial 1 again in which I've explained how to set up matrices and camera.

In this tutorial, I'm going to show you how to load images and convert them to OpenGL Textures and render them. If you are ok with the previous tutorials, let's continue.

Textures


For the time-being assume that texture is an image in OpenGL's raw format. By raw format, I mean that the data only contains pixel data in separate components of RED, GREEN, BLUE and ALPHA values for each pixel. We say to OpenGL the width and height of our texture, and create a ByteBuffer containing the pixel data and assign the buffer to the handle of the texture we will generate.

Don't worry if you're somewhat confused but you'll understand that with the code I'm going to show. You can skip this if you want to load textures from libraries like "Slick-Util", "TWL" etc., but I'm writing this since I'm trying to learn the underlying concepts as well. Let's start with a basic
Texture
class.

1  
2  
3  
4  
5  
6  
7  
8  
9  
10  
11  
12  
13  
14  
15  
16  
17  
18  
19  
20  
21  
22  
23  
24  
25  
26  
27  
public class Texture
{
    // ID of this texture
   public int id;

    // Width of this texture
   public int width;

    // Height of this texture
   public int height;

    // Private constructor to prevent duplication
   private Texture(int id, int width, int height)
    {
        this.id = id;
        this.width = width;
        this.height = height;
    }

    /**
     * Load the texture
     */

    public static Texture loadTexture(String name)
    {
        // Load and return the texture.
   }
}

It's basically ok, but how are we going to read the pixel values? For that purpose, we use
ImageIO
class to load a
BufferedImage
and later convert it. Okay, here's the next part of the
loadTexture()
method.

1  
2  
3  
4  
5  
6  
7  
8  
9  
10  
11  
12  
13  
// We need a BufferedImage which, we load with ImageIO
BufferedImage bimg = null;

try
{
    bimg = ImageIO.read(Texture.class.getClassLoader().getResourceAsStream(name));
}
catch (IOException e)
{
    e.printStackTrace();
    System.err.println("Unable to load Texture: " + name);
    Game.end();
}

In this part, we try to load the BufferedImage and if any errors, we print an error message and exit. Now let's create the necessary handle for it. You can simply use
glGenTextures();
which returns an integer handle to the texture we are going to create.

1  
int textureID = glGenTextures();

The next part is reading the pixel values from the BufferedImage.

Loading Pixel Data


We read the pixels in the BufferedImage into an integer array with it's
getRGB()
method.

1  
2  
3  
4  
// Multiply by four for all the four components of the pixel.
int[] pixels = new int[bimg.getWidth() * bimg.getHeight() * 4];
// Read all the pixels.
bimg.getRGB(0, 0, bimg.getWidth(), bimg.getHeight(), pixels, 0, bimg.getWidth());

Now we make a ByteBuffer to send these data to native OpenGL. This part can be done by using
BufferUtils
class in
orj.lwjgl.util
package.

1  
ByteBuffer buffer = BufferUtils.createByteBuffer(pixels.length);

Now, let's split the pixel components and add them to the buffer.

1  
2  
3  
4  
5  
6  
7  
8  
9  
10  
11  
12  
13  
14  
15  
16  
17  
18  
19  
for (int y=0; y<bimg.getHeight(); y++)
{
    for (int x=0; x<bimg.getWidth(); x++)
    {
        // Select the pixel
       int pixel = pixels[y * bimg.getWidth() + x];
        // Add the RED component
       buffer.put((byte) ((pixel >> 16) & 0xFF));
        // Add the GREEN component
       buffer.put((byte) ((pixel >> 8) & 0xFF));
        // Add the BLUE component
       buffer.put((byte) (pixel & 0xFF));
        // Add the ALPHA component
       buffer.put((byte) ((pixel >> 24) & 0xFF));
    }
}

// Now flip the buffer to reset the position pointer
buffer.flip();

This way, we've loaded the pixel data, split them into components and filled the ByteBuffer for OpenGL to read. The only part remaining is sending this buffer to OpenGL and generate the texture.

Sending Pixel Data to OpenGL


From here, we only work with native OpenGL functions. These involve binding the buffer to the texture handling and selecting the texture filters. We can use the functions
glBindTexture()
,
glTexParameteri()
and
glTexImage2D()
functions. I'll first show the code and then explain it.

1  
2  
3  
4  
5  
6  
7  
8  
9  
// Bind the texture handle to the context
glBindTexture(GL_TEXTURE_2D, textureID);

// Set the scaling filtering
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);

// Send the buffer to OpenGL
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, bimg.getWidth(), bimg.getHeight(), 0, GL_RGBA, GL_UNSIGNED_BYTE, buffer);

Okay, now let me explain them. First the function
glBindTexture()
is used to bind a texture to a target. It's syntax is

1  
void glBindTexture(int target, int textureID);

Where
target
varies between
GL_TEXTURE_2D
and
GL_TEXTURE_3D
depending on how we want to use that texture. The next function is
glTexParameteri()
for which the syntax is

1  
void glTexParameteri(int target, int paramName, int param);

This function is used to set some parameters to the texture. It also accepts the name of the parameter and the parameter for that texture. We specify the parameters
GL_TEXTURE_MIN_FILTER
and
GL_TEXTURE_MAG_FILTER
which says how the texture should be handled when it is minified and magnified respectively. For more info, see this link - glTexParameter - OpenGL Reference Page

Now comes the actual function which creates the texture. It is
glTexImage2D()
and it's syntax is

1  
2  
void glTexImage2D(int target, int level, int internalFormat, int width,
                  int height, int border, int format, int type, ByteBuffer pixels);

And now, that the texture has been created, we create a Texture object and return it.

1  
return new Texture(textureId, bimg.getWidth(), bimg.getHeight());

That's all about the texture creation. The tutorial didn't complete still, you'll need to know how to draw that texture to screen. Before drawing, you need to know what texture coordinates are.

Texture Coordinates


Never ever think that textures have the same width and height as of BufferedImage we load them. The width and height of texture will range from 0 to 1. These coordinates are called as texture coordinates or uv coordinates. See this for example.



This is how we map them to the rectangle. Remember how we created blended colors by specifying one color for each vertex and how OpenGL blended colors in between? It's the same concept that we use with texture coordinates.

What we have to do is to say OpenGL the correct texture coordinates for each vertex we define and OpenGL will draw the texture within those vertices by scaling them. To set the texture coordinates, we use the
glTexCoord2f()
function. Now, let's render the textures.

Drawing textures


Assuming that you've made setup the OpenGL using our Game class, Let's make a class level variable called texture. Now in the
init()
method

1  
2  
3  
4  
5  
6  
7  
8  
9  
10  
11  
12  
13  
14  
public void init()
{
    // Setup OpenGL here

    // Enable Texturing
   glEnable(GL_TEXTURE_2D);

    // Enable Blending to support transparency
   glEnable(GL_BLEND);
    glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);

    // Load the texture
   texture = Texture.loadTexture("resources/mytex.png");
}

Now let's skip to the render method.

1  
2  
3  
4  
5  
6  
7  
8  
9  
10  
11  
12  
13  
14  
15  
16  
17  
18  
19  
20  
21  
22  
23  
24  
25  
26  
27  
28  
29  
public void render()
{
    // Clear screen
   glClear(GL_COLOR_BUFFER_BIT);

    // Bind the texture
   glBindTexture(GL_TEXTURE_2D, texture.id);

    // Draw a QUAD with setting texture coordinates
   glBegin(GL_QUADS);
    {
        // Top left corner of the texture
       glTexCoord2f(0, 0);
        glVertex2f(0, 0);

        // Top right corner of the texture
       glTexCoord2f(1, 0);
        glVertex2f(100, 0);

        // Bottom right corner of the texture
       glTexCoord2f(1, 1);
        glVertex2f(100, 100);

        // Bottom left corner of the texture
       glTexCoord2f(0, 1);
        glVertex2f(0, 100);
    }
    glEnd();
}

You can see something like this on your screen if you had the texture at "resources/mytex.png"



Yay! You've drawn the first texture on the screen. Note that some graphic cards only support textures with width and height being power of two. Take care with this when creating them. This is the end of this tutorial and in the next part, let's learn "Display Lists".

If there are any errors or typos, please notify them to me with comments.

Source Code


Texture.java
Tutorial4.java

Offline quew8

JGO Coder


Medals: 23



« Reply #1 - Posted 2013-09-07 22:02:05 »

Yikes. You completely skipped over explaining what texture coords actually are. Also I think you missed out a few texture targets, like GL_TEXTURE_1D, GL_TEXTURE_CUBE_MAP, GL_TEXTURE_1D_ARRAY etc. etc. Actually now I think of it there are probably about 10 targets you missed.

I understand that texturing is a massive topic with too much to explain in a basic tutorial, but maybe link to another article which does. I'm afraid I don't remember one of the top of my head, but maybe the chapter of the red book (for which there are many online versions of the first (or maybe second) edition) on textures would do.
Offline SHC
« Reply #2 - Posted 2013-09-08 06:29:29 »

I didn't know of the existence of
GL_TEXTURE_1D
, Can you say more of them or how they are used? Also I think that the targets
GL_TEXTURE_1D_ARRAY
and so on are used with
glEnableClientState()
function right? I'm going to link to textures tutorial and I will update the article at once with UV coordinates. Thanks for that.

Games published by our own members! Check 'em out!
Legends of Yore - The Casual Retro Roguelike
Offline SHC
« Reply #3 - Posted 2013-09-08 07:22:13 »

Added section on Texture Coordinates. Thanks quew8 for pointing that out.

Offline quew8

JGO Coder


Medals: 23



« Reply #4 - Posted 2013-09-08 12:48:58 »

Those other targets are texture targets just as much as GL_TEXTURE_2D and _3D. 1D is literally a 1D texture, I'm not really sure how to explain. _CUBE_MAP is an acceleration for drawing cubes but can be used for other things as well. The XD_ARRAY textures are 3.0+ I think. Essentially they are an array of textures that can be bound all at the same time. They were added to allow people to use texture sheets without having to mess around stitching textures together and weird and wonderful texture coords. I don't think it's necessary to talk about them but it might be worth mentioning them. I wasn't being particularly serious when I said it.
Offline RobinB

JGO Knight


Medals: 37
Projects: 1
Exp: 3 years


Spacegame in progress


« Reply #5 - Posted 2013-09-08 18:23:01 »

Maybe add something about glGenerateMipmap(GL_TEXTURE_2D);
Can be a life saver to know Smiley
Offline SHC
« Reply #6 - Posted 2013-09-08 18:24:12 »

Found a new topic to learn, mipmaps. I'll add them in a new tutorial once I got them.

Offline Hermasetas
« Reply #7 - Posted 2014-02-15 14:44:16 »

Is this not overkill:
     int[] pixels = new int[bimg.getWidth() * bimg.getHeight() * 4];

This array is only going to be 1/4 filled. Right?

Instead the size of the bytebuffer only should be multiplied by 4.

Good tutorial though Smiley
Offline SHC
« Reply #8 - Posted 2014-02-15 16:10:09 »

@Hermasetas

Gotcha. You've found it. I'll change it soon. Thanks.

Offline andxbes

Junior Newbie





« Reply #9 - Posted 2014-03-14 20:33:04 »



Thanks for the lesson
Games published by our own members! Check 'em out!
Legends of Yore - The Casual Retro Roguelike
Offline SHC
« Reply #10 - Posted 2014-03-15 14:18:51 »

@andxbes

The UV texture coordinates range between 0 and 1. You used screen coordinates as the UV coordinates.

Offline andxbes

Junior Newbie





« Reply #11 - Posted 2014-03-19 12:43:50 »

We put in our texture memory of the GPU? Probably need to remove it manually, as with VBO?
Offline SHC
« Reply #12 - Posted 2014-03-19 14:05:41 »

@andxbes

There is
glDeleteTextures(int)
in
GL11
class of LWJGL. This method takes an integer, which is the ID of the texture. OpenGL automatically destroys the textures when the context is destroyed.

Pages: [1]
  ignore  |  Print  
 
 

 

Add your game by posting it in the WIP section,
or publish it in Showcase.

The first screenshot will be displayed as a thumbnail.

xsi3rr4x (12 views)
2014-04-15 18:08:23

BurntPizza (10 views)
2014-04-15 03:46:01

UprightPath (24 views)
2014-04-14 17:39:50

UprightPath (10 views)
2014-04-14 17:35:47

Porlus (27 views)
2014-04-14 15:48:38

tom_mai78101 (49 views)
2014-04-10 04:04:31

BurntPizza (107 views)
2014-04-08 23:06:04

tom_mai78101 (207 views)
2014-04-05 13:34:39

trollwarrior1 (176 views)
2014-04-04 12:06:45

CJLetsGame (182 views)
2014-04-01 02:16:10
List of Learning Resources
by Longarmx
2014-04-08 03:14:44

Good Examples
by matheus23
2014-04-05 13:51:37

Good Examples
by Grunnt
2014-04-03 15:48:46

Good Examples
by Grunnt
2014-04-03 15:48:37

Good Examples
by matheus23
2014-04-01 18:40:51

Good Examples
by matheus23
2014-04-01 18:40:34

Anonymous/Local/Inner class gotchas
by Roquen
2014-03-11 15:22:30

Anonymous/Local/Inner class gotchas
by Roquen
2014-03-11 15:05:20
java-gaming.org is not responsible for the content posted by its members, including references to external websites, and other references that may or may not have a relation with our primarily gaming and game production oriented community. inquiries and complaints can be sent via email to the info‑account of the company managing the website of java‑gaming.org
Powered by MySQL Powered by PHP Powered by SMF 1.1.18 | SMF © 2013, Simple Machines | Managed by Enhanced Four Valid XHTML 1.0! Valid CSS!