0
votes

EDIT: see at the end for new investigations on the subject.

I've been experiencing an odd behavior with my shaders. In short, I find it very strange that to pass a single float from a vertex shader to a fragment shader I have to pass also a fake variable, and I am looking for an explanation of this behavior.

In more details : I wrote two minimalistic shaders

  • a vertex shader that passes one float (only one, this is important), vertAlpha, to the fragment shader, like so:

    #version 150
    
    uniform float alphaFactor;
    uniform mat4 cameramodel;
    in float vertAlpha;
    in vec3 vert;
    in vec3 vertScale;
    in vec3 trans;
    out float fragAlpha;
    
    void main()
    {
        fragAlpha = alphaFactor * vertAlpha;
        gl_Position = cameramodel * vec4( trans + ( vert * vertScale ), 1.0f );
    }
    
  • and a fragment shader that uses the passed variable:

    #version 150
    
    in float fragAlpha;
    out vec4 finalColor;
    
    void main()
    {
       finalColor = vec4( 0.0f, 0.0f, 0.0f, fragAlpha );
    }
    

But that doesn't work, nothing appears on the screen, it seems that in the fragment shader, fragAlpha keeps it's initialization value of 0, and ignores the passed value.

After investigating, I found a "hack" to solve this. I found that the fragment shader "sees" the passed value for fragAlpha only if a fake (unused) value is passed with it (depending on the platform (osx / NVidia GeForce 9400, Windows laptop / Intel HD Graphics), a vec3 or a vec2 is sufficient).

So this vertex shader solves the problem:

#version 150

uniform float alphaFactor;
uniform mat4 cameramodel;
in vec3 vertFake;
in float vertAlpha;
in vec3 vert;
in vec3 vertScale;
in vec3 trans;
out float fragAlpha;
out vec3 fragFake;

void main()
{
    fragAlpha = alphaFactor * vertAlpha;
    fragFake = vertFake;
    gl_Position = cameramodel * vec4( trans + ( vert * vertScale ), 1.0f );
}

I find this more like a "hack" than a solution. What should I do to solve this properly?

Is there a "per-driver manufacturer minimum threshold" in terms of size of data that can pass from a vertex shader to a fragment shader?

EDIT:

After reading derhass comment, I went back to my code to see if I was doing something wrong.

After more investigation, I found that the problem is not inherent to the fact that I pass the attribute value to the fragment shader. I changed the order of attributes declarations in the vertex shader and saw that the attribute that has the "0" location (location returned by glGetAttribLocation) is not updated by a call to glVertexAttribute, it's value stays at 0.

It will occur for example for the "trans" attribute if it is declared before all other attributes. Introducing a fake attribute in my shader only fixed the issue because the fake attribute took the location "0".

In any case, glGetError returns no error anywhere.

I use glDrawElements to render, with a vbo that contains the vertex positions, maybe I'm using it inadequatly... or it's not well supported by my hardware(s)?

To give some more context, I copy here the calls i do to opengl, for setup and rendering:

Setup:

GLuint vao, vbo;    

glGenBuffers(1, &vbo);    
glGenVertexArrays(1, &vao);

glBindVertexArray(vao);

glBindBuffer(GL_ARRAY_BUFFER, vbo);


GLfloat vertexData[] = {
    //  X     Y     Z
    0.0f, 0.0f, 1.0f, // 0
    1.0f, 0.0f, 1.0f, // 1
    0.0f, 0.0f, 0.0f, // 2
    1.0f, 0.0f, 0.0f, // 3
    0.0f, 1.0f, 1.0f, // 4
    1.0f, 1.0f, 1.0f, // 5
    1.0f, 1.0f, 0.0f, // 6
    0.0f, 1.0f, 0.0f  // 7
};
glBufferData(GL_ARRAY_BUFFER, sizeof(vertexData), vertexData, GL_STATIC_DRAW);

GLint vert = glGetAttribLocation(p, "vert")
glEnableVertexAttribArray(vert);
glVertexAttribPointer(vert, 3, GL_FLOAT, GL_FALSE, 0, NULL);
glBindVertexArray(0);

render:

glUseProgram(p)

aphaFactor = glGetUniformLocation(p,"alphaFactor");
glUniform1f(alphaFactor, 1.0f);

cameramodel = glGetUniformLocation(p,"cameramodel");
glm::mat4 mat = gCamera.matrix();
glUniformMatrix4fv(cameramodel, 1, GL_FALSE, glm::value_ptr(mat); //

glBindVertexArray(vao);

GLubyte indices[36] =
{
    3, 2, 6, 7, 6, 2, 6, 7, 4, 2, 4, 7, 4, 2, 0, 3, 0, 2, 0, 3, 1, 6, 1, 3, 1, 6, 5, 4, 5, 6, 5, 4, 1, 0, 1, 4
};

GLint trans = glGetAttribLocation(p, "trans");
GLint alpha = glGetAttribLocation(p, "vertAlpha");
GLint scale = glGetAttribLocation(p, "vertScale");

loop over many entities:
glVertexAttrib3f(trans, m_vInstTransData[offset], m_vInstTransData[offset + 1], m_vInstTransData[offset + 2]);
glVertexAttrib1f(alpha, m_vInstTransData[offset + 3]);
glVertexAttrib3f(scale, m_vInstTransData[offset + 4], m_vInstTransData[offset + 5], m_vInstTransData[offset + 6]);

glDrawElements(GL_TRIANGLES, sizeof(indices) / sizeof(GLubyte), GL_UNSIGNED_BYTE, indices);
end loop                

glBindVertexArray(0);
1
No, there is no such minimum. My first guess with your description would be a bug in your client-side GL code, especially with the attribute and/or uniform locations.derhass
@derhass thanks for looking at this! I doubled check what I do in my code with attributes and uniforms and checked that the locations were all ok. Doing that, I found out some more about the issue, it seems it's related to the fact that the attribute has the "0" location (which is legal according to the spec, illegal location being -1). See my edit in the question to have more about that. I also copied gl calls I do in my program.Olivier Sohn
There is nothing wrong with attribute 0 in itself. Do you use the old glVertex* or glVertexPointer calls somewhere in your code? That builtin vertex attribute is aliasing attribute 0.derhass
@derhass the only glVertex* call in my app are glVertexAttrib and glVertexAttribPointer. What do you mean by alisasing in your comment?Olivier Sohn
I meant that using the old, deprecated glVertex.. and glVertexPointer commands will be equal to using the modern generic attribute functions with attribute index 0. But as you don't use that old stuff, it should not be a problem.derhass

1 Answers

0
votes

you need to specify the interpolator for this so to prevent interpolation between fragments of the same primitive change:

out float fragAlpha;
in float fragAlpha;

into:

out flat float fragAlpha;
in flat float fragAlpha;

because float is interpolated by default. Similarly mat3 is not interpolated by default and if you want to interpolate it then use smooth instead of flat ...

If I remember correctly Scalars (int,float,double) and vectors (vec?,dvec?) are interpolated by default and matrices (mat?) are not.

Not sure which property will be set in your flat variable my bet is the one set on the last vertex of the primitive pass ... In case you need to compute it on some specified vertex then you should move the computation into geometry shader.