纹理UV未正确发送到着色器

时间:2016-04-14 15:27:47

标签: java opengl glsl vbo texture-mapping

我使用GLSL着色器和VBO渲染网格,VBO存储4个属性; positionXYZ,normalXYZ,textureUV,colourRGBA。一切正常,除了紫外线(也可能是法线,但我还没有办法测试它们)。

发生的事情是,数组中的纹理UV位置偏移到数组中的正常x和y位置。顺便说一下,数组的结构为VVVNNNTTCCCC(顶点位置,法线,纹理,颜色)。我很确定问题出在将VBO发送到着色器的某个地方。我确信VBO中的数据顺序正确。

这是我的渲染代码:

VBO课程

public final class Mesh
{
    public static final int FLOAT_SIZE_BYTES = 4;
    public static final int FLOATS_PER_POSITION = 3;
    public static final int FLOATS_PER_NORMAL = 3;
    public static final int FLOATS_PER_TEXTURE = 2;
    public static final int FLOATS_PER_COLOUR = 4;
    public static final int VERTEX_SIZE_FLOATS = FLOATS_PER_POSITION + FLOATS_PER_NORMAL + FLOATS_PER_TEXTURE + FLOATS_PER_COLOUR;
    public static final int VERTEX_SIZE_BYTES = VERTEX_SIZE_FLOATS * FLOAT_SIZE_BYTES;

    public static final int POSITION_OFFSET_FLOATS = 0;
    public static final int NORMAL_OFFSET_FLOATS = POSITION_OFFSET_FLOATS + FLOATS_PER_POSITION;
    public static final int TEXTURE_OFFSET_FLOATS = NORMAL_OFFSET_FLOATS + FLOATS_PER_NORMAL;
    public static final int COLOUR_OFFSET_FLOATS = TEXTURE_OFFSET_FLOATS + FLOATS_PER_TEXTURE;
    public static final int POSITION_OFFSET_BYTES = POSITION_OFFSET_FLOATS * FLOAT_SIZE_BYTES;
    public static final int NORMAL_OFFSET_BYTES = NORMAL_OFFSET_FLOATS * FLOAT_SIZE_BYTES;
    public static final int TEXTURE_OFFSET_BYTES = TEXTURE_OFFSET_FLOATS * FLOAT_SIZE_BYTES;
    public static final int COLOUR_OFFSET_BYTES = COLOUR_OFFSET_FLOATS * FLOAT_SIZE_BYTES;
    public static final int POSITION_STRIDE_BYTES = VERTEX_SIZE_BYTES;
    public static final int NORMAL_STRIDE_BYTES = VERTEX_SIZE_BYTES;
    public static final int TEXTURE_STRIDE_BYTES = VERTEX_SIZE_BYTES;
    public static final int COLOUR_STRIDE_BYTES = VERTEX_SIZE_BYTES;

    public final static int VERTICES_PER_FACE = 3;

    public static final int ATTRIBUTE_LOCATION_POSITION = 0;
    public static final int ATTRIBUTE_LOCATION_NORMAL = 1;
    public static final int ATTRIBUTE_LOCATION_TEXTURE = 2;
    public static final int ATTRIBUTE_LOCATION_COLOUR = 3;

    private int vaoID;
    private int iboID;
    private int indexCount;

    private Mesh(int vaoID, int iboID, int indexCount)
    {
        this.vaoID = vaoID;
        this.iboID = iboID;
        this.indexCount = indexCount;
    }

    public void draw(AbstractShaderProgram shader, Texture texture)
    {
        glEnable(GL_TEXTURE_2D);
        if (texture != null) texture.bind(shader);
        else Texture.MISSING_TEXTURE.bind(shader);

        glBindVertexArray(vaoID);
        glEnableVertexAttribArray(ATTRIBUTE_LOCATION_POSITION);
//        glEnableVertexAttribArray(ATTRIBUTE_LOCATION_NORMAL);
//        glEnableVertexAttribArray(ATTRIBUTE_LOCATION_TEXTURE);
//        glEnableVertexAttribArray(ATTRIBUTE_LOCATION_COLOUR);

        glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, iboID);
        glDrawElements(GL_TRIANGLES, indexCount, GL_FLOAT, 0);
        glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);

        glDisableVertexAttribArray(ATTRIBUTE_LOCATION_POSITION);
//        glDisableVertexAttribArray(ATTRIBUTE_LOCATION_NORMAL);
//        glDisableVertexAttribArray(ATTRIBUTE_LOCATION_TEXTURE);
//        glDisableVertexAttribArray(ATTRIBUTE_LOCATION_COLOUR);

        glBindVertexArray(0);

        glDisable(GL_TEXTURE_2D);
    }

    public static Mesh compile(List<Face> faces)
    {
        if (faces.size() <= 0)
            throw new RuntimeException("Failed to compile mesh. No faces were provided.");

        HashMap<Vertex, Integer> indexMap = new HashMap<>();
        ArrayList<Vertex> vertices = new ArrayList<>();
        int vertexCount = 0;


        for (Face face : faces)
        {
            for (Vertex vertex : face.getVertices())
            {
                if (!indexMap.containsKey(vertex))
                {
                    indexMap.put(vertex, vertexCount++);
                    vertices.add(vertex);
                }
            }
        }

        int indicesCount = faces.size() * VERTICES_PER_FACE;

        int dataSize = vertexCount * VERTEX_SIZE_FLOATS;
        FloatBuffer vertexData = BufferUtils.createFloatBuffer(dataSize);
        if (vertexData == null)
            System.err.println("Failed to allocate FloatBuffer with size " + dataSize);

        for (Vertex vertex : vertices)
        {
            vertexData.put(vertex.getPosition().x);
            vertexData.put(vertex.getPosition().y);
            vertexData.put(vertex.getPosition().z);
//            vertexData.put(vertex.getNormal() == null ? 1.0F : vertex.getNormal().x);
//            vertexData.put(vertex.getNormal() == null ? 1.0F : vertex.getNormal().y);
//            vertexData.put(vertex.getNormal() == null ? 1.0F : vertex.getNormal().z);
//            vertexData.put(vertex.getTexture() == null ? 0.0F : vertex.getTexture().x);
//            vertexData.put(vertex.getTexture() == null ? 0.0F : vertex.getTexture().y);
//            vertexData.put(vertex.getColour() == null ? 1.0F : vertex.getColour().getRGBA().x);
//            vertexData.put(vertex.getColour() == null ? 1.0F : vertex.getColour().getRGBA().y);
//            vertexData.put(vertex.getColour() == null ? 1.0F : vertex.getColour().getRGBA().z);
//            vertexData.put(vertex.getColour() == null ? 1.0F : vertex.getColour().getRGBA().w);
        }
        vertexData.flip();

        IntBuffer indices = BufferUtils.createIntBuffer(indicesCount);

        for (Face face : faces)
        {
            for (Vertex vertex : face.getVertices())
            {
                int index = indexMap.get(vertex);
                indices.put(index);
            }
        }
        indices.flip();

        int vaoID = glGenVertexArrays();
        glBindVertexArray(vaoID);

        int vboID = glGenBuffers();
        glBindBuffer(GL_ARRAY_BUFFER, vboID);

        glBufferData(GL_ARRAY_BUFFER, vertexData, GL_STATIC_DRAW);

        glVertexAttribPointer(ATTRIBUTE_LOCATION_POSITION, FLOATS_PER_POSITION, GL_FLOAT, false, 0, 0);
//        glVertexAttribPointer(ATTRIBUTE_LOCATION_NORMAL, FLOATS_PER_NORMAL, GL_FLOAT, false, NORMAL_STRIDE_BYTES, NORMAL_OFFSET_BYTES);
//        glVertexAttribPointer(ATTRIBUTE_LOCATION_TEXTURE, FLOATS_PER_TEXTURE, GL_FLOAT, false, TEXTURE_STRIDE_BYTES, TEXTURE_OFFSET_BYTES);
//        glVertexAttribPointer(ATTRIBUTE_LOCATION_COLOUR, FLOATS_PER_COLOUR, GL_FLOAT, false, COLOUR_STRIDE_BYTES, COLOUR_OFFSET_BYTES);

        glBindBuffer(GL_ARRAY_BUFFER, 0);
        glBindVertexArray(0);

        int iboID = glGenBuffers();
        glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, iboID);
        glBufferData(GL_ELEMENT_ARRAY_BUFFER, indices, GL_STATIC_DRAW);
        glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);

        return new Mesh(vaoID, iboID, indicesCount);
    }
}

顶点着色器:

#version 400 core

in vec3 vertPosition;
in vec3 vertNormal;
in vec2 vertTexture;
in vec4 vertColour;

uniform mat4 projectionMatrix;
uniform mat4 viewMatrix;
uniform mat4 modelMatrix;

out vec3 pVertPosition;
out vec3 pVertNormal;
out vec2 pVertTexture;
out vec4 pVertColour;

void main()
{
    pVertPosition = vertPosition;
    pVertNormal = vertNormal;
    pVertTexture = vertTexture;
    pVertColour = vertColour;

    gl_Position = vec4(vec3(vertPosition.xy + vertTexture, vertPosition.z), 1.0);
}

片段着色器:

#version 400 core

in vec3 ppVertPosition;
in vec3 ppVertNormal;
in vec2 ppVertTexture;
in vec4 ppVertColour;

uniform sampler2D texture;

void main()
{
    gl_FragColor = texture2D(texture, ppVertTexture);
}

它们之间有一个几何着色器,但它目前是多余的,只是将信息直接填充到片段着色器(这就是out和in变量不匹配的原因。)另外,添加了textureUV的原因片段着色器中的顶点位置是调试实际传递的纹理UV值。这就是我知道UV偏移到正常xy的方式。如果我将纹理UV放入正常的xy中,它们的效果非常好。

如果你想看到任何额外的代码,我没有包含,我会添加它。我没有添加所有内容,例如整个VBO类,因为代码太多了。我只包括了我认为相关的内容以及我认为问题出在哪里。

编辑#1: 着色器中的变量位置(例如vertPositionvertNormal)是绑定的。这是我绑定它们的代码

glBindAttribLocation(program, Mesh.ATTRIBUTE_LOCATION_POSITION, "vertPosition");
glBindAttribLocation(program, Mesh.ATTRIBUTE_LOCATION_NORMAL, "vertNormal");
glBindAttribLocation(program, Mesh.ATTRIBUTE_LOCATION_TEXTURE, "vertTexture");
glBindAttribLocation(program, Mesh.ATTRIBUTE_LOCATION_COLOUR, "vertColour");

更改顶点着色器以使用布局(如此)会产生ex \ ct相同的结果;

layout(location = 0) in vec3 vertPosition;
layout(location = 1) in vec3 vertNormal;
layout(location = 2) in vec2 vertTexture;
layout(location = 3) in vec4 vertColour;

编辑#2 我决定发布整个Mesh类,而不仅仅是它的一部分。我也试图实现VAO而不是VBO,但它无法正常工作。

2 个答案:

答案 0 :(得分:0)

您正在将标准管道功能与自定义着色器变量混合使用。

调用glEnableClientState(GL_VERTEX_ARRAY);告诉OpenGL使用某个数据绑定点,一切都很好。

调用glVertexPointer(告诉OpenGL在哪里找到它的顶点。由于您之前启用了正确的数组,所有数据仍然很好。

然后我们到达顶点着色器并使用in vec3 vertPosition;但GLSL并不知道你想要你的顶点数据。当然,我们可以看到名称是&#34; vertPosition&#34;,但GLSL不应该根据变量名称猜测您想要的数据!相反,GLSL的默认管道行为是使用gl_Vertex,这是一个绑定到GL_VERTEX_ARRAY的预构建GLSL变量。

那为什么它适用于职位呢?我猜你真的很幸运,你定义的变量是偶然分配预先构建的常量。

您应该做的是将glEnableClientState切换为glEnableVertexAttribArray,使用Layouts为每个变量分配一个数字,然后拨打glVertexAttribPointer而不是glVertexPointer进行链接那个数字到正确的数据。

现在,您声明的变量(如vertPosition)指向缓冲区中的正确数据,不是偶然的,而是因为您告诉他们!

这是在现代OpenGL中执行操作的正确方法,使用预先构建的变量(如gl_Vertex)和glEnableClientState之类的函数被认为是旧的和坏的,因为它不灵活。

您也可以省略布局(因为它需要OGL 4+,并非每个人都有),但在链接着色器之前需要更多工作。

祝你好运!

More info on converting your code

(我希望我对此,我不能评论实际验证这是问题)

答案 1 :(得分:0)

好的,我终于开始了......我不知道VBO最初的问题是什么,但是一旦我切换到使用VAO,而不是使用glClientState进行渲染,它就可以正常工作。此外,我遇到的问题是VAO没有呈现任何有关的内容:

glDrawElements(GL_TRIANGLES, indexCount, GL_FLOAT, 0);

应该是

glDrawElements(GL_TRIANGLES, indexCount, GL_UNSIGNED_INT, 0);