Basic custom shader + camera

Hello,

I’m new to GLSL and am trying to implement my own basic set of shaders, starting with Lambert shading. I’ve looked at a bunch of examples but am stuck.

My shader draws a shadow on a sphere based on a single light source. My problem is that the shadow doesn’t update as I move the OrbitControls camera around.

Vertex shader:

  varying vec3 vViewPosition;
  varying vec3 vNormal;

  void main() {
    vViewPosition = (modelViewMatrix * vec4(position, 1.0)).xyz;
    vNormal = normalMatrix * normal;

    vec4 worldPosition = (modelMatrix * vec4(position, 1.));
    gl_Position = projectionMatrix * viewMatrix * vec4(worldPosition.xyz, 1.);
  }

Fragment shader:

  uniform vec3 lightPos;

  varying vec3 vNormal;
  varying vec3 vViewPosition;

  void main() {
    vec3 normal = normalize(vNormal);
    vec3 lightDir = normalize(lightPos - vViewPosition);
    float lambertian = max(dot(normal, lightDir), 0.0);
    gl_FragColor = vec4(vec3(1.0) * lambertian, 1.0);
  }

I think I must be missing something simple!

43

Link to Codepen

Here is your updated code: https://jsfiddle.net/znx6pm0b/

The problem is that you have to transform the position of the sun light into view space (and not world space). For this, you have to update the respective uniform per animation step (assuming the light position is going to change). I’ve refactored the code a bit to make this clear.

BTW: Camera.matrixWorldInverse is nothing else than the view matrix. Multiplying a vector in world space with this matrix transforms it into the (local) coordinate system of the camera.

2 Likes

Thank you! I understand the difference between view space and world space better now.

Per your suggestion, I wound up doing the correction in the shader by creating vViewLightPos = (viewMatrix * vec4(lightPos, 1.0)).xyz; and using that in place of lightPos.

1 Like

The problem with this approach is that your are performing the same computation for all fragments which is actually not necessary. You should do it once per frame in JavaScript and then just reuse the uniform.

2 Likes

Makes sense - thanks for pointing that out.