Lighting for skinning object

Please, help me to solve this problem with lighting:

animation

precision mediump float;

attribute vec3 aPosition;
attribute vec4 aNormal;
attribute vec2 aTexCoord;
attribute vec3 aJoints;
attribute vec3 aWeights;

uniform mat4 uMvpMatrix;
uniform mat4 uModelMatrix;
uniform mat4 uNormalMatrix;
uniform mat4 uTransforms[3];

varying vec3 vPosition;
varying vec3 vNormal;
varying vec2 vTexCoord;

void main()
{
    vec4 totalLocalPos = vec4(0.0);
    vec4 totalNormal = vec4(0.0);

    for (int i = 0; i < 3; i++)
    {
        int jointIndex = int(aJoints[i]);
        mat4 jointTransform = uTransforms[jointIndex];
        vec4 posePosition = jointTransform * vec4(aPosition, 1.0);
        totalLocalPos += posePosition * aWeights[i];

        vec4 worldNormal = jointTransform * aNormal;
        totalNormal += worldNormal * aWeights[i];
    }
    
    gl_Position = uMvpMatrix * totalLocalPos;
    vPosition = vec3(uModelMatrix * vec4(aPosition, 1.0));
    vNormal = totalNormal.xyz;
    vTexCoord = aTexCoord;
}
precision mediump float;

const vec3 lightColor = vec3(0.8, 0.8, 0.8);
const vec3 lightPosition = vec3(5.0, 10.0, 15.0);
const vec3 ambientLight = vec3(0.3, 0.3, 0.3);

uniform sampler2D uSampler;

varying vec3 vPosition;
varying vec3 vNormal;
varying vec2 vTexCoord;

void main()
{
    vec4 color = texture2D(uSampler, vTexCoord);
    vec3 normal = normalize(vNormal);
    vec3 lightDirection = normalize(lightPosition - vPosition);
    float nDotL = max(dot(lightDirection, normal), 0.0);
    vec3 diffuse = lightColor * color.rgb * nDotL;
    vec3 ambient = ambientLight * color.rgb;
    gl_FragColor = vec4(diffuse + ambient, color.a);
}

It is better to see this problem if I set a light position to top:

const vec3 lightPosition = vec3(0.0, 15.0, 0.0);

lighting-on-top

… wouldn’t it be in the uppersit direction, 180 degrees off course?

Aroch helped me on Russian forum here:

the normal only needs to be rotated.

Now it works as it should. I took shaders (except lighting model) from source for a video tutorial series from ThinMatrix. It works fine for ThinMatrix. This means that for some reason its lighting model is not affected by the translation of the normal vector. It will be necessary to figure out later why he does not have this problem. I made the transfer of the array of rotation matrices a separate uniform:

uniform mat4 uTransforms[3];
uniform mat4 uRotations[3];

...

    for (int i = 0; i < 3; i++)
    {
        int jointIndex = int(aJoints[i]);
        mat4 jointTransform = uTransforms[jointIndex];
        vec4 posePosition = jointTransform * vec4(aPosition, 1.0);
        totalLocalPos += posePosition * aWeights[i];

        mat4 rotation = uRotations[jointIndex];
        vec4 worldNormal = rotation * aNormal;
        totalNormal += worldNormal * aWeights[i];
    }

Gif Animation: https://gamedev.ru/files/images/solution-with-lighting.gif

solution-with-lighting

One way to explain this behavior is if aNormal.w == 0 in that example but aNormal.w == 1 (or at least is non-zero) in your code. The former would be a much simpler way to ensure that aNormal is only rotated and not translated when multiplied by jointTransform.

That is, simpler compared what you ended up doing: extracting and passing in separate rotation-only matrices in uniforms.

You could alternatively have just taken the upper-left 3x3 of those transform matrices with a mat3() cast in the shader and multiplied that by aNormal.xyz. Or just forced the w-component to be zero in the shader code before doing the mat4x4 multiply with:

    vec4 worldNormal = jointTransform * vec4( aNormal.xyz, 0 );