OpenGL 渲染上的伪像似乎特定于硬件

Artefacts on OpenGL rendering seemingly specific to hardware

我目前正在用 C++ 为 OpenGL 编写图形引擎。如果您有兴趣,这里是来源:https://github.com/freddycansic/OpenGL


这些是在我的笔记本电脑(Ryzen 5500U,集成 Radeon 显卡)上渲染具有纯色或纹理的立方体时的结果:

https://imgur.com/a/A50DHgW

这些是在我的桌面(Ryzen 5 2400G,NVIDIA 1060)上渲染相同结果时的结果:

https://imgur.com/a/DQrGsZ8

如您所见,在我的桌面上使用纯色进行渲染时开始出现伪像。


当渲染超过 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 的保证范围内无法实现您在此处尝试执行的操作。

您可以做的是: