How to add vertices to VBO?

Although you can learn a lot from using Google, I find that some of the jargon you come across isn’t explained properly, which leads to having to search up a lot more terms. Also, most of the code you find is C or C++ relate which can be a problem if you don’t know those languages.

Going back to your question on glDrawArrays:

Here you’re telling it to start rendering from position 4 in the buffer object. However, you’re still telling it to draw 8 vertices.

glDrawArrays(GL_TRIANGLE_STRIP,4,8);

Here however you are telling it to start rendering from position 4 in the buffer and you are telling it to draw 4 vertices.

glDrawArrays(GL_TRIANGLE_STRIP,4,4);

So yes, that’s the correct way to do it.

Textures:

Since I can’t be bothered writing out all the code for creating a texture, let me point you to this great tutorial on textures for LWJGL. It teaches you how to decode a PNG into a byte buffer and how to create a texture. It also teaches you about texture coordinates and texture wrapping. If you don’t want to use a library for decoding a texture, I’ll lead you to this tutorial by SHC which has the code for decoding a PNG image with the Java API.

If you want to add textures it’s probably best to use an interleaved buffer object. An interleaved buffer object is a buffer object where the data for both the vertices, and in this case the texture co-ordinates, is held in the same buffer. A texture coordinate is defined as a vector in tanget space, much like a vertex is defined as a vector in 3D space. The difference is however that tangent space represents direction and has a coordinate system of [s, t] or [u, v], whereas 3D space represents a position and is defined as [x, y, z]. Texture coordinates should be defined as 0 >= 1.

So first of all lets set up a basic interleaved buffer object which holds vertices and texture coordinates. If you have a co-ordinate system where (0, 0) is the top left you will need to change the texture coordinates, with [0, 0] being the top left.


float[] data = {
	-0.5f, -0.5f, 0f, // bottom left
	0f, 0f, // bottom left
	-0.5f, 0.5f, 0f, // top left
	0f, 1f, // top left
	0.5f, -0.5f, 0f, // bottom right
	1f, 0f, // bottom right
	0.5f, 0.5f, 0f, // top right
	1f, 1f}; // top right

glEnable(GL_VERTEX_ARRAY);
glEnable(GL_TEXTURE_2D); // enable textures

bufferId = glGenBuffers();

glBindBuffer(GL_ARRAY_BUFFER, bufferId);
glBufferData(GL_ARRAY_BUFFER, (FloatBuffer) BufferUtils.createFloatBuffer(20).put(data).flip(), GL_STATIC_DRAW)
glBindBuffer(GL_ARRAY_BUFFER, 0);

If you haven’t yet set up your texture, read the tutorial I linked to above.

Now for the render method.


glClearColor(0f, 0f, 0f, 1f); // set the clear colour as usual
glClear(GL_COLOR_BUFFER_BIT); // clear the colour buffer (screen)

glBindBuffer(GL_ARRAY_BUFFER, bufferId);
glBindTexture(GL_TEXTURE_2D, textureId);
glVertexPointer(3, GL_FLOAT, 5 << 2, 0); // there is a stride of 5 << 2, i.e every 5 float values there is a new vertex position
glTexCoordPointer(2, GL_FLOAT, 5 << 2, 3 << 2); // every 2 float values is a texture coordinate. there is a stride of 5 << 2 and an offset of 3 << 2, i.e after 3 float values the first texture coordinate begins
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); // there are 4 vertices to draw
glBindTexture(GL_TEXTURE_2D, 0);
glBindBuffer(GL_ARRAY_BUFFER, 0);

Note: glTexCoordPointer has the same parameters as glVertexPointer. If you don’t understand something just ask. :slight_smile:

Why are you using strides? I don’t think there’s much of a point of doing that?

Why not? There are less buffers to bind and less buffers to create, overall saving space on the GPU.

EDIT: If you meant why not format is as VVVVTTTT, it’s because I prefer to format it as VTVTVTVT. However as a reference for the OP, if you wanted to format the buffer as VVVVTTTT, you’d change your stride and offset to:


glVertexPointer(3, GL_FLOAT, 3 << 2, 0);
glTexCoordPointer(2, GL_FLOAT, 2 << 2, 12 << 2) // there would be 2 << 2 bytes between each texture coordinate vector. there would be 12 << 2 bytes between the beginning of the buffer and the start of the first texture coordinate

Just use GL_TRIANGLES with an index buffer to render sprites, using GL_QUADS is not only pointless as your driver can only render triangles in the first place, its also not portable to ES (if you ever want to make a mobile version)

If you specify 0 as stride it tells OpenGL that things are “tightly packed.”

All of these struggles just makes me wonder why OP doesn’t use LibGDX… :slight_smile: You can achieve the same thing in a fraction of the time, with far less headache.

You can just use their vertex/index buffer utilities to avoid the low-level GL debugging which is usually a pain in the ass.

Specifying 0 as the stride forces OpenGL to calculate the correct stride. Calculating strides yourself isn’t only good practice but gets you used to calculating strides when you need to.

Sure, the OP could use LibGDX but maybe the OP wants to learn OpenGL in the process.

[quote]forces OpenGL to calculate the correct stride
[/quote]
The performance difference is negligible. You may as well let OpenGL do it for you.

[quote]Sure, the OP could use LibGDX but maybe the OP wants to learn OpenGL in the process.
[/quote]
I’d argue it’s much easier to learn OpenGL and graphics programming by using a library, rather than writing boilerplate.

Learning how to write boilerplate doesn’t make you a good graphics programmer. It just means you know how to write the boilerplate. Programmers only write boilerplate once, and then they create abstractions on top of it. Once you have a firm grasp of the entire pipeline, it will be easier to tinker with the boilerplate if you ever find a need to (which is very rare).

But that’s just my opinion, after many years of graphics programming. :slight_smile:

For some reason i cant apply texture normal, whole quad just fills texture first pixel color.

Show us some code. :slight_smile:

 float[] datatex = {
				   100, 500, 0f, 
				   100, 100, 0f, 
				   500, 500, 0f,
				   500, 100, 0f,
				   0f, 0f, 
				   0f, 1f,
				   1f, 0f,
				   1f, 1f};
glBindBuffer(GL_ARRAY_BUFFER, bufferId);
		glBindTexture(GL_TEXTURE_2D, texture.id);
		glVertexPointer(3, GL_FLOAT, 3 << 2, 0);
		glTexCoordPointer(2, GL_FLOAT, 2 << 2, 12 << 2); vector. 
		glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); 
		glBindTexture(GL_TEXTURE_2D, 0);
		glBindBuffer(GL_ARRAY_BUFFER, 0);

I think this enough.

And thanks everyone for answers :).

EDIT:
i tried it with immediate mode, everything worked perfect

Your texture coordinates don’t seem to be correct. Try this;


float[] datatex = {
               100f, 100f, 0f, 
               100f, 500f, 0f, 
               500f, 100f, 0f,
               500f, 500f, 0f,
               0f, 0f, 
               0f, 1f,
               1f, 0f,
               1f, 1f};

still nothing.

maybe you need other part of code?

Oh your texture co-ordinates would have been correct before if you have a top-left coordinate system. Can you show the code you use for intermediate mode? The code you provided should work perfectly well.

	GL11.glBindTexture(GL11.GL_TEXTURE_2D, texture.id);
		GL11.glBegin(GL_QUADS);
				GL11.glTexCoord2f(0, 0);
				GL11.glVertex2f(0, 0);
				GL11.glTexCoord2f(1, 0);
				GL11.glVertex2f(100, 0);
				GL11.glTexCoord2f(1, 1);
				GL11.glVertex2f(100, 100);
				GL11.glTexCoord2f(0, 1);
				GL11.glVertex2f(0, 100);
		GL11.glEnd();
		GL11.glBindTexture(GL11.GL_TEXTURE_2D, 0);

And it appears in top left corner

Post your code for setting up the texture. I’ve not come across a problem like this before.

I am using this texture load class:

import java.awt.image.BufferedImage;
import java.io.IOException;
import java.nio.ByteBuffer;

import javax.imageio.ImageIO;

import org.lwjgl.BufferUtils;
import static org.lwjgl.opengl.GL11.*;

/**
 * An OpenGL Texture.
 * 
 * @author Sri Harsha Chilakapati
 */
public class Texture
{
    
    /**
     * Texture ID.
     */
    public int id;
    
    /**
     * Texture width.
     */
    public int width;
    
    /**
     * Texture height.
     */
    public int height;
    
    /**
     * Creates a texture.
     */
    private Texture(int id, int width, int height)
    {
        this.id = id;
        this.width = width;
        this.height = height;
    }
    
    /**
     * Loads a texture from a file.
     * 
     * @param name The name of the file.
     */
    public static Texture loadTexture(String name)
    {
        // Load the image
        BufferedImage bimg = null;
        try
        {
            bimg = ImageIO.read(Texture.class.getClassLoader().getResourceAsStream(name));
        }
        catch (IOException e)
        {
            e.printStackTrace();
            System.out.println("Unable to load Texture: " + name);
            Game.end();
        }

        // Gather all the pixels
        int[] pixels = new int[bimg.getWidth() * bimg.getHeight()];
        bimg.getRGB(0, 0, bimg.getWidth(), bimg.getHeight(), pixels, 0, bimg.getWidth());

        // Create a ByteBuffer
        ByteBuffer buffer = BufferUtils.createByteBuffer(bimg.getWidth() * bimg.getHeight() * 4);

        // Iterate through all the pixels and add them to the ByteBuffer
        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));
            }
        }

        // Reset the read location in the buffer so that GL can read from beginning.
        buffer.flip();

        // Generate a texture ID
        int textureID = glGenTextures();
        // Bind the ID to the context
        glBindTexture(GL_TEXTURE_2D, textureID);

        //Setup texture scaling filtering
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);

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

        // Return a new Texture.
        return new Texture(textureID, bimg.getWidth(), bimg.getHeight());
    }

}
  glEnable(GL_TEXTURE_2D);
        glEnable(GL_BLEND);
        glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
  texture = Texture.loadTexture("simpletex.png");
Texture texture;

Enabling blending might be causing the problem. Try commenting it out and see if that fixes it. If it doesn’t, I have no other suggestions. The code your using seems to me like it should work perfectly.

still not working, but when i am using this tutorial code, textures working perfect.

And i want to ask, should i use two VBOs in one chunk (one for textures, one for vertices). I want each tile in chunk have different textures. And i think i must use spritesheet, or am i wrong?

Spritesheets make it so you you can only bind one texture and render different parts of said texture. Analogues to how a normal sprite animations work but with different tiles instead of frames. Binding a new texture can be expensive

With modern opengl this is no longer a problem really.

without spritesheets.

Build chunks with all tiles using same texture.

Render chunks.
bindTexture for chunk
renderChunk()
bind
renderChunk()
.
.
.
etc

With one giant texture(2048x2048)

bind texture

render all chunks

For building VBO I say to pass a bytebuffer around and inteleave the VBO like such
V = vertex
C = color
T = texture
VCTVCTVCT
Then flip and upload.

Here is some code snippets. Warning not using modern stuff.


	public void build(ByteBuffer verts, int size)
	{
		if (vboID == -1)
			genVBO();
		GL15.glBindBuffer(GL15.GL_ARRAY_BUFFER, vboID);
		GL15.glBufferData(GL15.GL_ARRAY_BUFFER, verts, GL15.GL_STATIC_DRAW);
		GL15.glBindBuffer(GL15.GL_ARRAY_BUFFER, 0);
		this.size = size;
	}

Uploads data to gpu after bytebuffer is built.


	public void render()
	{
		GL11.glEnableClientState(GL11.GL_VERTEX_ARRAY);
		GL11.glEnableClientState(GL11.GL_COLOR_ARRAY);
		GL11.glEnableClientState(GL11.GL_NORMAL_ARRAY);
		GL11.glEnableClientState(GL11.GL_TEXTURE_COORD_ARRAY);
		GL15.glBindBuffer(GL15.GL_ARRAY_BUFFER, vboID);
		GL11.glVertexPointer(3, GL11.GL_FLOAT, 27, 0);
		GL11.glColorPointer(4, GL11.GL_UNSIGNED_BYTE, 27, 12);
		GL11.glTexCoordPointer(2, GL11.GL_FLOAT, 27, 16);
		GL11.glNormalPointer(GL11.GL_BYTE, 27, 24);

		GL11.glDrawArrays(GL11.GL_QUADS, 0, size * 4);

		GL11.glDisableClientState(GL11.GL_VERTEX_ARRAY);
		GL11.glDisableClientState(GL11.GL_NORMAL_ARRAY);
		GL11.glDisableClientState(GL11.GL_COLOR_ARRAY);

		GL11.glDisableClientState(GL11.GL_TEXTURE_COORD_ARRAY);
		GL15.glBindBuffer(GL15.GL_ARRAY_BUFFER, 0);
	}

Rendering using interleaved VBO without indices.


	private void addFront(ByteBuffer buff)
	{
		buff.putFloat(pos.x + .5f); // vertex
		buff.putFloat(pos.y + .5f);
		buff.putFloat(pos.z - .5f);
		addColor(buff, v1); // simply calls more buff.putStuff
		buff.putFloat(tex[0]); // texture coords using atlas
		buff.putFloat(tex[1]);
		buff.put((byte) 0); // normals 
		buff.put((byte) 0);
		buff.put((byte) 127);
		buff.putFloat(pos.x - .5f); //repeat 
		buff.putFloat(pos.y + .5f);
		buff.putFloat(pos.z - .5f);
		addColor(buff, v2);
		buff.putFloat(tex[2]);
		buff.putFloat(tex[3]);
		buff.put((byte) 0);
		buff.put((byte) 0);
		buff.put((byte) 127);
		buff.putFloat(pos.x - .5f);
		buff.putFloat(pos.y + .5f);
		buff.putFloat(pos.z + .5f);
		addColor(buff, v3);
		buff.putFloat(tex[4]);
		buff.putFloat(tex[5]);
		buff.put((byte) 0);
		buff.put((byte) 0);
		buff.put((byte) 127);
		buff.putFloat(pos.x + .5f);
		buff.putFloat(pos.y + .5f);
		buff.putFloat(pos.z + .5f);
		addColor(buff, v4);
		buff.putFloat(tex[6]);
		buff.putFloat(tex[7]);
		buff.put((byte) 0);
		buff.put((byte) 0);
		buff.put((byte) 127);
	}

Rendering a single face of a cube. Same as a quad but with a z.

Hope this helps some. If you do not understand an opengl call, really just google, “opengl name of call”. Like, “opengl vertex arrays” “opengl vbo” “opengl occlusion culling” “opengl etc” It cannot be useless as this is how I learned and I cannot be that bad since everything renders properly at except able speeds. Though, I really need to learn more on shaders and modern pipeline.

I found why that texture not worked! i just forgot to do this:

glEnableClientState(GL_TEXTURE_COORD_ARRAY);