Update plane texture offset from movement on a sphere - three.js

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.

Related

MapLibre GL JS with terrain layer: How to pin a horizontal plane to a specific altitude?

I based some code on the "Add a 3D model" example at maplibre.org in order to draw only a horizontal plane on a map which uses setTerrain to add a terrain layer.
My intention is to draw a couple of semitransparent layers at a given heights above sea level and have them intersect with mountains, somewhat similar to contour lines.
In my first test I just created a 1km-wide square at altitude 0.
I am somewhat confused by the behavior, since altitude 0 turns out to be the height of the terrain in the center of the visible map area. When I then drag the map and release the mouse, altitude 0 gets somehow reset again to the new altitude 0 of the terrain at the center, making the plane change its relative altitude.
The following animated GIF illustrates the problem:
The GIF has a mountain range to the right and the elevation is greatly exaggerated, in order to better illustrate the issue.
What do I need to do in order to be able to specify the height of the plane in meters above sea level and have it appear to be fixed at that height when dragging the map?
I think I need to get the height of the terrain at the center and then add/substract it from the plane's z-position in order for it to stay put at the height relative to given landmarks, but I have got no idea on how to do this inside of a custom layer's render function.

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.

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

Unproject Webgl

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.

How to load textures to different faces of a cube in GLGE? (or at least WebGL)

I have 6 textures I would like to load on 6 different faces of a cube. I'm trying to make a new texture by using GLGE.TextureCube();. And then I load all six images to the faces the supposedly should be on the cube like so
mapTex = new GLGE.TextureCube();
mapTex.setSrcNegX("models/map/negx.jpg"); // they are all 1024x1024
mapTex.setSrcNegY("models/map/negy.jpg");
mapTex.setSrcNegZ("models/map/negz.jpg");
mapTex.setSrcPosX("models/map/posx.jpg");
mapTex.setSrcPosY("models/map/posy.jpg");
mapTex.setSrcPosZ("models/map/posz.jpg");
And then I add the texture to the Wavefront object. However, it seems only one of the 6 texture images is getting mapped and its mapped incorrectly.
My guess is that when it creates the new texture map out of the other 6, it tiles them beside each other so the new texture map's co-ordinates no longer correspond to that my obj file.
How can I properly combine 6 textures to one map to be used with GLGE? Or is there a way to manually load a texture on a face of a Mesh?
Cube maps are somewhat special, as the usual UV (ST) texture coordinates don't work for them. A cube map, the name suggests it, consists of 6 quadratic textures, arranged as the faces of a cube. The texture coordinates are not absolute positions on the cube's faces, but directions from the center of the cube away, and the position where a ray from the center in the given direction hits the cube, is the position of the texture on that particular face.
If you apply texture coordinates with the third coordinate being zero, like those in Wavefront, you will address only a slice of the cube's face, namely the part that intersects with the XY plane. If you want to see a working cubemap in action, use the object's smooth normals as texture coordinates.
You'll need to use a different texture coordinate, eg:
materialLayer.setMapinput(GLGE.MAP_OBJ)
depending on what you want try GLGE.MAP_OBJ,GLGE.MAP_NORM or GLGE.MAP_ENV

Resources