Project 3D point on plane

I need to project a 3D point on a given plane - as of right now I am relying on scene.multiPickWithRay - however, I am under the impression that there has to be a better way. Anybody can point me to the right method implementation in Babylon?

getRayHitPointOnPlane(point) {                
        const length = 10;

        let origin = camera.position;
        let direction = point.subtract(origin).normalize();

        let ray = new BABYLON.Ray(origin, direction, length);
        var hits = scene.multiPickWithRay(ray);

        let pc = new BABYLON.Vector3(0, 0, 0);

        if (hits) {
            for (var i = 0; i < hits.length; i++) {
                let hit = hits[i];
                if (hit.pickedMesh.name == "apf-" + label.name) {
                    pc = hit.pickedPoint;                    
                }
            }
        } else {
            console.log("No hits");
        }

        return pc;
    }

Well honestly I would have done the same but with scene.pickWithRay

I have multiple planes and therefore use multi pick to make sure it projects on the plane I want.

so all good!

This approach is very slow however. I need to project a lot of points. Any other method that handles this?

Well you need to unproject no matter what. Then you can manually do the picking just using the bounding info of your plan?

here is the relevant code to repro:

(see the onlyBoundingInfo param)

I don’t follow. I am not sure what you mean by “using bounding info of your plan” - while I see your fastCheck flag and onlyBoundingInfo, I don’t really follow how to translate this to the code snippet above.

this is the code of multipick:

as you can see it uses internalMultiPick. So my point was that you may want to rewrite it based on the code I shared

But honestly not sure it will be faster as you still go through layers of code used to compute the intersection.

If you want to project a point onto a plane, you may simply want to do a mathematical projection: Projection of a point onto a plane, Projection of a point onto a line

Maybe people on the forum will be happy to add the function for you on the framework (cc @jerome, @JohnK)

@Deltakosh: The link you provided for mathematical projection provides an orthogonal projection - we are interested here in mathematically projecting along a given ray - i.e. the mathematical (and faster) way of getting the intersection point between a given ray (line) and plane. I have a simple solution that yields 20x improvements for me. Maybe the supporters on the forum can include it in the framework eventually.

For others that may have this issue:

If you have a mesh plane, make sure you use a source plane when creating it:

// apf position is the position of the plane
// The plane normal is the camera's forward ray normalized and negated
var sourcePlane = BABYLON.Plane.FromPositionAndNormal(apfPosition, camera.getForwardRay().direction.normalize().negate());
var plane = BABYLON.MeshBuilder.CreatePlane("apf-" + label.name, { width: this._width, height: this._height, sourcePlane: sourcePlane}, scene);

Now the function, that relies on the following slide: Ray-Plane Intersection

/**    
     * Projects a point to a plane along a ray starting from the camera origin and directed towards the point. 
     * @param {BABYLON.Vector3} point      
     * @param {BABYLON.Plane} plane
     * @return {BABYLON.Vector3} The projection of the point p on the plane
     */
    projectOnPlane(point, plane) {        
        let n = plane.normal;        
        let d = plane.d;

        
        // ray origin
        let p0 = camera.position;
        // ray direction
        let V = point.subtract(p0).normalize();

        let denom = BABYLON.Vector3.Dot(V, n);
        let t = -(BABYLON.Vector3.Dot(p0, n) + d)/denom;

        // P = P0 + t*V
        return p0.add(V.scale(t));
    }
3 Likes

Do you want to add that function to the Vector3 class?

Well I will add it for you (as you provided all the code :))

here is what you will have in the new nightly (it will be even faster as I will remove all GC call):

    /**    
     * Projects a point to a plane along a ray starting from a specified origin and directed towards the point. 
     * @param origin defines the origin of the projection ray
     * @param plane defines the plane to project to
     * @param result defines the Vector3 where to store the result
     */
    public projectOnPlaneToRef(plane: Plane, origin: Vector3, result: Vector3): void {  

Perfect - so including

<script src="https://preview.babylonjs.com/babylon.js"></script>

means that tomorrow morning I should have that method in the Vector3 class?

correct :smiley:

@Deltakosh sorry to revive this but I am confused, perhaps you will explain what I am doing wrong in trying to understand projectOnPlane.

  1. Taking the function in the solution post I get what I expect. When the ray hits the plane the yellow hit point lies on the ray, ie on most runs

Babylon.js Playground (Line 56 uses the function)

  1. Using the BJS method for projectOnPlane, the yellow hit point does not appear where expected.

https://playground.babylonjs.com/#QD54M6#1 (Line 56 use BJS method)

The only thing I can see is the use of normalize in the function but not in the BJS method.

If its not that then its definitely me.

Hey @JohnK smthg has been updated here Bug Fix - Vector3 project on plane by strutcode ¡ Pull Request #12663 ¡ BabylonJS/Babylon.js ¡ GitHub

I wonder if it might be the root cause of the issue ?

I am completely unsure of the correct way and would really appreciate if you could let us know ?

I think you are correct about the bug fix. I think that ‘strutcode’ was confused about the intention of the method. Whilst this topic make the intention clear the comments in the code do not.

/**
     * Projects the current vector3 to a plane along a ray starting from a specified origin and directed towards the point.
     * @param plane defines the plane to project to
     * @param origin defines the origin of the projection ray
     * @param result defines the Vector3 where to store the result
     */

It is not clear what is meant by the point. This would make it clearer.

/**
     * Projects the current point Vector3 to a plane along a ray starting from a specified origin and passing through the current point Vector3.
     * @param plane defines the plane to project to
     * @param origin defines the origin of the projection ray
     * @param result defines the Vector3 where to store the result
     */

The original intention was from an observational origin (camera.position) draw a line through the point vector the method is applied to and and see where it hits the plane.
Diagram below when plane passes through world origin.

In the original code these lines

1158        // ray direction
1159       this.subtractToRef(origin, V);

set the direction of the intersecting line; i.e. vector from origin to vector

In the updated version the vector the method is applied to is taken as a direction vector and forms the line through the origin that hits the plane as in the diagram below.

Looking at these lines it is the vector (this) used for the line direction.

1162         const { x, y, z } = this;

1188         result.set(origin.x + x * hitDistance, origin.y + y * hitDistance, origin.z + z * hitDistance);

I think the original code needs to be re-instated. However there are still somethings to be considered. The function projectOnPlane given in the post is based on the origin being the camera viewing the point vector and plane and hence it is unlikely that the ray is parallel to the plane. The change in BJS to a general origin could mean that the ray might be parallel to the plane or could be pointing away from the plane. I need to investigate further and see what changes need to be made to the original function for it to work with all origins and vector points.

1 Like

I have amended the original code to take care of the ray being close to parallel to the plane. The original code already took into account the direction the ray was pointing.

Here is a Javascript version of the function.

Now I will see if I can do a PR to re-instate the original code with these ammendments.

3 Likes

@JohnK you are awesome !!! and this is an under statement :slight_smile: I am so glad you are part of the community. Thanks you for your analysis.

whats that plane.d ?