After a lot of research, I didn't find any information about it. I would like to pilot the camera from the inside of the sphere. The rotating point would be behind the camera in position (0, 0, 0) and look at a variable position (like if you are looking at the star during the night)
A drawing is probably easier to understand than a long explanation:
In my case, I know how to block the zoom from 0.01 (just to avoid issue if I go to 0) to 0.999 and block the pan. The only issue is about the rotation point. It's always (?) the target is OrbitControl.js. I would like to change it but I don't find any information about how to do that.
This is the piece of code I found in the library :
// "target" sets the location of focus, where the control orbits around
// and where it pans with respect to.
this.target = new THREE.Vector3();
// center is old, deprecated; use "target" instead
this.center = this.target;
I think in the past, we could apply a center different to the target but now, this variable is useless. If it's impossible with OrbitControl.js, is there another library to do that ?
The idea is to change the rendering of this project.
Many thanks for any clue you may have !
Nicolas
Related
I am learning ThreeJS and trying to understand some fundamental concepts.
Suppose I have a camera in 3D space, looking at some target (defined in the camera.target property). The camera is located at x1, y1, z1.
I want to add a feature that when activated, moves the camera directly above the target, i.e. it should be looking down at the XY plane, as though the camera is in the sky looking STRAIGHT down.
My question is, how do I do this in ThreeJS, and also how do you think of this conceptually/mathematically?
While your question seems simple, here is a nuanced take with some tips that you might find helpful.
Position
Yes, the simplest answer is to assign the camera's position where the z value is the distance from the target.
camera.position.set( 0, 0, distance )
But this only works if the target is positioned at the origin. What if you target is at position ( 10, 20, 30 )?
You can use vector math to fix this, and three has this baked in for you.
Create a Vector3 with the position assigned as if the target was at the origin.
let offset = new THREE.Vector3( 0, 0, distance )
Add this vector to the target's position, and assign it to the camera's position.
camera.position.addVectors( target.position, offset )
The camera is now positioned above the target.
Rotation
In either case, simply repositioning you camera may not be enough to keep your target in view. After moving your camera, you will need to force it to look at the target.
camera.lookAt( target.position )
Now, lookAt is a fairly simple function, and may not result in the camera roll that you expect. You will need to figure out how best to compensate for this, by adjusting its up, quaternion, or other factors. (Compensating for this is outside the scope of this question.)
Global vs. Local
Another nuance is whether you want the camera to be "above" the part in a global sense, or in a local sense.
If your camera and target exist in a global space (directly in your scene), then the directions above will suit your use-case.
But if your target is rotated on its side within the global space (i.e. its +z axis points along the global +x axis), yet you want the camera's new orientation to be "above" the target in the sense that it is looking down the target's -z axis, then you will need to compensate for the target's rotation as well. Luckily, three also provides math functions that can accomplish this.
camera.position.copy( offset )
camera.position.applyMatrix4( target.matrixWorld )
This first line sets the camera's position to that of the "target at the origin" position. The second line updates that vector using the target's world transformation matrix, effectively translating it into the target's space.
Animation
Reading between the lines, it sounds like you might want to animate this process. There are a variety of animation libraries available, and you'll need to find one that suits your needs and purpose. That said, there are also many questions about animation on Stack Overflow, and I'm sure you can find someone to answer your questions on that topic, should you hit any resistance.
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)
So, im building an architectural visualization with Three.js, and one of the things the user should be able to do is to click on things and orbit around them. The problem is that the camera is able to clip through wall. I fixed that by assigning each clickable object its own limiting azimuth and polar angles. Now the Problem is that azimuth angles go from -PI to +PI and its impossible to limit between for example 1.5, and -2.4 because its limiting the "wrong" way. I hope this graphic explains that a little better:
Heres a link to the live version:
(You control by clicking on the ground)
https://jim-fx.com/modern/
As you can see, on objects on the right side of the room the limiting works flawless, but on the cabinet and the vases the camera clips through the wall.
If anyone could help me that would be amazing. And any other tipps are welcome aswell.
Greetings, Max
There is several solution to your problem. One is to implements a kind of collision detection with some real or virtual wall for your camera, wich stops the rotation. However, I guess your are looking for something simpler to implement.
As i don't know Three.js very well, I will provides you a generic solution, but which should be easily adaptable to Three.js.
The first thing is to do not use the built-in Three.js orbit control, but to implement your own, where you control all your transformations. And, this is in fact very easy.
To create an orbitable camera, you simply have to crate:
A "null" transformable object, which mean a simple transformable entity that does not embed any shape (is not rendered, is invisible, but exists). I hope Three.js provides such elementary thing.
A camera, which should be itself another transformable.
Once you have this, you simply parent the camera to the "null" object. Now parented to the "null" object, if you rotate the "null" object, you rotate the camera with. Then to orbits, you now have to move back the camera from the parent object:
Null Camera
+ - - - - - - - - - |>
Like this, the "null" object becomes the camera "look at point", and if you rotate the "null" object around Y (I believe Three.js use Y up), you controls the camera azimuth. If you rotate the "null" object in X or Z (depending coordinate system), you will control the camera altitude. Then, you even can control the camera forward-backward to close up to the "look at point" by moving your camera in its local Z axis..
Well, you now have an orbit-camera easy to control. But your problem is not yet solved: How to make this control Pi / -Pi possible in every camera initial orientation ?
Simple: You create second "null" transform object, name it "the socle", and you parent the first one to this last one: Like this, the rotation of the camera "look at point" is always local, and you can now rotate "the socle" to give your "Orbital camera" group, an initial orientation in the world space.
In fact, it is pretty like creating virtual gimbals. I hope I was clear, with pictures this would be more easy to visualize...
I'm working on my raytracer and it seems I can't manage to handle the case where the direction vector of my camera is parallel to the vector (0,1,0).
I think it is linked to my way to compute the vector up and right for camera but I can't manage to find a work around.
Here is how I do it:
cam_up = vector_cross(cam_dir, {0, 1, 0});
camp_right = vector_cross(cam_right, cam_dir);
Can somebody enlighten me?
You have the correct formula for calculation of an orthogonal axis from a single cameraOut vector. However, as has been stated this formula will not account for the camera roll, which could be any direction in the plane perpendicular to the camera direction. This will be apparent when moving a camera across the pole (y-axis) as there will be undesireable behavior (yes it will be correctly aimed, but no doubt the roll won't be desired).
For more information, look into gimbal lock.
The roll itself is not really incorrect, however in reality for this camera transition to be smooth and appear correct (rather than suddenly flip or spin as it's direction becomes 0,1,0), you need to correct any roll incurred. This is a rotation about the cameraOut axis and ideally should be relative to the previous cameraAlong. This means in order to maintain the correct roll (or perceived correct roll) you need to consider the camera POSE (position and orientation) from the previous frame and ensure the roll is mitigated. Of course, if the camera doesn't move (i.e. your rendering a frame with a static camera position) you do not have a previous camera state so the position cannot be calculated and instead must be explicitly defined as part of the scene definition.
Personally I store an entire orthogonal axis for a camera so the orientation and roll is always clearly defined. This is only for completeness, to be honest you don't need to store the entire axis, 2 vectors cameraOut and cameraAlong (the third one being cameraUp) are enough. cameraAlong is dependant on the handed-ness of your coordinate system (e.g. for initial camera position say position (0,0,0) in left hand coordinate system, the cameraAlong direction will be in the right direction in relation to the viewer, for right hand system the cameraAlong would be the other way around. The cameraUp and cameraOut would are the same in both coordinate systems).
Hope this helps.
P.S This isn't ray tracing specific and the same principles apply for OpenGL/DirectX or any 3D representation.
I'm working on a simple Three.js demo that uses OrbitControls.js.
I'd like to change the behavior of panning in OrbitControls. Currently, when you pan the camera, it moves the camera in a plane that is perpendicular to the viewing direction. I'd like to change it so that the camera stays a constant distance from the ground plane and moves parallel to it. Google Earth uses a similar control setup.
Edit: I should have mentioned this detail in the first place, but I'd also like the point where you click and start dragging to remain directly under the cursor throughout the entire drag. There needs to be that solid connection between the mouse movement and what the user expects to happen on the screen. Otherwise, it feels as though I'm 'slipping' when I try to move around the scene.
Can someone give me a high-level explanation of how this might be done (with or without OrbitControls.js)?
EDIT: OrbitControls now supports panning parallel to the "ground plane", and it is the default.
To pan parallel to screen-space (the legacy behavior), set:
controls.screenSpacePanning = true;
Also available is MapControls, which has an API similar to that of Google Earth.
three.js r.94
Some time ago I was working on exactly this issue, i.e. adaptation of OrbitControls.js to map navigation.
Here's the code of MapControls.js.
Here's the demo of the controls.
I figured it out. Here's the overview:
Store the mousedown event somewhere.
When the mouse moves, get the new mousedown event.
For each of those points, find the points on the plane where those clicks are located (You'll need to put the points into camera space, transform them into world space, then fire a ray from the camera through each point to find their intersections with the plane. This page explains the ray-plane intersection test).
Subtract the world-space start intersection point from the world-space end intersection point to get the offset.
Subtract that offset from the camera's target point and you're done!
In the case of OrbitControl.js, the camera always looks at the target point, and its position is relative to that point. So when you change the target, the camera moves with it. Since the target always lies on the plane, the camera moves parallel to that plane (as long as you're panning).
You should set your camera 'up' to z axe:
camera.up.set(0,0,1)
And then, the main problem with OrbitControl is its panUp() function. It should be fixed.
My pull request : https://github.com/mrdoob/three.js/pull/12727
y axe is relative to camera axes and should be relative to a fixed plan in the world. To define the expected y axe, make a 90° rotation of camera x axe, based on world z axe.
v.setFromMatrixColumn( objectMatrix, 0 ); // get X column of objectMatrix
v.applyAxisAngle( new THREE.Vector3( 0, 0, 1 ), Math.PI / 2 );
v.multiplyScalar( distance );
panOffset.add( v )
Enjoy!