Can't draw texture on a quad using glDrawElements

Started by frank03, October 03, 2019, 15:28:05

Previous topic - Next topic

frank03

Hi!
I have a problem using glDrawElements method.
I can draw a 2d quad with a texture on it using glVertex3f:

public void renderTerrainTile(float x0, float y0, float z0, Material mat){
        //Still 2D
        GL13.glEnable(GL11.GL_TEXTURE_2D);
        GL13.glBindTexture(GL11.GL_TEXTURE_2D, mat.getTexture().getID());
       
        GL11.glBegin(GL11.GL_TRIANGLE_STRIP); {
            GL11.glTexCoord2f(1.0f, 0.0f);
            GL11.glVertex3f(x0, y0 + 1, z0);
           
            GL11.glTexCoord2f(1.0f, 1.0f);
            GL11.glVertex3f(x0, y0, z0);
           
            GL11.glTexCoord2f(0.0f, 0.0f);
            GL11.glVertex3f(x0 + 1, y0 + 1, z0);
           
            GL11.glTexCoord2f(0.0f, 1.0f);
            GL11.glVertex3f(x0 + 1, y0, z0);
        } GL11.glEnd();
       
        GL13.glBindTexture(GL11.GL_TEXTURE_2D, 0);

        GL11.glPopMatrix();
    }

And it draws a normal square with my texture attached on it.
Then I have another method that uses mesh:

    public void render(Mesh mesh, Shader shader){
        GL30.glBindVertexArray(mesh.getVAO());

        GL30.glEnableVertexAttribArray(0);
        GL30.glEnableVertexAttribArray(1);
        GL30.glEnableVertexAttribArray(2);
       
        GL13.glEnable(GL11.GL_TEXTURE_2D);
       
        GL15.glBindBuffer(GL15.GL_ELEMENT_ARRAY_BUFFER, mesh.getIBO());

        GL13.glActiveTexture(GL13.GL_TEXTURE0);
        GL13.glBindTexture(GL11.GL_TEXTURE_2D, mesh.getMaterial().getTextureID());

        //shader.bind();

        GL11.glDrawElements(GL11.GL_TRIANGLES, mesh.getIndices().length, GL11.GL_UNSIGNED_INT, 0);

        //shader.unbind();

        GL15.glBindBuffer(GL15.GL_ELEMENT_ARRAY_BUFFER, 0);
        GL13.glBindTexture(GL11.GL_TEXTURE_2D, 0);

        GL30.glDisableVertexAttribArray(0);
        GL30.glDisableVertexAttribArray(1);
        GL30.glDisableVertexAttribArray(2);

        GL30.glBindVertexArray(0);
    }

I get a black square without shader when it should be the same as the square produced by the other method.
Can you help me to figure out the error? Thank you in advance!

KaiHH

Generic vertex attributes do not work with the fixed-function pipeline (built-in) shaders, because OpenGL does not know which attribute arrays are your texture coordinates.
You have to use e.g. glTexCoordPointer if you want to use the fixed-function pipeline without custom shaders.

EDIT: And in fact, even using the generic vertex attribute at index 0 for your vertex positions is illegal when using the fixed-function pipeline, even though drivers will usually let you get away with this by assuming generic vertex attribute index 0 to be the position.

frank03

Thank you for the reply. Now I totally changed approach, but now I get a white quad.

public void render(){
        glActiveTexture(GL_TEXTURE0);
       
        glBindTexture(GL_TEXTURE_2D, material.getTextureID());

        glBindVertexArray(getVAO());
       
        glEnableVertexAttribArray(0);
        glEnableVertexAttribArray(1);

        glDrawElements(GL_TRIANGLES, indices.length, GL_UNSIGNED_INT, 0);

        glDisableVertexAttribArray(0);
        glDisableVertexAttribArray(1);
       
        glBindVertexArray(0);
    }

Maybe is the problem here?

public Mesh mesh = new Mesh(new float[]{
        -0.5f,  0.5f,  0.0f,
        -0.5f, -0.5f,  0.0f,
         0.5f, -0.5f,  0.0f,
         0.5f,  0.5f,  0.0f,
    }, new float[]{
        0.5f, 0.0f, 0.0f,
        0.5f, 0.5f, 0.0f,
        0.0f, 0.0f, 0.0f,
        0.0f, 0.5f, 0.0f
    }, new int[] {
        0, 1, 2,
        2, 3, 0
    }, mat);

Where "mat" is the material object which contains the texture.
I really don't get the problem.

Thank you.

KaiHH

I obviously have no idea what you are doing with this "Mesh mesh" object and with the "mat" variable in it and what exactly you mean by "totally changed approach". Are you now using shaders or not? If not, then what did you do differently? You are still using generic vertex attributes like before. That's all I can see. You have to show a lot more of your code in order for anyone to help you.

frank03

Sorry. Here it is the Mesh class:

public class Mesh {
    private float[] positions;
    private float[] texcoords;
    private int[] indices;
    private Material material;
   
    private int vao;

    private List<Integer> vboIdList;

    private int vertexCount;
   
    public Mesh(float[] positions, float[] texcoords, int[] indices, Material material) {
        this.positions = positions;
        this.texcoords = texcoords;
        this.indices   = indices;
        this.material  = material;
    }
   
    public void create(){
        material.create();
       
        FloatBuffer posBuffer        = null;
        FloatBuffer textCoordsBuffer = null;
        IntBuffer indicesBuffer      = null;
       
        try {
            vertexCount = indices.length;
            vboIdList = new ArrayList<>();

            vao = glGenVertexArrays();
            glBindVertexArray(vao);

            int vboId = glGenBuffers();
            vboIdList.add(vboId);
           
            posBuffer = MemoryUtil.memAllocFloat(positions.length);
            posBuffer.put(positions).flip();
           
            glBindBuffer(GL_ARRAY_BUFFER, vboId);
            glBufferData(GL_ARRAY_BUFFER, posBuffer, GL_STATIC_DRAW);
            glVertexAttribPointer(0, 3, GL_FLOAT, false, 0, 0);

            vboId = glGenBuffers();
            vboIdList.add(vboId);
           
            textCoordsBuffer = MemoryUtil.memAllocFloat(texcoords.length);
            textCoordsBuffer.put(texcoords).flip();
           
            glBindBuffer(GL_ARRAY_BUFFER, vboId);
            glBufferData(GL_ARRAY_BUFFER, textCoordsBuffer, GL_STATIC_DRAW);
            glVertexAttribPointer(1, 2, GL_FLOAT, false, 0, 0);

            vboId = glGenBuffers();
            vboIdList.add(vboId);
           
            indicesBuffer = MemoryUtil.memAllocInt(indices.length);
            indicesBuffer.put(indices).flip();
           
            glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, vboId);
            glBufferData(GL_ELEMENT_ARRAY_BUFFER, indicesBuffer, GL_STATIC_DRAW);
           
            glBindBuffer(GL_ARRAY_BUFFER, 0);
            glBindVertexArray(0);
        } finally {
            if (posBuffer != null) {
                MemoryUtil.memFree(posBuffer);
            }
            if (textCoordsBuffer != null) {
                MemoryUtil.memFree(textCoordsBuffer);
            }
            if (indicesBuffer != null) {
                MemoryUtil.memFree(indicesBuffer);
            }
        }
    }

    public float[] getVertexPositions() {
        return positions;
    }
   
    public float[] getTextureCoords() {
        return texcoords;
    }

    public int[] getIndices() {
        return indices;
    }

    public int getVAO() {
        return vao;
    }
   
    public Material getMaterial() {
        return material;
    }
   
    public int getVertexCount(){
        return vertexCount;
    }
   
    public void render(){
        glActiveTexture(GL_TEXTURE0);
       
        glBindTexture(GL_TEXTURE_2D, material.getTextureID());

        glBindVertexArray(getVAO());
       
        glEnableVertexAttribArray(0);
        glEnableVertexAttribArray(1);

        glDrawElements(GL_TRIANGLES, indices.length, GL_UNSIGNED_INT, 0);

        glDisableVertexAttribArray(0);
        glDisableVertexAttribArray(1);
       
        glBindVertexArray(0);
    }
   
    public void destroy() {
        glBindBuffer(GL_ARRAY_BUFFER, 0);
       
        vboIdList.forEach((vboId) -> {
            glDeleteBuffers(vboId);
        });
       
        material.destroy();
       
        glBindVertexArray(0);
        glDeleteVertexArrays(vao);
    }
}

I'm calling the render() method from the main game loop:

@Override
    public void run(){
        initialization();
       
        while(running){
            update();
            render();
        }
    }

frank03


KaiHH

The answer in my first post still applies: Generic vertex attributes do not work without shaders!
You need to use the fixed-function pipeline function calls, such as glVertexPointer and glTexCoordPointer then.

frank03

I'm now using shaders but I still get the black square:

This is the shader code. Fragment:

#version 330 core

in vec3 passColor;
in vec2 passTextureCoord;

out vec4 outColor;

uniform sampler2D tex;

void main() {
    outColor = texture(tex, passTextureCoord);
}

vertex:

#version 460 core

in vec3 position;
in vec3 color;
in vec2 textureCoord;

out vec3 passColor;
out vec2 passTextureCoord;

void main() {
    gl_Position = vec4(position, 1.0);
    passColor = color;
    passTextureCoord = textureCoord;
}

KaiHH

You likely forgot to tell OpenGL how your generic vertex array indices map to the shader vertex inputs.
Read this: https://www.khronos.org/opengl/wiki/Vertex_Shader#Inputs

frank03

Still can't solve the problem.

I opened a pastebin with most of the code:
https://pastebin.com/9b0hDJSg

KaiHH

Please look more thoroughly at your own code. Your shader defines 3 vertex inputs: position at location/index 0, some unused color at location/index 1 and texture coordinates at location/index 2, but you only provide two generic vertex attributes: position at index 0 and texture coordinates at index 1. Those do not match! The shader is reading texture coordinates from an undefined input and the shader will likely read all zeroes.

frank03

Thank you for all the help. I'll try this soon. Sorry but i'm still learning :)

frank03

All right, solved!

The problem was in the shaders. That's the correct code:

//VERTEX
#version 330 core

layout (location = 0) in vec3 aPos;
layout (location = 1) in vec2 aTexCoord;

out vec2 TexCoord;

void main()
{
    gl_Position = vec4(aPos, 1.0);
    TexCoord = aTexCoord;
}

//SHADER
#version 330 core
out vec4 FragColor;

in vec2 TexCoord;

uniform sampler2D ourTexture;

void main()
{
    FragColor = texture(ourTexture, TexCoord);
}

Thank you for the help!!!