Three.js Shadow doesn't cast on a part of the scene - three.js

I am having this scene where I cast shadow with a sphere on my plan, all good in the first picture.
Picture1
When I put the sphere in another place, the shadow is not fully casted on the plan, like in this picture.
Picture2
This is the code:
//Create a DirectionalLight and turn on shadows for the light
var light = new THREE.DirectionalLight( 0xffffff, 0.7, 1 );
light.position.set( 1, 1,1);
light.castShadow = true;
light.shadowDarkness = 0.8;
scene.add( light );
//Set up shadow properties for the light
light.shadow.mapSize.width = 512;
light.shadow.mapSize.height = 512;
light.shadow.camera.near = 0.0;
light.shadow.camera.far = 5;
var sphereGeometry = new THREE.SphereBufferGeometry(1, 32, 32 );
var sphereMaterial = new THREE.MeshStandardMaterial( { color: 0xff0000 } );
var sphere = new THREE.Mesh( sphereGeometry, sphereMaterial );
sphere.translateY(1);
sphere.translateZ(-5);
sphere.translateX(-2);
sphere.castShadow = true;
sphere.receiveShadow = false;
scene.add( sphere );
Here I receive the shadow on my plan:
var objectLoader = new THREE.ObjectLoader();
objectLoader.load("stone/rush_tex01.json", function ( object ) {
object.traverse( function( node ) { if ( node instanceof THREE.Mesh ) { node.receiveShadow = true; } } );
scene.add( object );
} );
So what should it be?

Try to increase the frustum of the directional light's shadow camera. The code could look like this:
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;
The following example shows the entire code. You might have to adjust the value of d so it works for your specific use case.
https://threejs.org/examples/#webaudio_timing

Related

Three.JS VRAM memory leak when adding removing THREE.Geometry to scene

I have encountered a VRAM memory leak in my app.
The app adds and removes THREE.Geometry very often to create a volumetric animation.
If instead of a THREE.Geometry with it's own populated vertices, I used instead THREE.SphereBufferGeometry, the memory leak doesn't happen.
I have created a minimal app to prove this memory leak is real.
The memory leak increase VRAM memory very slowly, but it does fill up in the end.
I think that pools won't help, since it's VRAM and not managed memory.
I do use dispose.
If you can make this sample work and not have memory leak, that might solve my issue:
https://jsfiddle.net/4a7ksryd/16/
Edit: I am adding here the code of the app:
var camera, scene, renderer;
var geometry, material, mesh;
var lastSphere;
var lastGeo;
init();
animate();
function init() {
camera = new THREE.PerspectiveCamera( 70, window.innerWidth / window.innerHeight, 0.01, 10 );
camera.position.z = 1;
scene = new THREE.Scene();
renderer = new THREE.WebGLRenderer( { antialias: true } );
renderer.setSize( window.innerWidth, window.innerHeight );
renderer.shadowMap.enabled = true;
renderer.shadowMap.type = THREE.PCFSoftShadowMap; // default THREE.PCFShadowMap
var light = new THREE.DirectionalLight( 0xffffff, 1, 100 );
light.position.set( 0, 4, 0 ); //default; light shining from top
light.castShadow = true; // default false
//Set up shadow properties for the light
light.shadow.mapSize.width = 1024; // default
light.shadow.mapSize.height = 1024; // default
light.shadow.camera.near = 1; // default
light.shadow.camera.far = 20; // default
scene.add( light );
//Create a sphere that cast shadows (but does not receive them)
geometry = new THREE.SphereBufferGeometry( 0.1, 32, 32 );
material = new THREE.MeshStandardMaterial( { color: 0xff0000 } );
// geometry = new THREE.BoxGeometry( 0.2, 0.2, 0.2 );
// material = new THREE.MeshNormalMaterial();
mesh = new THREE.Mesh( geometry, material );
mesh.position.y = 0.1;
mesh.castShadows = true;
mesh.receiveShadow = false;
scene.add( mesh );
var planeGeometry = new THREE.PlaneBufferGeometry( 15, 15, 1, 1 );
var planeMaterial = new THREE.MeshStandardMaterial( { color: 0xffffff, emissive:0x111111 } )
var plane = new THREE.Mesh( planeGeometry, planeMaterial );
plane.position.y = -0.2;
plane.rotation.x = -Math.PI / 2;
plane.receiveShadow = true;
scene.add( plane );
document.body.appendChild( renderer.domElement );
}
function animate() {
requestAnimationFrame( animate );
mesh.rotation.x += 0.01;
mesh.rotation.y += 0.02;
var dim = 32;
var geo1 = new THREE.Geometry();
const numVertices = dim*dim;
var vertices = new Array(numVertices);
for (var i=0; i<vertices.length; i++)
{
const x = i%dim;
const y = (Math.floor(i/dim))%dim;
vertices[i] = new THREE.Vector3(x*0.1, y*0.1, 0);
}
const numFaces = (dim-1)*(dim-1)*2;
var faces = new Array(numFaces);
for (var i=0; i<(faces.length/2); i++)
{
const x = i%(dim-1);
const y = Math.floor(i/(dim-1))%(dim-1);
faces[2*i] = new THREE.Face3(x+y*dim, x+1+y*dim, x+(y+1)*dim);
faces[2*i+1] = new THREE.Face3(x+1+y*dim, x+1+(y+1)*dim, x+(y+1)*dim);
}
var uv = new Array(numFaces);
for (var i=0; i<uv.length; i++)
uv[i] = [new THREE.Vector2(0, 0), new THREE.Vector2(0, 0), new THREE.Vector2(0, 0)];
geo1.faces = faces;
geo1.vertices = vertices;
geo1.faceVertexUvs[0] = uv;
geo1.uvsNeedUpdate = true;
geo1.verticesNeedUpdate = true;
geo1.elementsNeedUpdate = true;
// var sphereGeometry = new THREE.SphereBufferGeometry( 0.1, 256, 256 );
var sphereGeometry = geo1;
var sphereMaterial = new THREE.MeshBasicMaterial( { color: 0xff0000 } );
var sphere = new THREE.Mesh( sphereGeometry, sphereMaterial );
sphere.position.y = 0.1+Math.sin(mesh.rotation.x)*0.1;
sphere.position.x = 0.5;
sphere.castShadow = true; //default is false
sphere.receiveShadow = false; //default
if (lastGeo!=null)
lastGeo.dispose();
if (lastSphere!=null)
scene.remove(lastSphere);
scene.add( sphere );
lastSphere = sphere;
lastGeo = sphereGeometry;
// geo1.dispose();
renderer.render( scene, camera );
}
This is actually a bug in three.js. I've filed a PR to fix the issue:
https://github.com/mrdoob/three.js/pull/20479

Directional light shadow of boxes is not rendered on plane mesh

The following light is added to my scene:
var light = new THREE.DirectionalLight( 0xffffff, 1.5 );
light.position.set( 1000, 1000, 1000 );
light.castShadow = true;
light.shadow = new THREE.DirectionalLightShadow(
new THREE.OrthographicCamera(
-100, 100,
100, -100, -500, 500 ) );
light.shadow.bias = - 0.00022;
light.shadow.mapSize.width = 2048;
light.shadow.mapSize.height = 2048;
The plane is as follows:
var planeGeometry = new THREE.PlaneGeometry( 200, 200 );
planeGeometry.rotateX( - Math.PI / 2 );
var planeMaterial = new THREE.ShadowMaterial();
planeMaterial.opacity = 1;
var plane = new THREE.Mesh( planeGeometry, planeMaterial );
plane.position.y = 0;
plane.receiveShadow = true;
scene.add( plane );
The boxes are:
var geometry = new THREE.BoxGeometry( 1, 1, 1 );
for ( var i = 0; i < 20; i ++ ) {
var object = new THREE.Mesh( geometry, new THREE.MeshLambertMaterial( { color: Math.random() * 0xffffff } ) );
object.scale.x = Math.random() * 5 + 1;
object.scale.y = Math.random() * 5 + 1;
object.scale.z = Math.random() * 5 + 1;
object.position.x = Math.random() * 30;
object.position.y = object.scale.y / 2.0;
object.position.z = Math.random() * 60;
object.castShadow = true;
object.receiveShadow = true;
scene.add( object );
}
Directional light shadow of boxes is not rendered on plane mesh. Could you pls help what do I wrong? Please refer to the following screenshot.
Some of the shadow map properties have been renamed in the recent versions.
Setting up the renderer for shadow maps (and choosing the more computational expensive shadow map type):
var renderer = new THREE.WebGLRenderer();
renderer.shadowMap.enabled = true;
renderer.shadowMap.type = THREE.PCFSoftShadowMap; // default THREE.PCFShadowMap
Setting up the light (notice how it also works with THREE.PointLight):
var light = new THREE.PointLight( 0xffffff, 1, 100 );
light.position.set( 0, 12, 0 );
light.castShadow = true; // default false
light.shadow.mapSize.width = 1024; // default 512
light.shadow.mapSize.height = 1024; // default 512
light.shadow.camera.near = 2; // default 0.5
light.shadow.camera.far = 100; // default 500
scene.add( light );
For plane material use var planeMaterial = new THREE.MeshLambertMaterial();
( ShadowMaterial works from r77 https://github.com/mrdoob/three.js/issues/1791)
var renderer = new THREE.WebGLRenderer( {antialias:true, alpha: true } );
renderer.shadowMap.enabled = true; // enable shadows rendering
renderer.shadowMap.type = THREE.PCFSoftShadowMap; // to antialias the shadow
var light_target = new THREE.Object3D(); // believe me
light_target.position.set(0,0,0); // not necessary, but to be clear
var light = new THREE.DirectionalLight( 0xffffff,1.5,1000) );
light.position.set(1000, 1000, 1000);
light.target = light_target;
light.castShadow = true;
light.shadowMapWidth = 2048*2; // px of the rendered shadow texture
light.shadowMapHeight = 2048*2; // px of the rendered shadow texture
var d = 100; /// your plane is 200x200 and we targeted its center
var distance = light.position.distanceTo(light_target.position);
light.shadowCameraLeft = -d;
light.shadowCameraRight = d; light.shadowCameraTop = d; light.shadowCameraBottom = -d; light.shadowCameraNear = distance-d; light.shadowCameraFar = distance+d; light.shadowBias = -0.001;
light.shadowDarkness = 0.5;
scene.add( light ); // you missed this!

Three.js Restrict the mouse movement to Scene only

I am working on the cube example from three.js (webgl_interactive_cubes_gpu.html). I realized that events goes to the entire html page. I mean i can rotate, zoom in or zoom out, even if the mouse pointer is not inside the scene..
I google a little bit and found some answers (Allow mouse control of three.js scene only when mouse is over canvas) but they do not work for me..Below is my code...
var container, stats;
var camera, controls, scene, renderer;
var pickingData = [], pickingTexture, pickingScene;
var objects = [];
var highlightBox;
var mouse = new THREE.Vector2();
var offset = new THREE.Vector3( 10, 10, 10 );
init();
animate();
function init() {
container = document.getElementById( "container" );
camera = new THREE.PerspectiveCamera(70, window.innerWidth / window.innerHeight, 1, 10000 );
camera.position.z = 1000;
//camera.translateZ( -500 );
controls = new THREE.TrackballControls(camera);
controls.rotateSpeed = 1.0;
controls.zoomSpeed = 4;
controls.panSpeed = 0.8;
controls.noZoom = false;
controls.noPan = false;
controls.staticMoving = true;
controls.dynamicDampingFactor = 0.3;
scene = new THREE.Scene();
pickingScene = new THREE.Scene();
pickingTexture = new THREE.WebGLRenderTarget( window.innerWidth, window.innerHeight );
pickingTexture.minFilter = THREE.LinearFilter;
pickingTexture.generateMipmaps = false;
scene.add( new THREE.AmbientLight( 0x555555 ));
var light = new THREE.SpotLight( 0xffffff, 1.5 );
light.position.set( 0, 500, 2000 );
scene.add( light );
var geometry = new THREE.Geometry(),
pickingGeometry = new THREE.Geometry(),
pickingMaterial = new THREE.MeshBasicMaterial( { vertexColors: THREE.VertexColors } ),
defaultMaterial = new THREE.MeshLambertMaterial({ color: 0xffffff, shading: THREE.FlatShading, vertexColors: THREE.VertexColors} );
function applyVertexColors( g, c ) {
g.faces.forEach( function( f ) {
var n = ( f instanceof THREE.Face3 ) ? 3 : 4;
for( var j = 0; j < n; j ++ ) {
f.vertexColors[ j ] = c;
}
} );
}
var geom = new THREE.BoxGeometry(0.005, 0.005, 0.005 );
var color = new THREE.Color();
var matrix = new THREE.Matrix4();
var quaternion = new THREE.Quaternion();
var coord="219_163_189;130_173_179;161_113_231;
var splitCoord=coord.split(";");
var coordColr="0_255_255;255_255_0;0_0_255;0_255_0;255_255_0;
var splitCoordColor=coordColr.split(";");
for ( var i = 0; i < splitCoord.length; i++ ) {
var position = new THREE.Vector3();
var xyz=splitCoord[i].split("_");
var col=splitCoordColor[i].split("_");
position.x = xyz[0];
position.y = xyz[1];
position.z = xyz[2];
var rotation = new THREE.Euler();
rotation.x = 0
rotation.y = 0;
rotation.z = 0;
var scale = new THREE.Vector3();
scale.x = 200 + 100;
scale.y = 200 + 100;
scale.z = 200 + 100;
quaternion.setFromEuler(rotation, false );
matrix.compose( position, quaternion, scale);
col[0]=col[0]/255;
col[1]=col[1]/255;
col[2]=col[2]/255;
applyVertexColors(geom, color.setRGB(col[0], col[1], col[2]));
geometry.merge(geom, matrix);
// give the geom's vertices a color corresponding to the "id"
applyVertexColors( geom, color.setHex( i ) );
pickingGeometry.merge( geom, matrix );
pickingData[ i ] = {
position: position,
rotation: rotation,
scale: scale
};
}
var drawnObject = new THREE.Mesh( geometry, defaultMaterial );
scene.add(drawnObject);
pickingScene.add( new THREE.Mesh( pickingGeometry, pickingMaterial ) );
highlightBox = new THREE.Mesh(
new THREE.BoxGeometry( 0.01, 0.01, 0.01 ),
new THREE.MeshLambertMaterial( { color: 0xffff00 }
) );
scene.add( highlightBox );
renderer = new THREE.WebGLRenderer( );
//renderer.setClearColor( 0xffffff );
renderer.setPixelRatio( window.devicePixelRatio );
renderer.setSize(800, 800);
renderer.sortObjects = false;
container.appendChild(renderer.domElement);
stats = new Stats();
stats.domElement.style.position = 'absolute';
stats.domElement.style.top = '0px';
container.appendChild( stats.domElement );
//renderer.domElement.addEventListener('mousemove', onMouseMove );
}
function onMouseMove( e ) {
mouse.x = e.clientX;
mouse.y = e.clientY;
}
function animate() {
requestAnimationFrame( animate );
render();
stats.update();
}
function pick() {
//render the picking scene off-screen
renderer.render( pickingScene, camera, pickingTexture );
//create buffer for reading single pixel
var pixelBuffer = new Uint8Array( 4 );
//read the pixel under the mouse from the texture
renderer.readRenderTargetPixels(pickingTexture, mouse.x, pickingTexture.height - mouse.y, 1, 1, pixelBuffer);
//interpret the pixel as an ID
var id = ( pixelBuffer[0] << 16 ) | ( pixelBuffer[1] << 8 ) | ( pixelBuffer[2] );
var data = pickingData[ id ];
if (data) {
//move our highlightBox so that it surrounds the picked object
if ( data.position && data.rotation && data.scale ){
highlightBox.position.copy( data.position );
highlightBox.rotation.copy( data.rotation );
highlightBox.scale.copy( data.scale ).add( offset );
highlightBox.visible = true;
}
} else {
highlightBox.visible = false;
}
}
function render() {
controls.update();
pick();
renderer.render( scene, camera );
}
any help is greatly appreciated..
Thanks
You can pass in the canvas as an argument to the TrackballsControls constructor.
var controls = new THREE.TrackballControls(camera, renderer.domElement);
That should solve the problem.
EDIT: included a working example,
var scene = new THREE.Scene();
var camera = new THREE.PerspectiveCamera(75, 400 / 300, 1, 1000);
var renderer = new THREE.WebGLRenderer();
renderer.setSize(400, 300);
document.body.appendChild(renderer.domElement);
var controls = new THREE.TrackballControls(camera, renderer.domElement);
controls.rotateSpeed = 1.0;
controls.zoomSpeed = 4;
controls.panSpeed = 0.8;
controls.noZoom = false;
controls.noPan = false;
controls.staticMoving = true;
controls.dynamicDampingFactor = 0.3;
var geometry = new THREE.BoxGeometry(1, 1, 1);
var material = new THREE.MeshBasicMaterial({
color: 0x00ff00
});
var cube = new THREE.Mesh(geometry, material);
scene.add(cube);
camera.position.z = 5;
var render = function() {
requestAnimationFrame(render);
controls.update();
cube.rotation.x += 0.1;
cube.rotation.y += 0.1;
renderer.render(scene, camera);
};
render();
could not get your code to run at all so..

ThreeJS shadows do not function as expected

I am having a simple three.js scene and have added some spheres. I want to achieve a simple lighting and shadowing effect but I get strange dark shapes on my spheres.
I guess something is wrong with my settings : jsFiddle
this is the core of this simple piece of code :
// renderer
renderer = new THREE.WebGLRenderer();
renderer.setSize( window.innerWidth, window.innerHeight );
renderer.physicallyBasedShading = true;
renderer.shadowMapEnabled = true;
renderer.shadowMapSoft = true;
renderer.shadowMapEnabled = true;
renderer.shadowMapSoft = true;
renderer.shadowCameraNear = 0.1;
renderer.shadowCameraFar = 1000;
renderer.shadowCameraFov = 100;
renderer.shadowMapBias = 0.0039;
renderer.shadowMapDarkness = 0.5;
renderer.shadowMapWidth = 1024;
renderer.shadowMapHeight = 1024;
document.body.appendChild( renderer.domElement );
// scene
scene = new THREE.Scene();
// camera
camera = new THREE.PerspectiveCamera( 40, window.innerWidth / window.innerHeight, 1, 1000 );
camera.position.set( 150, 50, 150 );
// controls
controls = new THREE.OrbitControls( camera );
// lights
var light = new THREE.SpotLight( 0xF5F6CE, 1 );
light.position.set( 300, 300, 60 );
light.castShadow = true;
light.shadowMapWidth = 1024; // power of 2
light.shadowMapHeight = 1024;
light.shadowCameraNear = 200; // keep near and far planes as tight as possible
light.shadowCameraFar = 500; // shadows not cast past the far plane
light.shadowCameraFov = 20;
light.shadowBias = -0.00022; // a parameter you can tweak if there are artifacts
light.shadowDarkness = 0.8;
light.shadowCameraVisible = true;
scene.add( light );
// axes
scene.add( new THREE.AxisHelper( 200 ) );
for (var x = 0; x < 3; x++) {
for (var y = 0; y < 3; y++) {
for (var z = 0; z < 3; z++) {
var sphere = new THREE.Mesh(new THREE.SphereGeometry(10,32, 32), new THREE.MeshPhongMaterial({ color: "#FFFF00", side: THREE.DoubleSide, })); // with MeshLambertMaterial still not working
sphere.position.set(20*x, 20*y, 20*z);
castShadow = true;
sphere.castShadow = true;
sphere.receiveShadow = true ;
scene.add(sphere);
}
}
}
The blackish artifacts are coming from the two-sidedness of the spheres combined with shadow mapping.
Remove side: THREE.DoubleSide from their material definition and you should be good.
http://jsfiddle.net/Us54P/331/
You can also get different results with tweaking the shading parameters, for example, you can add renderer.shadowMapType = THREE.PCFSoftShadowMap;. See here: http://jsfiddle.net/Us54P/332/
Could also be a bug (or a debugging feature), because it disappears when you disable light.shadowCameraVisible = true;: http://jsfiddle.net/Us54P/334/

Three.js shadow corresponds to bounding box, not actual model shape

I'm trying to get a correct-looking shadow for a dinosaur model (Three.js JSON format).
The shadow displays, but it's rectangular, as though it thinks the dinosaur model is just a simple cuboid (as screengrabbed below).
How can I generate a shadow that corresponds to the actual shape of the dinosaur?
I've checked in Blender that there's no containing box visible in the OBJ model, from which I created the JSON model file by using the Three.js OBJ to JSON converter.
Here's some snippets of the relevant pieces of code:
/* Dinosaur! */
var loader = new THREE.JSONLoader();
var filePath = 'models/trex/trex.js';
loader.load(filePath, function(geometry, materials) {
mesh = new THREE.Mesh( geometry,
new THREE.MeshFaceMaterial( materials ) );
mesh.scale.set(1000, 1000, 1000);
mesh.position.set( 0, -75, 0 );
mesh.rotation.y = Math.PI;
mesh.castShadow = true;
scene.add( mesh );
});
...
/* Lights */
var ambientLight = new THREE.AmbientLight( 0xFFFFFF );
scene.add( ambientLight );
var directionalLight = new THREE.DirectionalLight( 0xeeeeff, 0.5 );
directionalLight.position.set(0, 0, 1);
scene.add( directionalLight );
var spotlight = new THREE.SpotLight(0xFFFFFF, 0.2, 2000);
spotlight.position.set( 50, 100, 0 );
spotlight.target.position.set( 0, 0, 0 );
spotlight.castShadow = true;
scene.add( spotlight );
...
/* Renderer */
renderer = new THREE.WebGLRenderer();
renderer.setClearColor(0xffffff);
renderer.setSize(renderWidth, renderHeight);
renderer.shadowMapEnabled = true;
renderer.shadowMapSoft = true;
container.appendChild(renderer.domElement);
...
/* Terrain */
var img = new Image();
img.onload = function() {
heightData = buildHeightData(img);
var plane = new THREE.PlaneGeometry( 100, 100, HEIGHT_MAP_SIZE-1, HEIGHT_MAP_SIZE-1 );
var l = plane.vertices.length;
for( var i=0; i < l; i++ ) {
// We change z because by default the plane will be placed vertically.
// We rotate it afterwards (so the effect on z will end up being the
// effect on y).
plane.vertices[i].z = heightData[i] * 10;
}
terrainMesh = buildMesh(
{
geometry: plane,
scale: 100,
x: 0,
y: -370,
z: -1050,
material: terrainMaterial
} );
terrainMesh.rotation.x = -Math.PI / 2;
terrainMesh.receiveShadow = true;
scene.add( terrainMesh );
};
img.src = 'img/heightmap.jpg';
p.s. I'm using Three.js v66.
Oops. I had jumped to an incorrect conclusion. It was just because the spotlight was not high enough (see new screengrab, with shadowCameraVisible = true).

Resources