深度测试在平移时有效,但在指定 z 坐标(2D 渲染)时无效
Depth test works when translating, but not when specifying z-coordinates (2D rendering)
问题
我似乎发现 Android OpenGL ES 3.0 中的深度测试存在问题。
要么,要么我有某种实施缺陷。我注意到当我通过修改模型矩阵来转换我的几何体时,深度测试工作正常;然而,当我尝试通过在我的几何体中指定位置数据来做同样的事情时,深度测试失败了。最后渲染的几何体出现在先前渲染的几何体之后。
我的 objective 是执行 2D 渲染,使得每个像素对应于世界中的 1 个单位 space。为此,我对 glViewport
和 Matrix.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 个单位,有效的 zNear 和zFar 值分别为 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);
问题
我似乎发现 Android OpenGL ES 3.0 中的深度测试存在问题。 要么,要么我有某种实施缺陷。我注意到当我通过修改模型矩阵来转换我的几何体时,深度测试工作正常;然而,当我尝试通过在我的几何体中指定位置数据来做同样的事情时,深度测试失败了。最后渲染的几何体出现在先前渲染的几何体之后。
我的 objective 是执行 2D 渲染,使得每个像素对应于世界中的 1 个单位 space。为此,我对
glViewport
和 Matrix.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 个单位,有效的 zNear 和zFar 值分别为 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);