OpenGL 渲染上的伪像似乎特定于硬件
Artefacts on OpenGL rendering seemingly specific to hardware
我目前正在用 C++ 为 OpenGL 编写图形引擎。如果您有兴趣,这里是来源:https://github.com/freddycansic/OpenGL
这些是在我的笔记本电脑(Ryzen 5500U,集成 Radeon 显卡)上渲染具有纯色或纹理的立方体时的结果:
这些是在我的桌面(Ryzen 5 2400G,NVIDIA 1060)上渲染相同结果时的结果:
如您所见,在我的桌面上使用纯色进行渲染时开始出现伪像。
当渲染超过 1 个纹理时,我 运行 遇到更多问题:
笔记本电脑正常工作:https://imgur.com/a/YFsXnE3
桌面 2 纹理合并在一起?:https://imgur.com/a/5itdYHA
复现的一般代码如下
着色器代码:
顶点着色器:
#version 330 core
layout(location = 0) in vec4 a_Position;
layout(location = 1) in vec4 a_Color;
layout(location = 2) in vec2 a_TexCoord;
layout(location = 3) in float a_TexID;
out vec4 v_Color;
out vec2 v_TexCoord;
out float v_TexID;
uniform mat4 u_ViewProj;
void main() {
gl_Position = u_ViewProj * a_Position;
v_Color = a_Color;
v_TexCoord = a_TexCoord;
v_TexID = a_TexID;
};
片段着色器:
#version 330 core
out vec4 color;
in vec4 v_Color;
in vec2 v_TexCoord;
in float v_TexID;
uniform sampler2D u_Textures[32];
void main() {
int index = int(v_TexID);
if (index < 0) { // if index < 0 do a color
color = v_Color;
}
else { // else do a texture
color = texture(u_Textures[index], v_TexCoord);
}
};
创建顶点数组、着色器,为顶点缓冲区和索引缓冲区分配内存
unsigned int program = glCreateProgram();
unsigned int vertexShader = glCreateShader(GL_VERTEX_SHADER);
glShaderSource(vertexShader, 1, &vertexSource, nullptr);
glCompileShader(vertexShader);
// same with fragment
glAttachShader(program, vertexShader);
glAttachShader(program, fragmentShader);
glLinkProgram(program);
glValidateProgram(program);
glDeleteShader(vertexShader);
glDeleteShader(fragmentShader);
unsigned int vao;
glGenVertexArrays(1, &vao);
glBindVertexArray(vao);
unsigned int vbo;
glGenBuffers(1, &vbo);
glBindBuffer(GL_ARRAY_BUFFER, vbo);
glBufferData(GL_ARRAY_BUFFER, 50000 * sizeof(Vertex), nullptr, GL_DYNAMIC_DRAW);
unsigned int ibo;
glGenBuffers(1, &ibo);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ibo);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, 75000 * sizeof(GLuint), nullptr, GL_DYNAMIC_DRAW);
为位置、颜色、纹理坐标和纹理 ID 启用顶点属性。
glEnableVertexAttribArray(0);
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 40, (const void*)0);
glEnableVertexAttribArray(1);
glVertexAttribPointer(1, 4, GL_FLOAT, GL_FALSE, 40, (const void*)12);
glEnableVertexAttribArray(2);
glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, 40, (const void*)28);
glEnableVertexAttribArray(3);
glVertexAttribPointer(3, 1, GL_FLOAT, GL_FALSE, 40, (const void*)36);
现在为立方体生成顶点和索引,结果如下:
-1
的纹理 ID 表示我们没有使用纹理。
{{0, 0, 0}, {1, 0, 0, 1}, {0, 0}, -1}
// ... ommited for sanity
{{1, 1, 1}, {1, 0, 0, 1}, {1, 1}, -1}
0, 1, 2,
// ...
6, 7, 4
然后将这些顶点和索引存储在 CPU 上的单独向量中,直到批处理结束时:
glBindBuffer(GL_ARRAY_BUFFER, vbo);
glBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(Vertex) * CPUVertexBuffer.size(), CPUVertexBuffer.data());
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ibo);
glBufferSubData(GL_ELEMENT_ARRAY_BUFFER, 0, sizeof(GLuint) * CPUIndexBuffer.size(), CPUIndexBuffer.data());
glUseProgram(program);
glBindVertexArray(vao);
glDrawElements(GL_TRIANGLES, (GLsizei) CPUIndexBuffer.size(), GL_UNSIGNED_INT, nullptr);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glClearColor(0.1f, 0.1f, 0.1f, 1.0f);
如果有人以前见过这样的事情或知道导致这种情况的任何原因,我很乐意听取您的意见。
int index = int(v_TexID);
if (index < 0) { // if index < 0 do a color
color = v_Color;
}
else { // else do a texture
color = texture(u_Textures[index], v_TexCoord);
}
这段代码在两个方面是错误的。 GLSL spec 在第 4.1.7 节中明确指出:
Texture-combined sampler types are opaque types, declared and behaving
as described above for opaque types. When aggregated into arrays
within a shader, they can only be indexed with a dynamically uniform
integral expression, otherwise results are undefined.
在 GLSL 规范的规则中,单个调用组可以与整个渲染 API 调用一样大,因此除非您的 a_TexID
值对于所有顶点都不相同render 调用,其结果未定义。
此外,从第 8.1 节开始:
Some texture functions (non-“Lod” and non-“Grad” versions) may require
implicit derivatives. Implicit derivatives are undefined within
non-uniform control flow and for non-fragment shader texture fetches.
由于您的 texture
调用使用隐式导数,因此不得在 non-uniform 控制流中使用它。
在 OpenGL 4.6 的保证范围内无法实现您在此处尝试执行的操作。
您可以做的是:
- 不要使用
sampler2D
的数组,使用带有 array texture 的单个 sampler2Darray
。从数组中选择层不必是动态统一表达式。
- 使用
GL_ARB_bindless_textures
extension,这也删除了此限制。
我目前正在用 C++ 为 OpenGL 编写图形引擎。如果您有兴趣,这里是来源:https://github.com/freddycansic/OpenGL
这些是在我的笔记本电脑(Ryzen 5500U,集成 Radeon 显卡)上渲染具有纯色或纹理的立方体时的结果:
这些是在我的桌面(Ryzen 5 2400G,NVIDIA 1060)上渲染相同结果时的结果:
如您所见,在我的桌面上使用纯色进行渲染时开始出现伪像。
当渲染超过 1 个纹理时,我 运行 遇到更多问题:
笔记本电脑正常工作:https://imgur.com/a/YFsXnE3
桌面 2 纹理合并在一起?:https://imgur.com/a/5itdYHA
复现的一般代码如下
着色器代码:
顶点着色器:
#version 330 core
layout(location = 0) in vec4 a_Position;
layout(location = 1) in vec4 a_Color;
layout(location = 2) in vec2 a_TexCoord;
layout(location = 3) in float a_TexID;
out vec4 v_Color;
out vec2 v_TexCoord;
out float v_TexID;
uniform mat4 u_ViewProj;
void main() {
gl_Position = u_ViewProj * a_Position;
v_Color = a_Color;
v_TexCoord = a_TexCoord;
v_TexID = a_TexID;
};
片段着色器:
#version 330 core
out vec4 color;
in vec4 v_Color;
in vec2 v_TexCoord;
in float v_TexID;
uniform sampler2D u_Textures[32];
void main() {
int index = int(v_TexID);
if (index < 0) { // if index < 0 do a color
color = v_Color;
}
else { // else do a texture
color = texture(u_Textures[index], v_TexCoord);
}
};
创建顶点数组、着色器,为顶点缓冲区和索引缓冲区分配内存
unsigned int program = glCreateProgram();
unsigned int vertexShader = glCreateShader(GL_VERTEX_SHADER);
glShaderSource(vertexShader, 1, &vertexSource, nullptr);
glCompileShader(vertexShader);
// same with fragment
glAttachShader(program, vertexShader);
glAttachShader(program, fragmentShader);
glLinkProgram(program);
glValidateProgram(program);
glDeleteShader(vertexShader);
glDeleteShader(fragmentShader);
unsigned int vao;
glGenVertexArrays(1, &vao);
glBindVertexArray(vao);
unsigned int vbo;
glGenBuffers(1, &vbo);
glBindBuffer(GL_ARRAY_BUFFER, vbo);
glBufferData(GL_ARRAY_BUFFER, 50000 * sizeof(Vertex), nullptr, GL_DYNAMIC_DRAW);
unsigned int ibo;
glGenBuffers(1, &ibo);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ibo);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, 75000 * sizeof(GLuint), nullptr, GL_DYNAMIC_DRAW);
为位置、颜色、纹理坐标和纹理 ID 启用顶点属性。
glEnableVertexAttribArray(0);
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 40, (const void*)0);
glEnableVertexAttribArray(1);
glVertexAttribPointer(1, 4, GL_FLOAT, GL_FALSE, 40, (const void*)12);
glEnableVertexAttribArray(2);
glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, 40, (const void*)28);
glEnableVertexAttribArray(3);
glVertexAttribPointer(3, 1, GL_FLOAT, GL_FALSE, 40, (const void*)36);
现在为立方体生成顶点和索引,结果如下:
-1
的纹理 ID 表示我们没有使用纹理。
{{0, 0, 0}, {1, 0, 0, 1}, {0, 0}, -1}
// ... ommited for sanity
{{1, 1, 1}, {1, 0, 0, 1}, {1, 1}, -1}
0, 1, 2,
// ...
6, 7, 4
然后将这些顶点和索引存储在 CPU 上的单独向量中,直到批处理结束时:
glBindBuffer(GL_ARRAY_BUFFER, vbo);
glBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(Vertex) * CPUVertexBuffer.size(), CPUVertexBuffer.data());
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ibo);
glBufferSubData(GL_ELEMENT_ARRAY_BUFFER, 0, sizeof(GLuint) * CPUIndexBuffer.size(), CPUIndexBuffer.data());
glUseProgram(program);
glBindVertexArray(vao);
glDrawElements(GL_TRIANGLES, (GLsizei) CPUIndexBuffer.size(), GL_UNSIGNED_INT, nullptr);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glClearColor(0.1f, 0.1f, 0.1f, 1.0f);
如果有人以前见过这样的事情或知道导致这种情况的任何原因,我很乐意听取您的意见。
int index = int(v_TexID); if (index < 0) { // if index < 0 do a color color = v_Color; } else { // else do a texture color = texture(u_Textures[index], v_TexCoord); }
这段代码在两个方面是错误的。 GLSL spec 在第 4.1.7 节中明确指出:
Texture-combined sampler types are opaque types, declared and behaving as described above for opaque types. When aggregated into arrays within a shader, they can only be indexed with a dynamically uniform integral expression, otherwise results are undefined.
在 GLSL 规范的规则中,单个调用组可以与整个渲染 API 调用一样大,因此除非您的 a_TexID
值对于所有顶点都不相同render 调用,其结果未定义。
此外,从第 8.1 节开始:
Some texture functions (non-“Lod” and non-“Grad” versions) may require implicit derivatives. Implicit derivatives are undefined within non-uniform control flow and for non-fragment shader texture fetches.
由于您的 texture
调用使用隐式导数,因此不得在 non-uniform 控制流中使用它。
在 OpenGL 4.6 的保证范围内无法实现您在此处尝试执行的操作。
您可以做的是:
- 不要使用
sampler2D
的数组,使用带有 array texture 的单个sampler2Darray
。从数组中选择层不必是动态统一表达式。 - 使用
GL_ARB_bindless_textures
extension,这也删除了此限制。