Unproject Webgl - matrix

So I am trying to create a 2d drawing program in WebGL and I can draw everything fine but I am having a lot of difficulty detecting the location of mouse clicks.
I have a 4x4 projection matrix set up as follows:
mat4.perspective(45, this.gl.viewportWidth / this.gl.viewportHeight, 0.1, 100.0, projectionMatrix);
I then have a 4x4 view matrix which holds the camera position as follows:
mat4.translate(viewMatrix, [camera.getX(), camera.getY(), camera.getZoom()]);
I can get a 2d vector of the position of the mouse click on the screen ranging from [-1,-1] in the top left to [1,1] in the bottom right.
What I want to be able to do is take the current position of the camera, get the frustum bounds on the z-plane and then use my relative mouse click positions to get the actual world space position on the z-plane.
Note:
The camera does not rotate, only pan and zoom.
Everything is drawn on the model matrix which is on the z=0 plane.

Related

Convert screen space X,Y position into perspective projection with a specific z position

I'm using ThreeJS, but this is a general math question.
My end goal is to position an object in my scene using 2D screen space coordinates; however, I want a specific z position in the perspective projection.
As an example, I have a sphere that I want to place towards the bottom left of the screen while having the sphere be 5 units away from the camera. If the camera were to move, the sphere would maintain its perceived size and position.
I can't use an orthographic camera because the sphere needs to be able to move around in the perspective projection. At some point the sphere will be undocked from the screen and interact with the scene using physics.
I'm sure the solution is somewhere in the camera inverse matrix, however, that is beyond my abilities at the current moment.
Any help is greatly appreciated.
Thanks!
Your post includes too many questions, which is out of scope for StackOverflow. But I’ll try to answer just the main one:
Create a plane Mesh using PlaneGeometry.
Rotate it to face the camera, place it 5 units away from the camera.
Add it as a child with camera.add(plane); so whenever the camera moves, the plane moves with it.
Use a Raycaster’s .setfromCamera(cords, cam)
then
.intersectObject(plane)
method to convert x, y screen cords into an x, y, z world position where it
intersects the plane. You can read about it in the docs.
Once it’s working, make the plane invisible with visible = false
You can see the raycaster working in this official example: https://threejs.org/examples/#webgl_geometry_terrain_raycast

Updating the position attribute directly of a Shape geometry breaks the texture on the Mesh. How do I fix the textures?

I try to make a wall with a window in it. I draw the corners of the wall and the corners of the window after, from the bottom left, counter clockwise. Then I manipulate the 4 vertex of the window via javascript and update the position attribute of the geometry accordingly. The window moves correctly but the textures are not. Any help would be appreciated.
Here is what I do after calculating the new positions array:
this.geometry.setAttribute("position", new THREE.BufferAttribute(positions, 3));
Here is the full demo: https://jsfiddle.net/wg1phnz6/45/

ThreeJS - pan camera vertically without tilting?

I am using Mr Doob's periodic table code in my web app:
https://threejs.org/examples/css3d_periodictable.html
I am using it in Helix view. When I rotate the object left, I shift the camera's Y position by a certain amount too. My desire is to give the impression that the Helix is corkscrewing vertically up and down as you rotate the camera. This is the code I'm using, where angle and panAmount are constants that control how much rotation and vertical pan takes place per second:
let g_RotationMatrix = new THREE.Matrix4();
g_RotationMatrix.makeRotationY(angle);
// Apply matrix like this to rotate the camera.
self.object.position.applyMatrix4(g_RotationMatrix);
// Shift it vertically too.
self.object.position.y += panAmount;
// Make camera look at the target.
self.object.lookAt(self.target);
The problem I'm having is that from the camera's perspective, the object appears to tilt towards you and away from you respectively, depending on the rotation direction, as the camera shifts vertically. This makes sense to me because the I'm guessing that the lookat() function causes the camera to look at the center of the target, and not at point on the target that is closest point to it, so the camera has to tilt to focus on the targe's center of mass. I see the same effect when I use the mouse to pan vertically using the Orbit controls. I believe another way to describe the effect is that the object appears to pitch up and down as the camera shifts vertically.
The effect I want instead is that of a window washer on an automated lift being raised up and down the side of building, with the side of the building appearing perfectly flat to the camera regardless of the camera's current Y position.
How can I achieve this effect with the camera?
Make the camera lookAt the target, but at the same y level as the camera.
Assuming self.target is a vector3 object and self.object is the camera:
If, for example, self.target is the camera's rotation center of the object, you wouldn't want to change the actual self.target vector. Make a copy of it first.
const newTarget = new THREE.Vector3( );
function render( ){
// copy the target vector to newTarget so that the original self.target
// will not change and the camera can still rotate around the original
// target.
newTarget.copy( self.target );
// Set newTarget's Y from the camera Y. So that is looks horizontal.
newTarget.y = self.object.position.y;
// Make the camera look at the objects newTarget
self.object.lookAt( newTarget);
}

three.js delay in updating local clipping planes

For realising a scrollable text container (using own bitmap fonts that are basically small sprite meshes) I am using local clipping planes.
When my text container moves the clipping planes are updated according to the global boundaries of my container.
This works perfectly except for fast movements. In this case the clipping planes are slightly delayed behind the container making the text shine through where it shouldn't.
My first thought was that the necessary code for updating the clipping planes might cause the delay.. but when I use apply this order:
1. update the text box position
2. update the clipping planes
3. render()
the delay still exists
Is the reason maybe located in the threejs framework in how the actual clipping is applied?
Here's a small code snippet that shows how I compute my upper clippin plane using two helper meshes. The one is a plane that is positioned orthogonally on my text object (red plane in the picture). The other one is a THREE.Object3D that is positioned in the middle of the upper edge for computing the right plane constant.
// get the world direction of a helper plane mesh that is located orthogonally on my text plane
var upperClippingPlaneRotationProxyMeshWordDirection = _this.upperClippingPlaneRotationProxyMesh.getWorldDirection();
// get the world position of a helper 3d object that is located in the middle of the upper edge of my text plane
var upperClippingPlanePositionProxyObjPosition = _this.upperClippingPlanePositionProxyObj.getWorldPosition();
// a plane through origin which makes it easier for computing the plane constant
var upperPlaneInOrigin = new THREE.Plane(upperClippingPlaneRotationProxyMeshWordDirection, 0);
var dist = upperPlaneInOrigin.distanceToPoint(upperClippingPlanePositionProxyObjPosition);
var upperClippingPlane = new THREE.Plane(upperClippingPlaneRotationProxyMeshWordDirection, dist*-1);
// clipping plane update
_this.myUpperClippingPlane.copy(upperClippingPlane);
picture showing the text object with clipping plane helpers
I found the reason for the delay. In my matrix updating code I only used updateMatrix() on the text object when it moves. To make sure that its child objects including the helper meshes update instantly I had to call updateMatrixWorld(true), this makes sure that the clipping planes are computed correctly

Update plane texture offset from movement on a sphere

I'm working on a driving simulation in Three.js using height map data from the planet Venus.
GitHub repo here: https://github.com/hypothete/venus-walk
Here's how the simulation works so far:
In a hidden scene, a camera called the globeCamera moves at a fixed height over a sphere textured with the Venus height map. You can see this happening in the lower left viewport in my picture. The globeCamera renders its view to a WebGLRenderTarget to be used as a local height map. The result is in the second viewport in the middle left.
In the visible scene, a plane mesh called the terrainMesh has its vertices displaced up and down in correspondence with the values from the local height map. This gives the illusion that a vehicle placed in the center of the plane is moving across a surface when actually we're just updating the plane's vertices from the movement of the globeCamera.
Since I know the rotation of the globeCamera, I can pass that value to my fragment shader to rotate the terrainMesh's rock texture with the height map.
How can I offset the rock texture's position so that texture units translate with the terrain as well? I've tried tracking the globeCamera's offset as a 2D vector and adding that to the rotated UV in the fragment shader, but my results were inconsistent. Thanks for your help.

Resources