I want to draw (on the renderer) a fixed vertical line (or a bar) that gives info about size/distance or zooming factor like the one in Google Maps (see the image) (supposing to use a Orbit Control or a Trackball Control). Is it possibile?
why yes, good sir! You could find out the distance quite easily if you have THREE.OrbitControls object and a THREE.Camera:
var controls = new THREE.OrbitControls(...);
var camera = ....;
//get distance from camera to 'target' of OrbitControls
var distance = camera.position.distanceTo(controls.target);
luckily for us, THREE.OrbitControls also has minDistance and maxDistance attributes which control how close and how far away the camera can move from the target point. So, to get relative position of our camera between minDistance and maxDistance we can use the following code:
var normalizedDistance = (distance- controls.minDistnace) / (controls.maxDistance - controls.minDistance);
Now our new normalizedDistance variable will store a value between 0 and 1 relative to how far we are from the target, if we are at the minDistance - we can't get any closer and normalizedDistance === 0, if we are at maxDistance and can't get any further, out normalizedDistance === 1.
Also, be careful, by default THREE.OrbitControls.maxDistance is set to infinity, so to get a meaningful normalizedDistance you would have to set maxDistance to some reasonable non-infinite value, like say 100 or 1000.
Related
(Hello, it's my first ever post here)
here's what I'd like to incorporate in this simple example:
I would like to make the background turn from light to dark gradually when the user is closer to a particular orientation – in this case (example above) the desired orientation is a steep angle so that the foreshortened anamorphic image looks like a regular skull (the value of the background indicating the angle user should aim for – kind of like playing Hot and Cold)
when the user reaches the desired orientation (the background is then accordingly 100% dark) I would like it to lock rotation and trigger a video file in the background or a pop up window.
I assume it has to do with accessing the camera rotation values inside OrbitControls and setting some kind of an Event?? i have no idea how to access it.
Any kind of help, suggestions to edit the thread or explanation would be greatly appreciated, thank you so much in advance!
You could use camera.position to calculate the best vantage point. First, you have to figure out what the desired position is (I'm not sure how the wooden board is being placed, but this position seems to be close to: { x: 6.8, y: 0.6, z: -1.8})
var vantagePoint = new THREE.Vector3(6.8, 0.6, -1.8);
var distance = 100;
var normalized = 1;
var endColor = new THREE.Color(0xff9900);
var startColor = new THREE.Color(0x0099ff);
scene.background = startColor;
animate() {
distance = vantagePoint.distanceTo(camera.position);
normalized = THREE.Math.smoothstep(distance, 5, 100); // Converts [1, 100] => [0, 1]
// Resets the color on each frame
startColor.set(0x0099ff);
startColor.lerp(endColor, normalized);
}
The closer to 0 you are, the closer you are to seeing the skull. You can then use that value to change the color of scene.background. Anything beyond 10 and you're 'cold', and you get hotter as you approach 0.
https://threejs.org/docs/#api/en/math/Vector3.distanceTo
Update:
You can then transform the distance to a normalized value in the range of [0, 1] by using Math.smoothstep(). Then interpolate the value of the colors with this normalized value using Color.lerp
I think this is ultimately a pretty simple question, but it's hard to describe, thus, I provide a working example here (in the sample press 'z' to see rotation with unwanted translation and 'x' keys to rotate with a compensating re-position).
Basically, I am trying to rotate an object (a thumbstick) about the z-axis of a complex model loaded via gltf (a model of the oculus rift touch controller). It's easy to rotate about the x-axis because it's 90 deg. orthogonal to the x-axis. About the z-axis, it's harder because the plane the thumbstick is attached to is angled at 30 deg. I realize that if the thumbstick were using local coordinates, this wouldn't be a problem, but 'thumb.rotation.z' does not seem to be using local coordinates and is rotating about the model's (as a whole), or maybe even the scene's global y and z (?). Anyway, after a bunch of futzing around, I was able to get things to work by doing the following:
// occulus plane is angle at 30 deg, which corresponds to
// 5 units forward to 3 units down.
var axis = new THREE.Vector3(0, 5, -3).normalize();
factory.thumbstick.geometry.center();
var dir = (evt.key === 'x' ? 1 : -1);
thumb.rotateOnAxis(axis, factory.ONE_DEG * 5.0 * dir);
Basically, I'm rotating about a "tilted" axis, and then calling 'center' to make thumbstick centered on the pivot point, so it rotates about the pivot point, rather than around the pivot point (like the earth orbiting the sun).
Only problem is that when you call 'geometry.center()' and then call 'rotateOnAxis', it translates the thumbstick to the pivot point:
Note: the position on the thumbstick object is (0,0,0) before and after the calls.
I have empirically determined that if I alter the position of the thumbstick after the translation like so:
// magic numbers compensating position
var zDisp = 0.0475;
var yDisp = zDisp / 6.0
thumb.position.x = 0.001;
thumb.position.y = -yDisp;
thumb.position.z = zDisp;
Then it (almost) returns back to it's original position:
Problem is these numbers were just determined by interactively and repeatedly trying to re-position the thumbstick i.e. empirically. I simply cannot find a programmatic, analytical, api kind of way to restore the original position. Note: saving the original position doesn't work, because it's zero before and after the translation. Some of the things I tried were taking the difference between the bounding spheres of the global object and the thumbstick object, trying to come up with some 'sin x- cos x' relation on one distance etc. but nothing works.
My question is, how can I progammatically reverse the offset due to calling 'geometry.center()' and rotateOnAxis (which translates to the pivot point), without having to resort to hacked, empircal "magic" numbers, that could conceivably change if the gltf model changes.
Of course, if someone can also come up with a better way to achieve this rotation, that would be great too.
What's throwing me is the (peceived?) complexity of the gltf model itself. It's confusing because I have a hard time interpreting it and it's various parts: I'm really not sure where the "center" is, and in certain cases, it appears with the 'THREE.AxesHelper' I'm attaching that what it shows as 'y' is actually 'z' and sometimes 'up' is really 'down' etc, and it gets confusing fast.
Any help would be appreciated.
The breakthrough for me on this was to re-frame the problem as how do I change the pivot point for the thumbstick, rather than how do I move the thumbstick to the (default and pre-existing) pivot point. To paraphrase JFK, "ask not how you can move to the pivot, but ask how the pivot can move to you" :-)
After changing my angle of attack, I pretty quickly found the aforementioned link, which yielded my solution.
I posted an updated glitch here, so now pressing z works as I expected. Here is the relevant code portion:
factory.onModelLoaded = function(evt) {
console.log(`onModelLoaded: entered`);
factory.thumbstick = this.scene.children[1].children[2]
let thumb = factory.thumbstick;
// make the thumb red so it's easier to see
thumb.material = (new THREE.MeshBasicMaterial({color: 0xFF7777}));
// use method from https://stackoverflow.com/questions/28848863/threejs-how-to-rotate-around-objects-own-center-instead-of-world-center/28860849#28860849
// to translate the pivot point of the thumbstick to the the thumbstick center
factory.thumbParent = thumb.parent;
let thumbParent = factory.thumbParent;
thumbParent.remove(thumb);
var box = new THREE.Box3().setFromObject( thumb );
box.getCenter( thumb.position ); // this basically yields my prev. "magic numbers"
// thumb.position.multiplyScalar( - 1 );
var pivot = new THREE.Group();
thumbParent.add( pivot );
pivot.add( thumb );
thumb.geometry.center();
// add axeshelp after centering, otherwise the axes help, as a child of thumb,
// will increase the bounding box of thumb, and positioning will be wrong.
axesHelper = new THREE.AxesHelper();
thumb.add(axesHelper);
}
Which allows my "z" handler to just rotate without having to do translation:
case 'z':
case 'Z':
var axis = new THREE.Vector3(0, 5, -3).normalize();
var dir = (evt.key === 'z' ? 1 : -1);
thumb.rotateOnAxis(axis, factory.ONE_DEG * 5.0 * dir);
break;
Interestingly, it's the call to box.getCenter() that generates numbers very close to my "magic numbers":
box.getCenter()
Vector3 {x: 0.001487499801442027, y: -0.007357006114165027, z: 0.04779449797522323}
My empirical guess was {x: 0.001, y: -0.00791666666, z: 0.0475} which is %error {x: 32.7%, y: 7.6%, z: 0.61%}, so I was pretty close esp. on the z component, but still not the "perfect" numbers of box.getCenter().
after loading several gltf files, I am renaming these files and try to reposition the camera so that it is centered and looking at the centroid of the new objects and the whole scene fits within the camera.
But the centering does not always work, sometimes the centroid is calculated somewhere completely different. The following code is ran in render() only once after all objects have been loaded:
var all_centers = [];
scene.updateMatrixWorld();
scene.traverse(function(child){
if (child instanceof THREE.Mesh){
if (child.name.indexOf("_") !== -1){ // the newly imported objects
child.geometry.computeBoundingSphere();
var the_center = new THREE.Vector3();
child.getWorldPosition(the_center);
all_centers.push(the_center);
}
}
});
var the_centroid = getPointsCentroid(all_centers);
var cameraPosition = new THREE.Vector3(the_centroid.x,the_centroid.y,-55);
camera.position.copy(cameraPosition);
camera.lookAt(the_centroid);
and here is the function for the centroid:
function getPointsCentroid(points){
var centroid = [0.,0.,0.];
for(var i = 0; i < points.length; i++) {
var point = points[i];
centroid[0] += point.x;
centroid[1] += point.y;
centroid[2] += point.z;
}
centroid[0] /= points.length;
centroid[1] /= points.length;
centroid[2] /= points.length;
return new THREE.Vector3(centroid[0],centroid[1],centroid[2]);
}
For now ignoring the problem of getting the whole scene to fit within the camera (this is a common problem, I'm sure you can find useful information online (perhaps this?)).
Instead, let us focus on what seems to be your main question: You wish to find the center of a group of objects. What you are currently doing is you are computing an average of the object centers. This means that if you have one object to the far left, and 9 objects to the far right, your computed center point will also be far to the right. (This would be an approximation of the center of mass, assuming the objects are of similar mass.)
However, for the purpose of centering the camera so that every object is visible, you are not interested in the center of mass, but you wish to find a point such that the distance to the leftmost point is equal to the distance to the rightmost, and similarly the lowermost to the highermost, etc. Such a point can be found using the bounding box of all your objects. The center of this bounding box is the point you are looking for.
If your camera is to be aligned to the axes, you can easily compute such a bounding box for each object as follows:
new THREE.Box3().setFromObject(myMesh)
The bounding box of all the objects is simply the box represented by the lowest and highest coordinates of all the object bounding boxes you computed. The center of the complete bounding box will give you the point you are after.
Next, assuming the camera is aligned with the axes, the problem is simply finding a suitable distance from the camera to this point so that the entire bounding box fits inside the viewport.
I'm building a boardgame in WebGL. The board can be rotated/zoomed. I need a way to translate a click on the canvas element (x,y) into the relevant point in 3D space (x, y, z). The ultimate result is that I want to know the (x, y, z) coordinate that contains the point that touches the object closest to the user. For instance, the user clicks a piece, and you imagine a ray traveling through 3D space that goes through both the piece and the game board, but I want the (x, y, z) coord of the piece at the point where it was touched.
I feel like this must be a very common problem, but I can't seem to find a solution in my googles. There must be some way to project the current view of the 3D space into 2D so you can map each point in 2D space to the relevant point in 3D space. I want to the user to be able to mouse over a space on the board, and have the spot change color.
You're looking for an unproject function, which converts screen coordinates into a ray cast from the camera position into the 3D world. You must then perform ray/triangle intersection tests to find the closest triangle to the camera which also intersects the ray.
I have an example of unprojecting available at jax/camera.js#L568 -- but you'll still need to implement ray/triangle intersection. I have an implementation of that at jax/triangle.js#L113.
There is a simpler and (usually) faster alternative, however, called 'picking'. Use this if you want to select an entire object (for instance, a chess piece), and if you don't care about where the mouse actually clicked. The WebGL way to do this is to render the entire scene in various shades of blue (the blue is a key, while red and green are used for unique IDs of the objects in the scene) to a texture, then read back a pixel from that texture. Decoding the RGB into the object's ID will give you the object that was clicked. Again, I've implemented this and it's available at jax/world.js#L82. (See also lines 146, 162, 175.)
Both approaches have pros and cons (discussed here and in some of the comments after) and you'll need to figure out which approach best serves your needs. Picking is slower with huge scenes, but unprojecting in pure JS is extremely slow (since JS itself isn't all that fast) so my best recommendation would be to experiment with both.
FYI, you could also look at the GLU project and unproject code, which I based my code loosely upon: http://www.opengl.org/wiki/GluProject_and_gluUnProject_code
I'm working on this problem at the moment - the approach I'm taking is
Render objects to pick buffer each with unique colour
Read buffer pixel, map back to picked object
Render picked object to buffer with each pixel colour a function of Z-depth
Read buffer pixel, map back to Z-depth
We have picked object and approximate Z for the pick coords
This is the working demo
function onMouseUp(event) {
event.preventDefault();
x_pos = (event.clientX / window.innerWidth) * 2 - 1;
y_pos = -(event.clientY / window.innerHeight) * 2 + 1;
z_pos = 0.5;
var vector = new THREE.Vector3( x_pos , y_pos , z_pos );
var projector = new THREE.Projector();
projector.unprojectVector(vector, camera);
var raycaster = new THREE.Raycaster(camera.position, vector.sub(camera.position).normalize());
var intersects = raycaster.intersectObjects(intersectObjects);
if (intersects.length > 0) {
xp = intersects[0].point.x.toFixed(2);
yp = intersects[0].point.y.toFixed(2);
zp = intersects[0].point.z.toFixed(2);
destination = new THREE.Vector3( xp , yp , zp );
radians = Math.atan2( ( driller.position.x - xp) , (driller.position.z - zp));
radians += 90 * (Math.PI / 180);
console.log(radians);
var tween = new TWEEN.Tween(driller.rotation).to({ y : radians },200).easing(TWEEN.Easing.Linear.None).start();
}
weissner-doors.de/drone/
culted from one of the threads.
not sure about (x,y,z) but you can get the canvas(x,y) using
getBoundingClientRect()
function getCanvasCoord(){
var mx = event.clientX;
var my = event.clientY;
var canvas = document.getElementById('canvasId');
var rect = canvas.getBoundingClientRect();// check if your browser supports this
mx = mx - rect.left;
my = my - rect.top;
return {x: mx , y: my};
}
I've searched far and wide, so if there's a similar question please forgive me but I just couldn't find it.
To put what I'm trying to do in context: I want to create an infinitely-generated field of stars that disappear as they go offscreen and reappear at the edge of the screen where the camera is moving. I'm working with a top-down view, so it must be pretty simple to achieve this, but alas I haven't a clue.
I'm using the following code to determine whether a star has gone off-screen and then replace it:
//update camera frustum
camera.projScreenMatrix.multiplyMatrices(
camera.projectionMatrix,
camera.matrixWorldInverse
);
camera.frustum.setFromMatrix(camera.projScreenMatrix);
//loop through stars
var stars=scene.stars.geometry.vertices;
for(var i=0;i<stars.length;i++) {
if(!camera.frustum.containsPoint(stars[i])) {
stars[i]=new THREE.Vector3(
// fill in the blank
);
scene.stars.geometry.verticesNeedUpdate=true;
}
}
Since I'm using a perspective camera, I know I'll need to somehow factor in camera.fov and other perspective elements, but as you can tell I'm no expert on the third dimension.
Assuming I have an angle or normalized vector telling me the direction the view is panning, how would I go about creating a vertex along the edge of the screen regardless of its Z position?
If I'm not clear enough, I'll be happy to clarify. Thanks.
I know this is an old question, but I came across it while looking for an answer and found a simple, trigonometry reliant method to get the left edge of the camera frustum, and I'm sharing it in case someone else might find it useful:
// Get half of the cameras field of view angle in radians
var fov = camera.fov / 180 * Math.PI / 2;
// Get the adjacent to calculate the opposite
// This assumes you are looking at the scene
var adjacent = camera.position.distanceTo( scene.position );
// Use trig to get the leftmost point (tangent = o / a)
var left = Math.tan( fov ) * adjacent * camera.aspect;
Basically, this gets the leftmost point, but if you don't multiply by the aspect ratio you should get a point in a circle around your camera frustum, so you could translate a point any direction away from the cameras focus and it would always be outside the frustum.
It works by assuming that the imaginary plane that is the camera is perpendicular to the line connecting the camera and its focus, so there is a straight angle. This should work if you want objects further away as well (so if you want them at a further point from the camera you just need to increase the distance between the focus and the camera).
Well, countless headaches and another question later, I've come up with a fairly makeshift answer. Just in case by some unlikely chance someone else has the same question, the following function plots a point on the scene relative to the camera's current view with whatever Z specified:
//only needs to be defined once
var projector=new THREE.Projector();
//input THREE.Vector3
function(vector) {
var z=vector.z;
vector.z=0;
projector.unprojectVector(vector,camera);
return camera.position.clone().add(
vector
.sub(camera.position)
.normalize()
.multiplyScalar(
-(camera.position.z-z)/vector.z
)
);
The x and y, in this case, both range from -1 to 1 for bottom-left to top-right. You can use position/window.Width and position/window.Height for extra precision (using mouse coordinates or what have you).