Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

ThreeJS: How do I make a custom shader be lit by the scene's lights?

Tags:

three.js

I have made a simple custom shader using THREE.ShaderMaterial that only changes a plane's shape with a heightmap.

Does anyone know the proper way of using the scene's directional light source with a very simple custom shader?

EDIT

Vertex shader

uniform sampler2D heightTexture;
varying float displacement;
varying vec2 vUv;
varying vec3 vPosition;
varying vec3 vNormal;

void main() {
    vUv = uv;
    vec4 data = texture2D(heightTexture, uv);
    displacement = data.b;
    vec3 height = position + normal * displacement;
    vPosition = (modelMatrix * vec4(position, 1.0)).xyz;
    vNormal = (modelMatrix * vec4(normal, 0.0)).xyz;
    gl_Position = projectionMatrix * modelViewMatrix * vec4(height, 1.0);
}

Fragment shader

uniform sampler2D texture;
uniform vec3 pointLightColor[MAX_POINT_LIGHTS];
uniform vec3 pointLightPosition[MAX_POINT_LIGHTS];
uniform float pointLightDistance[MAX_POINT_LIGHTS];
varying vec2 vUv;
varying vec3 vPosition;
varying vec3 vNormal;

void main() {                  

    vec4 texture = texture2D(texture, vUv);
    vec4 lights = vec4(0.0, 0.0, 0.0, 1.0);
    for(int i = 0; i < MAX_POINT_LIGHTS; i++) {
        vec3 lightVector = normalize(vPosition - pointLightPosition[i]);
        lights.rgb += clamp(dot(-lightVector, vNormal), 0.0, 1.0) * pointLightColor[i];
    }
    gl_FragColor = texture * lights;
}

I tried with point lights because i couldn't use directional at all, even though I need to use directional. In the fragment shader, if i write gl_FragColor = lights; the light is visible, but if i try something like gl_FragColor = texture * lights; the plane becomes black. If I use a color instead of the texture, it also works but I need to use textures.

The second problem is that while lights are set as true and the uniforms are merged with THREE.UniformsLib['lights'], the heightmap doesn't affect the plane at all.

like image 383
Alex Avatar asked Sep 15 '25 00:09

Alex


1 Answers

Include the needed light uniforms by merging `THREE.UniformsLib['lights'] with your uniforms. eg.

var uniforms = THREE.UniformsUtils.merge(
        [THREE.UniformsLib['lights'],
        {
          diffuse: {type: 'c', value: new THREE.Color(0x0000ff)}
        }
        ]
    )

Set the ShaderMaterial light parameter to true. eg.

material = new THREE.ShaderMaterial(
        {
          uniforms : uniforms,
          vertexShader : vertexShader,
          fragmentShader : fragmentShader,
          lights: true
        });

Now in your shader you will have access to the light uniforms:

uniform vec3 directionalLightColor[ MAX_DIR_LIGHTS ];
uniform vec3 directionalLightDirection[ MAX_DIR_LIGHTS ];

HERE is an example I did with point lights.
Here are some other experiments I have been doing with shaders in three.js.

UPDATE

I have a vertex displacement demo HERE. You can easily view the shader code by clicking "source" in the dat.gui. It uses noise for the displacement, you can ignore it and just use your texture value.

If you are testing in the Chrome browser, it actually gives quite good error messages in the console.

UPDATE
There seems to be a problem merging the light uniforms and using textures, outlined HERE

like image 171
2pha Avatar answered Sep 17 '25 18:09

2pha