How to find the rotation of an object relative to the camera - javascript

I'm having trouble figuring out the angle of an object relative to the camera, I'm trying to code a spaceship with a camera following it. I have the camera following the ship but the rotation of the camera sometimes is a little off, here's my camera code:
var focalpoint = new THREE.Vector3(
actor.position.x,
actor.position.y,
actor.position.z + 14
);
//move camera closer to the object if it gets too far away
var calculatedDistance = distance(camera.position, actor.position);
var cameraTolerance = calculatedDistance - this.cameradistance.min;
var closingvelocity = cameraTolerance * 0.02;
if(calculatedDistance > this.cameradistance.max)cameravelocity.z = -closingvelocity;
if(calculatedDistance < this.cameradistance.min)cameravelocity.z = closingvelocity;
//slow down the camera
if(calculatedDistance < this.cameradistance.max && calculatedDistance > this.cameradistance.min){
cameravelocity.z = 0;
}
camera.translateX( cameravelocity.x );
camera.translateY( cameravelocity.y );
camera.translateZ( cameravelocity.z );
camera.lookAt(focalpoint);
camera.rotation.z = 0;
Now I need to limit the rotation of the spaceship (actor) so it doesn't start flying towards the camera, and to fix the camera flipping over problem. So I need to figure out how to find the rotation of the actor relative to the camera, I have absolutely no idea where to start calculating or even how.

found the answer, by inversing the target's rotation, then multiplying like so:
var rotationOffset = actor.quaternion.clone().inverse();
var rotation = camera.quaternion.clone().multiply( rotationOffset );

Related

three.js TubeBufferGeometry position change for camera animation

I'am trying to change position/translate dinamicaly of TubeBufferGeometry.parameters.path which i will use for camera animation
(all are based on this example: webgl_geometry_extrude_splines)
here is my peace of code so far
full code: Link to codesandbox with full code
Important part of code:
function init(){
// initial scene render etc. for full code take a look at codesanbox
// tube
var extrudePath = new Curves.GrannyKnot();
tubeGeometry = new THREE.TubeBufferGeometry( extrudePath, 50, 2, 1, true );
tubeGeometry.dynamic = true;
//unsuccessful approach to change the parameters of position
tubeGeometry.translate(100, 0, 0)
tubeGeometry.attributes.position.needsUpdate = true;
}
function animate() {
requestAnimationFrame( animate );
render();
}
function render() {
// animate camera along spline
var time = Date.now();
var looptime = 20 * 1000;
var t = ( time % looptime ) / looptime;
var pos = tubeGeometry.parameters.path.getPointAt( t );
pos.multiplyScalar( 1 );
splineCamera.position.copy( pos );
renderer.render( scene, splineCamera );
}
The resault is giving me no translation of camera its moving around GrannyKnot curve in default position.
What i'm expecting is to dynamic change position/translate of TubeBufferGeometry.parameters.path in some direction (for example position X + 100) which affects on my camera movement
EDIT
I just realised that i could change camera by adding
function render() {
// animate camera along spline
var time = Date.now();
var looptime = 20 * 1000;
var t = ( time % looptime ) / looptime;
var pos = tubeGeometry.parameters.path.getPointAt( t );
pos.x += 100; //<----this line added
pos.multiplyScalar( 1 );
splineCamera.position.copy( pos );
renderer.render( scene, splineCamera );
}
But if someone would show me how to change TubeBufferGeometry.parameters.path without changing definite pos.x like in code above it. I would be very glad for that.

Wandering shadows when DirectionalLight is attached to player object

I'm trying to get a directionalLight to move with the player. I do this to improve the shadow quality, so that the shadow camera's dimensions can be smaller. When the player moves the shadows wander a bit. It looks like it is not following the player close enough.
I have the following problem illustrated in this VERY POOR quality gif;
Is there a better way of doing this? Or did I forget to update something? Any advice is appreciated :)
In the player class I have an object where I attach the camera to and use that object as the target for the light. Then in the player.update function I set the light position with an offset to the object's position.
class Player{
constructor(){
this.object = new THREE.Object3D();
scene.add( this.object );
this.object.add( camera );
this.directionalLightOffset = new THREE.Vector3(-5000, 2, 5);
this.directionalLight = new THREE.DirectionalLight( 0xffffff, 0.85 );
scene.add( this.directionalLight);
this.directionalLight.position.copy( this.directionalLightOffset );
this.directionalLight.castShadow = true;
this.directionalLight.shadow.mapSize.width = 750;
this.directionalLight.shadow.mapSize.height = 750;
this.directionalLight.shadow.camera.left = -2000;
this.directionalLight.shadow.camera.right = 2000;
this.directionalLight.shadow.camera.top = -2000;
this.directionalLight.shadow.camera.bottom = 2000;
this.directionalLight.shadow.camera.near = 0.5;
this.directionalLight.shadow.camera.far = 90000;
this.directionalLight.target = this.object;
}
update(){
this.directionalLight.position.copy( this.object.position );
this.directionalLight.add( this.directionalLightOffset );
}
}
Try only moving the camera in increments of your shadowmap pixel size or similar. Even just delaying how often you move the camera can minimize the shimmer.. so put a radius around the player and only reset the camera position when the player has wandered out of that radius.

ThreeJS, get vector towards clicked direction

What I'm trying to achieve is to make a specific mesh move towards a specific vector until it will eventually be stopped by the player.
So far I have managed to get the XY coordinates of the clicked canvas and project them in 3d using the following piece of code. Unfortunately I'm not sure what approach to take in order to get the direction towards the clicked position.
var vector = new THREE.Vector3();
vector.set(
( event.clientX / window.innerWidth ) * 2 - 1,
+ ( event.clientY / window.innerHeight ) * 2 + 1,
0.5 );
vector.unproject( camera );
var dir = vector.sub( camera.position ).normalize();
var distance = + camera.position.z / dir.z;
var pos = camera.position.clone().add( dir.multiplyScalar( distance ) );
This assumes a target Vector3 and a maximum distance to be moved per frame of .01.
var vec1 = target.clone(); // target
vec1.sub(mesh.position); // target - position
var dist = Math.min(vec1.length(), .01); // assume .01 is maximum movement
if (dist > 0) {
vec1.setLength(dist); // this will be the movement
mesh.position.add(vec1); // this moves the messh
}

Three.js - How to check if object is behind a sphere (not visible)

I have a sphere (globe) with objects (pins) on the surface with DOM elements (labels) what are calculated from the pin position to 2d world.
My problem is that when the pins go behind the globe (with mouse dragging or animation) then I need to hide labels which are in DOM so that the text label isn’t visible without the pin.
My logic is that if I can get the pin which is in 3D world to tell me if it’s behind the globe then I can hide the label associated with the pin.
Codepen with whole the code.
The function that I have researched together:
function checkPinVisibility() {
var startPoint = camera.position.clone();
for (var i = 0; i < pins.length; i++) {
var direction = pins[i].position.clone();
var directionVector = direction.sub(startPoint);
raycaster.set(startPoint, directionVector.clone().normalize());
var intersects = raycaster.intersectObject(pins[i]);
if (intersects.length > 0) {
// ?
}
}
}
I have researched through many posts but can’t really get the result needed:
ThreeJS: How to detect if an object is rendered/visible
Three.js - How to check if an object is visible to the camera
http://soledadpenades.com/articles/three-js-tutorials/object-picking/
I have gotten it work by mouse XY position as a ray, but can’t really get a working solution with constant rendering for all the pins.
You want to know which points on the surface of a sphere are visible to the camera.
Imagine a line from the camera that is tangent to the sphere. Let L be the length of the line from the camera to the tangent point.
The camera can only see points on the sphere that are closer to the camera than L.
The formula for L is L = sqrt( D^2 - R^2 ), where D is the distance from the camera to the sphere center, and R is the sphere radius.
WestLangley's solution in code form. Please give him the accepted answer if you feel his answer the best.
function checkPinVisibility() {
var cameraToEarth = earth.position.clone().sub(camera.position);
var L = Math.sqrt(Math.pow(cameraToEarth.length(), 2) - Math.pow(earthGeometry.parameters.radius, 2));
for (var i = 0; i < pins.length; i++) {
var cameraToPin = pins[i].position.clone().sub(camera.position);
if(cameraToPin.length() > L) {
pins[i].domlabel.style.visibility = "hidden";
} else {
pins[i].domlabel.style.visibility = "visible";
}
}
}
Oddly enough it is still susceptible to that camera pan error. Very weird, but it's still better than my Projection-onto-LOOKAT solution.
MY OLD ANSWER:
I would have assumed its something like this, but this doesn't seem to work as expected.
if (intersects.length > 0) {
pins[i].domlabel.style.visibility = "visible";
} else {
pins[i].domlabel.style.visibility = "hidden";
}
I got close with this solution, but its still not perfect. What the code below does is it finds the distance along the LOOKAT direction of the camera to a pin (cameraToPinProjection) and compares it with the distance along the LOOKAT direction to the earth (cameraToEarthProjection).
If cameraToPinProjection > cameraToEarthProjection it means the pin is behind the centre of the earth along the LOOKAT direction (and then I hide the pin).
You will realise there's a "0.8" factor I multiply the cameraToEarth projection by. This is to make it slightly shorter. Experiment with it.
Its not perfect because as you rotate the Earth around you will notice that sometimes labels don't act the way you'd like them, I'm not sure how to fix.
I hope this helps.
function checkPinVisibility() {
var LOOKAT = new THREE.Vector3( 0, 0, -1 );
LOOKAT.applyQuaternion( camera.quaternion );
var cameraToEarth = earth.position.clone().sub(camera.position);
var angleToEarth = LOOKAT.angleTo(cameraToEarth);
var cameraToEarthProjection = LOOKAT.clone().normalize().multiplyScalar(0.8 * cameraToEarth.length() * Math.cos(angleToEarth));
var startPoint = camera.position.clone();
for (var i = 0; i < pins.length; i++) {
var cameraToPin = pins[i].position.clone().sub(camera.position);
var angleToPin = LOOKAT.angleTo(cameraToPin);
var cameraToPinProjection = LOOKAT.clone().normalize().multiplyScalar(cameraToPin.length() * Math.cos(angleToPin));
if(cameraToPinProjection.length() > cameraToEarthProjection.length()) {
pins[i].domlabel.style.visibility = "hidden";
} else {
pins[i].domlabel.style.visibility = "visible";
}
}
}

How to rotate a THREE.PerspectiveCamera around on object

I am making this program where you can click on an object, zoom to it, then look at it from all angles by holding the right mouse button and dragging. I need the camera to be going around the object, not rotate the object with the camera looking at it. I honestly just have no idea how to math it out!
For testing there is already a game object with an xyz we have selected and are looking at
var g = new GameObject(500, 0, 0);//The game object with xyz
this.selected = g;//set selected to g
//Create and set the camera
this.camera = new THREE.PerspectiveCamera(45, w/h, 1, 10000);
this.camera.position.x = 0;
this.camera.position.y = 0;
this.camera.position.z = 0;
//set camera to look at the object which is 500 away in the x direction
this.camera.lookAt(new THREE.Vector3(this.selected.x, this.selected.y, this.selected.z));
So the radius between the camera and the object is 500 and while selected and rotating, the camera should always be 500 away.
I update the scene here:
Main.prototype.update = function(){
this.renderer.render(this.scene, this.camera);//scene is just some ambient lighting
//what to do when mouse right is held down
if(this.rightMouseDown){
//placeholder functionality, needs to rotate around object based on mouse movements
this.camera.position.x -= 5;
}
}
How do I rotate this camera around g with a radius of 500?!?!
As gaitat mentioned, trackball controls are the best place to start with many configurable parameters to make camera rotation/revolution easy. One enormous potential benefit of this method ( especially for your project ) is avoiding "gimbal lock" which is the source of much frustration when working with rotations. Here's a link that might help you with Trackball controls and Orbitcontrols:
Rotate camera in Three.js with mouse
Another option would be setting camera coordinates yourself in the animation loop which is actually quite simple:
var angle = 0;
var radius = 500;
function animate() {
...
// Use Math.cos and Math.sin to set camera X and Z values based on angle.
camera.position.x = radius * Math.cos( angle );
camera.position.z = radius * Math.sin( angle );
angle += 0.01;
...
}
Another option would be to connect the camera to a pivot object and just rotate the pivot:
var camera_pivot = new THREE.Object3D()
var Y_AXIS = new THREE.Vector3( 0, 1, 0 );
scene.add( camera_pivot );
camera_pivot.add( camera );
camera.position.set( 500, 0, 0 );
camera.lookAt( camera_pivot.position );
...
camera_pivot.rotateOnAxis( Y_AXIS, 0.01 ); // radians
If you pursue this option, be aware that the camera object is in "camera pivot space", and might be more challenging to manipulate further.

Categories

Resources