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
Related
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);
What I'm trying to achieve is a rotation of the geometry around pivot point and make that the new definition of the geometry. I do not want te keep editing the rotationZ but I want to have the current rotationZ to be the new rotationZ 0.
This way when I create a new rotation task, it will start from the new given pivot point and the newly given rad.
What I've tried, but then the rotation point moves:
// Add cube to do calculations
var box = new THREE.Box3().setFromObject( o );
var size = box.getSize();
var offsetZ = size.z / 2;
o.geometry.translate(0, -offsetZ, 0)
// Do ratation
o.rotateZ(CalcUtils.degreeToRad(degree));
o.geometry.translate(0, offsetZ, 0)
I also tried to add a Group and rotate that group and then remove the group. But I need to keep the rotation without all the extra objects. The code I created
var box = new THREE.Box3().setFromObject( o );
var size = box.size();
var geometry = new THREE.BoxGeometry( 20, 20, 20 );
var material = new THREE.MeshBasicMaterial( { color: 0xcc0000 } );
var cube = new THREE.Mesh( geometry, material );
cube.position.x = o.position.x;
cube.position.y = 0; // Height / 2
cube.position.z = -size.z / 2;
o.position.x = 0;
o.position.y = 0;
o.position.z = size.z / 2;
cube.add(o);
scene.add(cube);
// Do ratation
cube.rotateY(CalcUtils.degreeToRad(degree));
// Remove cube, and go back to single object
var position = o.getWorldPosition();
scene.add(o)
scene.remove(cube);
console.log(o);
o.position.x = position.x;
o.position.y = position.y;
o.position.z = position.z;
So my question, how do I save the current rotation as the new 0 rotation point. Make the rotation final
EDIT
I added an image of what I want to do. The object is green. I have a 0 point of the world (black). I have a 0 point of the object (red). And I have rotation point (blue).
How can I rotate the object around the blue point?
I wouldn't recommend updating the vertices, because you'll run into trouble with the normals (unless you keep them up-to-date, too). Basically, it's a lot of hassle to perform an action for which the transformation matrices were intended.
You came pretty close by translating, rotating, and un-translating, so you were on the right track. There are some built-in methods which can help make this super easy.
// obj - your object (THREE.Object3D or derived)
// point - the point of rotation (THREE.Vector3)
// axis - the axis of rotation (normalized THREE.Vector3)
// theta - radian value of rotation
// pointIsWorld - boolean indicating the point is in world coordinates (default = false)
function rotateAboutPoint(obj, point, axis, theta, pointIsWorld){
pointIsWorld = (pointIsWorld === undefined)? false : pointIsWorld;
if(pointIsWorld){
obj.parent.localToWorld(obj.position); // compensate for world coordinate
}
obj.position.sub(point); // remove the offset
obj.position.applyAxisAngle(axis, theta); // rotate the POSITION
obj.position.add(point); // re-add the offset
if(pointIsWorld){
obj.parent.worldToLocal(obj.position); // undo world coordinates compensation
}
obj.rotateOnAxis(axis, theta); // rotate the OBJECT
}
After this method completes, the rotation/position IS persisted. The next time you call the method, it will transform the object from its current state to wherever your inputs define next.
Also note the compensation for using world coordinates. This allows you to use a point in either world coordinates or local space by converting the object's position vector into the correct coordinate system. It's probably best to use it this way any time your point and object are in different coordinate systems, though your observations may differ.
As a simple solution for anyone trying to quickly change the pivot point of an object, I would recommend creating a group and adding the mesh to the group, and rotating around that.
Full example
const geometry = new THREE.BoxGeometry();
const material = new THREE.MeshBasicMaterial({ color: 0xff0000 });
const cube = new THREE.Mesh(geometry, material);
scene.add(cube)
Right now, this will just rotate around its center
cube.rotation.z = Math.PI / 4
Create a new group and add the cube
const group = new THREE.Group();
group.add(cube)
scene.add(group)
At this point we are back where we started. Now move the mesh:
cube.position.set(0.5,0.5,0)
Then move the group
group.position.set(-0.5, -0.5, 0)
Now use your group to rotate the object:
group.rotation.z = Math.PI / 4
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.
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.