1
votes

I have implemented in my OpenGL/GLSL application a uniform block managing the mesh material data (Ambient, Diffuse and Specular lighting and Shininess).

For my first try, I have implemented the following uniform block syntax:

uniform MaterialBlock
{
   vec3 Ka, Kd, Ks;
   float Shininess;
};

Here's the client code:

scene::MaterialPtr pMaterial = this->FindMaterialByName(name);

GLuint bindingPoint = 0, bufferIndex = 0;
GLint blockSize = 0;
GLuint indices[4];
GLint offset[4];

const GLchar *names[4] = {"Ka", "Kd", "Ks", "Shininess" };

GLuint blockIndex = glGetUniformBlockIndex(this->m_Handle, "MaterialBlock");

glGetActiveUniformBlockiv(this->m_Handle, blockIndex, GL_UNIFORM_BLOCK_DATA_SIZE, &blockSize);
glGetUniformIndices(this->m_Handle, 4, names, indices);
glGetActiveUniformsiv(this->m_Handle, 4, indices, GL_UNIFORM_OFFSET, offset);

char *pBuffer = new char[blockSize];

memset(pBuffer, '\0', blockSize);

glm::vec3 ambient = pMaterial->GetAmbient();
glm::vec3 diffuse = pMaterial->GetDiffuse();
glm::vec3 specular = pMaterial->GetSpecular();

float shininess = pMaterial->GetShininess();

std::copy(reinterpret_cast<char*>(&ambient[0]),
    reinterpret_cast<char*>(&ambient[0]) + sizeof(glm::vec4), pBuffer + offset[0]);
std::copy(reinterpret_cast<char*>(&diffuse[0]), reinterpret_cast<char*>(
    &diffuse[0]) + sizeof(glm::vec4), pBuffer + offset[1]);
std::copy(reinterpret_cast<char*>(&specular[0]),
    reinterpret_cast<char*>(&specular[0]) + sizeof(glm::vec3), pBuffer + offset[2]);
std::copy(reinterpret_cast<char*>(&shininess), reinterpret_cast<char*>(
    &shininess) + sizeof(float), pBuffer + offset[3]);

glUniformBlockBinding(this->m_Handle, blockIndex, bindingPoint);
{
    glGenBuffers(1, &bufferIndex);
    glBindBuffer(GL_UNIFORM_BUFFER, bufferIndex);
    {
        glBufferData(GL_UNIFORM_BUFFER, blockSize, NULL, GL_DYNAMIC_DRAW);
        glBufferSubData(GL_UNIFORM_BUFFER, 0, blockSize, (const GLvoid *)pBuffer);
    }
    glBindBuffer(GL_UNIFORM_BUFFER, 0);
}
glBindBufferBase(GL_UNIFORM_BUFFER, bindingPoint, bufferIndex);

//TEXTURE.
{
    this->SetUniform("colorSampler", 0); //THE CHANNEL HAS TO BE CALCULATED! //int

    glActiveTexture(GL_TEXTURE0); //DYNAMICS.
    pMaterial->GetTexture()->Lock();
}

Variables content:

blockIndex: 0 //OK
blockSize: 48 //OK
Indices: {1, 2, 3, 78} //OK
Offset: {0, 16, 32, 44} //OK

The fragment shader code:

#version 440

#define MAX_LIGHT_COUNT 10

/*
** Output color value.
*/
layout (location = 0) out vec4 FragColor;

/*
** Inputs.
*/
in vec3 Position;
in vec2 TexCoords;
in vec3 Normal;

/*
** Material uniform block.
*/
uniform MaterialBlock
{
    vec3 Ka, Kd, Ks;
    float Shininess;
};

uniform sampler2D ColorSampler;

struct Light
{
    vec4 Position;
    vec3 La, Ld, Ls;
    float Kc, Kl, Kq;
};

uniform struct Light LightInfos[MAX_LIGHT_COUNT];

uniform unsigned int LightCount;

/*
** Light attenuation factor.
*/
float getLightAttenuationFactor(vec3 lightDir, Light light)
{
    float lightAtt = 0.0f;
    float dist = 0.0f;

    dist = length(lightDir);
    lightAtt = 1.0f / (light.Kc + (light.Kl * dist) + (light.Kq * pow(dist, 2)));
    return (lightAtt);
}

/*
** Basic phong shading.
*/
vec3 Basic_Phong_Shading(vec3 normalDir, vec3 lightDir, vec3 viewDir, int idx)
{
    vec3 Specular = vec3(0.0f);

    float lambertTerm = max(dot(lightDir, normalDir), 0.0f);

    vec3 Ambient = LightInfos[idx].La * Ka;
    vec3 Diffuse = LightInfos[idx].Ld * Kd * lambertTerm;

    if (lambertTerm > 0.0f)
    {
        vec3 reflectDir = reflect(-lightDir, normalDir);
        Specular = LightInfos[idx].Ls * Ks * pow(max(dot(reflectDir, viewDir), 0.0f), Shininess);
    }
    return (Ambient + Diffuse + Specular);
}

/*
** Fragment shader entry point.
*/
void main(void)
{
    vec3 LightIntensity = vec3(0.0f);

    vec4 texDiffuseColor = texture2D(ColorSampler, TexCoords);
    vec3 normalDir = (gl_FrontFacing ? -Normal : Normal);

    for (int idx = 0; idx < LightCount; idx++)
    {
        vec3 lightDir = vec3(LightInfos[idx].Position) - Position.xyz;
        vec3 viewDir = -Position.xyz;

        float lightAttenuationFactor = getLightAttenuationFactor(lightDir, LightInfos[idx]);

        LightIntensity += Basic_Phong_Shading(
            -normalize(normalDir), normalize(lightDir), normalize(viewDir), idx
        ) * lightAttenuationFactor;
    }
    FragColor = vec4(LightIntensity, 1.0f) * texDiffuseColor;
}

This code works perfectly. The output is the following:

enter image description here

But I know it's possible to use instance name (like strutures in C/C++) with Uniform Blocks as follows:

 uniform MaterialBlock
 {
    vec3 Ka, Kd, Ks;
    float Shininess;

 } MaterialInfos;

Of course, all the variable used in shader like 'Ka', 'Kd', 'Ks' and 'Shininess' become 'MaterialInfos.Ka', 'MaterialInfos.Kd', 'MaterialInfos.Ks' and 'MaterialInfos.Shininess'.

But unfortunatly the execution of the program fails because in the client code above the varibales 'indices' and 'offset' are not filled correctly.

Here's the log:

blockIndex: 0 //OK
blockSize: 48 //OK
Indices: {4294967295, 4294967295, 4294967295, 4294967295} //NOT OK
Offset: {-858993460, -858993460, -858993460, -858993460} //NOT OK

So the only the block index and the block size is correct. So to fix the problem I tried to change the line:

const GLchar *names[4] = {"Ka", "Kd", "Ks", "Shininess" };

by the following one:

const GLchar *names[4] = {"MaterialInfos.Ka", "MaterialInfos.Kd", "MaterialInfos.Ks", "MaterialInfos.Shininess" };

But I still have the same log for the variables 'indices' and 'offset'. Consequently my application still fails. I think it's a problem of syntax in the client code (not in GLSL code because I have no GLSL error) but I can't find the solution.

Do you have an idea where my problem comes from ?

2

2 Answers

2
votes

When using instanced uniform blocks, the OpenGL application uses the block name (in this case MaterialBlock) before the dot, not the instance name as you have in your current code. The instance name is only ever seen by the GLSL shader.

Therefore your names variable should be defined and initialized as such:

const GLchar *names[4] = {"MaterialBlock.Ka", "MaterialBlock.Kd", "MaterialBlock.Ks", "MaterialBlock.Shininess" };
0
votes

Try declaring your structType separately from the uniform (of type structType..)

    struct MaterialData
    {
        vec3 kAmbient;
        vec3 kDiffuse;
        vec3 kSpecular;
        float shininess;
    };

    uniform MaterialData material;

(if you follow this example, both your MatrialBlock and Light declarations are erroneous, for slightly different reasons)

Then you can set uniforms by referring to them as (eg) "material.kAmbient" on the cpu side and read them as material.kAmbient on the gpu side.