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);
glVertex*
orglVertexPointer
calls somewhere in your code? That builtin vertex attribute is aliasing attribute 0. – derhassglVertex..
andglVertexPointer
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