深度测试在平移时有效,但在指定 z 坐标(2D 渲染)时无效

Depth test works when translating, but not when specifying z-coordinates (2D rendering)

问题

我似乎发现 Android OpenGL ES 3.0 中的深度测试存在问题。 要么,要么我有某种实施缺陷。我注意到当我通过修改模型矩阵来转换我的几何体时,深度测试工作正常;然而,当我尝试通过在我的几何体中指定位置数据来做同样的事情时,深度测试失败了。最后渲染的几何体出现在先前渲染的几何体之后。


我的 objective 是执行 2D 渲染,使得每个像素对应于世界中的 1 个单位 space。为此,我对 glViewportMatrix.orthoM 进行了以下调用以设置投影矩阵:

GLES30.glViewport(0, 0, width, height);
Matrix.orthoM(projectionMatrix, 0, 0, width, 0, height, 0, 10.0f);

这应该会导致投影使用 TextureView 的完整宽度和高度,并给出深度为 10 个单位(z 轴)的剪辑 -space。

在表面创建期间,我确保启用深度测试(使用默认深度函数):

GLES30.glEnable(GLES30.GL_DEPTH_TEST);

最后但同样重要的是,我还确保在每个绘制帧时清除深度缓冲区:

GLES30.glClear(GLES30.GL_COLOR_BUFFER_BIT | GLES30.GL_DEPTH_BUFFER_BIT);

以上调用应设置深度缓冲。剩下的就是定义一个视图矩阵,该矩阵在渲染任何几何体之前的每个绘制帧期间完成:

Matrix.setLookAtM(viewMatrix, 0, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f);

参数指定eyePosition = { 0.0f, 0.0f, 1.0f}远离屏幕(朝向观察者),眼睛看着centerPosition = { 0.0f, 0.0f, 0.0f},这是世界的原点。这个视图矩阵应该只将相机从场景中移开而不旋转(因为我在 2D 中渲染)。


用模型矩阵转换几何:

通过用模型矩阵转换几何,我可以有效地让深度测试正常工作。几何图形根据指定的 z-index 按顺序呈现。

在绘图函数中,对我希望绘制的每个矩形(钢琴键)执行以下代码:

Matrix.setIdentityM(modelMatrix, 0);
if (keys[index].keyType == WHITE_KEY) {
    Matrix.translateM(modelMatrix, 0, 0.0f, 0.0f, keys[index].zIndex);
} else {
    Matrix.translateM(modelMatrix, 0, 0.0f, 0.0f, keys[index].zIndex);
}
Matrix.translateM(modelMatrix, 0, translateX, 0.0f, 0.0f);
GLES30.glUniformMatrix4fv(GLES30.glGetUniformLocation(shaderProgram, "model"), 1, false, modelMatrix, 0);
GLES30.glUniformMatrix4fv(GLES30.glGetUniformLocation(shaderProgram, "view"), 1, false, viewMatrix, 0);
GLES30.glUniformMatrix4fv(GLES30.glGetUniformLocation(shaderProgram, "projection"), 1, false, projectionMatrix, 0);

keys[index].draw();

这给出了正确的结果。请注意所有黑键都出现在顶部,即使所有键都是按从左到右的顺序呈现的。

用位置数据翻译几何:

我希望在矩形的实际顶点数据中指定 z-index。因此,我使用以下代码代替上面的渲染代码:

Matrix.setIdentityM(modelMatrix, 0);
Matrix.translateM(modelMatrix, 0, translateX, 0.0f, 0.0f);
GLES30.glUniformMatrix4fv(GLES30.glGetUniformLocation(shaderProgram, "model"), 1, false, modelMatrix, 0);
GLES30.glUniformMatrix4fv(GLES30.glGetUniformLocation(shaderProgram, "view"), 1, false, viewMatrix, 0);
GLES30.glUniformMatrix4fv(GLES30.glGetUniformLocation(shaderProgram, "projection"), 1, false, projectionMatrix, 0);

keys[index].draw();

请注意,唯一的区别是我不再根据 z-index 值平移几何。相反,我在实际顶点数据中指定了 z-index:

float[] vertexData = new float[] {
        x, y, zIndex, //Specify z-index in vertex data.
        0, 0, //Texture coordinates.
        x + width, y, zIndex, //Specify z-index in vertex data.
        1, 0, //Texture coordinates.
        x, y + height, zIndex, //Specify z-index in vertex data.
        0, 1, //Texture coordinates.
        x + width, y + height, zIndex, //Specify z-index in vertex data.
        1, 1  //Texture coordinates.
};

int[] indexData = new int[] {
        0, 1, 3,
        0, 3, 2
};

//Set up VAO, VBO, and EBO with the data above (works properly, but is
//provided for the sake of completion).
FloatBuffer vertexBuffer = ByteBuffer.allocateDirect(vertexData.length * 4)
        .order(ByteOrder.nativeOrder()).asFloatBuffer();
vertexBuffer.put(vertexData).position(0);

IntBuffer indexBuffer = ByteBuffer.allocateDirect(indexData.length * 4)
        .order(ByteOrder.nativeOrder()).asIntBuffer();
indexBuffer.put(indexData).position(0);

GLES30.glGenVertexArrays(1, vao, 0);
GLES30.glGenBuffers(1, vbo, 0);
GLES30.glGenBuffers(1, ebo, 0);

GLES30.glBindVertexArray(vao[0]);

GLES30.glBindBuffer(GLES30.GL_ARRAY_BUFFER, vbo[0]);
GLES30.glBufferData(GLES30.GL_ARRAY_BUFFER, vertexData.length * 4, vertexBuffer, GLES30.GL_STATIC_DRAW);
GLES30.glBindBuffer(GLES30.GL_ELEMENT_ARRAY_BUFFER, ebo[0]);
GLES30.glBufferData(GLES30.GL_ELEMENT_ARRAY_BUFFER, indexData.length * 4, indexBuffer, GLES30.GL_STATIC_DRAW);

//Point to vertex positions (stride = size of vertex in bytes = 5 components *4 bytes per float = 20)
GLES30.glEnableVertexAttribArray(0);
GLES30.glVertexAttribPointer(0, 2, GLES30.GL_FLOAT, false, VERTEX_STRIDE, 0);

GLES30.glEnableVertexAttribArray(1);
GLES30.glVertexAttribPointer(1, 2, GLES30.GL_FLOAT, false, VERTEX_STRIDE, VERTEX_POSITION_COMPONENTS * 4);

GLES30.glBindVertexArray(0);

删除翻译后,我收到以下结果。请注意,尽管我们是从左到右呈现最后一个键,但它们呈现在前一个键的后面。另请注意,zIndex = -1.0f 用于白键,而 zIndex = 0.0f 用于黑键。与黑键相比,这应该会导致白键更深入屏幕。两个键都应该在提供给 Matrix.orthoM 的剪裁内可见 -space(因为视图矩阵从屏幕平移了 1 个单位,有效的 zNearzFar 值分别为 1.0f-9.0f)。


问题

我需要做什么才能在顶点数据中指定 zIndex 同时获得正确的深度测试结果?


代码

顶点着色器

#version 300 es

layout (location = 0) in vec3 position;
layout (location = 1) in vec2 texCoord;

out vec2 TexCoord;

uniform mat4 model;
uniform mat4 view;
uniform mat4 projection;

void main()
{
    gl_Position = projection * view * model * vec4(position, 1.0f);

    //Pass texture coordinate to fragment shader.
    TexCoord = texCoord;
}

片段着色器

#version 300 es

in vec2 TexCoord;

out vec4 color;

uniform sampler2D texture;

void main()
{
    vec2 flippedTexCoord = vec2(TexCoord.x, 1.0 - TexCoord.y);
    color = texture(texture, flippedTexCoord);
}

您正在将位置属性设置为只有 2 个组件:

GLES30.glVertexAttribPointer(0, 2, GLES30.GL_FLOAT, false, VERTEX_STRIDE, 0);

第二个参数指定组件的数量。所以这应该是:

GLES30.glVertexAttribPointer(0, 3, GLES30.GL_FLOAT, false, VERTEX_STRIDE, 0);

或者,由于您似乎为此目的定义了一个常量,您不妨在这里使用它:

GLES30.glVertexAttribPointer(0, VERTEX_POSITION_COMPONENTS, GLES30.GL_FLOAT, false, VERTEX_STRIDE, 0);