I have made a scene with three.js in which I make use of a PointLight.
var distance = 10000;
var intensity = 10;
var decay = 0;
var hex = 0xFFFFFF;
var light1 = new THREE.PointLight(hex, intensity, distance, decay);
I want to use shadows, so I added
light1.shadow.camera.near = 200;
light1.shadow.camera.far = 10000;
light1.shadow.camera.fov = 90;
light1.shadow.bias = -0.00022;
light1.shadow.mapSize.width = 1024;
light1.shadow.mapSize.height = 1024;
Until here everything worked perfect. However, as soon as I include in the next line:
light1.castShadow = true;
Then the distance visibly changes from 10000 to 1000.
If I call the distance from light1 to the console, it is still 10000. Even though it is not like that in the canvas.
(I have changed it to a ~50-digit number with no change in distance)
Any suggestions on how to solve this?
You can try to create a shadow via THREE.Texture(), in this way the shadow will be an independent element and shouldn't influence the position of the PointLight.
var shadowTexture = new THREE.Texture(canvas);
shadowTexture.needsUpdate = true;
var shadowMaterial = new THREE.MeshBasicMaterial({map: shadowTexture, opacity: 0.5});
var shadowGeo = new THREE.PlaneBufferGeometry(100, 100, 100, 1);
// Locate the position of the shadows
mesh = new THREE.Mesh(shadowGeo, shadowMaterial);
mesh.position.y = 0;
mesh.rotation.x = - Math.PI / 2;
scene.add( mesh );
I am applying the shadow to a PlaneGeometry since I assume that the shadow is casted on the scene. In any case, give also a look at this example concerning shadows in a scene.
Related
Working with Three.js r113, I'm creating walls from coordinates of a blueprint dynamically as custom geometries. I've set up the vertices, faces and faceVertexUvs already successfully. Now I'd like to wrap these geometries with a textured material, that repeats the texture and keeps the original aspect ratio.
Since the walls have different lengths, I was wondering which is the best approach to do this?
What I've tried so far is loading the texture once and then using different texture.repeat values, depending on the wall length:
let textures = function() {
let wall_brick = new THREE.TextureLoader().load('../textures/light_brick.jpg');
return {wall_brick};
}();
function makeTextureMaterial(texture, length, height) {
const scale = 2;
texture.wrapS = THREE.RepeatWrapping;
texture.wrapT = THREE.RepeatWrapping;
texture.repeat.set( length * scale, height * scale );
return new THREE.MeshStandardMaterial({map: texture});
}
I then call the above function, after creating the geometry and assign the returned materials to the material array to apply it to faces of front and back of each wall. Note: material.wall is an untextured MeshStandardMaterial for the other faces.
let scaledMaterial = [
makeTextureMaterial(textures.wall_brick, this.length.back, this.height),
makeTextureMaterial(textures.wall_brick, this.length.front, this.height),
material.wall
];
this.geometry.faces[0].materialIndex = 0; // back
this.geometry.faces[1].materialIndex = 0; // back
this.geometry.faces[2].materialIndex = 1; // front
this.geometry.faces[3].materialIndex = 1; // front
this.geometry.faces[4].materialIndex = 2;
this.geometry.faces[5].materialIndex = 2;
this.geometry.faces[6].materialIndex = 2;
this.geometry.faces[7].materialIndex = 2;
this.geometry.faces[8].materialIndex = 2;
this.geometry.faces[9].materialIndex = 2;
this.geometry.faces[10].materialIndex = 2;
this.geometry.faces[11].materialIndex = 2; // will do those with a loop later on :)
this.mesh = new THREE.Mesh(this.geometry, scaledMaterial);
What happens is that the texture is displayed on the desired faces, but it's not scaled individually by this.length.back and this.length.front
Any ideas how to do this? Thank you.
I have just found the proper approach to this. The individual scaling is done via faceVertexUvs, as West Langley answered here: https://stackoverflow.com/a/27098476/4355114
I'm trying to set the position of my directional light shadow camera to be the same as my main perspective camera in the render loop:
sunlight = new THREE.DirectionalLight(0xffffff, 1);
sunlight.position.x = camera.position.x ;
sunlight.position.y = 300;
sunlight.position.z = camera.position.z ;from top
sunlight.castShadow = true;
sunlight.shadow.mapSize.width = 2048 ;
sunlight.shadow.mapSize.height = 2048 ;
sunlight.shadow.bias = -0.0027;
sunlight.shadow.camera.left = -75;
sunlight.shadow.camera.right = 75;
sunlight.shadow.camera.top = 75;
sunlight.shadow.camera.bottom = -75;
sunlight.shadow.camera.position = camera.position;
sunlight.shadow.camera.far = sunlight.position.distanceTo(scene.position) + 20;
sunlight.shadow.camera.near = sunlight.position.distanceTo(scene.position) - (camera.position.y * 2);
sunlight.shadow.camera.updateProjectionMatrix();
function render() {
renderer.render(scene, camera);
sunlight.shadow.camera.position = camera.position;
sunlight.shadow.camera.updateProjectionMatrix();
}
However the shadow camera does not move at all. Is it possible to update the position of a shadow camera during run-time?
The shadow.camera is more of an internal component to the sunlight.
The way to move the sunlight so that it follows the camera is more something like this:
sunlight.position.copy( camera.position );
sunlight.target.copy( camera.position );
sunlight.position.y += 20;
this ^ should make the sunlight stay above the camera, pointing down, at a distance of 20 units.
Also your shadow.camera.near / far setup looks a little weird.
I would just set those to something like
light.shadow.camera.near = 0.5;
light.shadow.camera.far = 50.0;
..or something like that.
In three.js, I'm using PointerLock controls the make a basic first person shooter.
I use
function onDocumentMouseDown( event ) {
var raycaster = new THREE.Raycaster();
mouse3D.normalize();
controls.getDirection( mouse3D );
raycaster.set( controls.getObject().position, mouse3D );
var intersects = raycaster.intersectObjects( objects );
...
}
to detect a collision with an object, which means you "shot" the object.
Now, I want to visualize the path the bullet took. I was thinking about drawing a line from where the user is looking to, in direction of the raycaster, but I can't figure out how to do this... Anyone who can help me? I'm new to three.js, never thought drawing a line would be this hard.
Update:
I'm trying to draw a line using:
var geometry = new THREE.Geometry();
geometry.vertices.push(...);
geometry.vertices.push(...);
var line = new THREE.Line(geometry, material);
scene.add(line);
but I can't figure out what to put in place of the "..." . How can I detect which point the line should go to? And how to determine which point it starts from? The player is able to move and even jump so the starting point is always different too.
You can use the following (using r83):
// Draw a line from pointA in the given direction at distance 100
var pointA = new THREE.Vector3( 0, 0, 0 );
var direction = new THREE.Vector3( 10, 0, 0 );
direction.normalize();
var distance = 100; // at what distance to determine pointB
var pointB = new THREE.Vector3();
pointB.addVectors ( pointA, direction.multiplyScalar( distance ) );
var geometry = new THREE.Geometry();
geometry.vertices.push( pointA );
geometry.vertices.push( pointB );
var material = new THREE.LineBasicMaterial( { color : 0xff0000 } );
var line = new THREE.Line( geometry, material );
scene.add( line );
Codepen at: https://codepen.io/anon/pen/evNqGy
You can use something like this:
function animate_Line(frame, totalFrames) {
//Calculate how much of the line should be drawn every iteration
var delta = lineDistance/(totalFrames);
var deltaSpeed = delta * frame;
for(var i=0; i<f_Ray_List[0].length; i++) {
for(var j=0; j<f_Ray_List[1].length; j++) {
//Change Offsets
line.geometry.vertices[1].y = line.geometry.vertices[0].y - deltaSpeed;
//Update Rays = true (Make FRT rays draw-able)
line.geometry.verticesNeedUpdate = true;
}
}
}
where frame is the current frame (a counter in your animate function), totalFrames would be the amount of frames that the line would take to be animated. The lineDistance can be calculated by using this:
lineDistance = line.geometry.vertices[0].y - line.vertices[1].y; //Add this line where you create the line object.
and remember to call line.geometry.verticesNeedUpdate = true; in every line individually, so that the line would able to be animated.
Notice that this is only based on Line.y axis. This would not be great at first. I'm currently working on converting this to Polar coordinates instead but I have no idea what is going on hahah.
I'm having troubles with getting shadows to work in three.js . On the image below you can see a spotlight casting a shadow on an object with plane geometries. There is no object in between the spotlight near plane and the plane geometries of the wall.
What can cause such behavior?
The light is configured this way:
var spotLight = new THREE.SpotLight(0xFFFFFF, 1);
spotLight.position.set(0, -20, 100);
spotLight.target.position.set(0,0,0);
spotLight.shadowDarkness = 0.5;
spotLight.shadowCameraVisible = true;
spotLight.castShadow = true;
spotLight.shadowMapWidth = 1024;
spotLight.shadowMapHeight = 1024;
spotLight.shadowCameraNear = 10;
spotLight.shadowCameraFar = 200;
spotLight.shadowCameraFov = 10;
spotLight.shadowBias = 0.1;
scene.add(spotLight);
The rectangular meshes are BufferGeometries converted from PlaneGeometries with an png uv mapped image as the texture with .receiveShadow set to true.
THREE.js version r71.
Any help will be very much appreciated.
Maybe it is self-shadowing, try to play with spotLight.shadowBias. Usually, a value of 0.05 is used for avoid self-shadowing.
I think this picture best explains my issue:
First I translate the box along the red line. Next, I want the effect of rotation to be the blue line in a, but what's actually happening is more like the blue line in b. It feels like changing the rotation is always relative to the original object space, but the translation (despite happening first) is always relative to the parent, and doesn't really affect the geometry points in relation to the object space. I apologize if that's confusing; clearly I'm new at this.
The important part of the code which produces this effect is below. Please keep in mind that the orientation of the image is different than this code produces; the image is merely an example to show the effect clearly.
var objectContainer = new THREE.Object3D();
var tubeRadius = 100;
var tubeGeometry = new THREE.CylinderGeometry(tubeRadius, tubeRadius, tubeRadius * 3, 36, 1, false);
var tube = new THREE.Mesh(tubeGeomtry, material);
scene.add( tube );
var boxes = new THREE.Object3D();
var boxEdge = 50;
var boxGeometry = new THREE.CubeGeometry(boxEdge, boxEdge, boxEdge);
var box1 = new THREE.Mesh( boxGeometry, material );
box1.translateX(tubeRadius + boxEdge / 2 + 5);
box1.translateY(boxEdge / 2);
box1.rotation = new THREE.Vector3(0, 2*Math.PI/3*0, 0);
boxes.add(box1);
var box2 = box1.clone();
box2.rotation = new THREE.Vector3(0, 2*Math.PI/3*1, 0);
boxes.add(box2);
var box3 = box1.clone();
box3.rotation = new THREE.Vector3(0, 2*Math.PI/3*2, 0);
boxes.add(box3);
scene.add( boxes );
The only solution I can think of is to wrap each box in another object space and rotate about that, but it seems like excessive work. What is the preferred method to achieve the result I'm looking for?
There are several ways of doing what you want, but I think the easiest is like so:
// parent
parent = new THREE.Group();
scene.add( parent );
// pivots
var pivot1 = new THREE.Object3D();
var pivot2 = new THREE.Object3D();
var pivot3 = new THREE.Object3D();
pivot1.rotation.z = 0;
pivot2.rotation.z = 2 * Math.PI / 3;
pivot3.rotation.z = 4 * Math.PI / 3;
parent.add( pivot1 );
parent.add( pivot2 );
parent.add( pivot3 );
// mesh
var mesh1 = new THREE.Mesh( geometry, material );
var mesh2 = new THREE.Mesh( geometry, material );
var mesh3 = new THREE.Mesh( geometry, material );
mesh1.position.y = 5;
mesh2.position.y = 5;
mesh3.position.y = 5;
pivot1.add( mesh1 );
pivot2.add( mesh2 );
pivot3.add( mesh3 );
Then in your render loop:
parent.rotation.z += 0.01;
three.js r.147
Creating a compound object whose centre will be the point about which the inner objects rotate is one obvious answer, and would be very quick to write. Just create an Object3D and add your box to it.
A similar approach is covered by this question. It shifts the point of the vertices for an object, so it effectively has a new centre.
Alternatively, you can mess around with the matrices by hand. Try this:
var boxGeometry = new THREE.CubeGeometry(boxEdge, boxEdge, boxEdge);
var mr = new THREE.Matrix4();
var mt = new THREE.Matrix4();
mt.setPosition(new THREE.Vector3(0,tubeRadius,0));
var box1 = new THREE.Mesh( boxGeometry, material );
box1.applyMatrix(mt);
var box2 = box1.clone();
mr.makeRotationZ(2 * Math.PI /3);
box2.applyMatrix(mr);
boxes.add(box2);
var box3 = box1.clone();
mr.makeRotationZ(4 * Math.PI /3);
box3.applyMatrix(mr);
boxes.add(box3);
boxes.add(box1);
scene.add( boxes );