I'm looking to translate the camera along its lookAt vector. Once I have this vector, I can do scalar translation along it, use that point to move the camera position in global coordinates and re-render. The trick is getting the arbitrary lookAt vector? I've looked at several other questions and solutions but they don't seem to work for me.
You can't get the lookAtVector from the camera itself, you can however create a new vector and apply the camera rotation to that.
var lookAtVector = new THREE.Vector3(0,0, -1);
lookAtVector.applyQuaternion(camera.quaternion);
The first choice should be cam.translateZ();
There is a second option as well. You can extract the lookAt vector from the matrix property of the camera object. You just need to take the elements corresponding to the local z-axis.
var lookAtVector = new THREE.Vector3(cam.matrix[8], cam.matrix[9], cam.matrix[10]);
Related
I need to get the camera up direction and i've tried many ways with no luck, i'm not an expert of quaternions so i'm doubting i did it right.
I've tried:
camera.up
camera.up.applyMatrix4(camera.matrixWorld);
new THREE.Vertex3(0,1,0).applyMatrix4(camera.matrixWorld);
camera.up.normalize().applyMatrix4(camera.matrixWorld);
after this i create two planes passing by two points of my interest, and add the plane helper to the scene and i can see they are very far from where i was expecting them. (i'm expecting two planes that looks like the top and bottom of the camera frustum).
P.s. the camera is a shadow camera of a directional light so an orthographic camera, and i manipulate the directional light position and target before doing this operation, but i've called updateMatrixWorld on the light, on it's target and the camera, on the camera i've called also updateProjectionMatrix... still no results
I've made a sandbox to see what i've tried till now, and better visualize what i want to achieve:
https://codesandbox.io/embed/throbbing-cache-j5yse
once i manage to get the green arrow to point to the top of the blue triangle of the camera helper i'm good to go
In the normal render flow, shadow camera matrices are updated as part of rendering the shadow map (WebGLShadowMap.render).
However, if you want the updated matrix values before the render, then you'll need to update them manually (you already understand this part).
The shadow camera is a property of (not a child of) the DirectionalLight. As such, it doesn't follow the same rules as other scene objects when it comes to updating its matrices (because it's not really a child of the scene). Instead, you need to call the shadow property's updateMatrices method (inherited from LightShadow.updateMatrices).
const dl = new THREE.DirectionalLight(0xffffff, 1)
dl.shadow.updateMatrices(dl) // <<------------------------ Updates the shadow camera
This updates the shadow camera with information from the DirectionalLight's own matrix, and its target's matrix, to properly orient the shadow camera.
Finally, it looks like you're trying to get the "world up" of the camera. Personally, I'd use the convenience function localToWorld:
let up = new THREE.Vector3(0, 1, 0)
dl.shadow.camera.localToWorld(up) // destructively converts "up" from local-to-camera into world coordinates
via trial and errors i've figured out that what gave me the correct result was:
calling
directionalLight.shadow.updateMatrices(...)
and then
new THREE.Vector3(0,1,0).applyQuaternion(directionalLight.shadow.camera.quaternion)
Let's say I have a vertical list of meshes created from PlaneBufferGeometry with ShaderMaterial. The meshes are distributed vertically and evenly spaced.
The list will have two states:
Displaying the meshes as they are
Displaying meshes with each object's vertices transformed by the vertex shader to the same arbitrary value, let's say z = -50. This gives a zoomed out effect and the user can scroll through this list (in the code we do this by moving the camera y position)
In my app I'm trying to make my mouseover events work for the second state but it's tricky since the GPU transforms the vertices so the updated vertices are not reflected in the attributes on the JS side.
*Note I've looked into GPU picking and do not want to use it because I believe there should be a simpler way to do this without render targets
Attempted Solution
My current approach is to manually change the boundingBox of each plane when we are in the second state like so:
var box = new THREE.Box3().setFromObject(plane);
box.min.z = -50;
box.max.z = -50;
plane.geometry.boundingBox = box;
And then to change the boundingSphere's center to have the same z position of -50 after computing it.
I did this approach because I looked into the Raycaster and Mesh code for THREE.js and it seems like they check both boundingSphere and boundingBox for object intersections. So I thought if I modified both of them to reflect the transforms done by the GPU, the raycaster would work fine but it doesn't seem to be working for me.
The relevant raycaster code is here:
// mouse being vec2 of normalized coordinates and camera being a perspective camera
raycaster.setFromCamera( mouse, camera );
const intersects = raycaster.intersectObjects( planes );
Possible Theories
The only thing I can think of that's wrong about this approach is maybe I'm not projecting the mouse coords right? Since all the objects now lie on the plane z = -50 would I need to project those mouse coordinates to that plane?
Inspired by the link posted by #prisoner849 I found a working solution to just create additional transparent planes equal to the number of planes in the scene. In these planes, I set the z position to -50 and just intersect with these when in state #2.
A bit hacky, but works for now.
I use a pivot group to rotate my planegeometries instead of each one individually. After rotation of the pivot group-object, I want to find the new positions for each child/planegeometry-mesh to correspond to the positions that is relative to the actual world position.
How do I go about doing this?
The easy way
Like Craig mentioned, getWorldPosition is a function on Object3D (the base class of pretty much everything in the scene), which returns a new Vector3 of the object's world position.
var childPlaneWorldPosition = childPlane.getWorldPosition();
The harder way:
There are two methods for converting between local and world positions: localToWorld and worldToLocal.
These are also functions on Object3D, and take a Vector3. The vector is then (destructively) converted to the desired coordinate system. Just know that it's not smart enough to know if the vector you're giving it is already in the right coordinate system--you'll need to keep track of that.
So, to convert a child plane's position from local to world coordinates, you would do this:
// clone because localToWorld changes the vector passed to it
var childPlanePosition = childPlane.position.clone();
childPlane.parent.localToWorld(childPlanePosition);
Notice that localToWorld is called on childPlane's parent. This is because the childPlane is local to its parent, and therefore its position is local to its parent's coordinate system.
The hard(er to understand) way:
Each childPlane stores not only its local transformation matrix (childPlane.matrix), but also its world transformation matrix (childPlane.matrixWorld). You can, of course, get the world position directly from the matrixWorld property in one step.
var childWorldPosition = new THREE.Vector3(
childPlane.matrixWorld.elements[12],
childPlane.matrixWorld.elements[13],
childPlane.matrixWorld.elements[14]
);
Edit to answer some questions
"If I understand correctly, can I find the "real" position of the meshes in the pivot-group children-array?"
Yes. If you called:
pivotGroup.add(childPlane);
Then that childPlane will be listed in the pivotGroup.children array, which you could use to iterate over all of the childPlane objects
"And clone these to the position object for each meshes?"
If you want the planes to be in world coordinates (in the scene), but you used the above code to add them to the group, then they are no longer direct children of the scene. You would need to re-add them to the scene:
scene.add(childPlane);
And then apply their calculated world positions. That said, why not just leave them in the group?
(You didn't ask this one) "How would you leave the planes as direct children of the scene, but rotate them as a group?"
Well, you wouldn't. But three.js does this group rotation by multiplying matrices to come up with finalized world matrices for each plane. So you could do the same thing manually, by creating a rotation matrix, and applying it to all of your planes.
var rotMat = new THREE.Matrix4().makeRotationMatrix(x, y, z);
for(var i = 0; i < planesArray.length; ++i){ // I guess this would loop over your 3D array
planesArray.applyMatrix(rotMat);
}
Use plane_mesh.getWorldPosition()
It seems like this would be a pretty common problem, but I can't find an example online and I'm too much of a math noob...
Using ThreeJS, I have a library to do spatial audio positioning (https://github.com/tmwoz/hrtf-panner-js) based on user position, but my code assumes the camera looks straight ahead and doesn't move. Since my camera is moving, I need to get the xyz position of a 3D object in relation to the camera's position and orientation.
//finds the object in world coordinates
var p = new THREE.Vector3();
p.setFromMatrixPosition(visual.object.matrixWorld);
this.audioTrack.updateHrtf(p.x, p.y, p.z);
How do a translate the object into camera-space coordinates? Thanks for your help!
Note: I know that the WebAudio API has a mechanism to do this simply, but it doesn't have the power of the HRTF (Head Related Transfer Function) library, which sounds much better.
To transform a Vector3 vec from world space to camera space, do this:
camera.matrixWorldInverse.getInverse( camera.matrixWorld );
vec.applyMatrix4( camera.matrixWorldInverse );
three.js r.73
I'm trying to make a Plane to always face the camera or another moving object but I want the Plane to only rotate on 1 axis. How can I use the lookAt function to make it only rotate side ways without tilting to look up or down at the moving object?
thanks, I managed to solve it easily by just keeping the y position of the rotating object constant.
if(planex){
var yaw_control = controls.getYawObject();
pos = new THREE.Vector3( yaw_control.position.x, planex.position.y, yaw_control.position.z );
planex.lookAt(pos);
}
http://www.lighthouse3d.com/opengl/billboarding/index.php?billCyl
maybe this article of any help for you. You are looking for those cylindrical billboards i think but read up from the first page ;) You can modify the specific mesh matrix yourself, although i am not sure if this is the most efficient way. I also did this myself once.
Get the camera look vec:
three.js set and read camera look vector
Then get the camera upVec and afterwards get the cross prodcut of those = rightVec according to the article above.
using those vectors, you can fill in a new Three.Matrix4() like explained in the article and then replace the meshes matrix with the newly created one. As I said, i am not quite into the matrix stuff in three.js but this works but it is probably not that efficient.
For this to work you will have to deactive the meshes auto matrix update with
mesh.matrixAutoUpdate = false;