Flipping (orthographic) camera axes - three.js

It seems possible to flip an axes of an orthographic camera in three, e.g. by the following code:
var tmp = camera.right;
camera.right = camera.left;
camera.left = tmp;
camera.updateProjectionMatrix();
However, this screws up the lighting, so it seems to not be supported (at least not with built in materials). Is this so? Is there another way to achieve this?
The use case I currently have is a scientific scene (loads of spheres and lines) that can be seen from either positive or negative Z direction. The requirement is that the X and Y axis point the same direction (e.g. right and up) no matter which Z-direction you are looking in. Is there an alternative solution if flipping left/right is not the way to go?
For an example of what I am talking about, see this fiddle: https://jsfiddle.net/mt0bpwcz/1/ Try double clicking the scene to switch the camera. Afterwards the lighting will be very strange (especially visible in the specular highlights).

Related

How to animate multiple sprites in one direction using one geometry in Three.js?

I'm trying to do something similar to this example, except instead of having the snow flakes flutter about in all directions I'm trying to animate these sprites in only one direction, like having the snow flakes fall to the ground.
The example above was able to load multiple sprites into one geometry since it can vary the rotations of the points object:
particles.rotation.x = Math.random() * 6;
particles.rotation.y = Math.random() * 6;
particles.rotation.z = Math.random() * 6;
However, this won't work if you're animating all the points in one direction. In this case, would I have to create a new geometry for each sprite, or is there a more efficient way to do this using just one geometry?
There are several options. Instead of rotating randomly, you could:
Decrease the y position on each frame with particles.position.y -= 0.01;. When it crosses a certain threshold (For example: y <= -100), move them back up to the origin (y = 100). You'll have to stagger a few Sprite objects so you don't notice the jump.
Rotate along the x-axis, so the spinning motion makes them go down when in front of the camera.
Since the snowflakes will spin up on the opposite side, you could use some fog to hide the far side, and give it a more wintry feel.
Animating via custom shaders, although this is much more complex if you don't know GLSL shader code.

Raycasting to intersect objects that have been displaced by vertex shader

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.

How do I place an element in front of the camera, facing it regardless of where it is?

I have this simple scene with a "tooltip" entity composed of some data, I'd like to know how to position it in front of the camera. The tooltip will have to face certain points a few meters away so the user can see it. It must obey camera direction (it can be gathered by calculating it from previousPoint to nextPoint where the camera will move), but only y axis (can't be tilted or anything like that).I tried digging through math but couldn't understand good enough to employ a solution for this little project; I appreciate all the help!
setTimeout(function(){
var camera = document.getElementById("cameraS");
var tt = document.getElementById("ttS");
var cameraPos = camera.getAttribute('position');
var ttPos = tt.getAttribute('position');
tt.setAttribute('position', cameraPos);
tt.setAttribute('rotation', {'y': -90});
}, 5000);
JSFiddle
EDIT
I made an image showing what I'm after: http://imgur.com/a/eDhqE
I have point A and point B; the camera will play an animation moving from previous point to the next, and upon reaching there the tooltip will be displayed a few meters away from the point (box) so we can see it. It must take camera orientation into consideration but it must be perpendicular to the ground (can't be tilted).
There is a command THREE.Object.lookAt(THREE.Vector3); that will rotate an object (assuming (0.0,1.0,0.0) is up) to face a vector. You can use this to have it face your camera.
If you only want Y rotation, you can copy the current rotation, then do look at, then copy the rotation.x and rotation.z from the previous frame rotation copy - so that way it'll only correct the y with .lookAt because you reset x and z.

Issues with small rotation values (precision?)

Excuse me if this is an obvious question, I'm quite new to Three.js.
I'm trying to rotate some geometry along its Y axis, quite slowly and I'm using a rotation matrix for that.
In my machine, when setting a value smaller than 0.0076rad for the rotation, the geometry starts spinning, but when it reaches PI/2, it's not able to proceed, and stays flapping in values around PI/2.
Find an example here: http://jsfiddle.net/vn0m7h81/ , where the speed value can be set at...
// Speeds below 0.0076 cause the cube to stop spinning when reaching
// Pi/2 rotation in the Y axis. Values > 0.0075 work fine.
var rotationSpeed = 0.0075;
For incr. values bigger than 0.0075 the geometry keeps spinning forever.
What's the reason for this behavior?
Do not modify mesh.matrix directly in three.js unless you are an experienced user. Instead, update mesh.rotation or mesh.quaternion, and let the renderer update the matrix for you. You can also use mesh.rotateY( radians ).
three.js r.73

Three.js & OrbitControls.js - pan camera parallel to ground plane (like Google Earth)

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!

Resources