three.js r75 pointLight shadows in wrong places - three.js

I have a jsfiddle containing a fixed Sun and Moons and a moving planet Earth which orbits the Sun.
Here is the code for the two Lights (Ambient and Point) and example objects.
var light2 = new THREE.AmbientLight(0x444444);//... for lighting the Sun and other MeshBasicMaterial meshes.
scene.add(light2);
//... PointLight
// http://threejs.org/docs/#Reference/Lights/PointLight
var light3 = new THREE.PointLight( 0xffffff, 10, 150000,1);
light3.castShadow = true;
light3.shadow.camera.near = 1;
light3.shadow.camera.far = 5000;
light3.shadow.camera.fov = 90;
// light3.shadowCameraVisible = true;
light3.shadow.bias = 0.001;
scene.add( light3 );
// SPHERES
var sphereGeom = new THREE.SphereGeometry( 40, 32, 16 );
var SunMaterial = new THREE.MeshBasicMaterial( { color: 0xffff00 } );
this.Sun01 = new THREE.Mesh( sphereGeom.clone(), SunMaterial );
Sun01.position.set(-500, 0, 220);
scene.add( Sun01 );
//Sun01.castShadow = false;
//Sun01.receiveShadow = false;
light3.position.set( Sun01.position.x, Sun01.position.y , Sun01.position.z);
var moonMaterial = new THREE.MeshPhongMaterial( { color: 0xaa00aa } );
var Moon02 = new THREE.Mesh( sphereGeom.clone(), moonMaterial );
Moon02.scale.set( 0.5,0.5,0.5 );
Moon02.position.set(-200, 0, 220);
scene.add( Moon02 );
Moon02.castShadow = true;
Moon02.receiveShadow = false;
There are two problems.
Firstly distant fixed moons are not illuminated by the PointLight even though they are within range.
Secondly shadows from the distant moons appear on the (Sun-orbitting) Earth even though the Earth is nearer the Sun than those fixed moons.
Note that an inner fixed moon (named Moon02, magenta in color) does get illuminated by the PointLight and it does cast a shadow on the Earth.
Here is the Renderer set-up code:-
renderer = new THREE.WebGLRenderer();
renderer.setClearColor( 0x000022 );
renderer.setPixelRatio( window.devicePixelRatio );
renderer.setSize( window.innerWidth, window.innerHeight );
//... Enable Shadows
renderer.shadowMap.enabled = true;//.shadowMapEnabled = true;
//renderer.shadowMap.type = THREE.BasicShadowMap;//
//renderer.shadowMap.type = THREE.PCFShadowMap
renderer.shadowMap.type = THREE.PCFSoftShadowMap;
My Question = What needs to be done to (a) illuminate the outer moons and (b) ensure the shadows of outer moons do not appear on the (inner, nearer-to-Sun) planet Earth.

Simply put, you're spacing things out too far.
Calculating shadows from a point light is very expensive. In fact, THREE.js only added functionality for it a few months ago. I can't find anything solid in the documentation yet, but it seems likely that there's a hard coded limit on how far out shadows will be calculated from a point light.
The solution is easy: reduce the space between your objects. There's absolutely no reason that objects need to be thousands of units away from each other when a dozen will suffice. I solved both of your problems just by reducing all distances and scales by a factor of 10. I also tweaked the intensity of the PointLight because 10 was pretty harsh haha.
// color, intensity, falloff radius, falloff amount
// if falloff radius is 0 then there is no falloff
var light3 = new THREE.PointLight( 0xffffff, 1, 0, 0);

Related

Shadows Distance problems in ThreeJS

I'm currently working with a directionnal ligth for my scene. A small character can move around. All shadows works. But if I start to go a bit further with the avatar, the shadows don't work anymore on it. I think it's may come from the angle of the light but I can't change it. I can draw a square around the shadow/non-shadow area.
Here is my code. Huge numbers were here for me to test things
const dirLight = new THREE.DirectionalLight( 0xffffff, 1 );
dirLight.color.setHSL( 0.1, 1, 0.95 );
dirLight.position.set( - 1, 1.75, 1 );
dirLight.position.multiplyScalar( 30 );
dirLight.distance = 1200;
dirLight.focus = 1200;
dirLight.angle = Math.PI / 1;
scene.add( dirLight );
dirLight.castShadow = true;
dirLight.shadow.mapSize.width = 2048;
dirLight.shadow.mapSize.height = 2048;
const d = 100;
dirLight.shadow.camera.left = - d;
dirLight.shadow.camera.right = d;
dirLight.shadow.camera.top = d;
dirLight.shadow.camera.bottom = - d;
dirLight.shadow.camera.far = 35000;
dirLight.shadow.bias = - 0;
const dirLightHelper = new THREE.DirectionalLightHelper( dirLight, 10 );
scene.add( dirLightHelper );
Here is the code of the ground that recive shadows
const groundGeo = new THREE.PlaneBufferGeometry( 10000, 10000 );
const groundMat = new THREE.MeshLambertMaterial( { color: 0xffffff } );
groundMat.color.setHSL( 0.095, 1, 0.75 );
const ground = new THREE.Mesh( groundGeo, groundMat );
ground.position.y = - 33;
ground.rotation.x = - Math.PI / 2;
ground.receiveShadow = true;
scene.add( ground );
I reused a huge part of the code provided by the ThreeJS example with the flamingo https://threejs.org/examples/?q=light#webgl_lights_hemisphere
Here is also a link to an imgur where I just put some picture to see the shadows
https://imgur.com/a/MGrc5cw
Thanks
If your shadows are working within a limited area, then it must be that your character is walking beyond the frustum of the shadow-camera.
To create directional shadows, you create an orthographic camera and assign its dimensions with .left, .right, .top, .bottom, in this case your camera is has 100 in each direction. When your character walks beyond those 200 units, it is outside the shadow camera, and it no longer receives shadows.
You should add a CameraHelper to the light shadow to let you see the space within its view frustum, as seen in the DirectionalLightShadow code sample
const helper = new THREE.CameraHelper( dirLight.shadow.camera );
scene.add( helper );
Maybe you could have the shadow camera follow the character so he'll always be inside of it.

Shadow on floor doesn't work in three.js v104 but works in r71

If you take a look here which is done with r71 the shadows work:
var shadowlight = new THREE.DirectionalLight( 0xffffff, 1.8 );
shadowlight.position.set( 0, 100, 0 );
shadowlight.castShadow = true;
shadowlight.shadowDarkness = 0.1;
this.scene.add(shadowlight);
this.renderer.setClearColor( 0xf1c140, 1 );
this.renderer.shadowMapEnabled = true;
this.renderer.shadowMapType = THREE.PCFSoftShadowMap;
https://codepen.io/nicolasdnl/pen/VYRXWr
However, if I change the version to 104, and make the necessary changes that it suggests:
.shadowMapEnabled is now .shadowMap.enabled.
.shadowMapType is now .shadowMap.type.
THREE.Light: .shadowDarkness has been removed.
The shadow doesn't work any more: https://codepen.io/bertug48/pen/YMowKx
How to enable the shadows like r71 on v104?
MeshBasicMaterial is not able to receive shadows for over three years now. You have to use a lit material for your ground or add an additional ground mesh with an instance of THREE.ShadowMaterial.
Demo: https://jsfiddle.net/38weog40/
var planeGeometry = new THREE.PlaneGeometry( 200, 200 );
planeGeometry.rotateX( - Math.PI / 2 );
var planeMaterial = new THREE.ShadowMaterial();
planeMaterial.opacity = 0.2;
var plane = new THREE.Mesh( planeGeometry, planeMaterial );
plane.position.y = -200;
plane.receiveShadow = true;
scene.add( plane );
BTW: Here is the reason why MeshBasicMaterial does not receive shadows anymore: https://github.com/mrdoob/three.js/issues/8116#issuecomment-183540170
three.js R104

Overlapping PointLight Shadows using MeshLambertMaterial

I'm building a small representation of my house in ThreeJS, and I have the walls and such sorted and am in the process of adding the lights. I'm using PointLights as they are representing lightbulbs.
The issue I'm having is that with two lights, only the area that they both cover is lit, and the remaining 'half-shadow' is pitch black, when I would expect them to be lit with half of the intensity. Graphical representation below.
Graphical Representation
In this image, the circles represent the lights, with the beams representing how I expect the light to fall in the smaller room. The area with the 'shading' represents where I expect to have the 'half-shadows'.
It seems to me that the only area that is actually lit in the scene is where BOTH of the two PointLights shine, and when only one would affect an area, the area is pitch black.
The walls are added as BoxGeometries, with the walls around the door as an ExtrudeGeometry of a Shape.
Here is the code for the lights:
scene.add( function() {
var mainLight1 = new THREE.PointLight( 0xFFFFFF, 0.33 );
mainLight1.position.set( -middleFloorDim.width / 5, middleFloorDim.height * 9.75 / 10, -middleFloorDim.depth / 4 );
mainLight1.castShadow = true;
return mainLight1;
}());
scene.add( function() {
var mainLight2 = new THREE.PointLight( 0xFFFFFF, 0.33 );
mainLight2.position.set( -middleFloorDim.width / 5, middleFloorDim.height * 9.75 / 10, middleFloorDim.depth / 4 );
mainLight2.castShadow = true;
return mainLight2;
}());
And here is the code for the renderer:
var renderer = new THREE.WebGLRenderer({ antialias: true, });
renderer.setSize( window.innerWidth, window.innerHeight );
renderer.shadowMap.enabled = true;
renderer.shadowMap.type = THREE.PCFShadowMap;
And an example of one of the walls:
scene.add( function() {
var northWall = new THREE.Mesh(
new THREE.BoxGeometry( middleFloorDim.depth, middleFloorDim.height , 0.01 ),
new THREE.MeshLambertMaterial({
color: PALETTE.MIDDLE_FLOOR_WALLS,
})
);
northWall.rotation.y = Math.PI / 2;
northWall.position.set( -middleFloorDim.width / 2, middleFloorDim.height / 2, 0 );
northWall.castShadow = true;
northWall.receiveShadow = true;
return northWall;
}());
You are having problems with shadows when you have multiple light sources and the material receiving the shadow is MeshLambertMaterial.
This is a limitation of MeshLambertMaterial due to the fact that it uses Gouraud shading -- the illumination calculation is computed in the vertex shader. Since shadows are computed in the fragment shader, there is no way to identify the light sources at that point.
For proper shadows, use MeshPhongMaterial or MeshStandardMaterial, for example.
three.js r.88

How do I manipulate shadows in Three.js without editing the underlying mesh?

I'm working on an app that should allow users to manipulate 3D objects in the scene and observe how their changes affect the ground shadow:
In this scene, the yellow cylinder casts a shadow on a white plane with the middle of the cylinder contained in the green cube. What I would like to happen is for the cube to remove the middle of the shadow, like so:
Obviosly, my first thought was to subtract the green cube volume from the yellow cylinder volume and after a bit of googling I found CSG.js. Unfortunately, CSG.js is too slow for the actual model that I'm going to use, which will going to have at least 15k vertices.
I started digging into the Three.js source and reading about shadow maps to understand how shadows are produced, but my shader-fu is not strong enough yet to fully grasp how I can tweak shadow rendering.
How can I achieve this "shadow subtraction" effect?
var camera, scene, renderer;
init();
animate();
function init() {
scene = new THREE.Scene();
camera = new THREE.PerspectiveCamera( 70, window.innerWidth / window.innerHeight, 1, 1000 );
camera.position.z = 500;
camera.position.y = 100;
camera.lookAt(scene.position);
var ambient = new THREE.AmbientLight(0x909090);
scene.add(ambient);
var directionalLight = new THREE.DirectionalLight( 0xffffff, 1.0 );
directionalLight.position.set( -300, 300, 0 );
directionalLight.castShadow = true;
directionalLight.shadow.camera.near = 10;
directionalLight.shadow.camera.far = 2000;
directionalLight.shadow.camera.right = 350;
directionalLight.shadow.camera.left = -350;
directionalLight.shadow.camera.top = 350;
directionalLight.shadow.camera.bottom = -350;
directionalLight.shadow.mapSize.width = 1024;
directionalLight.shadow.mapSize.height = 1024;
scene.add( directionalLight );
//var lightHelper = new THREE.CameraHelper(directionalLight.shadow.camera);
//scene.add(lightHelper);
var geometry = new THREE.CylinderGeometry( 50, 50, 400, 32 );
var material = new THREE.MeshPhongMaterial( {color: 0xffff00} );
var cylinder = new THREE.Mesh( geometry, material );
cylinder.castShadow = true;
scene.add( cylinder );
var geometry = new THREE.BoxGeometry( 110, 110, 110 );
var material = new THREE.MeshPhongMaterial( {color: 0x00ff00} );
var cube = new THREE.Mesh( geometry, material );
cube.castShadow = true;
scene.add( cube );
var geometry = new THREE.PlaneGeometry( 3000, 3000, 32 );
var material = new THREE.MeshPhongMaterial( {color: 0xffffff, side: THREE.DoubleSide} );
var plane = new THREE.Mesh( geometry, material );
plane.lookAt(new THREE.Vector3(0, 1, 0));
plane.position.y = -200;
plane.receiveShadow = true;
scene.add( plane );
renderer = new THREE.WebGLRenderer();
renderer.setPixelRatio( window.devicePixelRatio );
renderer.setSize( window.innerWidth, window.innerHeight );
renderer.shadowMap.enabled = true;
renderer.shadowMap.type = THREE.BasicShadowMap;
document.body.appendChild( renderer.domElement );
window.addEventListener( 'resize', onWindowResize, false );
}
function onWindowResize() {
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
renderer.setSize( window.innerWidth, window.innerHeight );
}
function animate() {
requestAnimationFrame( animate );
renderer.render( scene, camera );
}
jsFiddle
Update:
What about a more complicated scene? Is it possible for the shadow from the red cylinder to be unaffected (you can see it being cut in half with cube.customDepthMaterial = new THREE.MeshBasicMaterial({ depthTest: false}))?
Updated jsFiddle
You can subtract an object's shadow from the rest of scene by setting the object's .customDepthMaterial property like so:
var cube = new THREE.Mesh( geometry, material );
cube.castShadow = true;
cube.receiveShadow = false;
// The secret sauce
cube.customDepthMaterial =
new THREE.MeshBasicMaterial({ depthTest: false});
scene.add( cube );
jsFiddle
No shader-fu required.
Why This Works
When the shadow map is rendered, each object's depth material ( .customDepthMaterial or the default ) is used to render the scene from the light's perspective. The depth material's resulting render represents the object's depth from the camera packed as RGBA. Since THREE.MeshBasicMaterial defaults to { color: 0xffffff, opacity: 1 }, it will return the maximum depth which makes the object further than the shadow camera's far.
I disabled depthTest because in your desired result screenshot you clipped the area where the cube's given the cylinder wasn't there. Disabling depthTest means that parts of the cube which are blocked by the cylinder will still cut out the shadow, giving you your desired result.
Documentation
There unfortunately is no documentation on .customDepthMaterial yet but I did find an official example where it is used.
Updated Answer:
To allow an object's shadow to always show:
You can use the same trick as above just setting the material's color and opacity to 0
Make sure it's added to the scene after the 'subtractive shadow' object. This way the additive shadow will win out even though they both have depthTest disabled.
updated jsFiddle
If you have anything more complicated, it will be up to you to figure out a way to manage the order of the shadow rendering.
Tested in r77

in three.js, the spotlight is not showing a cone on a plane

I have a very simple example: a spot light pointed at a plane. I am expecting to see a cone of light whose diameter depends on the setting of the spot light angle. I cannot see any cone, the whole plane is illuminated, even for very narrow settings of angle.
Here is my jfiddle: http://jsfiddle.net/blwoodley/WLtL4/1/
I'd love to know the source code that produced this picture from https://github.com/mrdoob/three.js/pull/3291 by West Langley. It obviously is working fine in that case.
So I must be doing something obviously wrong, but I can't figure it out.
Some of the code from the jfiddle, it doesn't get much simpler than this:
function init() {
container = document.createElement('div');
document.body.appendChild(container);
camera = new THREE.PerspectiveCamera(30, window.innerWidth / window.innerHeight, 1, 100000);
camera.position.x = 100;
camera.position.y = 100;
camera.position.z = 200;
camera.lookAt({x: 0,y: 0,z: 0});
scene = new THREE.Scene();
var floorGeometry = new THREE.PlaneGeometry(1000, 1000, 10, 10);
var floorMaterial = new THREE.MeshLambertMaterial({ color: 0x222222, side:THREE.DoubleSide });
floor = new THREE.Mesh(new THREE.PlaneGeometry(2000,2000,10,10), floorMaterial);
floor.rotation.x = Math.PI / 2;
floor.position.y = -40;
scene.add(floor);
var light;
light = new THREE.SpotLight(0x008888);
light.position.set(0, 40, 0);
light.lookAt(floor);
light.angle = Math.PI/4;
light.intensity = 30;
light.distance=0;
scene.add(light);
// RENDERER
webglRenderer = new THREE.WebGLRenderer();
webglRenderer.setSize(SCREEN_WIDTH, SCREEN_HEIGHT);
webglRenderer.domElement.style.position = "relative";
container.appendChild(webglRenderer.domElement);
window.addEventListener('resize', onWindowResize, false);
}
This is subtle.
You are using MeshLambertMaterial for the plane. You need to change it to MeshPhongMaterial, so the lighting is rendered properly.
As explained here, for MeshLambertMaterial, the illumination calculation is performed only at each vertex.
For MeshPhongMaterial, the illumination calculation is performed at each texel.
So make these changes
floorGeometry = new THREE.PlaneGeometry( 1000, 1000 ); // no need to tessellate it now
var floorMaterial = new THREE.MeshPhongMaterial( { color: 0xffffff } ); // 0x222222 is too dark
light.intensity = 1; // a reasonable value
Updated fiddle: http://jsfiddle.net/WLtL4/5/
three.js r.63
Also try to disable target for testing.
I'm getting really weird behavior from it. Sometimes it makes it not render the light at all, no idea why yet. I'll make a demo of the problem later.

Resources