I have written a simple OpenGL application using GLSL shaders. Actually, my program just displays a simple cube with a phong lighting effect like below :
Here's a peace of the client code :
pShaderProgram->BindAttribLocation(scene::VERTEX_POSITION, "VertexPosition");
pShaderProgram->BindAttribLocation(scene::VERTEX_TEXTURE, "VertexTexture");
pShaderProgram->BindAttribLocation(scene::VERTEX_NORMAL, "VertexNormal");
glm::mat3 normalMatrix = glm::mat3(glm::vec3(pMeshSceneNode->GetModelViewMatrix()[0]),
glm::vec3(pMeshSceneNode->GetModelViewMatrix()[1]), glm::vec3(pMeshSceneNode->GetModelViewMatrix()[2]));
pShaderProgram->SetUniform("ModelViewProjMatrix", pMeshSceneNode->GetModelViewProjectionMatrix());
pShaderProgram->SetUniform("ModelViewMatrix", pMeshSceneNode->GetModelViewMatrix());
pShaderProgram->SetUniform("NormalMatrix", normalMatrix);
pShaderProgram->SetUniform("LightCount", (int)this->m_LightSceneNodeList.size());
pShaderProgram->SetUniform("colorMapSampler", 0);
glm::mat4 viewMatrix = this->m_pCameraSceneNode->GetViewMatrix();
glm::vec4 lightPosition = viewMatrix * glm::vec4(this->m_LightSceneNodeList[0]->GetPosition(), 1.0f);
glm::vec3 lightAmbient = this->m_LightSceneNodeList[0]->GetLightMaterial(scene::AMBIENT);
glm::vec3 lightDiffuse = this->m_LightSceneNodeList[0]->GetLightMaterial(scene::DIFFUSE);
glm::vec3 lightSpecular = this->m_LightSceneNodeList[0]->GetLightMaterial(scene::SPECULAR);
pShaderProgram->SetUniform("LightInfos[0].Position", lightPosition);
pShaderProgram->SetUniform("LightInfos[0].La", lightAmbient);
pShaderProgram->SetUniform("LightInfos[0].Ld", lightDiffuse);
pShaderProgram->SetUniform("LightInfos[0].Ls", lightSpecular);
glm::vec3 materialAmbient = pMeshGeometry->GetMaterial()->GetAmbient();
glm::vec3 materialDiffuse = pMeshGeometry->GetMaterial()->GetDiffuse();
glm::vec3 materialSpecular = pMeshGeometry->GetMaterial()->GetSpecular();
float shininess = pMeshGeometry->GetMaterial()->GetShininess();
pShaderProgram->SetUniform("MaterialInfos.Ka", materialAmbient);
pShaderProgram->SetUniform("MaterialInfos.Kd", materialDiffuse);
pShaderProgram->SetUniform("MaterialInfos.Ks", materialSpecular);
pShaderProgram->SetUniform("MaterialInfos.Shininess", shininess);
pMeshGeometry->GetVertexArray().Lock();
{
if (pMeshGeometry->GetMaterial()->HasTexture())
{
glActiveTexture(GL_TEXTURE0);
pMeshGeometry->GetMaterial()->GetTexture()->Lock();
}
pMeshGeometry->GetIndexBuffer().Lock();
{
glDrawElements(pMeshGeometry->GetPrimitiveType(),
pMeshGeometry->GetIndexBufferSize(), GL_UNSIGNED_INT, OFFSET_BUFFER(0));
}
pMeshGeometry->GetIndexBuffer().Unlock();
if (pMeshGeometry->GetMaterial()->HasTexture())
pMeshGeometry->GetMaterial()->GetTexture()->Unlock();
}
pMeshGeometry->GetVertexArray().Unlock();
Here's my vertex shader :
#version 440
/*
** Vertex attributes.
*/
layout (location = 0) in vec4 VertexPosition;
layout (location = 1) in vec2 VertexTexture;
layout (location = 2) in vec3 VertexNormal;
/*
** Uniforms.
*/
uniform mat3 NormalMatrix;
uniform mat4 ModelViewMatrix;
uniform mat4 ModelViewProjMatrix;
/*
** Outputs.
*/
out vec3 Position;
out vec2 TexCoords;
out vec3 Normal;
/*
** Vertex shader entry point.
*/
void main(void)
{
Position = vec3(ModelViewMatrix * VertexPosition);
Normal = NormalMatrix * VertexNormal;
TexCoords = VertexTexture;
gl_Position = ModelViewProjMatrix * VertexPosition;
}
And my fragment shader :
#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;
/*
** Uniforms.
*/
uniform sampler2D ColorMapSampler;
struct Light
{
vec4 Position;
vec3 La, Ld, Ls;
};
struct Material
{
vec3 Ka, Kd, Ks;
float Shininess;
};
uniform struct Light LightInfos[MAX_LIGHT_COUNT];
uniform struct Material MaterialInfos;
uniform int LightCount;
/*
** 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 * MaterialInfos.Ka;
vec3 Diffuse = LightInfos[idx].Ld * MaterialInfos.Kd * lambertTerm;
if (lambertTerm > 0.0f)
{
vec3 reflectDir = reflect(-lightDir, normalDir);
Specular = LightInfos[idx].Ls * MaterialInfos.Ks * pow(max(dot(reflectDir, viewDir), 0.0f), MaterialInfos.Shininess);
}
return (Ambient + Diffuse + Specular);
}
/*
** Fragment shader entry point.
*/
void main(void)
{
vec3 LightIntensity = vec3(0.0f);
vec4 texDiffuseColor = texture2D(ColorMapSampler, 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;
LightIntensity += Basic_Phong_Shading(
-normalize(normalDir), normalize(lightDir), normalize(viewDir), idx
);
}
FragColor = vec4(LightIntensity, 1.0f) * texDiffuseColor;
}
All works perfectly. But as you can see in the client code there is a lot of 'SetUniform' calls. I think it could be judicious to gather some variables like materials in a simple float array and send this unique variable with a single call of 'SetUniform' instead one for each material component.
Until here the idea was to declare in the fragment shader a structure like below (this is the same way for lights materials):
struct Material
{
vec3 Ka, Kd, Ks;
float Shininess;
};
uniform struct Material MaterialInfos;
And the corresponding client code :
glm::vec3 materialAmbient = pMeshGeometry->GetMaterial()->GetAmbient();
glm::vec3 materialDiffuse = pMeshGeometry->GetMaterial()->GetDiffuse();
glm::vec3 materialSpecular = pMeshGeometry->GetMaterial()->GetSpecular();
float shininess = pMeshGeometry->GetMaterial()->GetShininess();
pShaderProgram->SetUniform("MaterialInfos.Ka", materialAmbient);
pShaderProgram->SetUniform("MaterialInfos.Kd", materialDiffuse);
pShaderProgram->SetUniform("MaterialInfos.Ks", materialSpecular);
pShaderProgram->SetUniform("MaterialInfos.Shininess", shininess);
Do you think it's possible to avoid a data struture like this and use a uniform variable like the following one:
uniform float MaterialBuffer[9];
The 3 first indices should be allocated for the ambient light components, the 3 following one for the diffuse light components and finally the 3 lasts for the specular light components.
I tried to use the opengl function 'glUniform3fv' which takes in parameter a float * but without success. So, do you think it's possible to not use the structure in the shader and use a simple array of floats or not? (I know that matrices can be stored into a Texture Buffer Object (TBO) and can be used as an array of samplerBuffer in the fragment shader but for the materials I don't know if my proposition could be possible...).
What are your opinions ?
Thanks a lot in advance for your help !