0
\$\begingroup\$

I am encountering an issue when using a normal map with directional and/or point lighting. Without using the lighting, I am able to render the object fine. When any amount of lighting is applied, it causes the object to flicker (as shown below).

The lighting also works fine when I am not using a normal map. I believe this is an issue with the shader code. The shader code does allow for more than one light source at a time. However, even if I just use one light source (e.g. one directional light) and the normal mapping, the flickering occurs.

enter image description here

Full Repo: https://github.com/paulburgess1357/ExtraLifeEngine

Vertex Code:

#version 330 core
#define MAXIMUM_DIR_LIGHTS 2
#define MAXIMUM_POINT_LIGHTS 2

struct DirectionalLight {
    vec3 direction;
};

struct PointLight {
    vec3 position;
    
    float constant;
    float linear;
    float quadratic;
};

layout (location = 0) in vec3 model_position;
layout (location = 1) in vec3 model_normals;
layout (location = 2) in vec2 tex_coords;
layout (location = 3) in vec3 in_tangent;
layout (location = 4) in vec3 in_bitangent;

// Global uniforms
layout (std140) uniform uniform_matrices {
    mat4 projection_matrix;
    mat4 view_matrix;
};

layout (std140) uniform uniform_camera_world_position{
    vec3 camera_world_position;
};

uniform int active_dirlight_qty; // Set to -1 during shader initialization
uniform DirectionalLight dirlight[MAXIMUM_DIR_LIGHTS];

uniform int active_pointlight_qty; // Set to -1 during shader initialization
uniform PointLight pointlight[MAXIMUM_POINT_LIGHTS];

// Input 
uniform mat4 model_matrix;
uniform mat3 normal_matrix;

// Output
out PointLight tangent_pointlights[MAXIMUM_POINT_LIGHTS];
out DirectionalLight tangent_dirlights[MAXIMUM_DIR_LIGHTS];

out vec3 tangent_camera_position;
out vec3 tangent_fragment_position;
out vec2 fragment_tex_coords;

// Function Definitions
mat3 calc_tbn_matrix(mat3 normal_matrix, vec3 model_normals, vec3 tangent_vec);
PointLight convert_pointlight_to_tangent_space(PointLight pointlight, mat3 tbn_matrix);
DirectionalLight convert_dirlight_to_tangent_space(DirectionalLight dirlight, mat3 tbn_matrix);

void main(){
   
    // TBN Matrix
    mat3 tbn_matrix = calc_tbn_matrix(normal_matrix, model_normals, in_tangent);

    // Calc fragment_position for gl_Position and tangent_fragment_position
    vec3 fragment_position = vec3(model_matrix * vec4(model_position, 1.0)); // World space 

    // Convert Pointlights to Tangent Space
    for(int i = 0; i < active_pointlight_qty; i++){
        tangent_pointlights[i] = convert_pointlight_to_tangent_space(pointlight[i], tbn_matrix);
    }

    // Convert Directional Lights to Tangent Space
    for(int i = 0; i < active_dirlight_qty; i++){
        tangent_dirlights[i] = convert_dirlight_to_tangent_space(dirlight[i], tbn_matrix);
    }          

    // Output (tangent_pointlights and tangent_dirlights are also output)
    fragment_tex_coords = tex_coords;
    tangent_camera_position = tbn_matrix * camera_world_position;
    tangent_fragment_position = tbn_matrix * fragment_position;
    gl_Position = projection_matrix * view_matrix * vec4(fragment_position, 1.0);
    
}

mat3 calc_tbn_matrix(mat3 normal_matrix, vec3 model_normals, vec3 tangent_vec){

    vec3 t = normalize(normal_matrix * tangent_vec);
    vec3 n = normalize(normal_matrix * model_normals);    
    t = normalize(t - dot(t, n) * n);
    vec3 b = cross(n, t);
    mat3 tbn_matrix = transpose(mat3(t, b, n));

    return tbn_matrix;

};

PointLight convert_pointlight_to_tangent_space(PointLight pointlight, mat3 tbn_matrix){
    pointlight.position = normalize(tbn_matrix * pointlight.position);
    return pointlight;

};

DirectionalLight convert_dirlight_to_tangent_space(DirectionalLight dirlight, mat3 tbn_matrix){
    dirlight.direction = normalize(tbn_matrix * dirlight.direction);
    return dirlight;
};

Fragment Code

#version 330 core
#define MAXIMUM_DIR_LIGHTS 2
#define MAXIMUM_POINT_LIGHTS 2

struct DiffuseMaterial {
    sampler2D m_sampler;
};

struct SpecularMaterial {
    sampler2D m_sampler;
    float m_shininess;
};

struct NormalMaterial {
    sampler2D m_sampler;
};

struct DirectionalLight {
    vec3 direction;
};

struct SceneLight {
    vec3 ambient;
    vec3 diffuse;
    vec3 specular; 
};

struct PointLight {
    vec3 position;
    
    float constant;
    float linear;
    float quadratic;
};

// Shader Uniforms
// uniform int active_diffuse_qty;
uniform DiffuseMaterial diffuse_material;

// uniform int active_specular_qty;
uniform SpecularMaterial specular_material;

// uniform int active_normal_qty
uniform NormalMaterial normal_material;

uniform SceneLight scenelight;

uniform int active_dirlight_qty; // Set to -1 during shader initialization
uniform int active_pointlight_qty; // Set to -1 during shader initialization

in DirectionalLight tangent_dirlights[MAXIMUM_DIR_LIGHTS];
in PointLight tangent_pointlights[MAXIMUM_POINT_LIGHTS];

in vec3 tangent_camera_position;
in vec3 tangent_fragment_position;
in vec2 fragment_tex_coords;

// Output
out vec4 fragment_color;

// Function Definitions
vec3 calc_directional_light(DirectionalLight dirlight, 
                            DiffuseMaterial diffuse_material,
                            SpecularMaterial specular_material,
                            SceneLight scenelight, 
                            vec3 normalized_frag_model_normals, 
                            vec3 view_direction, 
                            vec2 fragment_tex_coords);

vec3 calc_point_light(PointLight pointlight, 
                      DiffuseMaterial diffuse_material,
                      SpecularMaterial specular_material,
                      SceneLight scenelight,                       
                      vec3 normalized_frag_model_normals, 
                      vec3 view_direction, 
                      vec2 fragment_tex_coords,
                      vec3 fragment_position);

// Shader
void main() {
    
    // Normal map to normal vector; Transform to range -1, 1
    vec3 normalized_frag_model_normals = normalize(texture(normal_material.m_sampler, fragment_tex_coords).rgb);
    
    // Convert normal to tangent space
    normalized_frag_model_normals = normalize(normalized_frag_model_normals * 2 - 1); 
    vec3 tangent_view_direction = normalize(tangent_camera_position - tangent_fragment_position);
    
    // Lighting
    vec3 result = vec3(0.0f);
    
    // Directional
    for(int i = 0; i <= active_dirlight_qty; i++) {
        result += calc_directional_light(tangent_dirlights[i], diffuse_material, specular_material, scenelight, normalized_frag_model_normals, tangent_view_direction, fragment_tex_coords);
    }

    // Point (active pointlight qty set to -1 during shader compilation)
    for(int i = 0; i <= active_pointlight_qty; i++){
        result += calc_point_light(tangent_pointlights[i], diffuse_material, specular_material, scenelight, normalized_frag_model_normals, tangent_view_direction, fragment_tex_coords, tangent_fragment_position);
    }

    if(result.x == 0.0f && result.y==0.0f && result.z==0.0f){
       result = scenelight.ambient  * vec3(texture(diffuse_material.m_sampler, fragment_tex_coords)); 
    }
    
    fragment_color = vec4(result, 1.0);
};

// Function Definitions
vec3 calc_directional_light(DirectionalLight dirlight, 
                            DiffuseMaterial diffuse_material,
                            SpecularMaterial specular_material,
                            SceneLight scenelight, 
                            vec3 normalized_frag_model_normals, 
                            vec3 view_direction, 
                            vec2 fragment_tex_coords){
    
    vec3 light_direction = normalize(dirlight.direction);
    vec3 halfway_btwn_view_and_light_dir = normalize(light_direction + view_direction);

    // Ambient
    vec3 ambient = scenelight.ambient * vec3(texture(diffuse_material.m_sampler, fragment_tex_coords));

    // Diffuse
    float diffuse_impact = max(dot(normalized_frag_model_normals, light_direction), 0.0);
    vec3 diffuse = scenelight.diffuse  * diffuse_impact * vec3(texture(diffuse_material.m_sampler, fragment_tex_coords));

    // Specular
    float specular_impact = pow(max(dot(normalized_frag_model_normals, halfway_btwn_view_and_light_dir), 0.0), specular_material.m_shininess);
    vec3 specular = scenelight.specular * specular_impact * vec3(texture(specular_material.m_sampler, fragment_tex_coords));
            
    return (ambient + diffuse + specular);
}  

vec3 calc_point_light(PointLight pointlight, 
                      DiffuseMaterial diffuse_material,
                      SpecularMaterial specular_material,
                      SceneLight scenelight,                       
                      vec3 normalized_frag_model_normals, 
                      vec3 view_direction, 
                      vec2 fragment_tex_coords,
                      vec3 fragment_position){
    
    vec3 light_direction = normalize(pointlight.position - fragment_position);
    vec3 halfway_btwn_view_and_light_dir = normalize(light_direction + view_direction);
    
    // Ambient
    vec3 ambient  = scenelight.ambient * vec3(texture(diffuse_material.m_sampler, fragment_tex_coords));

    // Diffuse
    float diffuse_impact = max(dot(normalized_frag_model_normals, light_direction), 0.0);
    vec3 diffuse = scenelight.diffuse * diffuse_impact * vec3(texture(diffuse_material.m_sampler, fragment_tex_coords));

    // Specular
    float specular_impact = pow(max(dot(normalized_frag_model_normals, halfway_btwn_view_and_light_dir), 0.0), specular_material.m_shininess);
    vec3 specular = scenelight.specular * specular_impact * vec3(texture(specular_material.m_sampler, fragment_tex_coords));
    
    // Attenuation
    float distance_to_light = length(pointlight.position - fragment_position);
    float attenuation = 1.0 / (pointlight.constant + pointlight.linear * distance_to_light + pointlight.quadratic * (distance_to_light * distance_to_light));    
    
    ambient  *= attenuation;
    diffuse  *= attenuation;
    specular *= attenuation;

    return (ambient + diffuse + specular);
} 

\$\endgroup\$

1 Answer 1

0
\$\begingroup\$

This is solved! The light variables in the vertex shader were not being converted to tangent space (< vs <=). The fix is below:

    // Convert Pointlights to Tangent Space
    for(int i = 0; i <= active_pointlight_qty; i++){
        tangent_pointlights[i] = convert_pointlight_to_tangent_space(pointlight[i], tbn_matrix);
    }

    // Convert Directional Lights to Tangent Space
    for(int i = 0; i <= active_dirlight_qty; i++){
        tangent_dirlights[i] = convert_dirlight_to_tangent_space(dirlight[i], tbn_matrix);
    } 
\$\endgroup\$

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .