Geometric Vectors: Vector-to-Planar Projection and LookAt

      After the confidential development of the research institute, I came back to isolate two waves. I haven’t touched the computer on the external network for nearly four or five months. It can be said that I have become a primitive person.
      Because of some development details, it is necessary to implement the vector projection and LookAt functions, and record them.
      First implement vector-to-plane projection, as follows:
insert image description here

      We talked about the relationship between point-vector planes before, and it seems that we have written several articles, so we will keep it simple here. In fact, calculating the projection is nothing more than calculating the projection P0 of P on the plane G. The calculation is as follows:
Please add a picture description      We obtain the ABCD component according to the plane G equation, and then calculate the coefficient k, and bring it into the X0, Y0, Z0 algebraic formula to reach the P0 coordinates, as follows:

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class PALAVectorProjection : MonoBehaviour
{
    
    
    public Transform ground0;
    public Transform ground1;
    public Transform ground2;

    public Transform from;
    public Transform to;

    void Start()
    {
    
    

    }

    void Update()
    {
    
    
        Vector3 g0 = ground0.position;
        Vector3 g1 = ground1.position;
        Vector3 g2 = ground2.position;

        Debug.DrawLine(g0, g1, Color.black);
        Debug.DrawLine(g1, g2, Color.black);
        Debug.DrawLine(g2, g0, Color.black);

        Vector3 f = from.position;
        Vector3 t = to.position;

        Debug.DrawLine(f, t, Color.yellow);

        Vector3 f0 = GetProjectPoint(f, g0, g1, g2);
        Vector3 t0 = GetProjectPoint(t, g0, g1, g2);

        Debug.DrawLine(f0, t0, Color.white);
    }

    /// <summary>
    /// 获取向量到平面投影向量
    /// </summary>
    /// <param name="f"></param>
    /// <param name="t"></param>
    /// <param name="g0"></param>
    /// <param name="g1"></param>
    /// <param name="g2"></param>
    /// <returns></returns>
    private Vector3 GetProjectVector(Vector3 f, Vector3 t, Vector3 g0, Vector3 g1, Vector3 g2)
    {
    
    
        Vector3 f0 = GetProjectPoint(f, g0, g1, g2);
        Vector3 t0 = GetProjectPoint(t, g0, g1, g2);
        Vector3 vproj = t0 - f0;
        return vproj;
    }

    /// <summary>
    /// 计算点到平面投影
    /// </summary>
    /// <param name="p"></param>
    /// <param name="g0"></param>
    /// <param name="g1"></param>
    /// <param name="g2"></param>
    /// <returns></returns>
    private Vector3 GetProjectPoint(Vector3 p, Vector3 g0, Vector3 g1, Vector3 g2)
    {
    
    
        Vector3 gv1 = g1 - g0;
        Vector3 gv2 = g2 - g0;
        Vector3 n = Vector3.Cross(gv1, gv2).normalized;

        //构建平面参数
        float A = n.x, B = n.y, C = n.z;
        float D = -A * g0.x - B * g0.y - C * g0.z;

        //构建投影参数
        float X = p.x, Y = p.y, Z = p.z;
        float k = (A * X + B * Y + C * Z + D) / (A * A + B * B + C * C);

        float X0 = X - k * A;
        float Y0 = Y - k * B;
        float Z0 = Z - k * C;

        return new Vector3(X0, Y0, Z0);
    }
}

      The effect is as follows:
insert image description here
      the white line segment is the projection of the yellow line segment on the plane G.
      Go ahead, and then implement the LookAt function, first look at the API of unity, as follows:

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class PALATestLookAt : MonoBehaviour
{
    
    
    public Transform from;
    public Transform to;

    void Start()
    {
    
    

    }

    void Update()
    {
    
    
        Debug.DrawLine(from.position, to.position, Color.blue);
        Debug.DrawLine(from.position, from.position + from.right, Color.red);
        Debug.DrawLine(from.position, from.position + from.up, Color.green);
        from.LookAt(to);
    }
}

      The effect is as follows:
insert image description here
      Now I realize it myself, as follows:
insert image description here

      Orientation calculation is the 180-degree rotation calculation of the half-angle vector. Let the vector ft0 with the modulus length on the Z-axis equal to the ft modulus rotate 180 degrees around the half-angle vector ft1, and it becomes a vector ft.
      Realize it:

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class PALAAxisLookAt : MonoBehaviour
{
    
    
    public Transform from;
    public Transform to;

    void Start()
    {
    
    

    }

    void Update()
    {
    
    
        Debug.DrawLine(from.position, to.position, Color.blue);
        Debug.DrawLine(from.position, from.position + from.right, Color.red);
        Debug.DrawLine(from.position, from.position + from.up, Color.green);

        GetAxisLookAt();
    }
    /// <summary>
    /// 绕中轴旋转180度
    /// </summary>
    private void GetAxisLookAt()
    {
    
    
        Vector3 f = from.position;
        Vector3 t = to.position;

        Vector3 ft = t - f;

        float ftlen = Vector3.Distance(f, t);
        Vector3 ft0 = Vector3.forward * ftlen;

        Vector3 ft1 = ft + ft0;

        from.eulerAngles = Vector3.zero;

        //绕ft1轴旋转180度
        from.RotateAround(f, ft1, 180);
    }
}

      The effect is as follows:
insert image description here
      This is an implementation method of LookAt.
      Continue to explore, the orientation calculation that exists in reality, tanks are a good example, as follows:
insert image description here

      The turret of the tank can be imagined as rotating around the Y axis of the world coordinates, and the barrel can be imagined as rotating around the X axis of the local coordinates. Draw a little bit, as follows:
insert image description here

      First find the projection ft0 of ft on the plane xfz, the angle θy degrees between ft' (fz) and the projection ft0 around the world coordinate Y axis is the rotation degree of the "turret", and then the angle between ft0 and ft around the local coordinate X axis θx degrees It is the degree of rotation of the "barrel", and the rotation is based on the left-hand rule . Then the following code is implemented:

using System.Collections;
using System.Collections.Generic;
using System.Reflection;
using UnityEngine;

public class PALAProjectionLookAt : MonoBehaviour
{
    
    
    public Transform from;
    public Transform to;

    void Start()
    {
    
    
        Application.targetFrameRate = 60;
    }

    void Update()
    {
    
    
        Vector3 f = from.position;
        Vector3 t = to.position;
        Vector3 lx = f + from.right;
        Vector3 ly = f + from.up;
        Vector3 lz = f + from.forward;

        Debug.DrawLine(f, lx, Color.red);
        Debug.DrawLine(f, ly, Color.green);
        Debug.DrawLine(f, t, Color.black);

        GetProjectLookAt();
    }

    /// <summary>
    /// 世界坐标Y轴投影旋转
    /// 本地坐标X轴旋转
    /// </summary> 
    private void GetProjectLookAt()
    {
    
    
        Vector3 f = from.position;
        Vector3 t = to.position;
        Vector3 x = f + Vector3.right;
        Vector3 y = f + Vector3.up;
        Vector3 z = f + Vector3.forward;

        //重置旋转
        from.eulerAngles = Vector3.zero;

        //绕世界Y轴旋转
        Vector3 yaxis = Vector3.up;
        Vector3 ft0 = GetProjectVector(f, t, f, x, z);
        Vector3 fz = Vector3.forward;
        float θy = GetVectorAxisAngle(fz, ft0, yaxis);
        from.RotateAround(f, yaxis, θy);
        //绕本地X轴旋转
        Vector3 xaixs = from.right;
        Vector3 ft = t - f;
        float θx = GetVectorAxisAngle(ft0, ft, xaixs);
        from.RotateAround(f, xaixs, θx);

#if UNITY_EDITOR
        Debug.LogFormat("PALAProjectionLookAt θx = {0} θy = {1}", θx, θy);
#endif
    }

    /// <summary>
    /// 获取绕axis轴f到t的角度
    /// </summary>
    /// <param name="f"></param>
    /// <param name="t"></param>
    /// <param name="axis"></param>
    /// <returns></returns>
    private float GetVectorAxisAngle(Vector3 f, Vector3 t, Vector3 axis)
    {
    
    
        float deg = Vector3.SignedAngle(f, t, axis);
        return deg;
    }

    /// <summary>
    /// 计算f(不变)的投影向量
    /// </summary>
    /// <param name="f"></param>
    /// <param name="t"></param>
    /// <param name="g0"></param>
    /// <param name="g1"></param>
    /// <param name="g2"></param>
    /// <returns></returns>
    private Vector3 GetProjectVector(Vector3 f, Vector3 t, Vector3 g0, Vector3 g1, Vector3 g2)
    {
    
    
        Vector3 t0 = GetProjectPoint(t, g0, g1, g2);
        Vector3 ft0 = t0 - f;
        return ft0;
    }

    /// <summary>
    /// 计算点到平面投影
    /// </summary>
    /// <param name="p"></param>
    /// <param name="g0"></param>
    /// <param name="g1"></param>
    /// <param name="g2"></param>
    /// <returns></returns>
    private Vector3 GetProjectPoint(Vector3 p, Vector3 g0, Vector3 g1, Vector3 g2)
    {
    
    
        Vector3 gv1 = g1 - g0;
        Vector3 gv2 = g2 - g0;
        Vector3 n = Vector3.Cross(gv1, gv2).normalized;

        //构建平面参数
        float A = n.x, B = n.y, C = n.z;
        float D = -A * g0.x - B * g0.y - C * g0.z;

        //构建投影参数
        float X = p.x, Y = p.y, Z = p.z;
        float k = (A * X + B * Y + C * Z + D) / (A * A + B * B + C * C);

        float X0 = X - k * A;
        float Y0 = Y - k * B;
        float Z0 = Z - k * C;

        return new Vector3(X0, Y0, Z0);
    }
}

      The effect is as follows:
insert image description here
      This is also an implementation method of LookAt.
      There is also a LookAt implementation method, which is rotated by cross product vector, as follows:
insert image description here

      It can be seen that after calculating the fc cross product vector, and then calculating the rotation angle θ from fdir to ft, the LookAt orientation can be calculated. The code is as follows:

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class PALACrossLookAt : MonoBehaviour
{
    
    
    public Transform from;
    public Transform to;

    void Start()
    {
    
    

    }

    void Update()
    {
    
    
        Debug.DrawLine(from.position, to.position, Color.blue);
        Debug.DrawLine(from.position, from.position + from.right, Color.red);
        Debug.DrawLine(from.position, from.position + from.up, Color.green);

        GetCrossLookAt();
    }
    /// <summary>
    /// 绕叉积向量旋转θ度
    /// </summary>  
    private void GetCrossLookAt()
    {
    
    
        Vector3 f = from.position;
        Vector3 t = to.position;

        Vector3 ft = t - f;

        Vector3 fdir = from.forward;

        Vector3 c = Vector3.Cross(fdir, ft);

        float θ = Vector3.SignedAngle(fdir, ft, c);

        from.RotateAround(f, c, θ);
    }
}

      The effect is as shown in the figure:
insert image description here

      This is also an implementation of LookAt.
      The advantage of implementing LookAt by yourself is that you can freely control the orientation axis, and the API may not necessarily be suitable for development needs.
      OK, time to continue later.

Guess you like

Origin blog.csdn.net/yinhun2012/article/details/128098726