Reputation: 1322
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);
Upvotes: 0
Views: 5675
Reputation: 51893
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.
Upvotes: 0