0

I have some code that draws squares by passing points through a geometry shader. I construct an array which is sequences of 3 floats, bind that to the in vec3 vert attribute of my vertex shader, and everything is fine.

However, I want to add another float which the fragment shader will use to calculate color. This is in the vertex shader (to pass through) as in float val. Despite being able to find vert, glGetAttribLocation can't find val (get_program_attrib(): Atrrib val not found (-1)).

Code:

void load_model(GLuint* vao, GLuint* vbo) {
    glGenVertexArrays(1, vao);
    glBindVertexArray(*vao);
    glGenBuffers(1, vbo);
    glBindBuffer(GL_ARRAY_BUFFER, *vbo);

    float data[SQUARES_PER_AXIS_SQ * 4] = {0};
    squares_count = 0;

    for (int i = 0; i < SQUARES_PER_AXIS_SQ; i++) {
        int x_pos = i % SQUARES_PER_AXIS;
        int y_pos = i / SQUARES_PER_AXIS;
        if (fabs(squares[i]) > 0.0) {
            data[squares_count * 4 + 0] = x_pos / ((float)SQUARES_PER_AXIS) * 2 - 1;
            data[squares_count * 4 + 1] = (SQUARES_PER_AXIS - y_pos) / ((float)SQUARES_PER_AXIS) * 2 - 1;
            data[squares_count * 4 + 2] = 0.5f;
            data[squares_count * 4 + 3] = (float)squares[i];
            squares_count++;
        }
    }
    DPRINT("Loaded %d squares\n", squares_count);

    glBufferData(GL_ARRAY_BUFFER, squares_count * 4 * sizeof(float), data, GL_STATIC_DRAW);

    glEnableVertexAttribArray(get_program_attrib(main_shader, "vert"));
    glEnableVertexAttribArray(get_program_attrib(main_shader, "val"));
    glVertexAttribPointer(get_program_attrib(main_shader, "vert"), 3, GL_FLOAT, GL_FALSE, 4, NULL);
    glVertexAttribPointer(get_program_attrib(main_shader, "val"), 1, GL_FLOAT, GL_FALSE, 4, (float*)(3 * sizeof(float)));

    glBindBuffer(GL_ARRAY_BUFFER, 0);
    glBindVertexArray(0);
}

GLuint get_program_attrib(program_t* prog, GLchar* name) {
    if (!name) {
        DPRINT("ERROR: name == NULL\n");
        return -1;
    }
    GLint attrib = glGetAttribLocation(prog->id, name);
    if (attrib < 0)
        DPRINT("Atrrib %s not found (%d)\n", name, attrib);
    return attrib;
}

Vertex shader:

#version 150

in vec3 vert;
in float val;
out float value;

void main() {
    gl_Position = vec4(vert, 1);
    value = val;
}

Fragment shader:

#version 150

in float value;
out vec4 color;

void main() {
    color = vec4(value, 0, 0, 1);
}

Geometry shader:

#version 150

layout (points) in;
layout (triangle_strip, max_vertices=4) out;

uniform float square_size;

void main() {
    vec4 position = gl_in[0].gl_Position;

    gl_Position = vec4(position.x, position.y, position.zw);
    EmitVertex();
    gl_Position = vec4(position.x,  position.y + square_size, position.zw);
    EmitVertex();
    gl_Position = vec4(position.x + square_size, position.y, position.zw);
    EmitVertex();
    gl_Position = vec4(position.x + square_size,  position.y + square_size, position.zw);
    EmitVertex();

    EndPrimitive();
}
5
  • Are you sure you have the fragment shader properly compiled and attached? The fragment shader is the only thing that consumes value, which is based on val. If it is not attached, then val is not an active attribute. vert, on the other hand, is used to assign gl_Position; anything used to compute gl_Position will be active no matter what the contents of your fragment shader looks like. Commented Jun 13, 2014 at 19:02
  • Yes, the fragment shader compiles correctly.
    – vgel
    Commented Jun 13, 2014 at 19:03
  • If you change gl_Position to gl_Position = vec4 (vert, val) does that change anything? I still think the problem is to do with your fragment shader. Commented Jun 13, 2014 at 19:05
  • Oh, as a matter of fact - I just re-read your question. You talk of passing things through a geometry shader. I am now absolutely certain that this is your problem. If you can include your geometry shader, I can show you the steps necessary to fix it. The problem basically boils down to the fact that the GS now sits inbetween the VS and FS. For a vertex attribute to be active, it has to go through all 3 of those stages; otherwise it is inactive and you get -1 for its location. Commented Jun 13, 2014 at 19:10
  • I just added the geometry shader. But you're just saying I have to pass value through?
    – vgel
    Commented Jun 13, 2014 at 19:18

1 Answer 1

3

Vertex shader outputs are not passed directly to the fragment shader when you have a geometry shader.

It is this that is causing all of your problems. For a vertex attribute to be active, it has to contribute to the final output of your program. Basically that means something calculated in the fragment shader has to be based off of it.

Unfortunately, that is not happening right now. You have a variable called value that is output from your vertex shader and a variable called value that is input by your fragment shader. Because the geometry shader sits inbetween the two of them, the fragment shader only looks for an output named value in the geometry shader -- no such output exists.

Naturally you might think that the solution would be to create a variable called value in the geometry shader that serves as the input and the output. However, that will not work, you would have to declare it inout value and that is invalid.


Here are the necessary corrections:

Vertex shader:

#version 150

in vec3 vert;
in float val;
out float value_vtx; // Output is fed to the Geometry Shader

void main() {
    gl_Position = vec4(vert, 1);
    value_vtx = val;
}

Fragment shader:

#version 150

in float value_geo; // Takes its input from the Geometry Shader
out vec4 color;

void main() {
    color = vec4(value_geo, 0, 0, 1);
}

Geometry shader:

#version 150

layout (points) in;
layout (triangle_strip, max_vertices=4) out;

uniform float square_size;

in  float value_vtx []; // This was output by the vertex shader
out float value_geo;    // This will be the input to the fragment shader

void main() {
    vec4 position = gl_in[0].gl_Position;

    gl_Position = vec4(position.x, position.y, position.zw);
    value_geo   = value_vtx[0];
    EmitVertex();

    gl_Position = vec4(position.x,  position.y + square_size, position.zw);
    value_geo   = value_vtx[0];
    EmitVertex();

    gl_Position = vec4(position.x + square_size, position.y, position.zw);
    value_geo   = value_vtx[0];
    EmitVertex();

    gl_Position = vec4(position.x + square_size,  position.y + square_size, position.zw);
    value_geo   = value_vtx[0];
    EmitVertex();

    EndPrimitive();
}

You may be asking why I assigned value_geo 4 times when it is constant. That is because EmitVertex (...) causes all output variables to become undefined when it returns, so you have to set it every time.

3
  • #version 320 is too new for the graphics card I'm using (it's pretty ancient), and 150 seems to work with no issues. However, adding the inputs/outputs to the geometry shader gives me the error error C7544: OpenGL requires geometry inputs to be arrays.
    – vgel
    Commented Jun 13, 2014 at 19:39
  • @Rotten194: Yes, I had to correct a few things, sorry. See the revised answer. Commented Jun 13, 2014 at 19:39
  • @Rotten194: I have been misreading GLSL version numbers < 330 all week long for some reason (150 is GL 3.2 and there is no 320). So #version 150 is correct ;) Commented Jun 13, 2014 at 19:46

Not the answer you're looking for? Browse other questions tagged or ask your own question.