Finally, i need a normal shadow. But using Spot / Directional lights with Lambert / Phong materials i get no proper result:
picture with examples
When i use Spot Light with Lambert Material, this material don't react to light (pic. 1, 2).
When i use Spot Light with Phong Material, i get shadow, like pattern, not smooth (pic. 3, 4).
When i use Directional Light with Lambert / Phong material, i get smooth, but not proper shadow (pic. 5 - 8).
I use this preferences for shadows:
renderer.shadowMap.enabled = true;
renderer.shadowMapSoft = true;
renderer.shadowCameraNear = 3;
renderer.shadowCameraFar = camera.far;
renderer.shadowCameraFov = 50;
renderer.shadowMapBias = 0.0039;
renderer.shadowMapDarkness = 0.5;
renderer.shadowMapWidth = 1024;
renderer.shadowMapHeight = 1024;
And this for lights:
var ambientLight =new THREE.AmbientLight( 0x555555 );
scene.add(ambientLight);
and
var spotLight = new THREE.SpotLight( 0xffffff);
spotLight.position.set( 12, 22, -25 );
spotLight.castShadow = true;
scene.add(spotLight );
and
var directionalLight=new THREE.DirectionalLight( 0xffffff, 0.5 );
directionalLight.position.set( 12, 22, -25 );
directionalLight.castShadow = true;
scene.add(directionalLight);
Also, i use the same castShadow and receiveShadow propertyes for all of this examples.
If it needing, other code can be watched as sourcecode of this page:
Spot Light, Lambert Material
This code the same for all of my examples, excluding light - material combinations.
Realtime shadows in Three.js are tricky in general. Here are some basics to follow to improve your example.
Limit the shadow.camera-frustum:
spotLight.shadow.camera.near = 25;
spotLight.shadow.camera.far = 50;
spotLight.shadow.camera.fov = 30;
Increase the shadow.mapSize:
spotLight.shadow.mapSize.width = 2048;
spotLight.shadow.mapSize.height = 2048;
Use shadowBias to reduce artefacts:
spotLight.shadowBias = -0.003;
The result isnt perfect because now light seams inside the room are showing up. It requires more tweaking and trade-ofs, but maybe its good enough for your needs:
https://jsfiddle.net/wbrj8uak/8/
Just leaving a comment here, regarding 2pha´s updated example and why im restoring it:
setting the camera position results in a disappearing shadow inside the room. This is sure confusing for the poster who wants to have a shadow inside, thats why i just left his code the way he supplied it.
Related
I have no experience with javascript or three.js, but recently I am working on modifying a three.js scene.
I have added shadow over my GLTF model. And the shadow displays nicely over my model.
But I also see these yellow and red lines beneath my model. I don't know what they are or how to remove them.
Bellow I will add some of the code I use:
// LOAD TEXTURE - Cube
const loader = new THREE.CubeTextureLoader();
loader.setPath( 'textures/3/' );
textureCube = loader.load( [ 'px.png', 'nx.png', 'py.png', 'ny.png', 'pz.png', 'nz.png' ] );
textureCube.encoding = THREE.sRGBEncoding;
scene.background = textureCube;
// Cube Light
const ambient = new THREE.AmbientLight( 0x404040 );
scene.add( ambient );
let light = new THREE.DirectionalLight(0xfffffff, 0.5);
light.position.set(0, 1, 0);
light.castShadow = true;
light.shadow.mapSize.width = 512;
light.shadow.mapSize.height = 512;
light.shadow.camera.near = 0.5;
light.shadow.camera.far = 500
scene.add(new THREE.CameraHelper(light.shadow.camera));
scene.add(light);
Below you can also see a screenshot with these yellow and red lines that I would like to remove:
What you're seeing there is a camera helper.
You have added it to your scene when you added the line
scene.add(new THREE.CameraHelper(light.shadow.camera));
If you don't want to see it, then don't add that line.
I'm working on building up quite a complex static render in the browser with three.js and have gotten stuck early in the process trying to produce correct shadows with a single THREE.DirectionalLight representing the sun in my scene. All the geometry (contained in another .js file) has shadows enabled. The green sphere is for debugging purposes and is translated (50,0,50) to the center of the plane to represent the target for the camera and location of DirectionalLight.target. The directional light position and main camera position did set correctly.
My theory on why the shadows aren't working is because the orthogonal camera representing the shadow camera is pointing off in the wrong direction. I failed yesterday to figure out and solve the behaviour of the directional light helper (white line to the origin) and shadow camera helper (right).
I'm assuming the correct orientation, and the orientation I'm aiming for, has the directional light helper and shadow camera helper aligned to the center of the plane. After so much research yesterday, my shadow camera doesn't seem to automatically pick up the light position / light target vector. Why are they still anchored to the origin?
Does anyone have any suggestions about how to fix the DirectionalLight.target in my scene? Why are the DirectionalLightHelper and CameraHelper inconsistent?
// Set up
const canvus = document.getElementById('canvus');
const scene = new THREE.Scene();
const renderer = new THREE.WebGLRenderer({ canvas, antialias: true });
renderer.shadowMap.enabled = true;
renderer.shadowMap.type = THREE.PCFShadowMap;
//Camera
const camera = new THREE.PerspectiveCamera(50, window.innerWidth / window.innerHeight, 1, 500);
camera.position.set(200, 100, 100);
camera.lookAt(50, 0, 50);
// Lighting
const directionalLight = new THREE.DirectionalLight(0xffffff, 0.8);
directionalLight.position.set(100, 200, 200);
directionalLight.target.position.set(50, 0, 50);
directionalLight.castShadow = true;
directionalLight.shadow.bias = 0.0001;
directionalLight.shadow.mapSize.width = 1024; // default
directionalLight.shadow.mapSize.height = 1024; // default
const view_n = 50;
directionalLight.shadow.camera = new THREE.OrthographicCamera(
-view_n,
view_n,
view_n,
-view_n,
60,
150
);
scene.add(directionalLight, directionalLight.target);
//helpers
const lighthelper = new THREE.DirectionalLightHelper(directionalLight, 10);
const camerahelper = new THREE.CameraHelper(directionalLight.shadow.camera);
scene.add(lighthelper);
scene.add(camerahelper);
//Main Render
createBasicGeometry(scene); // from geometry.js
createGroundPlane(scene); // from geometry.js
renderer.render(scene, camera);
Update 2020-1-5
I had initially tried setting the camera and also found examples of people setting a new ortho shadow camera directly. As I'm motivated to overcome the issue, and for thoroughness, I updated my code to reflect the suggestion and unfortunately the problem persists. I've re-checked that all mesh geometry is set to both object.receiveShadow = true and object.castShadow = true with the MeshPhongMaterial. It's completely confounding why directionalLight.target.position.set(50, 0, 50) is not updating as expected. What is the cause of this behaviour?
// Updated Lighting
const view_n = 50;
directionalLight.castShadow = true;
directionalLight.shadow.bias = 0.0001;
directionalLight.shadow.camera.right = view_n;
directionalLight.shadow.camera.left = -view_n;
directionalLight.shadow.camera.top = view_n;
directionalLight.shadow.camera.bottom = -view_n;
directionalLight.shadow.camera.near = 60;
directionalLight.shadow.camera.far = 150;
directionalLight.shadow.mapSize.width = 1024; // default
directionalLight.shadow.mapSize.height = 1024; // default
scene.add(directionalLight, directionalLight.target);
When I dump the directionalLight I get the target position I expected, though not aligned correctly in the scene. While the camera position gives another strange results.
console.log(directionalLight.target.position);
//Vector3 {x: 50, y: 0, z: 50, isVector3: true}
console.log(directionalLight.shadow.camera.position);
directionalLight.shadow.camera = new THREE.OrthographicCamera(
-view_n,
view_n,
view_n,
-view_n,
60,
150
);
Please do not overwrite the camera reference of LightShadow.camera. Configure the directional light like so:
dirLight.castShadow = true;
dirLight.shadow.camera.top = view_n;
dirLight.shadow.camera.bottom = - view_n;
dirLight.shadow.camera.left = - view_n;
dirLight.shadow.camera.right = view_n;
dirLight.shadow.camera.near = 60;
dirLight.shadow.camera.far = 150;
Besides shadow casting only works if all shadow casting objects (like your boxes) have castShadow set to true. All shadow receiving objects (like your floor) must set receiveShadow to true.
Perhaps because I'm doing this render statically without an animation loop this problem cropped up but was solved by inserting updateMatrixWorld to the directional light target. (Unforuately I wasn't able to update the shadow's camerahelper but at least the shadows are now working as expected.)
directionalLight.target.updateMatrixWorld();
scene.add(directionalLight);
scene.add(directionalLight.target);
I have a large plane with a texture map in three.js and I'm finding that the default settings I'm using cause too much blurring in the mid-distance. I want to increase the DOF so more of the floor material is in focus (especially along the right side).
http://i.imgur.com/JBYtFk6.jpg
Original: http://jsfiddle.net/5L5vxjkm/4/
Performance is not a factor so anything that improves the texture fidelity and/or focus is acceptable provided it works on latest Firefox in Xvfb (ie, using OS mesa drivers).
I did attempt to adapt http://threejs.org/examples/webgl_postprocessing_dof.html but it isn't giving me the expected results (still too blurry):
With DOF Postprocessing: http://jsfiddle.net/u7g48bt2/1/
The abbreviated code is below (see jsFiddle link for complete source)
doRender = function() {
renderer = new THREE.WebGLRenderer({antialias:true, preserveDrawingBuffer:true});
FOV = 60;
camera = new THREE.PerspectiveCamera(FOV, WIDTH/HEIGHT, .1, 8000);
camera.position.x = -100;
camera.position.y = 300;
camera.position.z = 1000;
camera.lookAt(new THREE.Vector3( 0, 300, 0 )); // look down and center
// Add Floor planes
// FLOOR
floorTexture.needsUpdate = true;
var floorMaterial = new THREE.MeshPhongMaterial( { map: floorTexture, side: THREE.DoubleSide } );
var floorGeometry = new THREE.PlaneBufferGeometry(4*1024, 4*1024, 256, 256);
var floor = new THREE.Mesh(floorGeometry, floorMaterial);
floor.doubleSided = true;
floor.rotation.x = Math.PI / 2;
floor.rotation.z = Math.PI / 3.9; // increase to rotate CCW
scene.add(floor);
var moreFloor2 = floor.clone();
moreFloor2.translateY(-4*1024);
scene.add(moreFloor2);
}
window.onload = function() {
// Enable cross-origin access to images
THREE.ImageUtils.crossOrigin = '';
floorTexture = THREE.ImageUtils.loadTexture('http://i.imgur.com/iEDVgsN.jpg?1', THREE.UVMapping, doRender);
};
Solution was simple in the end:
floorTexture.anisotropy = renderer.getMaxAnisotropy();
Which sets anisotropy to 16 I think.
UPDATE: Works on FF for Windows but under Xvfb / Mesa renderer.maxAnisotropy returns 0. Any workarounds?
UPDATE 2: It LIES! Manually setting floorTexture.anisotropy to values up to 16 actually works, meaning the maxAnisotropy returned by three.js under xvfb/mesa is plain wrong. Therefore this solution does work after all with a minor change:
floorTexture.anisotropy = 16;
UPDATE 3: My mistake! Anisotropic was NOT working. Solution was to switch the backend mesa driver to one that does support it:
DISPLAY=:5 LIBGL_ALWAYS_SOFTWARE=1 GALLIUM_DRIVER=softpipe firefox &
Many thanks to glennk on dri-devel#irc.freenode.org for this fix.
I'm pretty new to 3d and to threejs and I can't figure out how I can get a PlaneGeometry to show individually illuminated polygons i.e. receive shadows or show reflection. What I basically do is taking a PlaneGeometry applying some noise to every z value of the vertices. Then I have a simple directional light in my scene which is supposed to make the emerging noise pattern on the plane visible. I tried different things like plane.castShadow = true or renderer.shadowMapEnabled = true without success. Am I just missing a simple option or is this way more complicated than I think?
Here's are the relevant pieces of my code
renderer.setSize(width, height);
renderer.setClearColor(0x111111, 1);
...
var directionalLight = new THREE.DirectionalLight( 0xffffff, 0.9);
directionalLight.position.set(10, 2, 20);
directionalLight.castShadow = true;
directionalLight.shadowCameraVisible = true;
scene.add( directionalLight );
var geometry = new THREE.PlaneGeometry(20, 20, segments, segments);
var index = 0;
for(var i=0; i < segments + 1; i++) {
for(var j=0; j < segments + 1; j++) {
zOffset = simplex.noise2D(i * xNoiseScale, j * yNoiseScale) * 5;
geometry.vertices[index].z = zOffset;
index++;
}
}
var material = new THREE.MeshLambertMaterial({
side: THREE.DoubleSide,
color: 0xf50066
});
var plane = new THREE.Mesh(geometry, material);
plane.rotation.x = -Math.PI / 2.35;
plane.castShadow = true;
plane.receiveShadow = true;
scene.add(plane);
This is the output I get. Obviously the plane is aware of the light because the bottom side is darker than the upper side but there is no sign of any individual polygons receiving individual lightening and no 3d structure is visible. Interestingly when I put in a different geometry like a BoxGeometry individual polygons are illuminated individually (see 2nd image). Any ideas?
Ok I figured it out thanks to this post. The trick is to use the THREE.FlatShading shader on the material. Important to note is that after every update of the vertices two things need to be done. Before rendering geometry.normalsNeedUpdate must be set to true so the renderer also incorporates the newly oriented vertices. Also geometry.computeFaceNormals() needs to be called before rendering because when you alter the vertices the normals are not the same anymore.
I have the following problem. When I use Three.js point light like this:
var color = 0xffffff;
var intensity = 0.5;
var distance = 200;
position_x = 0;
position_y = 0;
position_z = 0;
light = new THREE.PointLight(color, intensity, distance);
light.position.set(position_x, position_y, position_z);
scene.add(light);
It works as expected when there is a "small" object (mesh) positioned close to the light on the scene. However, when there is a large object (let us say a floor):
var floorTexture = new THREE.ImageUtils.loadTexture( 'floor.jpg' );
floorTexture.wrapS = floorTexture.wrapT = THREE.RepeatWrapping;
floorTexture.repeat.set( 1, 1);
var floorMaterial = new THREE.MeshBasicMaterial( { map: floorTexture, side: THREE.DoubleSide } );
var floorGeometry = new THREE.PlaneGeometry(1000, 1000, 10, 10);
var floor = new THREE.Mesh(floorGeometry, floorMaterial);
floor.position.y = -0.5;
floor.rotation.x = Math.PI / 2;
scene.add(floor);
Then the light will not be shown on it. At first I thought it is due to the fact that the floor center is positioned further away from the point light so the point light cannot reach it with the distance set to 200 (even though part of the floor is closer than the mentioned distance). Therefore I have tryied to increase this distance - no luck.
There is a workaround to create a floor out of small parts. Then the point light again works as expected but there is a problem with this approach - namely it drastically decreases FPS due to the large number of "floor objects" to be rendered.
My guess is that I am missing something. I know that there are other types of light which cover the whole scene but I am trying to create a lamp, so I think I need to use a point light. But I might be wrong. Any help or hint how to make this work would be appreciated.
MeshBasicMaterial does not support lights. Use MeshPhongMaterial.
MeshLambertMaterial also supports lights, but it is not advisable in your case for reasons explained here: Three.js: What Is The Exact Difference Between Lambert and Phong?.
three.js r.66