简单的 GL 片段着色器在较新的 GPU 上表现异常

Simple GL fragment shader behaves strangely on newer GPU

这个问题让我焦头烂额!我有一个简单的顶点和片段着色器,它在旧的 Vaio 笔记本电脑上运行良好(现在仍然如此)。它用于粒子系统,并使用点精灵和单个纹理来渲染粒子。

当我 运行 我的桌面上的程序使用更新的显卡 (Nvidia GTX 660) 时,问题就出现了。我很确定我已经将它缩小到片段着色器,就好像我忽略了纹理并简单地再次将 inColor 传递出去,一切都按预期进行。

当我像您在下面看到的那样在着色器计算中包含纹理时,使用该着色器时绘制的所有点都会出现在屏幕中央,无论相机位置如何。

您可以使用可疑着色器看到一团乱七八糟的粒子死点,以及正确渲染到右侧的无纹理粒子。

要安全的顶点着色器:

#version 150 core
in vec3 position;
in vec4 color;

out vec4 Color;

uniform mat4 view;
uniform mat4 proj;
uniform float pointSize;

void main() {
   Color = color;
   gl_Position = proj * view * vec4(position, 1.0);
   gl_PointSize = pointSize;
}

片段着色器我怀疑是问题所在,但真的不明白为什么:

#version 150 core
in vec4 Color;
out vec4 outColor;
uniform sampler2D tex;

void main() {
   vec4 t = texture(tex, gl_PointCoord);

   outColor = vec4(Color.r * t.r, Color.g * t.g, Color.b * t.b, Color.a * t.a);
}

无纹理粒子使用相同的顶点着色器,但使用以下片段着色器:

#version 150 core
in vec4 Color;
out vec4 outColor;
void main() {
   outColor = Color;
}

主程序有一个循环处理 SFML window 事件,并调用 2 个函数,绘制和更新。更新在任何时候都不会触及 GL,绘制看起来像这样:

void draw(sf::Window* window)
{
    glClearColor(0.3f, 0.3f, 0.3f, 1.0f);
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

    sf::Texture::bind(&particleTexture);

    for (ParticleEmitter* emitter : emitters)
    {
        emitter->useShader();
        camera.applyMatrix(shaderProgram, window);
        emitter->draw();
    }

}

emitter->useShader() 只是调用 glUseShader(),使用指向创建时存储在发射器对象中的着色器程序的 GLuint。

camera.applyMatrix() :

GLuint projUniform = glGetUniformLocation(program, "proj");
glUniformMatrix4fv(projUniform, 1, GL_FALSE, glm::value_ptr(projectionMatrix));
...
GLint viewUniform = glGetUniformLocation(program, "view");
glUniformMatrix4fv(viewUniform, 1, GL_FALSE, glm::value_ptr(viewMatrix));

emitter->draw() 是完整的:

    glGenVertexArrays(1, &vao);
    glBindVertexArray(vao);

    // Build a new vertex buffer object
    int vboSize = particles.size() * vboEntriesPerParticle;
    std::vector<float> vertices;
    vertices.reserve(vboSize);

    for (unsigned int particleIndex = 0; particleIndex < particles.size(); particleIndex++)
    {
        Particle* particle = particles[particleIndex];
        particle->enterVertexInfo(&vertices);
    }

    // Bind this emitter's Vertex Buffer
    glBindBuffer(GL_ARRAY_BUFFER, vbo);

    // Send vertex data to GPU
    glBufferData(GL_ARRAY_BUFFER, sizeof(float) * vertices.size(), &vertices[0], GL_STREAM_DRAW);

    GLint positionAttribute = glGetAttribLocation(shaderProgram, "position");
    glEnableVertexAttribArray(positionAttribute);
    glVertexAttribPointer(positionAttribute,
        3,
        GL_FLOAT,
        GL_FALSE,
        7 * sizeof(float),
        0);

    GLint colorAttribute = glGetAttribLocation(shaderProgram, "color");
    glEnableVertexAttribArray(colorAttribute);
    glVertexAttribPointer(colorAttribute,
        4,
        GL_FLOAT,
        GL_FALSE,
        7 * sizeof(float),
        (void*)(3 * sizeof(float)));

    GLuint sizePointer = glGetUniformLocation(shaderProgram, "pointSize");
    glUniform1fv(sizePointer, 1, &pointSize);

    // Draw
    glDrawArrays(GL_POINTS, 0, particles.size());

最后,粒子->enterVertexInfo()

vertices->push_back(x);
vertices->push_back(y);
vertices->push_back(z);
vertices->push_back(r);
vertices->push_back(g);
vertices->push_back(b);
vertices->push_back(a);

我很确定这不是完成所有这些的有效方法,但这是我一个学期前写的课程作业。我只是重新访问它来录制它的视频。

所有着色器编译并且 link 没有错误。通过使用片段着色器,我已经确认我可以使用 gl_PointCoord 来改变粒子之间的纯色,因此它按预期工作。当粒子绘制在屏幕中央时,纹理绘制正确,尽管位置错误,因此加载和绑定也正确。我绝不是 GL 专家,所以这就是我自己能想到的尽可能多的调试。

如果它不能在旧笔记本电脑上完美运行,这不会让我如此烦恼!

编辑:包含大量代码

如评论中所示,用于设置相机相关制服的 shaderProgram 变量并不依赖于实际使用的程序。结果,在绘制纹理粒子时,为不同的程序查询了统一位置。

统一位置分配完全是特定于实现的,例如 nvidia 倾向于按统一名称的字母顺序分配它们,因此 view 的位置会根据 tex 是否实际存在(并积极使用)或不存在。如果其他实现只是按照它们在代码或其他方案中出现的顺序分配它们,事情可能会意外地工作。