0

I was following the skeletal animation tutorial on learnopengl.com and it works fine, animates properly. However, lighting does not work when the 3D model is moving (appears black), but works when it is static. I assume the issue is with the normals. How do you calculate normals for an animated model?

Vertex shader- the vec3 localnormal does nothing, but I assume its used for something?

#version 330 core

layout(location = 0) in vec3 pos;
layout(location = 1) in vec3 norm;
layout(location = 2) in vec2 tex;
layout(location = 5) in ivec4 boneIds;
layout(location = 6) in vec4 weights;
    
uniform mat4 projection;
uniform mat4 view;
uniform mat4 model;
    
const int MAX_BONES = 100;
const int MAX_BONE_INFLUENCE = 4;
uniform mat4 finalBonesMatrices[MAX_BONES];
    
out vec2 TexCoords;
out vec3 FragPos;
out vec3 Normal;
    
void main()
{
    vec4 totalPosition = vec4(0.0f);
    for(int i = 0 ; i < MAX_BONE_INFLUENCE ; i++)
    {
        if(boneIds[i] == -1)
            continue;
        if(boneIds[i] >=MAX_BONES)
        {
            totalPosition = vec4(pos,1.0f);
            break;
        }
        vec4 localPosition = (finalBonesMatrices[boneIds[i]] * weights[i]) * vec4(pos,1.0f);
        totalPosition += localPosition;
        vec3 localNormal =mat3(transpose(inverse(finalBonesMatrices[boneIds[i]]))) * norm;
    }
    mat4 viewModel = view * model;
    gl_Position =  projection * viewModel * totalPosition;
    TexCoords = tex;
    FragPos = pos;
    Normal = norm;
}

Fragment Shader

#version 330 core
out vec4 FragColor;

struct Material {
    sampler2D texture_diffuse1;
    sampler2D texture_specular1;
    float shininess;
};

struct DirLight {
    vec3 direction;
    
    vec3 ambient;
    vec3 diffuse;
    vec3 specular;
    float intensityVal;
};

struct PointLight {
    vec3 position;
    
    float constant;
    float linear;
    float quadratic;
    
    vec3 ambient;
    vec3 diffuse;
    vec3 specular;
    float intensityVal;
};

struct SpotLight {
    vec3 position;
    vec3 direction;
    float cutOff;
    float outerCutOff;
  
    float constant;
    float linear;
    float quadratic;
  
    vec3 ambient;
    vec3 diffuse;
    vec3 specular;
    float intensityVal;
};

#define MAX_POINT_LIGHTS 1
#define MAX_SPOT_LIGHTS 1
#define MAX_DIR_LIGHTS 1

in vec3 FragPos;
in vec3 Normal;
in vec2 TexCoords;

uniform vec3 viewPos;
uniform DirLight dirLight[MAX_DIR_LIGHTS];
uniform PointLight pointLights[MAX_POINT_LIGHTS];
uniform SpotLight spotLight[MAX_SPOT_LIGHTS];
uniform Material material;

// function prototypes
vec3 CalcDirLight(DirLight light, vec3 normal, vec3 viewDir);
vec3 CalcPointLight(PointLight light, vec3 normal, vec3 fragPos, vec3 viewDir);
vec3 CalcSpotLight(SpotLight light, vec3 normal, vec3 fragPos, vec3 viewDir);

void main()
{
    // properties
    vec3 norm = normalize(Normal);
    vec3 viewDir = normalize(viewPos - FragPos);
    vec3 result=vec3(0.0);
    


    // directional lighting
    for(int i = 0; i < MAX_DIR_LIGHTS; i++)
        result += CalcDirLight(dirLight[i], norm, viewDir);
    //point lights
    for(int i = 0; i < MAX_POINT_LIGHTS; i++)
        result += CalcPointLight(pointLights[i], norm, FragPos, viewDir);
    // spot light
    for(int i = 0; i < MAX_SPOT_LIGHTS; i++)
        result += CalcSpotLight(spotLight[i], norm, FragPos, viewDir);
    
    FragColor = vec4(result, 1.0);
}

// calculates the color when using a directional light.
vec3 CalcDirLight(DirLight light, vec3 normal, vec3 viewDir)
{
    vec3 lightDir = normalize(-light.direction);
    // diffuse shading
    float diff = max(dot(normal, lightDir), 0.0);
    // specular shading
    vec3 reflectDir = reflect(-lightDir, normal);
    float spec = pow(max(dot(viewDir, reflectDir), 0.0), material.shininess);
    // combine results
    vec3 ambient = light.ambient * vec3(texture(material.texture_diffuse1, TexCoords));
    vec3 diffuse = light.diffuse * diff * vec3(texture(material.texture_diffuse1, TexCoords))*light.intensityVal;
    vec3 specular = light.specular * spec * vec3(texture(material.texture_specular1, TexCoords))*light.intensityVal;
    return (ambient + diffuse + specular);
}

// calculates the color when using a point light.
vec3 CalcPointLight(PointLight light, vec3 normal, vec3 fragPos, vec3 viewDir)
{
    vec3 lightDir = normalize(light.position - fragPos);
    // diffuse shading
    float diff = max(dot(normal, lightDir), 0.0);
    // specular shading
    vec3 reflectDir = reflect(-lightDir, normal);
    float spec = pow(max(dot(viewDir, reflectDir), 0.0), material.shininess);
    // attenuation
    float distance = length(light.position - fragPos);
    float attenuation = 1.0 / (light.constant + light.linear * distance + light.quadratic * (distance * distance));
    // combine results
    vec3 ambient = light.ambient * vec3(texture(material.texture_diffuse1, TexCoords));
    vec3 diffuse = light.diffuse * diff * vec3(texture(material.texture_diffuse1, TexCoords))*light.intensityVal;
    vec3 specular = light.specular * spec * vec3(texture(material.texture_specular1, TexCoords))*light.intensityVal;
    ambient *= attenuation;
    diffuse *= attenuation;
    specular *= attenuation;
    return (ambient + diffuse + specular);
}

// calculates the color when using a spot light.
vec3 CalcSpotLight(SpotLight light, vec3 normal, vec3 fragPos, vec3 viewDir)
{
    vec3 lightDir = normalize(light.position - fragPos);
    // diffuse shading
    float diff = max(dot(normal, lightDir), 0.0);
    // specular shading
    vec3 reflectDir = reflect(-lightDir, normal);
    float spec = pow(max(dot(viewDir, reflectDir), 0.0), material.shininess);
    // attenuation
    float distance = length(light.position - fragPos);
    float attenuation = 1.0 / (light.constant + light.linear * distance + light.quadratic * (distance * distance));
    // spotlight intensity
    float theta = dot(lightDir, normalize(-light.direction));
    float epsilon = light.cutOff - light.outerCutOff;
    float intensity = clamp((theta - light.outerCutOff) / epsilon, 0.0, 1.0);
    // combine results
    vec3 ambient = light.ambient * vec3(texture(material.texture_diffuse1, TexCoords));
    vec3 diffuse = light.diffuse * diff * vec3(texture(material.texture_diffuse1, TexCoords))*light.intensityVal;
    vec3 specular = light.specular * spec * vec3(texture(material.texture_specular1, TexCoords))*light.intensityVal;
    ambient *= attenuation * intensity;
    diffuse *= attenuation * intensity;
    specular *= attenuation * intensity;
    return (ambient + diffuse + specular);
}

How do I pass the correct normals to the fragment shader?

Juroza
  • 17
  • 5

1 Answers1

1

How do you calculate normals for an animated model?

Just like with position, you need to calculate the weighted average of the transformed normals.

Vertex shader- the vec3 localnormal does nothing, but I assume its used for something?

That's right. This code looks unfinished. localNormal is the input normal transformed by the ith bone transformation. What's left is to average those and pass that to the fragment shader:

vec4 totalPosition = vec4(0.0f);
vec3 totalNormal = vec3(0.0f);
for(int i = 0 ; i < MAX_BONE_INFLUENCE ; i++)
{
    ...
    vec3 localNormal =mat3(transpose(inverse(finalBonesMatrices[boneIds[i]]))) * norm;
    totalNormal += localNormal*weights[i];
}
...
Normal = totalNormal;
Yakov Galka
  • 70,775
  • 16
  • 139
  • 220
  • I have added the total normal but it seems that it is still not working. Is there an issue with my FragPos? I have tried FragPos = vec3(viewModel * totalPosition) but didn't work – Juroza Sep 30 '22 at 08:50
  • @Juroza I think that your fragment shader expects FragPos to be in world space rather than view space; to match that you need `FragPos = totalPosition` without applying `viewModel`. – Yakov Galka Sep 30 '22 at 09:16