Search code examples
qtopenglglslshadervertex-shader

How the vertex shader access the vertex buffer data bound with another shaderprogram attribute?


I have created two shader programs shaderProgram0 and shaderProgram1. I have appended all related shaders and variables with either 0 or 1 to show their relation with either shaderProgram0 or shaderProgram1. Both shader programs work as designed. shaderProgram0 use SimpleVertexShader0.vert as a vertex shader:

#version 330
in vec3 vertexPosition0;
void main()
{
    gl_Position = vec4(vertexPosition0, 1);
}

The output of shaderProgram0 is like this:

enter image description here

shaderProgram1 use SimpleVertexShader1.vert as a vertex shader:

#version 330
in vec3 vertexPosition1;
void main()
{
    gl_Position = vec4(vertexPosition1, 1);
}

The output of shaderProgram1 is like this:

enter image description here

Now the fun part is this; when using shaderProgram1, I accidentally commented the binding of vertex attribute array vao1 and left the binding of vao0 uncommented which resulted in output like the following picture which is in fact the output which (I think) could be generated only by shaderProgram0!:

enter image description here

Code is simplified and is written using Qt Creator in Windows:

void OpenGLWidget::initializeGL()
{
    initializeOpenGLFunctions();
    glClearColor(1.0f, 1.0f, 1.0f, 0.0f);

    shaderProgram0.addShaderFromSourceFile(QOpenGLShader::Vertex, ":/SimpleVertexShader0.vert");
    shaderProgram0.addShaderFromSourceFile(QOpenGLShader::Fragment, ":/SimpleFragmentShader0.frag");
    shaderProgram0.link();

    shaderProgram1.addShaderFromSourceFile(QOpenGLShader::Vertex, ":/SimpleVertexShader1.vert");
    shaderProgram1.addShaderFromSourceFile(QOpenGLShader::Fragment, ":/SimpleFragmentShader1.frag");
    shaderProgram1.link();
}

void OpenGLWidget::resizeGL(int w, int h)
{
    glViewport(0, 0, (GLsizei)w, (GLsizei)h);
}

void OpenGLWidget::paintGL()
{
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);


    GLfloat vertexBufferData0[] = {
       -1.0f, -1.0f, 0.0f,
        1.0f, -1.0f, 0.0f,
        0.0f,  1.0f, 0.0f,
    };
    GLuint vbo0;
    glGenBuffers(1, &vbo0);
    glBindBuffer(GL_ARRAY_BUFFER, vbo0);
    glBufferData(GL_ARRAY_BUFFER,
                 sizeof(vertexBufferData0),
                 vertexBufferData0,
                 GL_STATIC_DRAW);
    GLuint vao0;
    glGenVertexArrays(1, &vao0);
    glBindVertexArray(vao0);
    glBindBuffer(GL_ARRAY_BUFFER, vbo0);
    glVertexAttribPointer(glGetAttribLocation(shaderProgram0.programId(),"vertexPosition0"), 3, GL_FLOAT, GL_FALSE, 0, (void*)0);


    GLfloat vertexBufferData1[] = {
       -1.0f, -1.0f, 0.0f,
        1.0f, -1.0f, 0.0f,
        1.0f,  1.0f, 0.0f,
    };
    GLuint vbo1;
    glGenBuffers(1, &vbo1);
    glBindBuffer(GL_ARRAY_BUFFER, vbo1);
    glBufferData(GL_ARRAY_BUFFER,
                 sizeof(vertexBufferData1),
                 vertexBufferData1,
                 GL_STATIC_DRAW);
    GLuint vao1;
    glGenVertexArrays(1, &vao1);
    glBindVertexArray(vao1);
    glBindBuffer(GL_ARRAY_BUFFER, vbo1);
    glVertexAttribPointer(glGetAttribLocation(shaderProgram1.programId(),"vertexPosition1"), 3, GL_FLOAT, GL_FALSE, 0, (void*)0);


    // Now Rendering-----------------------------------------------------
    glBindVertexArray(vao0);
    glEnableVertexAttribArray(glGetAttribLocation(shaderProgram0.programId(),"vertexPosition0"));

//    glBindVertexArray(vao1);
//    glEnableVertexAttribArray(glGetAttribLocation(shaderProgram1.programId(),"vertexPosition1"));

    shaderProgram1.bind();
    glDrawArrays(GL_TRIANGLES, 0, 3);
}

Isn't it strange that the vertex shader of shaderProgram1 access the buffer data which is bound with shaderProgram0 attribute? I thought it should not have generated any output as the valid vertex attribute array is not enabled! Please explain this scenario if somebody knows how this works. If you don't understand what i am asking then please look at the code thoroughly you will get the point or i will explain further.

EDIT:

// Now Rendering-----------------------------------------------------
glBindVertexArray(vao0);
glEnableVertexAttribArray(glGetAttribLocation(shaderProgram0.programId(),"vertexPosition0"));

shaderProgram0.bind();
glDrawArrays(GL_TRIANGLES, 0, 3);

glBindVertexArray(vao1);
glEnableVertexAttribArray(glGetAttribLocation(shaderProgram1.programId(),"vertexPosition1"));

shaderProgram1.bind();
glDrawArrays(GL_TRIANGLES, 0, 3);

Output of the edited code is:

enter image description here

Here a question arise if both programs are using the same location for the only attribute then they should either generate one or the other triangle not both due overwriting!? Bear with me please, i have just started learning it.


Solution

  • Isn't it strange that the vertex shader of shaderProgram1 access the buffer data which is bound with shaderProgram0 attribute?

    No.

    If you are not explicitly specifying attribute locations from your shader, or using glBindAttribLocation before linking the program, then the implementation will arbitrarily assign vertex attribute locations for you. There is no requirement that separate programs use separate attribute locations. Indeed, it's generally advised that you try to make your attribute location interfaces compatible between programs where possible.

    In your case, the implementation happened to assign them both to the same location. So either VAO will work with either program.