Search code examples
c++openglshaderopengl-3lighting

Problems passing OpenGL 3.0 uniforms through multiple classes


I'm loosely following the tutorial on www.learnopengl.com, I'm currently passing multiple lights into my fragmentshader. For that I've got multiple lightclasses and one class that acts as a container to be able to pass my lights uniforms into the shader.

The shader is set up correctly. However when I call the function to pass the uniforms from the Lights class, the uniforms doesn't seem to get passed correctly. But when I call the light's bind function without using the Lights class it works(i.e. in the main update loop).

Light.h

#ifndef LIGHT_H
#define LIGHT_H

#include <iostream>
#include <vector>
#include <string>
#include <GL/glew.h>

#include <glm/glm.hpp>
#include <glm/gtc/type_ptr.hpp>
//#include <glm/gtx/string_cast.hpp>

///Declare the classes <--- Restructure the code so that I won't have to
//Needed for the Lights class
class DirectionalLight;
class PointLight;
class SpotLight;

enum typeOfLight{
    pointLight,
    directionalLight,
    spotLight
};

class Lights{
    public:
        Lights();
        ~Lights();

        void addLight(DirectionalLight LightToAdd, typeOfLight type);
        void bindLights(GLuint shader);
    private:
        std::vector<DirectionalLight> dirLights;
        std::vector<DirectionalLight> pointLights;
        std::vector<DirectionalLight> spotLights;
};

class DirectionalLight
{
    public:
        DirectionalLight();
        ~DirectionalLight();

        void bindLight(GLuint shader, int i);
        void initialize(glm::vec3 dir, glm::vec3 ambient, glm::vec3 diffuse, glm::vec3 spec);
    //protected:
        glm::vec3 direction;
        glm::vec3 ambientColor;
        glm::vec3 diffuseColor;
        glm::vec3 specularColor;
    private:
};


class PointLight : public DirectionalLight{
    public:
        PointLight();
        ~PointLight();
        void bindLight(GLuint shader, int i);
        void initialize(glm::vec3 pos, glm::vec3 ambient, glm::vec3 diffuse, glm::vec3 spec, float constAtt, float linAtt, float quadAtt);
   // private:
        float constantAttenuation, linearAttenuation, quadraticAttenuation;

};

class SpotLight : public DirectionalLight{
    public:
        SpotLight();
        ~SpotLight();
        void bindLight(GLuint shader, int i);
        void initialize(glm::vec3 pos, glm::vec3 direction, glm::vec3 ambient, glm::vec3 diffuse, glm::vec3 specular, float curOff, float outerCutOff, float constAtt, float linAtt, float quadAtt);
    //private:
        glm::vec3 pos;
        float cutOff, outerCutOff, constantAttenuation, linearAttenuation, quadraticAttenuation;
    };
    #endif // LIGHT_H

Light.cpp

#include "Light.h"
Lights::Lights(){}
Lights::~Lights(){}

void Lights::addLight(DirectionalLight LightToAdd, typeOfLight type){
    if(type == directionalLight){
        dirLights.push_back(LightToAdd);
        std::cout << "Added a dir Light" << std::endl;
    }else if(type == pointLight){
        pointLights.push_back(LightToAdd);
        std::cout << "Added a point Light" << std::endl;
    }else if(type ==  spotLight){
        spotLights.push_back(LightToAdd);
        std::cout << "Added a spot Light" << std::endl;
    }
}

void Lights::bindLights(GLuint shader){
    /*for(DirectionalLight Light : dirLights){
        Light.bindLight(shader, 0);
    }
    for(int i = 0; i < pointLights.size(); i++){
        pointLights[i].bindLight(shader, i);
        std::cout << "Bound pointLight nr "<<i<<std::endl;
    }
    for(int i = 0; i < spotLights.size(); i++){
        spotLights[i].bindLight(shader, i);
    }*/
    ///The above is commented out due to debugging
    dirLights[0].bindLight(shader, 0);
    pointLights[0].bindLight(shader, 0);
    pointLights[1].bindLight(shader, 1);
    spotLights[0].bindLight(shader, 0);
}
DirectionalLight::DirectionalLight(){}

DirectionalLight::~DirectionalLight(){}

void DirectionalLight::bindLight(GLuint shader, int i){
    glUniform3f(glGetUniformLocation(shader, "dirLight.direction"), direction.x, direction.y, direction.z);
    glUniform3f(glGetUniformLocation(shader, "dirLight.ambient"), ambientColor.x, ambientColor.y, ambientColor.z);
    glUniform3f(glGetUniformLocation(shader, "dirLight.diffuse"), diffuseColor.x, diffuseColor.y, diffuseColor.z);
    glUniform3f(glGetUniformLocation(shader, "dirLight.specular"), specularColor.x, specularColor.y, specularColor.z);
}

void DirectionalLight::initialize(glm::vec3 dir, glm::vec3 ambient, glm::vec3 diffuse, glm::vec3 spec){
    direction = dir;
    ambientColor = ambient;
    diffuseColor = diffuse;
    specularColor = spec;
}

PointLight::PointLight(){}
PointLight::~PointLight(){}
void PointLight::bindLight(GLuint shader, int i){
    glUniform3f(glGetUniformLocation(shader, ("pointLight[" + std::to_string(i) + "].position").c_str()), direction.x, direction.y, direction.z);
     glUniform3f(glGetUniformLocation(shader, ("pointLight[" + std::to_string(i) + "].ambient").c_str()), ambientColor.x, ambientColor.y, ambientColor.z);
    glUniform3f(glGetUniformLocation(shader, ("pointLight[" + std::to_string(i) + "].diffuse").c_str()), diffuseColor.x, diffuseColor.y, diffuseColor.z);
    glUniform3f(glGetUniformLocation(shader, ("pointLight[" + std::to_string(i) + "].specular").c_str()), specularColor.x, specularColor.y, specularColor.z);
     glUniform1f(glGetUniformLocation(shader, ("pointLight[" + std::to_string(i) + "].constantAttenuation").c_str()), constantAttenuation);
     glUniform1f(glGetUniformLocation(shader, ("pointLight[" + std::to_string(i) + "].linearAttenuation").c_str()), linearAttenuation);
     glUniform1f(glGetUniformLocation(shader, ("pointLight[" + std::to_string(i) + "].quadraticAttenuation").c_str()), quadraticAttenuation);
}

 void PointLight::initialize(glm::vec3 pos, glm::vec3 ambient, glm::vec3 diffuse, glm::vec3 spec, float constAtt, float linAtt, float quadAtt){
    direction = pos;
    ambientColor = ambient;
    diffuseColor = diffuse;
    specularColor = spec;
    constantAttenuation = constAtt;
    linearAttenuation = linAtt;
    quadraticAttenuation = quadAtt;
}
SpotLight::SpotLight(){}

SpotLight::~SpotLight(){}


void SpotLight::bindLight(GLuint shader, int i){
    glUniform3f(glGetUniformLocation(shader, ("spotLight[" + std::to_string(i) + "].position").c_str()), pos.x, pos.y, pos.z);
    glUniform3f(glGetUniformLocation(shader, ("spotLight[" + std::to_string(i) + "].direction").c_str()), direction.x, direction.y, direction.z);
    glUniform3f(glGetUniformLocation(shader, ("spotLight[" + std::to_string(i) + "].ambient").c_str()), ambientColor.x, ambientColor.y, ambientColor.z);
    glUniform3f(glGetUniformLocation(shader, ("spotLight[" + std::to_string(i) + "].diffuse").c_str()), diffuseColor.x, diffuseColor.y, diffuseColor.z);
    glUniform3f(glGetUniformLocation(shader, ("spotLight[" + std::to_string(i) + "].specular").c_str()), specularColor.x, specularColor.y, specularColor.z);
    glUniform1f(glGetUniformLocation(shader, ("spotLight[" + std::to_string(i) + "].cutOff").c_str()), cutOff);
    glUniform1f(glGetUniformLocation(shader, ("spotLight[" + std::to_string(i) + "].outerCutOff").c_str()), outerCutOff);
    glUniform1f(glGetUniformLocation(shader, ("spotLight[" + std::to_string(i) + "].constantAttenuation").c_str()), constantAttenuation);
    glUniform1f(glGetUniformLocation(shader, ("spotLight[" + std::to_string(i) + "].linearAttenuation").c_str()), linearAttenuation);
    glUniform1f(glGetUniformLocation(shader, ("spotLight[" + std::to_string(i) + "].quadraticAttenuation").c_str()), quadraticAttenuation);

}

void SpotLight::initialize(glm::vec3 pos, glm::vec3 direction, glm::vec3 ambient, glm::vec3 diffuse, glm::vec3 spec, float cutOff, float outerCutOff, float constAtt, float linAtt, float quadAtt){
    this->pos = pos;
    this->direction = direction;
    this->ambientColor = ambient;
    this->diffuseColor = diffuse;
    this->specularColor = spec;
    this->cutOff = cutOff;
    this->outerCutOff = outerCutOff;

    constantAttenuation = constAtt;
    linearAttenuation = linAtt;
    quadraticAttenuation = quadAtt;
}

When I just call the bindLight() function from main.cpp directly it looks like this: (This works)

dirLight.bindLight(unlit.getShaderProgram(), 0);
pointLight1.bindLight(unlit.getShaderProgram(), 0);
spotLight1.bindLight(unlit.getShaderProgram(), 0);
pointLight2.bindLight(unlit.getShaderProgram(), 1);

Otherwise I just call: (This does not work)

lights.bindLights(unlit.getShaderProgram());

(Where lights is an object of the class Lights)

Fragmentshader: (Shouldn't be any problems here considering it works when I pass the uniforms without the Lights class)

#version 130

struct Material{
    sampler2D diffuse;
    sampler2D specular;
    float shininess;
};

struct DirectionalLight{
    vec3 direction;
    vec3 diffuse;
    vec3 ambient;
    vec3 specular;
};

struct PointLight{
    vec3 position;
    vec3 diffuse;
    vec3 ambient;
    vec3 specular;

    float constantAttenuation;
    float linearAttenuation;
    float quadraticAttenuation;
};

struct SpotLight{
    vec3 position;
    vec3 direction;
    vec3 ambient;
    vec3 diffuse;
    vec3 specular;
    float cutOff;
    float outerCutOff;

    float constantAttenuation;
    float linearAttenuation;
    float quadraticAttenuation;
};

out vec4 outColor;

in vec3 fragPos;
in vec3 normal;
in vec2 texcoord;
in vec3 viewPos;

uniform vec3 lightPos;
uniform vec3 lightColor;

uniform Material material;
uniform DirectionalLight dirLight;
#define NR_POINT_LIGHTS 2
uniform PointLight pointLight[NR_POINT_LIGHTS];
#define NR_SPOT_LIGHTS 1
uniform SpotLight spotLight[NR_SPOT_LIGHTS];

vec3 calcDirectionalLight(vec3 Normal, vec3 viewDir);
vec3 calcPointLight(PointLight Light, vec3 Normal, vec3 viewDir);
vec3 calcSpotLight(SpotLight Light, vec3 Normal, vec3 viewDir);

void main(){
    vec3 viewDir = normalize(viewPos - fragPos);
    vec3 Normal = normalize(normal);

    vec3 result = calcDirectionalLight(Normal, viewDir);
    for(int i = 0; i < NR_POINT_LIGHTS; i++){
        result += calcPointLight(pointLight[i], Normal, viewDir);
    }
    for(int i = 0; i < NR_SPOT_LIGHTS; i++){
        result += calcSpotLight(spotLight[i], Normal, viewDir);
    }
    outColor = vec4(result, 1.0f);
}

vec3 calcDirectionalLight(vec3 Normal, vec3 viewDir){
    vec3 ambient = dirLight.ambient * vec3(texture(material.diffuse, texcoord));

    //Diffuse

    vec3 lightDir = normalize(-dirLight.direction);
    float diff = max(dot(Normal, lightDir), 0.0f);
    vec3 diffuse = dirLight.diffuse * diff * vec3(texture(material.diffuse, texcoord));
    //texture(tex, texcoord)

    //Specular
    vec3 reflectDir = reflect(-lightDir, Normal);
    float spec = pow(max(dot(viewDir, reflectDir), 0.0), material.shininess);
    vec3 specular = dirLight.specular * spec * vec3(texture(material.specular, texcoord));

    vec3 result = (ambient + diffuse + specular);
    return result;
}

vec3 calcPointLight(PointLight Light, vec3 Normal, vec3 viewDir){
    vec3 lightDir = normalize(Light.position - fragPos);
    //Diffuse
    float diff = max(dot(Normal, lightDir), 0.0f);
    //Specular
    vec3 reflectDir = reflect(-lightDir, Normal);
    float spec = pow(max(dot(viewDir, reflectDir), 0.0f), material.shininess);
    //Attenuation
    float distance = length(Light.position - fragPos);
    float attenuation = 1.0f / (Light.constantAttenuation + Light.linearAttenuation * distance + Light.quadraticAttenuation * (distance * distance));

    //Colors
    vec3 ambient = Light.ambient * vec3(texture(material.diffuse, texcoord));
    vec3 diffuse = Light.diffuse * diff * vec3(texture(material.diffuse, texcoord));
    vec3 specular = Light.specular * spec * vec3(texture(material.specular, texcoord));

    ambient *= attenuation;
    diffuse *= attenuation;
    specular *= attenuation;
    return (ambient + diffuse + specular);
}

vec3 calcSpotLight(SpotLight Light, vec3 Normal, vec3 viewDir){
    vec3 lightDir = normalize(Light.position - fragPos);


   // if(theta > Light.cutOff){
        float diff = max(dot(Normal, lightDir), 0.0f);
        vec3 diffuse = Light.diffuse * diff * vec3(texture(material.diffuse, texcoord));

        vec3 reflectDir = reflect(-lightDir, Normal);
        float spec = pow(max(dot(viewDir, reflectDir), 0.0f), material.shininess);
        float distance = length(Light.position - fragPos);
        vec3 specular = Light.specular * spec * vec3(texture(material.specular, texcoord));
        float attenuation = 1.0f / (Light.constantAttenuation + Light.linearAttenuation * distance + Light.quadraticAttenuation * (distance * distance));

        float theta = dot(lightDir, normalize(-Light.direction));
        float epsilon = Light.cutOff - Light.outerCutOff;
        float intensity = clamp((theta - Light.outerCutOff) / epsilon, 0.0f, 1.0f);

        diffuse *= intensity;
        specular *= intensity;

        diffuse *= attenuation;
        specular *= attenuation;

        return (diffuse + specular);

}

I'm using OpenGL 3.0. Should you want any more code samples, just thrown in a comment on this post and I'll edit it with the code.


Solution

  • It had to do with the fact that as PointLight and SpotLight were derived from DirectionalLight, they (the PointLight and SpotLight objects) would get casted to their base class, removing their "special attributes" and when I would do light.bindLight(shader); it would call the directionalLight's bindLight function. I solved it by adding creating three different addLight functions taking DirectionalLight, PointLight and SpotLight as parameters respectivly. The Lights code now looks like this.

    Lights.h

    class Lights{
        public:
            Lights();
            ~Lights();
            void addLight(DirectionalLight lightToAdd);
            void addLight(SpotLight lightToAdd);
            void addLight(PointLight lightToAdd);
            void bindLights(GLuint shader);
    
        private:
            std::vector<DirectionalLight> dirLights;
            std::vector<PointLight> pointLights;
            std::vector<SpotLight> spotLights;
    
    };
    

    Lights.cpp

    Lights::Lights();
    Lights::~Lights();
    Lights::addLight(DirectionalLight lightToAdd){
        dirLights.push_back(lightToAdd);
    }
    Lights::addLight(SpotLight lightToAdd){
        spotLights.push_back(lightToAdd);
    }
    Lights::addLight(PointLight lightToAdd){
        pointLights.push_back(lightToAdd);
    }
    
    Lights.bindLights(GLuint shader){
        for(int i = 0; i < dirLights.size(); i++){
            dirLights[i].bindLight(shader, i);
        }
        for(int i = 0; i < pointLights.size(); i++){
            pointLights[i].bindLight(shader, i);
        }
        for(int i = 0; i < spotLights.size(); i++){
            spotLights[i].bindLight(shader, i);
        }
    
    }
    

    Than you for your time!