【问题标题】:Unable to render simple vertex array无法渲染简单的顶点数组
【发布时间】:2018-07-27 19:18:49
【问题描述】:

我正在从传统 -> 现代 OpenGL 迁移 2D 游戏。对我来说,这意味着使用着色器和顶点数组而不是 glProjectionglOrthoglBegin / glEnd

我已将相关代码精简为一个非常基本的示例。相机在 (0, 0, -1) 看 (0, 0, 0),我正在尝试用点绘制一个三角形:

(0, 0, 0)
      X
      | '
      |   '
      |     '
      X-------X
(0, 1, 0)   (1, 1, 0)

即使使用非常简单的着色器将所有片段渲染为白色,我也看不到这个三角形,只是闪烁的背景颜色。

代码

package client.render.opengl;

import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.FloatBuffer;

import org.joml.Matrix4f;
import org.lwjgl.BufferUtils;
import org.lwjgl.glfw.GLFW;
import org.lwjgl.glfw.GLFWErrorCallback;
import org.lwjgl.opengl.GL;
import org.lwjgl.opengl.GL11;
import org.lwjgl.opengl.GL15;
import org.lwjgl.opengl.GL20;
import org.lwjgl.opengl.GL30;

import client.res.gfx.ScreenShader;

public class RenderingTest {

    private static final int NUM_VERTICES = 3;

    private static long window;
    private static int shaderProgram;
    private static int vao;
    private static int uniformLoc;
    private static Matrix4f viewProjMatrix = new Matrix4f();
    private static FloatBuffer fb16 = BufferUtils.createFloatBuffer(16);

    public static void main(String[] args) throws IOException {

        init();

        while (!GLFW.glfwWindowShouldClose(window)) {
            GLFW.glfwPollEvents();
            render();
            GLFW.glfwSwapBuffers(window);
        }
    }

    private static void init() throws IOException {

        // Setup an error callback
        GLFW.glfwSetErrorCallback(
                GLFWErrorCallback.createPrint(System.err)
        );

        // Initialise GLFW
        if (!GLFW.glfwInit()) {
            throw new IllegalStateException("Unable to initialize GLFW");
        }

        // Create window
        window = GLFW.glfwCreateWindow(800, 600, "test", 0, 0);
        GLFW.glfwMakeContextCurrent(window);
        GL.createCapabilities();
        GLFW.glfwShowWindow(window);

        // Load shader
        // Assume this code is good for now, it's too long to post here
        ScreenShader.initialise();
        shaderProgram = ScreenShader.shader.getProgram();
        uniformLoc = ScreenShader.shader.getUniformLoc(ScreenShader.UNIFORM_VIEW_PROJ_MATRIX);

        // Define our vertices
        ByteBuffer vertexBuffer = ByteBuffer.allocateDirect(NUM_VERTICES * 2 * Float.BYTES);
        vertexBuffer.putFloat(0).putFloat(0); // Vertex 1
        vertexBuffer.putFloat(0).putFloat(1); // Vertex 2
        vertexBuffer.putFloat(1).putFloat(1); // Vertex 3
        vertexBuffer.flip();

        // Create VAO
        vao = GL30.glGenVertexArrays();
        GL30.glBindVertexArray(vao);

        // Create VBO and fill it with vertex positions
        int vboIdPositions = GL15.glGenBuffers();
        GL15.glBindBuffer(GL15.GL_ARRAY_BUFFER, vboIdPositions);
        GL15.glBufferData(GL15.GL_ARRAY_BUFFER, vertexBuffer, GL15.GL_STATIC_DRAW);
        GL20.glVertexAttribPointer(0, 2, GL11.GL_FLOAT, false, 0, 0);
        GL15.glBindBuffer(GL15.GL_ARRAY_BUFFER, 0); // Deselect

        // Unbind the VAO
        GL30.glBindVertexArray(0);
    }

    public static void render() {

        // Render a random background to prove the loop is working
        GL11.glClearColor(
                (float) (Math.random() * 0.2f),
                (float) (Math.random() * 0.2f),
                (float) (Math.random() * 0.2f),
                1.0f);
        GL11.glClear(GL11.GL_COLOR_BUFFER_BIT);

        // Use shader
        GL20.glUseProgram(shaderProgram);

        // Get camera position
        float cameraX = 0;
        float cameraY = 0;

        // Set view and projection
        viewProjMatrix
                .setOrtho(
                        0,
                        800,
                        600,
                        0,
                        0.01f,
                        10
                ).lookAt(
                        cameraX, cameraY, -1,    // Camera position
                        cameraX, cameraY, 0,     // Camera "look" vector
                        0, 1, 0                  // Camera "up" vector
                );

        // Pass this matrix to our shader
        GL20.glUniformMatrix4fv(uniformLoc, false, viewProjMatrix.get(fb16));

        // Bind our vertex array
        GL30.glBindVertexArray(vao);

        // Enable vertex attributes
        GL20.glEnableVertexAttribArray(0);

        // Draw the vertices
        GL11.glDrawArrays(GL11.GL_TRIANGLES, 0, NUM_VERTICES);

        // Disable vertex attributes
        GL20.glDisableVertexAttribArray(0);

        // Unbind vertex array
        GL30.glBindVertexArray(0);

        // Release shader
        GL20.glUseProgram(0);
    }

}

顶点着色器

#version 330

uniform mat4 view_proj_matrix;

layout(location = 0) in vec2 in_vertex;

void main(void) {
    gl_Position = view_proj_matrix * vec4(in_vertex, 0, 1);
}

片段着色器

#version 330

out vec4 frag_colour;

void main(void) {
    frag_colour = vec4(1, 1, 1, 1);
}

我不知道如何进步,因为我什至无法让这个玩具示例工作。谁能看到我做错了什么,或者指出一个将 JOML 与 LWJGL2 和着色器一起使用的工作示例?

【问题讨论】:

  • 你确定三角形不只是很小吗?使用您使用的投影矩阵,长度为 1 的三角形将有 1 个像素大。
  • 把它放大了 100 倍,仍然没有任何迹象!

标签: java opengl shader lwjgl coordinate-transformation


【解决方案1】:

使用您的视图和投影矩阵,三角形的大小为 1 像素,位于左侧屏幕之外。


如果您的三角形的坐标为 (0, 0, 0), (0, 1, 0), (1, 1, 0),则您的窗口大小为 (800, 600),并且您设置了正交投影链接:

viewProjMatrix.setOrtho(0, 800, 600, 0, 0.01f, 10)

三角形在屏幕上的大小为 1 个像素。

你必须改变正交投影:

float aspect = 800.0f/600.0f;
viewProjMatrix.setOrtho(-aspect, aspect, 1, -1, 0.01f, 10)

注意,投影矩阵描述了从场景的 3D 点到视口的 2D 点的映射。在正交投影中,视图空间中的坐标被线性映射到裁剪空间坐标。


把它放大了 100 倍,仍然没有任何迹象!

如果您只是将三角形三角形缩放到 (0, 0, 0), (0, 100, 0), (100, 100, 0),您也不会看到三角形,因为视图矩阵:

lookAt(0, 0, -1, 0, 0, 0, 0, 1, 0 );

使用此视图矩阵,x 轴在Right-handed 坐标系中反转。视图坐标系统描述了观察场景的方向和位置。视图矩阵从世界空间转换到视图(眼睛)空间。在视图空间中,X 轴指向左侧,Y 轴向上,Z 轴在视图外(注意在右手系统中,Z 轴是 X 轴和 Y 轴的叉积-轴)。

你的视图定义如下

position = (0, 0, -1)
center   = (0, 0, 0)
up       = (0, 1, 0)

y轴和z轴是这样的:

y-axis = up = (0, 1, 0)
z-axis = position - center = (0, 0, -1)

x 轴是 y 轴和 z 轴的叉积:

x-axis = cross(y-axis, z-axis) = (-1, 0, 0)

这意味着您查看三角形的背面。这会导致三角形“翻转”出视口。它被绘制在屏幕的左侧,所以你看不到它:

        (0, 0)
              X---------------------------x
            / |                           |
          /   |                           |
        X-----X                           |
(-100, 100)   |         viewport          |
              |                           |
              |                           |
              |                           |
              x---------------------------x 
                                            (800, 600)

倒置视线解决问题:

lookAt(0, 0, 1, 0, 0, 0, 0, 1, 0 );

【讨论】:

  • 感谢您的详细解释,这真的很有帮助。然而,即使将我的相机移动到三角形的另一侧并将投影更改为“放大”得多,我仍然看不到三角形!
  • 我的字节顺序错误(请参阅 Ripi 的回答中的 cmets),但我会将其标记为解决方案,因为这也是一个很大的帮助。
【解决方案2】:

您的着色器需要 OpenGL 3.3 版。要使用 GLFW 创建此版本的核心配置文件,您可以调用:

GLFW.glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
GLFW.glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
GLFW.glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
window = GLFW.glfwCreateWindow(800, 600, "test", 0, 0);

此外,您似乎以逆时针顺序传递顶点。因为glCullFace的默认值是back,表示你想看的那一面没有渲染。更改顶点顺序或使用glCullFace

【讨论】:

  • 我以为逆时针的脸默认是正面的,人脸剔除默认是禁用的(khronos.org/opengl/wiki/Face_Culling)。我添加了您建议的线条并颠倒了顶点顺序,但仍然没有三角形!
  • @Dan 是的,CCW 脸是默认的正面。我不擅长java,是putFloat(0).putFloat(1)之后ByteBuffer中的数据顺序,(0,1)还是(1,0)?
  • 你明白了 - 问题是字节顺序。致电 vertexBuffer.order(ByteOrder.nativeOrder()); 或使用 LWJGL 的 BufferUtils.createFloatBuffer 可解决此问题。非常感谢您的帮助。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-03-25
  • 1970-01-01
  • 2014-10-01
  • 2011-08-10
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多