Bring point to nearest camera position by camera rotation - three.js

I have a scene in Three.js (r67) with a camera that is controlled by OrbitControls.
If I now select an arbitrary point (Vector3) in the scene, what would be the best way to bring this point (programmatically) to the nearest camera position just by rotating the camera?
Example Scenario
In the below picture the left side is the starting point. The camera rotates around the green sphere (like OrbitControls) where the center of the camera is the center of the sphere. I now like to automatically rotate the camera around the sphere (doing the minimum amount of moves) so that the red box is nearest to the camera (like on the right side).

Independntly to the method of selecting the point in the scene, there's several understanding to what you mean by "bringing camera just by rotating".
I suppose, You want to rotate the camera in the way, to make the selected point in the center of the screen.
This is simple:
camera.lookAt(your_point_in_scene);
You could do this more complicated. Firstly, find the current pointing vector. By default camera looks in direction Vector(0,0,1). When we rotate it in the same rotation as a camera, we will have camera direction:
var vec = new THREE.Vector3(0,0,1);
vec.applyQuaternion(camera.rotation._quaternion);
Now we must determine angle to rotate our camera, and axis, around which we would rotate.
Axis of rotation could be found as a cross product of camera direction and vector from camera to object. Angel could be extracted from dot product:
var object_dir = object_world_point.clone().sub(camera.position);
var axis = vec.clone().crossProduct(object_dir);
var angle = Math.acos( vec.clone().dot(object_dir) / vec.length() / object_dir.length());
Having angle and axis, we could rotate camera:
camera.rotateOnAxis(axis, angle);
Or, if you want to make it smooth:
// before animation started
var total_rotation = 0,
rotateon,
avel = 0.01; // 0.01 radian per second
if(total_rotation < angle){
rotateon = avel * time_delta;
camera.rotateOnAxis(axis, angle);
total_rotation += rotateon;
}

Well that's not hard Oo
You have a center/target point for the camera. You calculate the difference from the target position to the point position and normalize that vector to the length of the camera-centerpoint-distance (i.e. something like pointdistance.multiplyScalar(cameradistance.length() / pointdistance.length()) ).
And that's it. If I understood your question correctly. All you do is "extend" the point's positioni onto your "camera movement dome" and then you have the ideal new camera position. The camera's rotation is done automatic since you always target the center point.
Aaand if you want to smoothen the camera movement a bit you can just interpolate the angle (not the positions directly) with e.g. an exponential function, whatever you prefer.

Hi Dear please follow this
Independntly to the method of selecting the point in the scene, there's several understanding to what you mean by "bringing camera just by rotating".
I suppose, You want to rotate the camera in the way, to make the selected point in the center of the screen.
This is simple:
camera.lookAt(your_point_in_scene);
You could do this more complicated. Firstly, find the current pointing vector. By default camera looks in direction Vector(0,0,1). When we rotate it in the same rotation as a camera, we will have camera direction:
var vec = new THREE.Vector3(0,0,1);
vec.applyQuaternion(camera.rotation._quaternion);
Now we must determine angle to rotate our camera, and axis, around which we would rotate.
Axis of rotation could be found as a cross product of camera direction and vector from camera to object.
Angle could be extracted from dot product:
var object_dir = object_world_point.clone().sub(camera.position);
var axis = vec.clone().crossProduct(object_dir);
var angle = Math.acos( vec.clone().dot(object_dir) / vec.length() / object_dir.length());

Related

Three.js - get terrain height (position.y) of the mesh at specific position.x,z - without mouse and raycaster?

Let's say I have a sort of rather simple terrain from Blender exported as GLB object, which is a Group and contains a Mesh with BufferGeometry. I have another GLB object which is a model of vehicle. How can I read proper position.y at specific x,z locations (idealy 4 locations for setting car position and rotation) without moving mouse and using raycaster? I need to know what is elevation and height at specific region. Any simple clue without game-physics engine on top of ThreeJS?
Just use a Raycaster. I don't know why you don't want to use it, it's the easiest way to find an intersection without a physics engine and without tons of math.
Simply use Raycaster.set() to point straight down from your XZ coords and see where it intersects the terrain:
var ray = new THREE.Raycaster();
var rayPos = new THREE.Vector3();
// Use y = 100 to ensure ray starts above terran
rayPos.set(x, 100, z);
var rayDir = new THREE.Vector3(0, -1, 0); // Ray points down
// Set ray from pos, pointing down
ray.set(rayPos, rayDir);
// Check where it intersects terrain Mesh
let intersect = ray.intersectObject(terrainMesh);
console.log(intersect);
See here for the intersect object. It includes the point in space where the intersection takes place.

three.js: Y-rotation of group, relative to camera

Even after hours of googling, I can't really get my head around this. Maybe somebody here can help me a bit with this.
I basically want to determine the Y-rotation of an object (that is always in the viewport's center), relative to my camera. Imagine the object standing on the center of a record player/turntable, that slowly rotates around its Y axis, and my camera always facing the center of that object while using OrbitControls to change the cam's position around the object. Imagine the camera not moving, but the turntable turning, one revolution equals this Y rotation to be between 0° and 360°.
For example, this Y rotation would be:
0° when cam's position is [x=0, y=0, z=100], or [x=0, y=100, z=200] (the cam's y position doesn't matter, it always looks down/up to the group's center),
45° when cam's position is [x=100, y=0, z=100] or [x=100, y=200, z=100],
90° when cam's position is [x=100, y=0, z=0] or [x=200, y=100, z=0], etc.
Thing is, both of these can have some pretty random positions & rotations in the world coordinate system, so it's not given that the object's position is [x=0, y=0, z=0].
Any ideas? Thanks a lot!
I'm not sure if I'm being helpful, but perhaps Object3D.getWorldQuaternion and Quaternion.angleTo() might help?
something like :
const cameraQuaternion = new THREE.Quaternion();
camera.getWorldQuaternion(cameraQuaternion);
const targetQuaternion = new THREE.Quaternion();
target.getWorldQuaternion(targetQuaternion);
const delta = cameraQuaternion.angleTo(targetQuaternion);
const euler = new THREE.Euler().setFromQuaternion(delta);
console.log(euler.y / Math.PI * 180);

Three.js - change camera POV on click

Some project background:
I have a Sprite particle field that is randomly generated. The camera is located at position 0, 0, 0. The particle field is all around the camera. I'm using Raycaster to be able to select the particle that is clicked on and change it's color. Once clicked I would like the camera to focus on this particle. I'm also attempting to use Tween to glide the particle into view.
I've attempted several different methods and none of them work. They are described here:
A traditional lookAt method that used Raycaster to pick up the intersect point from clicking.
var raycaster = new THREE.Raycaster();
raycaster.setFromCamera(mouse, this.camera);
var intersects = raycaster.intersectObjects( this.starfield.children );
this.camera.lookAt(intersects[0].object.position)
A distanceTo method where the distance between the camera and the intersect coordinates is used to move the camera. This only moves the camera along the z plane. It wont actually change its POV.
var cameraPosition = new THREE.Vector3(this.camera.position.x, this.camera.position.y, this.camera.position.z);
var intersectPosition = new THREE.Vector3(intersects[0].object.position.x, intersects[0].object.position.y , intersects[0].object.position.z );
var zoomPos = intersectPosition.distanceTo( cameraPosition );
const newCameraPosition = cameraPosition.addVectors(this.camera.position, vector.setLength(zoomPos));
I calculated the angle of rotation for each X, Y, and Z axis via tan and cos equations. I then attempted to rotate the camera by those degrees. I even tried converting them to radians to see if that would make a difference with the rotation method. It didnt :(
I don't know what else to do. At this stage I'm completely open to a different approach as long as I get this camera working. I'm very stuck,
any help would be greatly appreciated!
Instead of using
intersects[0].object.position
try using
intersects[0].point
.point is the world space position of the hit.
.objectis the object the triangle belongs to. .object.position is just the origin of that object, in this case the particle system. The particle positions themselves are relative to this origin.

Ray.intersectObjects() always comes up no hits

I've created a sphere and the centre of the sphere is located at 0,0,0.
The radius of the sphere is 9.
I've created a cube that is positioned above the surface/faces of the sphere.
When I click on the cube and then proceed to click on any point on the surface of the sphere my cube will rotate it's relative position to the point clicked on the sphere (to look in the direction of the point so to say) and then it will move along the surface of the sphere towards the point clicked. The rotation and movement all happen within a render loop.
What I want to do is cast a Ray from a point relative to the cubes position but at a greater distance to the centre of the sphere. So for instance if the distance to any given point on any given face of my sphere is ~8.8 - 9 (of course the vertices would be at a distance of 9 and the centre of any face would be ~8.8 - 8.9) The distance of my cube from the centre of the sphere is 9.1. I want to cast a ray from about a distance of 12 towards the centre of my sphere.
So, if my cube is located at 0,0,9.1 then I want to cast a ray who's origin would be 0,0,12 and who's destination would be 0,0,0. Then only target the sphere as the object to intersect, determine the distance to any given point along any given face and then set the distance of the cube to 12 - someDistance. That way it would seem as though the cube is actually moving along the surface of the sphere. And if I modify the features of the sphere, the cube would appear to move along the contours of the surface.
Here is my code which is located inside of a looping render function.
Unfortunately it turns up nothing.
var direction = new THREE.Vector3(0,0,0);
var origin = new THREE.Vector3(object_cubi[x-1].posiX, object_cubi[x-1].posiY, object_cubi[x-1].posiZ);
origin.normalize();
origin.x *= 12;
origin.y *= 12;
origin.z *= 12;
var disRay = new THREE.Raycaster();
disRay.ray.set(origin, direction);
var rayIntersect = disRay.intersectObjects( targetList );
document.getElementById("test7").value = rayIntersect.length;
rayIntersect.length is always 0.
What am I missing?
To select the cube and pick a point on the surface I had to use raycaster and that code works fine. However it does incorporate projector().
all I needed to do was cast the ray from the centre of the sphere and make the material of the sphere double sided.
material.side = THREE.DoubleSide;
Now my cube moves along the contours of my sphere.

Three.js plane facing away from camera aligned with viewport

I'm trying to have a plane face away from the camera with same orientation so it's aligned in the viewport.
I have a plane in front of the camera, perfectly aligned to the cameras viewport, and I want to flip it in front of the camera, along the objects Y axis, regardless of camera orientation.
The following will orient my plane to face at the camera and works for any orientation:
target.rotation.copy(camera.rotation);
The following will then flip the plane along the plane's Y axis:
target.rotation.y += Math.PI;
All good so far? Except when the camera rotation has a funky tilt to it, let's say it's looking up and to the left, tilted slightly to the right, the plane's flip is tilted, but not the same way as the camera, leaving me with a plane tilted either to the left or right...
I've tried several things such as:
target.rotation.z -= camera.rotation.z;
Nothing... Thanks for your help.
So the problem I was running into was when the camera was in negative z coordinates. This causes the flip on the Y axis to get messed up.
So basically you would do something like this:
var target = new THREE.Object3D();
//position
target.position.copy(s.camera.position);
target.position.add(THREE.Utils.cameraLookDir(s.camera).multiplyScalar(300));
//rotation
target.rotation.copy(s.camera.rotation);
target.rotation.y += PI;
target.rotation.z = -s.camera.rotation.z;
if (s.camera.position.z < 0) {
target.rotation.z = s.camera.rotation.z;
}
EDIT:
Add the following to appropriate spots in your program.
camera.rotation.eulerOrder = 'XZY';
target.rotation.eulerOrder = 'XZY';
Seems to solve previously encountered tilt issues! (see below)
RESOLVED:
Flipped planes tilted the wrong way in some instances, for example when in negative z coords and also the y rotation is not equal to 0, example: point in space hovering and looking at 0, 0, 0.
This is the solution I was looking for when I found this page (taken from this answer):
mesh.lookAt( camera.position );
The local z-axis of the mesh should then point toward the camera.

Resources