Search code examples
three.jslightingshadermaterial

Three.js ShaderMaterial lighting not working


I'm experimenting with Three.js ShaderMaterial and trying to implement lighting. I have working code for r70 but the same code (with minor changes - the MAX_POINT_LIGHTS constant has been renamed NUM_POINT_LIGHTS) doesn't work for r76.

Looking at a trace in WebGL Inspector it's clear that no light data is being sent to the shader. So, has lighting broken or do I need to set up something else to get it to work?

Using r70 (working)

http://codepen.io/anon/pen/KzjXNr?editors=1010

Fragment Shader

uniform vec3 diffuse;
varying vec3 vPos;
varying vec3 vNormal;
uniform vec3 pointLightColor[MAX_POINT_LIGHTS];
uniform vec3 pointLightPosition[MAX_POINT_LIGHTS];
uniform float pointLightDistance[MAX_POINT_LIGHTS];

void main() {
  vec4 addedLights = vec4(0.1, 0.1, 0.1, 1.0);
  for(int l = 0; l < MAX_POINT_LIGHTS; l++) {
    vec3 lightDirection = normalize(vPos - pointLightPosition[l]);
    addedLights.rgb += clamp(dot(-lightDirection, vNormal), 0.0, 1.0) * pointLightColor[l];
  }
  gl_FragColor = addedLights;
}

JavaScript - setting up the Shadermaterial with UniformsUtils and UniformsLib

var uniforms = THREE.UniformsUtils.merge([
    THREE.UniformsLib['lights'],
    { diffuse: { type: 'c', value: new THREE.Color(0xff00ff) } }
]);
var vertexShader = document.getElementById('vertexShader').text;
var fragmentShader = document.getElementById('fragmentShader').text;
material = new THREE.ShaderMaterial({
      uniforms: uniforms,
      vertexShader: vertexShader,
      fragmentShader: fragmentShader,
      lights: true
    });

var geometry = new THREE.BoxGeometry(200, 200, 200);
mesh = new THREE.Mesh(geometry, material);
scene.add(mesh);

Using r76 (futzed)

http://codepen.io/anon/pen/ZWdXLZ?editors=1010

Fragment Shader

uniform vec3 diffuse;
varying vec3 vPos;
varying vec3 vNormal;
uniform vec3 pointLightColor[NUM_POINT_LIGHTS];
uniform vec3 pointLightPosition[NUM_POINT_LIGHTS];
uniform float pointLightDistance[NUM_POINT_LIGHTS];

void main() {
  vec4 addedLights = vec4(0.1, 0.1, 0.1, 1.0);
  for(int l = 0; l < NUM_POINT_LIGHTS; l++) {
    vec3 lightDirection = normalize(vPos - pointLightPosition[l]);
    addedLights.rgb += clamp(dot(-lightDirection, vNormal), 0.0, 1.0) * pointLightColor[l];
  }
  gl_FragColor = addedLights;
}

JavaScript

Unchanged


Solution

  • The light uniforms have been changed to structs in r74 (Specifically in #7324). Note that though the change to structs happened in r74, the following works r75 and later.

    A single array of structs is given for each light type. Each struct has the position and color properties you need.

    Fragment Shader

    uniform vec3 diffuse;
    varying vec3 vPos;
    varying vec3 vNormal;
    
    struct PointLight {
      vec3 position;
      vec3 color;
    };
    uniform PointLight pointLights[ NUM_POINT_LIGHTS ];
    
    void main() {
      vec4 addedLights = vec4(0.1, 0.1, 0.1, 1.0);
      for(int l = 0; l < NUM_POINT_LIGHTS; l++) {
        vec3 adjustedLight = pointLights[l].position + cameraPosition;
        vec3 lightDirection = normalize(vPos - adjustedLight);
        addedLights.rgb += clamp(dot(-lightDirection, vNormal), 0.0, 1.0) * pointLights[l].color;
      }
      gl_FragColor = addedLights;//mix(vec4(diffuse.x, diffuse.y, diffuse.z, 1.0), addedLights, addedLights);
    }
    

    Note that the light position is now relative to the camera so you offset the light position with the camera position.

    Working Fiddle