How do I make a THREE.Plane always face the camera? - javascript

Plane has no lookAt method, so I can't just do lookAt(camera.position). Instead, it's defined with its normal, represented as a Vector3. How do I make this plane always face the camera?

Plane.setFromNormalAndCoplanarPoint can be used to accomplish this.
For a simple example, let's assume your camera is orbiting the origin. In this case, you can set up your plane to face the camera very easily:
// assume plane is a THREE.Plane
plane.setFromNormalAndCoplanarPoint( camera.position.clone().normalize(), scene.position )
This uses the camera's (normalized) position as the plane's normal, which works in this case.
If your camera is somewhere in free space, looking in an arbitrary direction, this is a little harder, but it's mostly visualization.
Remember, that a camera lives in its own "space," where it resides at the origin, and looks down the -Z axis. Now imagine where your plane would need to be created within that space. Let's just say that it could be placed at ( 10, 10, -10 ). We can again use some easy operations to compute the normal. The "hard" part is moving everything into world space, but three.js has convenience functions for that, too.
// in camera space...
let planePosition = new THREE.Vector3( 10, 10, -10 )
let normal = planePosition.clone().inverse().normalize()
// convert to world space...
camera.localToWorld( planePosition )
camera.localToWorld( normal )
normal.sub( camera.position ) // fix the normal based on the camera position
plane.setFromNormalAndCoplanarPoint( normal, planePosition )
One note about the code above, planePosition.clone().inverse().normalize() gets a copy of the plane position, inverts it across the origin, then normalizes it. This has the effect of creating a normal which will point at the camera because the camera sits at the origin in its own space. This is why you need to adjust the normal by the camera's position after converting everything to world coordinates.

Related

How to find all mesh faces illuminated by a SpotLight?

I have a THREE.Mesh consisting of a THREE.BufferGeometry with "position" and "normal" THREE.BufferAttributes.
The mesh is illuminated by a THREE.SpotLight (a cone-shaped light source).
Is there a way to find all mesh faces illuminated by the cone? There are no other objects in the scene so I don't need to worry about blocking.
A basic approach
From a very basic perspective, "illuminated by" can also be read as "within or intersecting, and facing the cone."
So first, determine if the face is inside or intersecting the cone. To do this, take all three vertices of the triangle and build a Vector3 that indicates the direction from the spotlight.position to vertex.
// Note: Extracting the vertices of a face will be different
// depending on if it is indexed or not.
// Assume "vertex1", "vertex2", and "vertex3" are the vertices of the face.
// Convert the vertices into World Coordinates
mesh.localToWorld( vertex1 )
mesh.localToWorld( vertex2 )
mesh.localToWorld( vertex3 )
// Get the spotlight's "look at" direction
const spotLook = new Vector3().subVectors( spotlight.target.position, spotlight.position )
// Make the vertex vectors relative to the spotlight
vertex1.sub( spotlight.position )
vertex2.sub( spotlight.position )
vertex3.sub( spotlight.position )
// Get the angles between the vectors
const angle1 = spotLook.angleTo( vertex1 )
const angle2 = spotLook.angleTo( vertex2 )
const angle3 = spotLook.angleTo( vertex3 )
Now, if ANY of these angles is less than the spotlight.angle value, then that vertex is inside the cone of the spotlight. If they're all greater than the spotlight's angle, then they're all outside the cone.
Now you need to determine whether the face is angled toward the spotlight. You can do this by normalizing the vectors between the vertices then crossing them.
// These are the original values of the vertices
vertex1.sub( vertex2 )
vertex1.normalize()
vertex3.sub( vertex2 )
vertex3.normalize()
const crossed = new Vector3().crossVectors( vertex3, vertex1 )
This gives you the "face normal," or the direction in which the face is facing. Again, use angleTo to get the angle against the spotlight's direction. If the angle is greater than Math.PI/2 (90°), then the face leans toward the spotlight. If the angle is greater than that value, then the face leans away from the spotlight.
If a face passes both criteria--facing toward the spotlight, AND at least one vertex is inside the cone--then the face can be considered illuminated.
Caveats
Of course, this is a basic approach, and provides only basic results.
There may be scenarios where your shape has sections that occlude its own faces (self-shadow).
The actual normals of the face may also reduce its acceptance of light. Even if the face its self is angled toward the spotlight, if all the normals are angled away, then the face would be within acceptable bounds, but the shader would NOT illuminate the face.
There may also be scenarios where the penumbra of your spotlight actually causes a face to NOT be illuminated, even though some or all of it is within your spotlight cone.
These are scenarios you'll need to take into account to arrive at the results you seek.

Force camera.lookAt to maintain current Z rotation

So, I'm putting together a 3D project that will eventually make it's way onto a kiosk in the office of a client, basically shows all of their branches as points on a globe. One of the features requested is that end users be able to swipe on the screen to orbit around the globe, in addition, they should be able to rotate the camera on it's Z axis via a rotation gesture. The problem is I'm using camera.lookAt in the animation loop, which relies on the up vector being updated correctly whenever I rotate the camera in order to not "snap" back to place along the previous up vector when a user swipes, and for the life of me, I cannot get it to cooperate.
Currently, what I'm doing to update the up vector (based off of another stackoverflow thread with a similar issue) is this:
//Current full 360 degree angle of rotation, calculated earlier
let radian = THREE.Math.degToRad(full);
//Create new vector at radian angle to camera's current position
let v1 = new THREE.Vector3(_this.object.position.x + Math.cos(radian), _this.object.position.y + Math.sin(radian), _this.object.position.z).sub( _this.object.position ).normalize();
//_this.target = 0,0,0
let v2 = _this.target.clone().sub( _this.object.position ).normalize();
//Cross vectors to get the proper up
let v3 = new THREE.Vector3().crossVectors( v1, v2 ).normalize();
_this.object.up.copy( v3 );
And this works... up to the point where the camera seemingly inverts once I head near the side of the globe opposite the camera's starting position (0,0,1.75) and then negates my rotations (as far as I can tell) which causes the same "snap" to a different rotation like before.
Once I rotate the camera, I want it to maintain the rotation when using lookAt, regardless of the lookAt inverting everything.

updating an object's geometry when camera is moving causes glitches - three.js

I have a problem updating a vertex of a line in three.js
So, I want to have a line in my scene, that its start is always at the (0,0,0) and its end is always in a specific position of the users screen (in x,y coordinates).
What I do to achieve that (and I almost succeed) is to have an invisible plane looking always to the camera and also have its position always a little bit in front of the camera. The reason I do that is because I want the line to seem like "going towards" the user's screen. So I "send" a raycaster from the desired screen position (in x,y) and I check in which point of the plane it intersect and that's my 3D point in three.js scene. Then I update one of the 2 vertices of the line.
The problem
What I do works fine, the line end is where I want to be, but something in updating the camera and the vertex is not synchronized and causes some noticeable glitches. When I move the camera, the line do not update itself quickly and smoothly, and as a result I see the line in other position before I see it in the calculated and desireable one.
Please take a look at this jsfiddle I created to emulate the problem.
What can I do to avoid these glitches?
Thanks
code i use in render function :
var cameToCenterScaled = camera.position.clone();
cameToCenterScaled.setLength(cameToCenterScaled.length()*0.9);
plane.position.set(cameToCenterScaled.x, cameToCenterScaled.y, cameToCenterScaled.z);
plane.lookAt(camera.position);
// define in pixels where in screen we want the line to end
var notePos = findNotePoint(120,30);
linemesh.geometry.vertices[ 1 ].set(notePos.x, notePos.y, notePos.z) ;
linemesh.geometry.verticesNeedUpdate = true;
when you raycast you set the raycaster from camera, you have to make sure the camera matrices are updated
simply add
camera.updateMatrixWorld();
before you call
raycaster.setFromCamera( new THREE.Vector2( x_, y_ ) , camera );
and the line will behave as you described

How do I 'wrap' a plane over a sphere with three.js?

I am relatively new to three.js and am trying to position and manipulate a plane object to have the effect of laying over the surface of a sphere object (or any for that matter), so that the plane takes the form of the object surface. The intention is to be able to move the plane on the surface later on.
I position the plane in front of the sphere and index through the plane's vertices casting a ray towards the sphere to detect the intersection with the sphere. I then try to change the z position of said vertices, but it does not achieve the desired result. Can anyone give me some guidance on how to get this working, or indeed suggest another method?
This is how I attempt to change the vertices (with an offset of 1 to be visible 'on' the sphere surface);
planeMesh.geometry.vertices[vertexIndex].z = collisionResults[0].distance - 1;
Making sure to set the following before rendering;
planeMesh.geometry.verticesNeedUpdate = true;
planeMesh.geometry.normalsNeedUpdate = true;
I have a fiddle that shows where I am, here I cast my rays in z and I do not get intersections (collisions) with the sphere, and cannot change the plane in the manner I wish.
http://jsfiddle.net/stokewoggle/vuezL/
You can rotate the camera around the scene with the left and right arrows (in chrome anyway) to see the shape of the plane. I have made the sphere see through as I find it useful to see the plane better.
EDIT: Updated fiddle and corrected description mistake.
Sorry for the delay, but it took me a couple of days to figure this one out. The reason why the collisions were not working was because (like we had suspected) the planeMesh vertices are in local space, which is essentially the same as starting in the center of the sphere and not what you're expecting. At first, I thought a quick-fix would be to apply the worldMatrix like stemkoski did on his github three.js collision example I linked to, but that didn't end up working either because the plane itself is defined in x and y coordinates, up and down, left and right - but no z information (depth) is made locally when you create a flat 2D planeMesh.
What ended up working is manually setting the z component of each vertex of the plane. You had originaly wanted the plane to be at z = 201, so I just moved that code inside the loop that goes through each vertex and I manually set each vertex to z = 201; Now, all the ray start-positions were correct (globally) and having a ray direction of (0,0,-1) resulted in correct collisions.
var localVertex = planeMesh.geometry.vertices[vertexIndex].clone();
localVertex.z = 201;
One more thing was in order to make the plane-wrap absolutely perfect in shape, instead of using (0,0,-1) as each ray direction, I manually calculated each ray direction by subtracting each vertex from the sphere's center position location and normalizing the resulting vector. Now, the collisionResult intersection point will be even better.
var directionVector = new THREE.Vector3();
directionVector.subVectors(sphereMesh.position, localVertex);
directionVector.normalize();
var ray = new THREE.Raycaster(localVertex, directionVector);
Here is a working example:
http://jsfiddle.net/FLyaY/1/
As you can see, the planeMesh fits snugly on the sphere, kind of like a patch or a band-aid. :)
Hope this helps. Thanks for posting the question on three.js's github page - I wouldn't have seen it here. At first I thought it was a bug in THREE.Raycaster but in the end it was just user (mine) error. I learned a lot about collision code from working on this problem and I will be using it later down the line in my own 3D game projects. You can check out one of my games at: https://github.com/erichlof/SpacePong3D
Best of luck to you!
-Erich
Your ray start position is not good. Probably due to vertex coordinates being local to the plane. You start the raycast from inside the sphere so it never hits anything.
I changed the ray start position like this as a test and get 726 collisions:
var rayStart = new THREE.Vector3(0, 0, 500);
var ray = new THREE.Raycaster(rayStart, new THREE.Vector3(0, 0, -1));
Forked jsfiddle: http://jsfiddle.net/H5YSL/
I think you need to transform the vertex coordinates to world coordinates to get the position correctly. That should be easy to figure out from docs and examples.

Three.js lookat seems to be flipped

I have a demo of what I mean here: Test Site or (Backup)
For some reason, even though the mouse vector is correct my object is rotated by 90 degrees always in favor of the positive Y axis. The only call that this could be going wrong, as far as I can tell, in is the call: ship.mesh.lookAt(mouse);, I call this every time the screen is animated.
Can anyone tell me what to do to fix this and why it is doing it?
object.lookAt( position ) orients the object so that the object's local positive z-axis points toward the desired position.
Your "ship's" front points in the direction of the local positive y-axis.
EDIT:
To re-orient your geometry, apply a matrix right after the geometry is created, like so:
geometry.applyMatrix( new THREE.Matrix4().makeRotationX( Math.PI / 2 ) );

Categories