绘制在一起时,OpenGL纹理出现在错误的位置

时间:2016-01-02 23:35:08

标签: java opengl lwjgl opengl-3

我是OpenGL的新手,我正在尝试绘制两个不同纹理的正方形。我使用lwjgl 3作为OpenGL的接口,但我相信对于在其他语言中使用OpenGL的人来说,OpenGL调用看起来应该很熟悉。我的主循环看起来像这样:

    while (glfwWindowShouldClose(windowId) == GLFW_FALSE) {
        glClear(GL_COLOR_BUFFER_BIT);

        glUseProgram(shaderProgramId);
        glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ebo);

        // DRAW TEXTURE 1
        specifyVertexAttributes(shaderProgramId);
        glBindTexture(GL_TEXTURE_2D, texture1.getId());
        glBindBuffer(GL_ARRAY_BUFFER, vbo1);
        glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0);

        // DRAW TEXTURE 2
        specifyVertexAttributes(shaderProgramId);
        glBindTexture(GL_TEXTURE_2D, texture2.getId());
        glBindBuffer(GL_ARRAY_BUFFER, vbo2);
        glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0);

        glfwSwapBuffers(windowId);

        glfwPollEvents();
    }

当我注释掉绘制纹理2的代码时,纹理1绘制在正确的位置:

texture1

当我注释掉绘制纹理1的代码时,纹理2绘制在正确的位置:

texture2

但是当我尝试绘制两个纹理时,它们会切换位置:

combined

我意识到上面的代码段可能不足以诊断此问题。我创建了一个独立的java类,其中包含我正在制作的所有OpenGL调用以绘制这些纹理:StandaloneMultiTextureExample。包含该文件的repo也使用gradle构建。对于愿意帮助检查回购并运行该示例类的任何人来说应该很容易。

编辑:StandaloneMultiTextureExample.java的副本(没有导入)

public class StandaloneMultiTextureExample {

    private final GLFWErrorCallback errorCallback = new LoggingErrorCallback();
    private final GLFWKeyCallback keyCallback = new ApplicationClosingKeyCallback();

    public void run() {
        if ( glfwInit() != GLFW_TRUE ) {
            throw new IllegalStateException("Unable to initialize GLFW");
        }

        glfwSetErrorCallback(errorCallback);
        int width = 225;
        int height = 200;
        long windowId = createWindow(width, height);
        glfwSetKeyCallback(windowId, keyCallback);

        glfwShowWindow(windowId);
        GL.createCapabilities();

        Texture texture1 = createTexture("multiTextureExample/texture1.png");
        Texture texture2 = createTexture("multiTextureExample/texture2.png");

        int shaderProgramId = createShaderProgram(
                "multiTextureExample/textureShader.vert",
                "multiTextureExample/textureShader.frag");

        IntBuffer elements = BufferUtils.createIntBuffer(2 * 3);
        elements.put(0).put(1).put(2);
        elements.put(2).put(3).put(0);
        elements.flip();

        int ebo = glGenBuffers();
        glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ebo);
        glBufferData(GL_ELEMENT_ARRAY_BUFFER, elements, GL_STATIC_DRAW);

        float x1 = 0;
        float y1 = 0;
        float x2 = x1 + texture1.getWidth();
        float y2 = y1 + texture1.getHeight();
        float x3 = 25;
        float x4 = x3 + texture2.getWidth();

        FloatBuffer texture1Vertices = BufferUtils.createFloatBuffer(4 * 7);
        texture1Vertices.put(x1).put(y1).put(1).put(1).put(1).put(0).put(0);
        texture1Vertices.put(x2).put(y1).put(1).put(1).put(1).put(1).put(0);
        texture1Vertices.put(x2).put(y2).put(1).put(1).put(1).put(1).put(1);
        texture1Vertices.put(x1).put(y2).put(1).put(1).put(1).put(0).put(1);
        texture1Vertices.flip();

        FloatBuffer texture2Vertices = BufferUtils.createFloatBuffer(4 * 7);
        texture2Vertices.put(x3).put(y1).put(1).put(1).put(1).put(0).put(0);
        texture2Vertices.put(x4).put(y1).put(1).put(1).put(1).put(1).put(0);
        texture2Vertices.put(x4).put(y2).put(1).put(1).put(1).put(1).put(1);
        texture2Vertices.put(x3).put(y2).put(1).put(1).put(1).put(0).put(1);
        texture2Vertices.flip();

        int vbo1 = glGenBuffers();
        glBindBuffer(GL_ARRAY_BUFFER, vbo1);
        glBufferData(GL_ARRAY_BUFFER, texture1Vertices, GL_STATIC_DRAW);

        int vbo2 = glGenBuffers();
        glBindBuffer(GL_ARRAY_BUFFER, vbo2);
        glBufferData(GL_ARRAY_BUFFER, texture2Vertices, GL_STATIC_DRAW);

        specifyUniformVariables(windowId, shaderProgramId);

        glClearColor(0.5f, 0.5f, 0.5f, 1.0f);

        while (glfwWindowShouldClose(windowId) == GLFW_FALSE) {
            glClear(GL_COLOR_BUFFER_BIT);

            glUseProgram(shaderProgramId);
            glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ebo);

            specifyVertexAttributes(shaderProgramId);
            glBindTexture(GL_TEXTURE_2D, texture1.getId());
            glBindBuffer(GL_ARRAY_BUFFER, vbo1);
            glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0);

            specifyVertexAttributes(shaderProgramId);
            glBindTexture(GL_TEXTURE_2D, texture2.getId());
            glBindBuffer(GL_ARRAY_BUFFER, vbo2);
            glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0);

            glfwSwapBuffers(windowId);

            glfwPollEvents();
        }
    }

    private void specifyUniformVariables(long windowId, int shaderProgramId) {
        int uniModel = getUniform(shaderProgramId, "model");
        FloatBuffer model = BufferUtils.createFloatBuffer(16);
        new Matrix4f().get(model);
        glUniformMatrix4fv(uniModel, false, model);

        FloatBuffer view = BufferUtils.createFloatBuffer(16);
        new Matrix4f().get(view);
        int uniView = getUniform(shaderProgramId, "view");
        glUniformMatrix4fv(uniView, false, view);

        WindowSize windowSize = getWindowSize(windowId);
        int uniProjection = getUniform(shaderProgramId, "projection");
        FloatBuffer projection = BufferUtils.createFloatBuffer(16);
        new Matrix4f().ortho2D(0, windowSize.getWidth(), 0, windowSize.getHeight()).get(projection);
        glUniformMatrix4fv(uniProjection, false, projection);
    }

    private void specifyVertexAttributes(int shaderProgramId) {
        int stride = 7 * Float.BYTES;

        int posAttrib = getAttribute(shaderProgramId, "position");
        glEnableVertexAttribArray(posAttrib);
        glVertexAttribPointer(posAttrib, 2, GL_FLOAT, false, stride, 0);

        int colAttrib = getAttribute(shaderProgramId, "color");
        glEnableVertexAttribArray(colAttrib);
        glVertexAttribPointer(colAttrib, 3, GL_FLOAT, false, stride, 2 * Float.BYTES);

        int texAttrib = getAttribute(shaderProgramId, "texcoord");
        glEnableVertexAttribArray(texAttrib);
        glVertexAttribPointer(texAttrib, 2, GL_FLOAT, false, stride, 5 * Float.BYTES);
    }

    public long createWindow(int width, int height) {
        glfwDefaultWindowHints();
        glfwWindowHint(GLFW_VISIBLE, GLFW_FALSE);
        glfwWindowHint(GLFW_RESIZABLE, GLFW_TRUE);

        glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
        glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 0);

        long windowId = glfwCreateWindow(width, height, "Hello World!", NULL, NULL);
        if (windowId == NULL) {
            throw new RuntimeException("Failed to create the GLFW window");
        }

        // Get the resolution of the primary monitor
        GLFWVidMode vidmode = glfwGetVideoMode(glfwGetPrimaryMonitor());
        // Center our window
        glfwSetWindowPos(
            windowId,
            (vidmode.width() - width) / 2,
            (vidmode.height() - height) / 2
        );

        // Make the OpenGL context current
        glfwMakeContextCurrent(windowId);
        // Enable v-sync
        glfwSwapInterval(1);

        return windowId;
    }

    public WindowSize getWindowSize(long windowId) {
        IntBuffer width = BufferUtils.createIntBuffer(1);
        IntBuffer height = BufferUtils.createIntBuffer(1);
        GLFW.glfwGetFramebufferSize(windowId, width, height);
        return new WindowSize(width.get(), height.get());
    }

    public Texture createTexture(String textureResource) {
        int textureId = glGenTextures();
        glBindTexture(GL_TEXTURE_2D, textureId);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);

        ARGBImage image = new ImageService().loadClasspathImage(textureResource);
        glTexImage2D(
                GL_TEXTURE_2D,
                0,
                GL_RGBA8,
                image.getWidth(),
                image.getHeight(),
                0,
                GL_RGBA,
                GL_UNSIGNED_BYTE,
                image.getContents());

        return new Texture(textureId, image.getWidth(), image.getHeight());
    }

    public int createShaderProgram(String vertexResource, String fragmentResource) {
        int vertexShader = glCreateShader(GL_VERTEX_SHADER);
        String vertexSource = getClasspathResource(vertexResource);
        glShaderSource(vertexShader, vertexSource);
        glCompileShader(vertexShader);
        validateShaderCompilation(vertexShader);

        int fragmentShader = glCreateShader(GL_FRAGMENT_SHADER);
        String fragmentSource = getClasspathResource(fragmentResource);
        glShaderSource(fragmentShader, fragmentSource);
        glCompileShader(fragmentShader);
        validateShaderCompilation(fragmentShader);

        int shaderProgramId = glCreateProgram();
        glAttachShader(shaderProgramId, vertexShader);
        glAttachShader(shaderProgramId, fragmentShader);
        glLinkProgram(shaderProgramId);
        validateShaderProgram(shaderProgramId);
        glUseProgram(shaderProgramId);

        return shaderProgramId;
    }

    private static String getClasspathResource(String resourceName) {
        URL url = Resources.getResource(resourceName);
        try {
            return Resources.toString(url, Charsets.UTF_8);
        } catch (IOException e) {
            throw Throwables.propagate(e);
        }
    }

    private static void validateShaderCompilation(int shader) {
        int status = glGetShaderi(shader, GL_COMPILE_STATUS);
        if (status != GL_TRUE) {
            throw new RuntimeException(glGetShaderInfoLog(shader));
        }
    }

    private static void validateShaderProgram(int shaderProgram) {
        int status = glGetProgrami(shaderProgram, GL_LINK_STATUS);
        if (status != GL_TRUE) {
            throw new RuntimeException(glGetProgramInfoLog(shaderProgram));
        }
    }

    public int getUniform(int shaderProgramId, String uniformName) {
        int location = glGetUniformLocation(shaderProgramId, uniformName);
        if (location == -1) {
            throw new IllegalArgumentException("Could not find uniform: "
                    + uniformName + " for shaderProgramId: " + shaderProgramId);
        } else {
            return location;
        }
    }

    public int getAttribute(int shaderProgramId, String attribute) {
        int location = glGetAttribLocation(shaderProgramId, attribute);
        if (location == -1) {
            throw new IllegalArgumentException("Could not find attribute: "
                    + attribute + " for shaderProgramId: " + shaderProgramId);
        } else {
            return location;
        }
    }

    public static void main(String[] args) {
        new StandaloneMultiTextureExample().run();
    }

}

1 个答案:

答案 0 :(得分:1)

您通过在错误的时间绑定顶点缓冲区来错误地设置顶点数组。

当您使用顶点数组对象(VAO)渲染[1]时,GL_ARRAY_BUFFER绑定的唯一时间是glVertexAttribPointer调用。调用glVertexAttribPointer获取当前绑定的GL_ARRAY_BUFFER对象的名称,并将其与属性和VAO关联;之后,GL_ARRAY_BUFFER绑定根本不重要,绑定另一个数组缓冲区不会以任何方式修改VAO。

在您的代码中,在致电specifyVertexAttributes 之前,请致电glBindBuffer设置您的VAO 。这意味着glVertexAttribPointer保存的数组缓冲区是前一个使用的缓冲区。在前两个示例中,您只需要在任何时间点绑定一个数组缓冲区,它就可以正常运行"因为前一帧的绑定缓冲区仍然存在并在下一帧中被读取;如果你在第一帧暂停你的程序,它可能会是黑色的。

您案例中的解决方案很简单;将glBindBuffer调用移到specifyVertexAttributes调用之上,以便glVertexAttribPointer次调用读取正确的缓冲区。

请注意,适用于GL_ELEMENT_ARRAY_BUFFER绑定;每当你绑定一个新的绑定时,绑定都会保存在VAO中。

[1]您在技术上使用默认VAO,它仅在兼容性上下文中受支持,尽管它很容易创建和绑定一直使用的全局VAO。