Three.js - Create 3D text that's unaffected by zooming and panning - javascript

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

Related

How to add an overlay-section using three.js and orbit-controls which reacts to movement of main view?

I want to add a section in one corner of my main view in which I display a small version of the main view.
I am programming a webpage using JavaScript with three.js. I have a main view window in which I display some geometries. There are rotatable and movable using OrbitControls.
In the corner of my main view I want an separate section, in which I can display a small cube, which rotates the same way my main-view rotates. But I shall not zoom in or out if I zoom the main view.
var orientationGeometry = new THREE.Mesh( geometry, material );
camera.add( orientationGeometry );
// in animate function:
orientationCube.rotation.x = controls.getPolarAngle();
orientationCube.rotation.y = controls.getAzimuthalAngle();
This rotates the small cube correctly and by adding this cube as a child to the camera it stays fixed on the screen. But when I zoom in or out this small Box as well zooms away from the camera.
Is there a way to make an extra section like shown in the attached image?
To render a second viewport on the screen you can enable WebGLRenderer.ScissorTest, set the desired scissor, scale the viewport accordingly and render the scene again. Don't forget to clearDepth() or nothing will get rendered.
Now, in order to get a different camera behavior, you need to add a second camera to the scene and update it depending on your needs. If you want it to rotate and move just like the full-screen camera, you need to update those parameters.
In order to keep a fixed zoom state, you can get the normalized position of the camera and multiply by the set distance you want to use.
function animate() {
// render full scene.
// ...
// setup scissor viewport.
renderer.clearDepth(); // important!
renderer.setScissorTest( true );
renderer.setScissor( 20, 20, insetWidth, insetHeight );
renderer.setViewport( 20, 20, insetWidth, insetHeight );
// update second camera.
camera2.position.copy( camera.position );
camera2.position.normalize().multiplyScalar( distance );
camera2.quaternion.copy( camera.quaternion );
// render small scene.
renderer.render( scene, camera2 );
renderer.setScissorTest( false );
}
Here's a working example https://jsfiddle.net/qwb39spx/

Three js plane geometry disappears when rotating

I'm trying to create a very simple scene containing a triangular planar face continuously rotating about the x axis.
Here's the code creating the geometry object, as indicated in this previous SO question:
// create triangular plane geometry
var geometry_1 = new THREE.Geometry();
var v1 = new THREE.Vector3(0,0,0);
var v2 = new THREE.Vector3(3,0,0);
var v3 = new THREE.Vector3(0,3,0);
geometry_1.vertices.push(v1);
geometry_1.vertices.push(v2);
geometry_1.vertices.push(v3);
geometry_1.faces.push(new THREE.Face3(0, 1, 2));
The animation function renders the scene and adds a small rotation to the mesh:
function animate() {
requestAnimationFrame( animate );
mesh_1.rotation.x += 0.005;
renderer.render( scene, camera );
}
Everything works fine until the value of mesh.rotation.x goes into the [Math.PI, 2*Math.PI] interval, at which point it disappears for exactly half of the cycle. This JSFiddle replicates the behavior I'm observing.
This is not a light problem, as there are an ambient light and a directional light supposed to illuminate the mesh at all points of it revolution.
This should not be a material problem, as I did set its side property to THREE.DoubleSide and in fact in the interval mesh.rotation.x into [0, Math.PI] I already observe both faces.
I tried adding another face to the same geometry with geometry_1.faces.push(new THREE.Face3(0, 2, 1)); but that still didn't solve the problem.
Adding a second geometry with an opposite face geometry_2.faces.push(new THREE.Face3(0, 2, 1)); and having the mesh rotate negatively mesh_2.rotation.x -= 0.005; allows me to observe the desired result because the two geometries are now disappearing in opposite halves of the [0, 2*Math.PI] interval. This however is a hacky and not ideal solution.
So what is going on? How can I solve it? Thanks!
Documentation says:
OrthographicCamera( left, right, top, bottom, near, far )
so, set your camera like this:
camera = new THREE.OrthographicCamera(-FRUSTUM_SIDE/2, FRUSTUM_SIDE/2,
FRUSTUM_SIDE/2, -FRUSTUM_SIDE/2);
thus you'll have default near and far camera frustrum planes (0.1 and 2000).
Explanation: You set your cam at z-position, which is equal to FRUSTRUM_SIDE/2 and also you set your far camera frustrum plane with the same value. So you see everything between your cam position and the distance from it, which is FRUSTRUM_SIDE/2. In world coordinates, your far plane is at point (0, 0, 0). That's why your triangle disappears when it goes further then the distance of FRUSTRUM_SIDE/2 from your cam.
Extending the answer from #prisoner849, the problem shown in the JSFiddle has nothing to do with the geometry or the material of the mesh, but with the shape and extension of the frustum defined by the OrthographicCamera.
As explained nicely in this video tutorial and in the documentation the frustum of an OrthographicCamera is a rectangular parallelepiped defined by the values left, right, top, bottom, near, far:
The camera should effectively be thought of as being attached to the surface on the near side and facing towards negative values of the z axis.
Once the frustum's shape is defined with:
FRUSTUM_SIDE = 20;
camera = new THREE.OrthographicCamera(
-FRUSTUM_SIDE/2, FRUSTUM_SIDE/2,
FRUSTUM_SIDE/2, -FRUSTUM_SIDE/2,
-FRUSTUM_SIDE/2, FRUSTUM_SIDE/2);
we will be able to see in our image all the objects in the scene which are entirely contained in it.
However, after defining the frustum the position of the camera is changed: camera.position.z = FRUSTUM_SIDE/2; (line 24 of the fiddle). This effectively moves the whole frustum and not just the location of the image, so while previously any object in (0,0,0) was in the center of the frustum, now it will lie on the very far end plane of it.
The animation function:
function animate() {
requestAnimationFrame( animate );
mesh_1.rotation.x += 0.005;
renderer.render( scene, camera );
}
rotates the triangle towards the image plane, but for angles between [Math.PI, 2*Math.PI] the plane is effectively pointing outside of the frustum and thus becomes invisible.
Removing the camera.position.z = FRUSTUM_SIDE/2; line, or defining a different frustum, or moving the mesh position to the middle of the new frustum are all possible solutions. I.e. corrected fiddle.

How can I apply TrackballControls to a moving target?

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.

OrbitControls : updating position and World/View scale of a 'helper'

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.

Point SpotLight in same direction as camera three.js (Flashlight)

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).

Categories