Weird effect when using receiveShadow - three.js

I'm trying to get receiveShadow to work on a cube. Not only it doesn't work, but when I set cube.receiveShadow = true, the light on the cube shows weird dark borders (links to images below).
There's no other object and I've pretty much only have a cube, a plane, and a spotlight in the scene.
let geometry = new THREE.BoxGeometry(5,5,5);
let material = new THREE.MeshLambertMaterial({color: 0x11bb22});
let cube = new THREE.Mesh(geometry, material);
cube.castShadow = true;
//cube.receiveShadow = true
scene.add(cube);
light = new THREE.SpotLight(0xffffff, 2, 100, Math.PI/4, 0.1, 0);
light.position.y = 10;
light.castShadow = true;
light.shadow.bias = 0.001;
light.shadow.mapSize.width = 2048;
light.shadow.mapSize.height = 2048;
scene.add(light);
renderer = new THREE.WebGLRenderer();
renderer.shadowMap.enabled = true;
renderer.shadowMap.type = THREE.PCFShadowMap;
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);
With castShadow=false
With castShadow=true
Thanks!

Thanks, setting the bias to 0 did the trick. both 0.001 and -0.001 cause weird effects where the shadow does not look realistic.

Related

Directional light shadow in mapbox

I have a custom mesh geometry (three js) in mapbocx. I am trying to create a light for casting directional shadows but I always end up woth the light source in the base plane (which results in no casted shadows on my objects above the plane). Does anyone know how I can move the light source so it is above the plane? I added a helper to see the scope box and I would like to move it upwards along the z-vector in the image below.
//Create a WebGLRenderer and turn on shadows in the renderer
const renderer = new THREE.WebGLRenderer();
renderer.shadowMap.enabled = true;
renderer.shadowMap.type = THREE.PCFSoftShadowMap; // default THREE.PCFShadowMap
//Add Ambient light
const amblight = new THREE.AmbientLight(0xffffff, 0.8);
amblight.position.set(8, 10, 5); //default; light shining from top
scene.add(amblight);
//Create a DirectionalLight and turn on shadows for the light
const light = new THREE.DirectionalLight(0xffffff, 0.5);
//light.position.set(8, 10, 5); //default; light shining from top
light.position.y = 2000;
light.position.x = 10;
light.position.z = 5;
light.castShadow = true; // default false
scene.add(light);
//scene.add(light.target);
//Set up shadow properties for the light
light.shadow.mapSize.width = 512;
light.shadow.mapSize.height = 512;
light.shadow.camera.left = -100;
light.shadow.camera.right = 100;
light.shadow.camera.top = 100;
light.shadow.camera.bottom = -100;
light.shadow.camera.near = 0.5;
light.shadow.camera.far = 100; //Scope box depth
//Create a plane that receives shadows (but does not cast them)
const planeGeometry = new THREE.PlaneGeometry(1000, 1000, 10, 10);
const planeMaterial = new THREE.MeshPhongMaterial({
color: 0x808080,
opacity: 0.8,
transparent: true,
});
const plane = new THREE.Mesh(planeGeometry, planeMaterial);
plane.receiveShadow = true;
scene.add(plane);
const meshString = result.mesh.meshString;
const mesh = meshToThreejs(rhino, meshString, THREE);
//scene.add(mesh);
//Add shadows
mesh.castShadow = true; //default is false
mesh.receiveShadow = true; //default
scene.add(mesh);
//ENd shadows
//Create a helper for the shadow camera (optional)
const helper = new THREE.CameraHelper(light.shadow.camera);
scene.add(helper);
"move the light source so it is above the plane" - It looks like you already know how to do this, just change the z number.
light.position.z = 20;
// or
light.position.set(0, 0, 20);
// Check note below - If y is up
light.position.y = 20;
// or
light.position.set(0, 20, 0);
Just a note, by default Y is up in Three.js unless you have already handled that in code not shown here. If you need to check this add the axesHelper to your scene. The X axis is red. The Y axis is green. The Z axis is blue. Make sure the camera is moved in the correct direction.
const axesHelper = new THREE.AxesHelper( 100 );
scene.add( axesHelper );
If you are still not getting shadows you could try to add a sphere like in the Three.js docs (https://threejs.org/docs/#api/en/lights/shadows/DirectionalLightShadow)
//Create a sphere that cast shadows (but does not receive them)
const sphereGeometry = new THREE.SphereGeometry( 5, 32, 32 );
const sphereMaterial = new THREE.MeshStandardMaterial( { color: 0xff0000 } );
const sphere = new THREE.Mesh( sphereGeometry, sphereMaterial );
sphere.castShadow = true; //default is false
sphere.receiveShadow = false; //default
scene.add( sphere );
If that is casting a shadow correctly then perhaps there is an issue with your mesh, or the height of those buildings is so small that the shadows are really small

I can't seem to get shadows to work in my Three.js scene

I want to get shadows in my scene but the shadow doesn't show up or is glitched/not right.
I've tried to get it to work with AmbientLight, PointLight, DirectionalLight but none of them seem to work.
I've set the lights to cast shadows and set all the meshes in the scene to cast and receive shadows.
My renderer
var renderer,
scene,
camera;
//RENDERER
renderer = new THREE.WebGLRenderer({
antialias: true
});
renderer.setClearColor("#e5e5e5");
renderer.setPixelRatio(window.devicePixelRatio);
renderer.setSize(window.innerWidth, window.innerHeight);
renderer.shadowMap.enabled = true;
renderer.shadowMap.type = THREE.BasicShadowMap;
document.body.appendChild(renderer.domElement);
The light
//LIGHTS
var directionalLight = new THREE.DirectionalLight( 0xffffff );
directionalLight.position.set( 150, 50, 100 );
var d = 5;
directionalLight.castShadow = true;
directionalLight.shadow.camera.left = - d;
directionalLight.shadow.camera.right = d;
directionalLight.shadow.camera.top = d;
directionalLight.shadow.camera.bottom = - d;
directionalLight.shadow.camera.near = 1;
directionalLight.shadow.camera.far = 20;
scene.add( directionalLight );
var light2 = new THREE.PointLight(0xffffff, 0.5);
light2.position.set(0,50,50);
light2.castShadow = true;
scene.add(light2);
How I load my objects and set them to cast and receive shadows
//LOAD OBJECTS
var loader = new THREE.GLTFLoader();
loader.load('landingpagev2.glb', handle_load);
var mesh;
function handle_load(gltf) {
mesh = gltf.scene;
for(var i = 0; i < mesh.children.length; i++){
mesh.children[i].material = new THREE.MeshPhongMaterial();
mesh.children[i].castShadow=true;
mesh.children[i].receiveShadow=true;
}
scene.add( mesh );
mesh.position.set(4,2.3,-10);
mesh.rotation.set(-80,0,0);
}
The shadows keep showing glitched-like or don't show at all when working with different lights.
The whole code is online at ramonhoffman.nl/three so you can see what I'm working with.
Thanks for your time!

Three.js materials all come out black

My objects are all coming out black. I think it might have something to do with the light but I'm not sure. Here's an image showing the problem and here is my code:
var scene = new THREE.Scene();
var camera = new THREE.PerspectiveCamera(75, window.innerWidth/window.innerHeight, 0.1, 1000);
var renderer = new THREE.WebGLRenderer();
var directionalLight = new THREE.DirectionalLight(0xffffff, 1.0);
var geometry = new THREE.Geometry();
var controls = new THREE.TrackballControls(camera);
var antMaterial = new THREE.MeshLambertMaterial({color: 0xff0000});
var material, mesh;
function init()
{
renderer.setSize(window.innerWidth, window.innerHeight);
renderer.setClearColorHex(0x0088FF, 1);
document.body.appendChild(renderer.domElement);
camera.position.z += 5;
camera.position.y += 1;
//Controls
controls.rotateSpeed = 1.0;
controls.zoomSpeed = 1.0;
controls.panSpeed = 1.0;
controls.noZoom = false;
controls.noPan = false;
controls.staticMoving = true;
controls.addEventListener('change', render);
scene.add(ant());
directionalLight.position.set(1, 1, 1);
scene.add(directionalLight);
render();
animate();
}
The answer to your problem is most likely that your directional light comes from below the pane. This makes the colors on the other side, visible in your screenshot, black because the light does not reach this faces. Try to use an ambient light first or change the position to:
directionalLight.position.set(0,0,-1); // change these values for the direction of the light source

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.

Three.js DoubleSided material doesn't cast shadow on both sides of planar parametric geometry

See this jfiddle: http://jsfiddle.net/blwoodley/5Tr4D/1/
I have a blue spot light that shines on a rotating rotating square. This casts a shadow to the underlying ground. Except that it only casts a shadow on one side of the square.
I saw this discussion: https://github.com/mrdoob/three.js/issues/3544 which indicates that face culling on planar surfaces is the cause. the recommendation is to give my square some depth, i.e. make it a cube.
I can do that with this simple example, but I'm encountering the same problem with a parametric geometry that is a surface. Is there a way to get both sides to cast a shadow without having to give my geometry a depth in doesn't have or need?
Here is the main function in the fiddle that replicates the problem with a plane:
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 = 100;
camera.lookAt({x: 0,y: 0,z: 0});
scene = new THREE.Scene();
var groundMaterial = new THREE.MeshLambertMaterial({
color: 0xffffff, side:THREE.DoubleSide
});
plane = new THREE.Mesh(new THREE.PlaneGeometry(2000,2000,10,10), groundMaterial);
plane.rotation.x = Math.PI / 2;
plane.position.y = -40;
plane.receiveShadow = true;
scene.add(plane);
var light;
light = new THREE.SpotLight(0x0000ff);
light.position.set(40, 40, 0);
light.castShadow = true;
light.shadowCameraVisible = true;
light.shadowMapWidth = 2048;
light.shadowMapHeight = 2048;
light.position.set(24, 20, 0);
light.lookAt(plane);
light.castShadow = true;
light.angle = .8;
light.intensity = 30;
light.distance=0;
light.shadowCameraNear = 2;
light.shadowCameraFar = 100;
light.shadowCameraFov = 100;
light.shadowDarkness = 1;
light.shadowCameraVisible = true;
scene.add(light);
var planeGeo = new THREE.PlaneGeometry(20,20,20,20)
_planeMesh = new THREE.Mesh(planeGeo, new THREE.MeshLambertMaterial( { color: 0x00ff00, side:THREE.DoubleSide } ) );
_planeMesh.castShadow = true;
scene.add( _planeMesh );
// RENDERER
webglRenderer = new THREE.WebGLRenderer();
webglRenderer.setSize(SCREEN_WIDTH, SCREEN_HEIGHT);
webglRenderer.domElement.style.position = "relative";
webglRenderer.shadowMapEnabled = true;
webglRenderer.shadowMapSoft = true;
container.appendChild(webglRenderer.domElement);
window.addEventListener('resize', onWindowResize, false);
}
Yes, this is a feature.
WebGLRenderer, by default, culls front faces when rendering shadows. This is OK, because it is assumed that objects have depth. You can cull back faces, instead, if you want:
renderer.shadowMapCullFace = THREE.CullFaceBack;
... but culling neither is not an option.
The material.side property is not taken into consideration when casting shadows.
three.js r.63

Resources