Vertex Shader Sphere Projection and CPU Distance Calculation

I have multiple planes whose vertices are transformed to a position on a sphere in the vertex shader. The GLSL function called pointToSphere transforms a point p to a position on a sphere with a radius of 100 units centered around localcenter.

I need to find a way to calculate the distance between the newly transformed planes and some poistion C on the CPU side. This doesn’t work plane.distanceTo(C)

EXAMPLE CODE

Screenshot 2023-09-21 114556

What about Sphere.distanceToPoint? You can create a Sphere object (this is a mathematical object, not graphical sphere) that matches your drawn sphere. Then use its distanceToPoint method.

I don’t think it would work because my algorithm has N number of planes for each side, and I need to find the distance between each plane and point C, not the entire sphere. (if that makes sense)

Yes, this is a significant change and requires a way more complex computation. Off the top of my head I can think of this algorithm:

  • find whether the point, when projected on the sphere, falls on the patch
  • if yes, then the distance to the patch is the same as the distance to the sphere
  • if not, then the distance should be to some point on the boundary of the patch – the boundary is made of four circular arcs, so calculate the minimal distance to any of the arcs and take the grand minimum

(Alternative approach is to find another route for your project, so that such distances are not needed)

To do this, you have to transform the planes into a sphere on the CPU side so that you have the 3D information of the planes for 3js. The center of each of your planes then lies exactly on the sphere. Then you can work with “distanceTo”. If the center of your sphere is the origin then you can do this with

plane.position.normalize(); 
plane.position.multiplyScalar(radius); 

If you do this for all planes, the centers of all planes are exactly on the sphere surface.

1 Like

A small remark: .setLength() does this internally

1 Like

Calculating the distance to the center of a patch (left image) is easy. Calculating the distance to the nearest point from the patch (right image) is slightly more difficult.

Maybe @michealmyers81 should specify what does "distance between the newly transformed planes and some poistion C mean.

1 Like

I tried the algorithm, that I proposed earlier – to see whether it works. The result looks plausible for when point C is outside the sphere.

Phew!

An arrow points to the nearest point of the patch. The dotted line (hard to see its dottedness in the video) is perpendicular to the surface, so it is easier to see when the closest point is not straight below. To make the calculations more interesting, the patch is NOT aligned to parallels of latitudes and meridians of longitudes.

The first 30 seconds of the video show one point C, the rest – 200 points.

1 Like

Amazing, that’s really neat! How did you end up finding the nearest point(s)? Also for the 200 points are you using the acceleratedRaycast as part of three-mesh-bvh?

I’m using only the traditional THREE.Raycaster for the first check, all the rest is vector arithetics. Generally, the algorithm is:

  • cast a ray from the sphere center towards the point and if it intersects the plane, this is good. The intersection point is projected on the sphere and this is the closest point. (case A)
  • if there is no intersection, then the point is “outside” the plane frustum, so the nearest point should be somewhere along the curved perimeter of the patch.
  • the patch is made of four circular arcs, so for each of them I project the point on the plane of the arc, then check whether its projection on the arc’s circle is circularily between the end points. If it is then this point is a candidate for closest point.
  • when all four arc are processed, the closest point from their candidates is selected (case B)
  • however, if none of the arcs produces a candidate, then the nearest point is one of the four vertices of the patch, so I check them and pick the closest (case C).

Here is an oversimplified diagram of the three cases (the original cases are not with rectangle, not with well aligned sides, not in 2D, but still the concept is very similar):

image

PS. As for BVH, I tried to use it in the past, but failed so badly, that I haven’t gain the courage to try again.

1 Like

Neat, that’s very interesting, great approach!

this raycast example is pretty straight forward if you check the source code essentially it’s embedding the accelerated raycaster into the Mesh class with .prototype and then the same with computeBoundsTree into the BufferGeometry class so the raycaster can cast against that boundsTree…

import {
	acceleratedRaycast, computeBoundsTree, disposeBoundsTree
} from 'three-mesh-bvh';

THREE.Mesh.prototype.raycast = acceleratedRaycast;
THREE.BufferGeometry.prototype.computeBoundsTree = computeBoundsTree;
THREE.BufferGeometry.prototype.disposeBoundsTree = disposeBoundsTree;

It makes raycasting on that sort of scale rapid.

2 Likes

I was thinking more of the left image, the distance between point C and the center of a patch

do you have an example of how this is done?

Ignore what I have suggested, it is for the right image. Instead, have a look at what @Attila_Schroeder and @prisoner849 suggest. They show how to get the center of the projected plate. You only have to get the distance to C (with Vector3.distanceTo).

ok, that’s what I was afraid of. Doing it that way I would have to project the vertices on the CPU side.

  let radius = 8
  let center = new THREE.Vector3().copy(cnt.position);
  let localCenter = new THREE.Vector3();
  let v3 = new THREE.Vector3();
    planeMesh.worldToLocal(localCenter.copy(center));
    let pos = planeMesh.geometry.attributes.position;
    for(let i = 0; i < pos.count; i++){
      v3.fromBufferAttribute(pos, i);
      v3.sub(localCenter);
      v3.setLength(radius ).add(localCenter);
      pos.setXYZ(i, v3.x, v3.y, v3.z);
    }
    planeMesh.geometry.computeVertexNormals();
    pos.needsUpdate = true;

  console.log(planeMesh.position.distanceTo(C.position))

No!

Please, read what @Attila_Schroeder suggested.

You need to “project” only one point (the center of the plane, which by default is its position). In the image below, you deal only with the red points in order to find the center of the curved plane.

Edit 1: just to clarify, by “its position” I refer to the position of the mesh (planeMesh.position), not the attribute position, which is planeMesh.geometry.attributes.position.

Edit 2: If the plane is off-centered, then the center of the plane does not project into the center of the projected plane. Here is illustration of this:

image

I will try to make a demo.

2 Likes

Here it is (the plane is automatically changed every second):

https://codepen.io/boytchev/pen/NWeyEWm?editors=0010

Because there are different definitions of what is considered a center of such curved shape, the demo shows the two simplest ones:

Look at line 182, you can switch which definition to use:

  • randomizeScene1 → calculates the center by projecting the center of the plane (i.e. one point is used); this is very inaccurate method especially if the plane is off-centered, but at least it will give a point inside the curved plane

  • randomizeScene4 → calculates the center by averaging the four corner points of the projected plane, this is a better method

Maybe the best method (in terms of visual perception) is to use the center of mass, but it is harder to calculate. I hope that randomizeScene4 could be sufficient for your project.

3 Likes

yes, this is exactly what I was looking for. Thanks

1 Like

amazing stuff! this reminds me of an (previously) unsolved scientific math equation that had a huge bounty a good few years ago… finding the area of a circle which is projected onto the surface of a torus…

1 Like

I solved this mathematically. Calculating the exact distance from any arbitary point on the sphere to the edges requires some mathematical effort (geodesic distance, azimuth, haversine, …). How does the rayCaster work? I can imagine that an iterative approach like yours can be more efficient than a strictly mathematical one like mine.

1 Like