Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

ThreeJS material with shadows but no lights

Tags:

three.js

I want a material with:

  • Textures
  • Not receiving lights
  • Receiving shadows

I tried with the following library materials:

  • MeshBasicMaterial: Does not support shadows
  • MeshLamberMaterial: If you disable lights (material.lights = false) it also disables shadows
  • ShadowMaterial: Does not support textures

Is a custom ShaderMaterial the only way to achieve it?

like image 761
Pablo Avatar asked Nov 18 '17 14:11

Pablo


2 Answers

In three.js, as in real life, shadows are the absence of light. So for a built-in three.js material to receive shadows, it must respond to light.

However, you can modify a built-in material's shader to achieve the effect you want with just a few lines of code. Here is an example to get you started:

THREE.ShaderLib[ 'lambert' ].fragmentShader = THREE.ShaderLib[ 'lambert' ].fragmentShader.replace(

    `vec3 outgoingLight = reflectedLight.directDiffuse + reflectedLight.indirectDiffuse + totalEmissiveRadiance;`,

    `#ifndef CUSTOM
        vec3 outgoingLight = reflectedLight.directDiffuse + reflectedLight.indirectDiffuse + totalEmissiveRadiance;
    #else
        vec3 outgoingLight = diffuseColor.rgb * ( 1.0 - 0.5 * ( 1.0 - getShadowMask() ) ); // shadow intensity hardwired to 0.5 here
    #endif`

);

Then, to use it:

var material = new THREE.MeshLambertMaterial( { map: texture } );
material.defines = material.defines || {};
material.defines.CUSTOM = "";

In spite of its name, this material will behave like MeshBasicMaterial, but will darken when it is in shadow. And furthermore, MeshLambertMaterial will still work as expected.

three.js r.88

like image 86
WestLangley Avatar answered Nov 09 '22 02:11

WestLangley


In a past version, maybe .72, you could cast and receive shadows with the MeshBasicMaterial. It was simple. Then the concept of ambient light changed in three.js and MeshBasicMaterial could no longer support shadows.

THREE.ShadowMaterial was introduced to compensate for the limitation. It works great! But it really only works on PlaneGeometry because by it's nature, THREE.ShadowMaterial is transparent, so the shadows cast inside and outside the object3d with ShadowMaterial are seen.

The idea is that you use two meshes, one with the MeshBasicMaterial, and the other with ShadowMaterial.

shape = new THREE.BoxGeometry(1,1,1),

basicMaterial = new THREE.MeshBasicMaterial({
  color: 0xff0000
}),
mesh = new THREE.Mesh(shape, basicMaterial),

shadowMaterial = new THREE.ShadowMaterial({opacity:.2}),
mesh2 = new THREE.Mesh(shape, shadowMaterial),

You can see an example of the problem, here: https://jsfiddle.net/7d47oLkh/

The shadows cast at the bottom of the box are incorrect for the use-case.

The answer is, NO. There is no easy way to support full-bright basic materials that also accept and cast a shadow in three.js.

like image 20
Radio Avatar answered Nov 09 '22 04:11

Radio