传递给着色器的颜色不起作用

Color passed to shaders doesn't work

本文关键字:颜色 不起作用      更新时间:2023-10-16

我对OpenGL很陌生,目前有一个简单的程序。

我遇到的问题是,当我传递一个数组来表示点的颜色时,颜色最终只是黑色。如果我只是在片段着色器中显式定义一种颜色,并且如果我在 VAO 中切换数据的索引,三角形将移动到作为颜色的点,它工作正常。

我尝试使用 API 跟踪并得到以下内容:

6872: message: major api error 1282: GL_INVALID_OPERATION error generated. <program> is not a program object, or <shader> is not a shader object.
6872 @0 glAttachShader(program = 1, shader = 1)
6872: warning: glGetError(glAttachShader) = GL_INVALID_OPERATION
6876: message: major api error 1282: GL_INVALID_OPERATION error generated. Handle does not refer to the expected type of object (GL_SHADER_OBJECT_ARB).
6876 @0 glDeleteShader(shader = 1)
6876: warning: glGetError(glDeleteShader) = GL_INVALID_OPERATION
6878: message: major api error 1282: GL_INVALID_OPERATION error generated. Handle does not refer to the expected type of object (GL_SHADER_OBJECT_ARB).
6878 @0 glDeleteShader(shader = 1)
6878: warning: glGetError(glDeleteShader) = GL_INVALID_OPERATION
Rendered 507 frames in 8.52598 secs, average of 59.4653 fps

我的所有代码都在这里,但它正在进行中。可能导致某种类型问题的位是下面的绘图段:

    // TODO: Use this code once per mesh
    // Make a VBO to hold points
    GLuint vbo = 0;
    glGenBuffers(1, &vbo);
    glBindBuffer(GL_ARRAY_BUFFER, vbo);
    glBufferData(GL_ARRAY_BUFFER, points.size() * sizeof(GLfloat), &points[0], drawType);
    // VBO TO hold colors
    GLuint colorVbo = 0;
    glGenBuffers(1, &colorVbo);
    glBindBuffer(GL_ARRAY_BUFFER, colorVbo);
    float colors[] = {
            0.5, 0.0, 0.0,
            0.0, 0.5, 0.0,
            0.0, 0.0, 0.5
    };
    glBufferData(GL_ARRAY_BUFFER, 9 * sizeof(float), colors, GL_STATIC_DRAW);
    // Make a VAO for the points VBO
    GLuint vao = 0;
    glGenVertexArrays(1, &vao);
    glBindVertexArray(vao);
    // Points
    glBindBuffer(GL_ARRAY_BUFFER, vbo);
    glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, NULL);
    // Colors
    glBindBuffer(GL_ARRAY_BUFFER, colorVbo);
    glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 0, NULL);
    GLint colorpos = glGetAttribLocation(Program::getCurrentProgram(), "vertColor");
    glEnableVertexAttribArray(0);   // Points
    glEnableVertexAttribArray(1);   // Colors
    glLinkProgram(Program::getCurrentProgram());
    // Draw Mesh
    glDrawArrays(drawShape, 0, points.size()/2);
}

您可能需要知道的一些事情是 points 是一个 Vector<float>Program::getCurrentProgram()是一个静态函数,返回当前正在使用的程序,在这种情况下drawShapeGL_TRIANGLES

顶点着色器:

#version 400 core
layout(location=0) in vec3 vert;
layout(location=1) in vec3 vertColor;
out vec3 color;
void main()
{
    color = vertColor;
    gl_Position = vec4(vert, 1);
}

片段着色器:

#version 400 core
in vec3 color;
out vec4 finalColor;
void main()
{
    finalColor = vec4(color, 1.0);
}

如果这是一个需要稍微看一下的问题,我深表歉意。在过去的一天左右的时间里,我环顾四周,尝试了几种不同的东西,但都没有奏效。如果需要任何其他信息,以便有人不必对我的所有代码进行排序,请告诉我。

我从 apitrace 得到的信息似乎表明我可能对着色器 ID 做了一些错误,尽管如果我将颜色编码到片段着色器中并且颜色然后工作,该错误仍然会发生。

我正在使用GLFW来创建我的OpenGL上下文。我已经设置了一个错误回调,但我没有从中得到任何东西,我的印象是它也应该通过 OpenGL 错误,尽管我在他们的常见问题解答中没有看到任何明确说明的内容。

我还在编译着色器和链接程序时检查错误,那里也没有发生任何事情。

另外,我想知道一旦对象超出范围并调用delete,使用 C++ 是否会丢失 OpenGL 状态。

编辑:这里有几件事被提到我没有展示:

着色器初始化:

// Create OpenGL Shader
shaderID = glCreateShader(shaderType);
glShaderSource(shaderID, 1, &cShaderString, NULL);
// Compile
glCompileShader(shaderID);

着色器析构函数只是调用glDeleteShader(shaderID);

这是非常标准的。这是在着色器类的构造函数中,shaderIDShader的成员变量。 shaderType是一个GLenum,在这种情况下要么是GL_VERTEX_SHADER,要么是GL_FRAGMENT_SHADER

程序初始化: 创建程序 programID = glCreateProgram((;

// Iterate through Shaders
for(std::vector<Shader>::iterator shader = shaders.begin(); shader != shaders.end(); shader++)
{
    // Attach Shader
    glAttachShader(programID, shader->getShaderID());
}
// Link program
glLinkProgram(programID);

程序析构函数:

// Iterate through attached shaders
for(std::vector<Shader>::iterator shader = shaders.begin(); shader != shaders.end(); shader++)
{
    // Detach Shader
    glDetachShader(programID, shader->getShaderID());
}
// Delete program
glDeleteProgram(programID);

再一次,这似乎是非常标准的,并且是Program类的构造函数。

编辑2:在弄乱了一些代码之后,我发现了一件相当奇怪的事情。如果我移动创建和绑定缓冲区的代码,并将三角形绘制到我用作类来表示三角形的构造函数,我会将位置作为颜色而不是位置。这与我在数据上切换索引(位置为 1,颜色为 0(时发生的情况相同。

编辑3:实际上,多看了一会儿,它似乎画了一个与颜色完全不同的三角形。为 Triangle 类创建一个 vao 成员并将其绑定到绘制似乎可以解决此问题。

编辑4:似乎glValidateProgram()确实产生了成功的结果,这让我怀疑它的着色器有问题。

另外,我想知道一旦对象超出范围并调用删除,使用 C++ 是否会丢失 OpenGL 状态。

是的,有。我相信这正是这里正在发生的事情。这并不意味着你不能将OpenGL对象包装在C++类中,但你必须非常小心。

您在这里遇到的问题是,您在C++对象的析构函数中删除了包装的 OpenGL 对象。因此,每当这些对象中的一个超出范围,或由于任何其他原因被销毁时,它都会带走相应的 OpenGL 对象。

如果复制对象,这会导致特别丑陋的问题。让我们看一个人工的例子:

Shader a(...);
{
    Shader b = a;
}

在此之后,您在 a 的构造函数中创建的 OpenGL 对象将被删除。当创建b作为a的副本时,默认的复制构造函数会将存储在a中的着色器ID复制到b。然后,当b超出范围时,其析构函数将删除着色器 ID。它仍然存储在a,但现在无效。

当然,您不会像上面那样编写代码。但是,如果按值将对象传递给函数/方法,则可以有非常相似的方案,这部分发生在代码中。

危险的是,因为更难识别,如果你将这些对象存储在容器中会发生什么,比如 std::vector .当添加新元素会使vector超出其当前容量时,它会重新分配其内存。在重新分配期间,它会在vector中创建所有现有元素的副本(这将调用复制构造函数(,然后销毁原始元素。这再次与上面的示例非常相似,其中您最终会得到引用已删除的 OpenGL 对象的对象。

这正是您的情况下发生的情况。您将Shader对象推送到vector中,并且当您推入更多元素时,已在vector中的对象的 OpenGL ID 将作为重新分配vector的一部分被删除。

核心问题是无法安全地复制这些C++对象。默认复制行为(成员分配(不起作用。通常,您需要在默认实现不足的类的C++中实现复制构造函数和赋值运算符。但是,如果您将 OpenGL 对象的 ID 作为成员,则实际上没有好方法可以做到这一点,除非您实现可能涉及共享子对象、引用计数等的更精细的方案。

我总是建议的一件事是为无法正确复制的类使用私有复制构造函数和赋值运算符。这将确保在意外复制对象时出现编译时错误,而不是神秘的运行时行为。

然后,若要将对象存储在容器中,最简单的方法是存储指向容器中对象的指针,而不是对象本身。您可以使用智能指针(例如 smart_ptr (,以简化内存管理。