How to manage shadow target of THREE.DirectionalLight - three.js

I use three.js rev.76
I don't know how to manage shadow target of THREE.DirectionalLight.
We can set position of DirectionalLight like:
dirLight.position.set(200, 1000, 200 );
But DirectionalLight will look at (0.0.0), and shadow camera too. How i can make it to look at (100.200.300)?

Related

From Blender to THREEJS, export with GLTF a mesh with UV texture & material color: material color lost

In my game, i use mesh (skeleton, animations and materials without texture) made with Blender (2.79b) ,then exported in glb format (gltf binary v2). In THREEJS scene, i modify dynamicaly the color of some materials by changing the color. I use the r109 version for THREEJS. The gltf exporter version is 1.2.0
I wanted to improve my mesh by adding an UV texture to my materials, like adding a decals.
Unfortunately, in the THREEJS scene, if the texture rendering is ok, the color of the material is black.
To better explain my problem, i used the default cube of Blender. I created:
UV map
1 material per face with a different color + 1 UV texture
To build my UV texture:
"Smart UV project"
UVs -> Export UV Layout (cube.png)
(change Fill opacity to 0)
Modified cube.png with Gimp.
Image -> Open image (cube.png)
I let the default options of Blender.
Then i exported the mesh as usual with the GLTF exporter and rendered into THREEJS scene.
var ambientLight =new THREE.HemisphereLight( 0xffffbb, 0x080820, 1 );
ambientLight.position.set( 0,5, 0 );
scene.add( ambientLight );
var otherCube=new THREE.Mesh( new THREE.CubeGeometry(1,1,1),new THREE.MeshBasicMaterial({color: "blue"}) )
otherCube.position.x=-2
scene.add( otherCube);
var GLTF_loader=new THREE.GLTFLoader();
GLTF_loader.load( '/cube.glb', function(geometry) {
var mesh=geometry.scene.getObjectByName( "Cube" );
scene.add(mesh);
});
The texture rendering seems ok but the color of each face is lost. I built a blue cube to show it is not a problem of light into the scene.
It looks like an alpha problem, i tried to activate certain options in Blender before export but that didn't change anything. In addition, the rendering in Blender is good.
What would be the options to use in Blender or/and THREEJS so that i get the color of my faces? (and change the color later)

Rendering shadows from plane with partially transparent texture in three.js

I'm trying to render shadows using the latest version of three.js (r102), and I'm not sure what I am doing wrong. I am using MeshPhongMaterial with castShadow and receiveShadow set on all relevant meshes, and a directional light facing towards the scene content. Could someone take a look at this and help me figure out how to get these shadows working? Thanks!
Live demo (toggle shadows in the menu):
https://argonjs.github.io/three-web-layer/
Source:
https://github.com/argonjs/three-web-layer
If you add a small cube in front of your WebLayer3D, it correctly casts shadows on rendered DOM layers:
//in app.ts just after light with shadow camera:
let geometryBox = new THREE.BoxBufferGeometry( 0.01, 0.01, 0.01 )
let materialRed = new THREE.MeshPhongMaterial( {color: 0xff0000} )
let cubeSmall = new THREE.Mesh( geometryBox, materialRed )
cubeSmall.position.set( 0.1, -0.03, 0.1 )
cubeSmall.castShadow = true
cubeSmall.receiveShadow = true
scene.add( cubeSmall )
So, only the planes produced by the WebLayer3D do not cast shadows, the setup for the scene / camera / light is correct.
Update: the explanation below is not the reason, see the solution with material.shadowSide in another answer.
If you look at the tree of objects in three.js realm (i.e. traversing through children[]), starting with todoLayer - a lot of them will have castShadow at "false". You will have to re-think your strategy here. Also note, castShadow=false on parent Object3D turns it off for the children.
I figured it out after the hint from Alex (thanks Alex!).
Basically, as strange as it seems, a plane in three.js will not cast shadows unless it is double-sided (Update: Or unless material.shadowSide is set to THREE.FrontSide). Once I set THREE.DoubleSide on the plane material, it worked as expected. Basically, for a textured plane to cast shadows, the following is needed (as of three.js r102):
var mesh = new THREE.Mesh(
new THREE.PlaneGeometry(1,1,2,2),
new THREE.MeshPhongMaterial({
map: texture,
side: THREE.DoubleSide, // important!
alphaTest: 0.1,
})
mesh.customDepthMaterial = new THREE.MeshDepthMaterial({
map: texture
depthPacking: THREE.RGBADepthPacking,
alphaTest: 0.1
})
I also had to adjust the light's shadow bias in order to eliminate artifacts.

ThreeJS material with shadows but no lights

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?
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
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.

Unable to get a DirectionalLight shadow to update correctly

I have a DirectionalLight in my scene. When I first create the light the shadow covers the intended area. When the viewport is resized (canvas size) I update the shadowCamera parameters (right,left,top,bottom), and the light's position and target, but the shadow only covers the area before the update.
What flag do I have to set to get the shadow properly updated to reflect the new settings?
Here is the pattern you need to follow to update a shadow camera on-the-fly:
light.shadowCameraLeft = -100;
light.shadowCameraRight = 100;
light.shadowCamera.left = light.shadowCameraLeft;
light.shadowCamera.right = light.shadowCameraRight;
...
light.shadowCamera.updateProjectionMatrix();
three.js r.64

How do you inherit the world location of a mesh?

I have a Three.js Scene with several meshes moving around. I want to take a snap shot and copy all of the mesh locations by placing a new instance of a mesh (with the same geometry) in the scene in the same location & with the same rotation. I can't simply copy the mesh's .position & .rotation because the mesh is a child to other meshes. I tried to .clone() the mesh's matrixWorld but that didn't work. How do you inherit the world location of a mesh?
This is what I am trying to do:
// mesh is an existing mesh loaded into a scene
// geom is an existing geometry definition
var material = new THREE.MeshFaceMaterial();
var newMesh = new THREE.Mesh( geom, material);
newMesh.matrixWorld = mesh.matrixWorld.clone();
scene.add(newMesh);
Any help would be much appreciated.
It's as simple as this:
newMesh.position.copy( mesh.matrixWorld.getPosition() );
EDIT: See this updated answer, instead.

Resources