Search code examples
c#unity-game-engineshaderquaternionsplane

Unity: plane defined by three points to quaternions


For a study project, I am working on a 3D file in Unity. The aim is to place three markers, draw a plane through the three markers and make a section cut using that plane. I am able to define the markers, add a plane, and make a section cut. However, as the section cut requires the plane to be described in quaternions, I have to translate the values from the plane that I have somehow and I don't know how.

The markers that define a plane (sample values):

Vector3 p1 = new Vector3(1.1f, 2.6f, 3.5f);
Vector3 p2 = new Vector3(.4f,  4.5f, 2.5f);
Vector3 p3 = new Vector3(6.1f, 6.9f, 1.4f);
Plane cut  = new Plane(p1, p2, p3);

Currently, I define the plane using new Plane but I am willing to define it differently (e.g., using PrimitiveType.Plane). I only use this plane for this reference. Next, I add a section cut using an existing shader. However, this shader requires a PlaneNormal and a PlanePosition with four values, see the shader screenshot.

In short, my question is: how do I translate the three coordinates that I have to the eight values required for the shader? Alternatively, if another shader is better suited here, that is fine as well.

Solution

With the help of your answers, I was able to find a solution. Using three points, I define a plane and store its normal as a Vector4.

Plane finger = new Plane(p1, p2, p3);
Vector3 normal = (finger.normal);
Vector4 planeNormal = Vector4.zero;
planeNormal.x = normal.x;
planeNormal.y = normal.y;
planeNormal.z = normal.z;
digitNormals[digit-1] = planeNormal;

Likewise, I define the position as the average of three points. Therefore, I have written a function AvgVector() that takes the average value of the x, y, and z axis.

Vector3 position = AvgVector(p1, p2, p3);
Vector4 planePosition = Vector4.zero;
planePosition.x = position.x;
planePosition.y = position.y;
planePosition.z = position.z;
Quaternion camNormal = Quaternion.LookRotation(normal);
digitPlanes[digit-1] = planePosition;

I store the values of camPos and camNormal in a new GameObject. Later, I grab these values and apply them to my camera.

rend.material.SetVector("_PlaneNormal", digitNormals[dig-1]);
rend.material.SetVector("_PlanePosition", digitPlanes[dig-1]);
SectionCamera.transform.position = section.transform.position;
SectionCamera.transform.LookAt(clone.transform);
RelocateMarker(c, 3);

I know that I use quite some variables and that might limit the usefulness of these snippits. However, the entire code will soon be online at https://github.com/TimJohDij/marker-maker.


Solution

  • TL;DR: These fields aren't interpreted as quaternions. Just plug your plane position and plane normal into these fields as if they were Vector3's, ignoring the w field. If you're not sure how to get these vectors, read below.

    Let's first take a look at the two quaternions the shader is asking for.

    planeNormal

    planeNormal is the direction the plane is facing or the vector pointing away from the plane. So if you wanted to use this shader to slice an object perfectly horizontal, planeNormal would be the quaternion representing Vector3.Up. A more in-depth explanation is the normal vector of a plane is the perpendicular vector to any two vectors lying on the plane.

    planePosition

    planePosition is the center point of the plane. Think of it as the slice offset. Setting this to anything other than Quaternion.identity would move your plane around, otherwise it will always intersect your objects center point.

    How to Approach the Problem

    Before we can come up with our Quaternions we need to have the planePosition and planeNormal as vectors. The planePosition is up to you, so the rest of this answer will focus on the planeNormal.

    Given that

    1. the normal vector of a plane is the perpendicular vector of any two vectors lying on the plane
    2. we can find a perpendicular vector by finding the cross product of two vectors.
    3. we can make a vector with any two points.
    4. You already have the 3 points we need to make two vectors lying on your plane (we'll re-use 1 point).

    So we need to make 2 vectors from the three points you used to define your plane, we'll cross product those two vectors and the resulting vector will be our normal vector.

    Vector3 vec1 = p1 - p3;
    Vector3 vec2 = p2 - p3;
    Vector3 normalVec = Vector3.Cross(vec1, vec2);
    

    Now, after playing around with the shader, I found that those fields are actually just Vector3's with an extra value. As far as I could tell the w value is ignored, so once you find your normal vector, you can just plug its values into the x,y, and z fields.

    But to actually answer your question, the easiest way to convert a vector to a Quaternion is using LookRotation().

    Quaternion planeNormal = Quaternion.LookRotation(normalVec)