简体   繁体   中英

The vertex shade bug - variables declaration order

I've started studying C++/OpenGL for a year, I've stumbled on a vague bug with GLSL language. I wonder if someone had got it similarly.

The bug is produced only when I omitted the layout(location =?) in the vertex shade and I've to specify the index of the generic vertex attribute for both glVertexAttribPointer and glEnableVertexArrayAttrib manually, I mean, by omitting the use of glGetAttribLocation which will eventually get the index-location for vertex attributes from the vertex shade. The strange part is, to declare the input and output variables in specific order. In my vertex shade, eg if I have declared them with this order:

gl_Position = vec4(aVertex, 1.0);
color = aColor;

It won't generate the glitch however, if I reversed the declaration order it will become obvious.

Fortunately, I fixed it. But I need more explanation about it especially in GLSL language, why is the variables declaration order a matter. In C/C++, it seems irrelevant.

Here complete example of this bug: [I used SFML/Glew libraries]

#include <SFML/Window.hpp>
#include <GL/GLew.h>

//turn it ON/OFF e.g Enable_Bug [1|0]
#define Enable_Bug 1 // to produce the Bug, we should omit glGetAttribLocation(), 

// shader sources
static const GLchar* vert_R = R"(#version 440 core
// We have a Bug! 

#define Enable_Bug_GLSL 0   // here the catch, if we turned this OFF by turning [Enable_Bug_GLSL 0] 
                            // and both [Enable_Bug_GLSL_Attrib | Enable_Bug] are ON, it will fix the Bug!! weird

#define Enable_Bug_GLSL_Attrib 1 // to produce the Bug, we should omit layout(location = ?)

#if Enable_Bug_GLSL_Attrib
    in vec3 aVertex;
    in vec4 aColor;
#else
    layout(location = 0) in vec3 aVertex;
    layout(location = 1) in vec4 aColor;
#endif

    out vec4 color;

    void main()
    {
#if Enable_Bug_GLSL
        color = aColor;
        gl_Position = vec4(aVertex, 1.0);
#else
        gl_Position = vec4(aVertex, 1.0);
        color = aColor;
#endif
    }
)";

static const GLchar* frag_R = R"(#version 440 core
    in vec4 color;
    layout(location = 0) out vec4 FragColor;
    void main()
    {
        FragColor = color;
    }
)";

static void checkStatus(GLuint obj)
{
    GLint status = GL_FALSE;

    if (glIsShader(obj))   glGetShaderiv(obj, GL_COMPILE_STATUS, &status);
    if (glIsProgram(obj))  glGetProgramiv(obj, GL_LINK_STATUS, &status);
    if (status == GL_TRUE) return;
    glDeleteShader(obj);
    obj = 0;
}

static void attachShader(GLuint program, GLenum type, const GLchar* src)
{
    GLuint shader = glCreateShader(type);

    glShaderSource(shader, 1, &src, NULL);
    glCompileShader(shader);
    checkStatus(shader);
    glAttachShader(program, shader);
    glDeleteShader(shader);
}

static GLuint loadShader(const GLchar* vert, const GLchar* frag, const GLchar* geom = nullptr)
{
    GLuint progam = glCreateProgram();
    if (vert) attachShader(progam, GL_VERTEX_SHADER, vert);
    if (geom) attachShader(progam, GL_GEOMETRY_SHADER, geom);
    if (frag) attachShader(progam, GL_FRAGMENT_SHADER, frag);
    glLinkProgram(progam);
    checkStatus(progam);
    return progam;
}

int main()
{
    sf::Window window(sf::VideoMode(800, 600), "OpenGL");

    //Initialize GLEW
    glewExperimental = GL_TRUE;
    GLenum err = glewInit();

    //If GLEW hasn't initialized
    if (err != GLEW_OK)
    {
        fprintf(stderr, "Error: %s\n", glewGetErrorString(err));
        throw std::runtime_error("Failed to initialize GLEW\n");
        return -1;
    }

    GLfloat position[] = {
        -1.0f, -1.0f, 0.0f,
         1.0f, -1.0f, 0.0f,
        -1.0f,  1.0f, 0.0f,
         1.0f,  1.0f, 0.0f,
    };

    GLfloat color[] = {
        1.0f, 0.0f, 1.0f, 1.0f,
        1.0f, 0.0f, 1.0f, 1.0f,
        1.0f, 0.0f, 1.0f, 1.0f,
        1.0f, 0.0f, 1.0f, 1.0f,

    };

    GLuint program = loadShader(vert_R, frag_R);
    glUseProgram(program);
    GLint location;

#if Enable_Bug
    location = 0;
#else
    location = glGetAttribLocation(program, "aVertex");
#endif

    glEnableVertexAttribArray(location);
    glVertexAttribPointer(location, 3, GL_FLOAT, GL_FALSE, 0, position);

#if Enable_Bug
    location = 1;
#else
    location = glGetAttribLocation(program, "aColor");
#endif

    glEnableVertexAttribArray(location);
    glVertexAttribPointer(location, 4, GL_FLOAT, GL_FALSE, 0, color);

    // Initialize clear colors
    glClearColor(0.f, 0.f, 0.f, 1.f);

    while (window.isOpen())
    {
        sf::Event windowEvent;
        while (window.pollEvent(windowEvent))
        {
            if (windowEvent.type == sf::Event::Closed)
                window.close();
        }

        // render
        glClear(GL_COLOR_BUFFER_BIT);

        glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);

        window.display();
    }
    return 0;
}

If you do not specify the attributes indices through layout qualifiers , the attribute indexes are not specified and can have any value. There is no guarantee of the order nor that the indices are 0 and 1.
You must get the attribute indexes with glGetAttribLocation after you link the program, or you must specify the attribute indexes with glBindAttribLocation before you link the program.

If you omit the locations in GLSL, OpenGL will assign them as it sees fit. So if you do not even query them with glGetAttribLocation and assume aVertex is at 0, aColor at 1, then if the compiler disagrees, there are gonna be glitches of course.

It is entirely possible that the GLSL linker assignes locations based on the first usage, but again, nothing is guaranteed. I see no downsides to setting the location explicitly, O

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM