I would like to apply the three.js script TrackballControls to a moving object in a way that preserves the ability to zoom and rotate the camera while the object moves through the scene. For example, I would like to be able to have the camera "follow" a moving planet, while the user retains the ability to zoom and rotate around the planet. Here is a basic jsfiddle:
http://jsfiddle.net/mareid/8egUM/3/
(The mouse control doesn't actually work on jsfiddle for some reason, but it does on my machine).
I would like to be able to attach the camera to the red sphere so that it moves along with it, but without losing the ability to zoom and rotate. I can get the camera to follow the sphere easily enough by adding lines like these to the render() function:
mouseControls.target = new THREE.Vector3(object.position);
camera.position.set(object.position.x,object.position.y,object.position.z+20);
But if I do that, the fixed camera.position line overrides the ability of TrackballControls to zoom, rotate, etc. Mathematically, I feel like it should be possible to shift the origin of all of the TrackballControls calculations to the centre of the red sphere, but I can't figure out how to do this. I've tried all sorts of vector additions of the position of the red sphere to the _this.target and _eye vectors in TrackballControls.js, to no avail.
Thanks for your help!
I'd recommend OrbitControls rather than TrackballControls. My answer here applies primarily to OrbitControls; the same principle might also work with TrackballControls (I have not looked at its code in a while).
If you look at how OrbitControls works you should notice it uses this.object for the camera, and this.target for where the camera is looking at, and it uses that vector (the difference between them) for some of its calculations. Movement is applied to both positions when panning; only the camera is moved when rotating. this.pan is set to shift the camera, but out of the box it only deals with a panning perpendicular to the this.object to this.target vector because of the way it sets the panning vector.
Anyway, if you subtract the vector from object.position to controls.target and set controls.pan to that vector, that should make it jump to that object:
Fixed example code:
Add a helper to OrbitControls.js which has access to its local pan variable:
function goTo( vector ) {
// Cloning given vector since it would be modified otherwise
pan.add( vector.clone().sub( this.target ) );
}
Your own code:
function animate() {
requestAnimationFrame(animate);
mouseControls.goTo( object.position ); // Call the new helper
mouseControls.update();
render();
}
You'll also probably want to set mouseControls.noPan to true.
Related
I'm using a PlaneGeometry as water, and have added a ship (gltf model) on it. Problem is, when the boat slightly rests into the water, the water is shown inside the boat, even though the boat is afloat. Is there a way to clip the water when a boat (or other models/objects) intersect with it with motion?
Every material in Three.js has an AlphaMap property that you can use to change the opacity of the mesh. So you could draw a black rectangle where the boat is to "cut out" that part of the water plane with an opacity of 0.
I presume your boat is going to be moving, so your texture would also need to move this black rectangle. To solve this, you could use an HTML <canvas> element to draw and move the black rectangle. Then you could use THREE.CanvasTexture to turn the <canvas> into a texture for your plane's alpha.
const drawingCanvas = document.getElementById( 'drawing-canvas' );
const canvasTexture = new THREE.CanvasTexture( drawingCanvas );
const waterMaterial = new THREE.MeshBasicMaterial({
transparent: true,
alphaMap: squareTexture
});
See this working demo for how to use a 2D canvas as a texture in your 3D scene. When you draw on the top-left square, you'll see it being applied to the cube. You could copy this approach, but instead of assigning it to material.map, you'd use it on material.alphaMap.
You could check for the position of the hull vertices of the ship each frame and, given enough points on the PlaneGeometry, you could displace all vertices of the surface inside the ship hull to the y coordinate of the nearest hull vertex. This could probably also be done by a custom shader, which is probably a more efficient solution.
#Marquizzo's idea with the alpha channel is probably a better solution, too, since you don't really want to simulate the displacement of the water, as I assume, but simply get rid of the display of water inside the ship. In this case, I would place an orthographic camera above the ship, set the near and far clipping planes as close as possible to the plane, and use the alpha channel or rather CanvasTexture inside the alpha channel as rendertarget. This way, you'll get a real time alpha map that also reacts to rolling, pitching and heave of the ship.
Effect of floating is made with sin function, applied to y-coordinate. You can use the same principle to make gltf model(ship) move on any coordinate axis.
Example:
position.y = Math.sin(ship.userData.initFloating + t) * 0.15;
I have an interface I developed with three.js using the CSS3DObject rendering tool.
I have set the orbit to 0 to prevent rotating and limit my movement to panning and zooming.
Please note I'm also using Orbit Control.
I set the position of the camera to x=-2000 with the following code:
camera.position.x=-2000;
camera.position.z=4000;
When I do this, the camera moves positions but is still pointing to (0,0,0) resulting in a skewed look.
So I assume that I need to give it a vector
camera.up = new THREE.Vector3(0,1,0); //keeps the camera horizontal
camera.lookAt(new THREE.Vector3(2000,0,0)); //should point the camera straight forward
Please note that I'm still trying to find a good explanation of how setting up the lookAt works.
After a bit more research, it seems that the orbit control is overriding the camera.lookAt and as a result, doesn't do anything.
To achieve the panning I set the location of the camera x position equal to the value of the target.
I also removed the camera.up line.
var myCameraX = -2000;
var myCameraY = 500;
camera.position.x=myCameraX;
camera.position.y=myCamerYa;
control.target.set(myCameraX,myCameraY,0);
Hope that helps someone.
Three.js version: r79
Basically, I want to have a 3D object (a mesh created with THREE.TextGeometry) act like it's in 2D space but is always in the same exact place on the screen (never moves with the camera, no matter if I zoom or pan). Is there a way to do this?
I'm actually not quite sure how unless I make what I feel is a giant hack and update the coordinates of the text mesh every time there is a mouse scroll event or pan event.
One solution is to add the mesh as a child of the camera.
scene.add( camera ); // required, since the camera has a child
camera.add( mesh );
mesh.position.set( 0, 0, - 100 ); // or whatever
three.js r.79
I'm trying to manage a 3Dobject that visualize the controls target in order to make rotation and zoom more immediatly understandable ...
I've made a sample here with an AxisHelper which is supposed to indicate the target and to keep the same size. In OrbitControls.js there are comments on each line I've added.
As you can see if you pan and zoom (right click and scroll) it manages cursors too, but the 'helper' has two problems :
the position and scale of the helper is set after the renders, that why it seems to be somewhat elastic ... And if I place the position/scale updates into the scope.update() function that's the same thing.
the function bellow scales the helper to a constant size, it computes a World/View scale at a defined point (the control's target) from a unit vector. But it seems it's not the good solution because when you scroll to the max zoom the helper is growing.
var point = new THREE.Vector3( 1, 0, 0 );
point = point.applyMatrix4( scope.object.matrixWorld );
var scale = point.distanceTo( scope.target );
helper.scale.set(scale, scale, scale);
So if you have an idea to achieve this you are welcome ...
i'm currently developing something similar with threejs and i think that:
I can't see the demo, my proxy not allow it.
I think that is correct...if the object helper is a mesh or a sprite added to the scene when orbit controls zoom the camera became more near to the objects in the scene, the objects dimension remain the same.
I'm really new in this stuff. I want to make a simple 3D scene, where i can fly around with PointerLockControls, but i want also to have some kind of flashlight. So spot light should point same direction as camera does.
I have made spotlight to follow camera but its target is bound to 0,0,0.
What is the best way to achieve this?
Thank you.
The SpotLight target is an Object3D, not a Vector3.
spotlight.target = myObject;
The best solution in your case is to use a PointLight instead, and use this pattern:
scene.add( camera );
camera.add( pointLight );
If you still want to use a spotlight, then do something like this:
scene.add( camera );
camera.add( spotLight.target );
spotLight.target.position.set( 0, 0, -1 );
spotLight.position.copy( camera.position ); // and reset spotlight position if camera moves
It is not generally required that the camera be added as a child of the scene, but it is required in this case because the light is added as a child of the camera.
three.js r.69
I had the same problem which I solved as follows:
flashlight = new THREE.SpotLight(0xffffff,4,40);
camera.add(flashlight);
flashlight.position.set(0,0,1);
flashlight.target = camera;
Since a SpotLight's .target needs to be an object (and not a position) I found it mentally easier to simply place the flashlight directly behind the camera, and then aim it at the camera. Thus the light shines through the camera and lights up the things in front of it.
This approach is fine if you are after a flashlight effect where the flashlight is held close to the chest (central to the body) and not off on one side.
Inspired by WestLangley's solution above, I found out that spotlight.target and spotlight itself can be added as children to the same object, whether that is the camera or another object, like a car or a gun. Then they are positioned relative to the parent object, so that there is no need to keep copying the position from one object to another.
You could, for instance, do something like this:
scene.add(camera);
camera.add(gun);
gun.position.set(-30,-30,0);
gun.add(spotlight);
spotlight.position.set(0,0,30);
gun.add(spotlight.target);
spotlight.target.position.set(0,0,31);
And now the gun will, by default, follow the camera, and the spotlight will light up along the gun. If the gun is for some reason rotated (deflecting a bullet or crawling on the ground or whatever), the spotlight will rotate too. THREE is a nice piece of software. :-)
If you attach the spotlight to the camera and point it in the same direction as the camera and don't position it away from the center, then the light cone will look constantly circular. For many applications it looks cooler and more realistic that it changes shape dynamically in the projection. A small offset is all it takes (such as in my example above, though I haven't tested that one).