2
votes

I am trying to pass

out float texture_contribs[16]

from the vertex shader to the frament shader using glsl 3.3

However the values in the frament shader are always 0.0, no matter the value in the vertex shader.

Here is my vertex shader code:

uniform mat4 projectionMatrix;
uniform mat4 viewMatrix;
uniform mat4 modelMatrix;
uniform mat4 normalMatrix;

layout(location = 0) in vec4 in_position;
layout(location = 1) in vec4 in_colour;
layout(location = 2) in vec2 in_coord;
layout(location = 3) in vec3 in_normal;
layout(location = 4) in float texture_contributions[16];

out vec2 texcoord;
out vec4 pass_colour;
out float texture_contribs[16];

smooth out vec3 vNormal;

void main()
{
    gl_Position = projectionMatrix * viewMatrix * modelMatrix * in_position;
    texcoord = in_coord;
    vec4 vRes = normalMatrix*vec4(in_normal, 0.0); 
    vNormal = vRes.xyz;
    pass_colour = in_colour;
    for (int i = 0; i < 16; i++)
    {
        texture_contribs[i] = texture_contributions[i];
    }
}

and here is the fragment shader code:

uniform sampler2D texture[16];

in vec2 texcoord;
in vec4 pass_colour;
in float texture_contribs[16];

smooth in vec3 vNormal;

out vec4 out_colour;

struct SimpleDirectionalLight
{
   vec3 vColor;
   vec3 vDirection;
   float fAmbientIntensity;
};

uniform SimpleDirectionalLight sunLight;

void main()
{
    vec4 vTexColor = texture2D(texture[2], texcoord) * texture_contribs[2] + texture2D(texture[3], texcoord) * texture_contribs[3];
    if(vTexColor.a < 0.1)
        discard;
    float fDiffuseIntensity = max(0.0, dot(normalize(vNormal), -sunLight.vDirection));
    out_colour = vTexColor*pass_colour*vec4(sunLight.vColor*(sunLight.fAmbientIntensity+fDiffuseIntensity), 1.0);
}

I attempted splitting the array into individual variables and passing them separately and their values still disappear in the fragment shader.

1
This: layout(location = 4) in float texture_contributions[16]; strikes me as very unusual. That requires 16 attribute slots, and most implementations limit you to 16 total. You can use layout(location = 4) in mat4 texture_contributions; to do the same thing while only taking 4 attribute slots. It will be assigned to attribute slots 4,5,6,7.Andon M. Coleman
Are you actually checking the compiler log for this shader? Scalars take the same storage as vec4s, so when you aggregate them into an array like that instead of packing into vec4 or mat4s you are playing with fire.Andon M. Coleman
Yes I've checked the compiler log and it's clean. I considered packing them into a mat4 but decided to try it this way for a cleaner syntax. I will try switching it to a mat4 and see if that helpskkuryllo
@AndonM.Coleman I was able to get it to work by using mat4 as you suggested and then in vertex shader transferring the values to float[16] and passing that to the fragment shader. Passing the mat4 to the fragment shader didn't work properly. I'm assuming opengl doesn't interpolate matrices in the way I would need it too.kkuryllo
I would go with what you have. The 16 attribute limitation that this allows you to circumvent only applies to input vertex data. You can structure the data you output from the vertex shader any way you want, it's not limited to 16.Andon M. Coleman

1 Answers

7
votes

OpenGL 3.3 requires a minimum of 16 vertex attribute locations.

Vertex Attribute Limit (as defined by GLSL):

const int gl_MaxVertexAttribs = 16; // Minimum: 16 Vertex Attribute Slots

Vertex Attribute Limit (queried in OpenGL):

GLuint max_vtx_attribs;
glGetIntegerv (GL_MAX_VERTEX_ATTRIBS, &max_vtx_attribs);

AMD is the only vendor I am aware of that offers more than the minimum of 16 attributes (they give 29-32 on some driver/hardware combinations). Intel, Apple, NVIDIA and Mesa all give you 16. If you want to write portable code, you should try and target no more than 16 vertex attributes. Otherwise, you have to write different code paths for different vendors, and that is no fun.

There are not many practical applications that require more than 16 per-vertex attributes, and ones that do generally rely on something better suited for storing large amounts of data such as Texture / Shader Storage Buffer Objects.


Things get messy with your GLSL vertex shader the way it is currently written.

OpenGL 3.3 Core Profile Specification - 2.7 Vertex Specification - pp. 26

Vertex shaders (see section 2.11) access an array of 4-component generic vertex attributes. The first slot of this array is numbered 0, and the size of the array is specified by the implementation-dependent constant GL_MAX_VERTEX_ATTRIBS.

Each vertex attribute location in GLSL 3.3 is capable of storing a single vec4, data types that are larger than vec4 (e.g. mat4) will span multiple locations. Data types that are smaller than vec4 consume an entire location and this is where your shader starts to run into issues. You have declared a 16-element array of scalars, each of which is given its own sequential location beginning at 4. This means that the array occupies locations 4 - 19, but most implementations do not have 20 locations to hand out.

OpenGL 3.3 Core Profile Specification - 2.11.3 Vertex Attributes - pp. 55

A generic attribute variable is considered active if it is determined by the compiler and linker that the attribute may be accessed when the shader is executed. Attribute variables that are declared in a vertex shader but never used will not count against the limit. In cases where the compiler and linker cannot make a conclusive determination, an attribute will be considered active. A program object will fail to link if the number of active vertex attributes exceeds GL_MAX_VERTEX_ATTRIBS.

The first 4 vertex attributes (0-3) are active, as are the 16 created by your scalar array. Your GLSL program (should) fail to link on any implementation that does not provide at least 20 vertex attributes. If it does not, then you have a non-compliant implementation.


The problem here is that each of your scalars is wasting 3-components worth of storage.

You do not really need to consume 16 attribute slots to store all of your per-vertex data. If you replace this 16-element array of float with a 4-element array of vec4 or a single mat4, then you can store the same amount of data using only 4 attribute slots.

Then your storage requirements become:

  4 (in_position, in_colour, in_coord, in_normal) + 4 (texture_contributions) = 8

This also vastly simplifies setting up vertex pointers, as you only need 8 of them instead of 20.