你能从 vuforia 目标图像创建一个平面吗?

Can you create a plane from vuforia target images?

如下图所示,我有 4 张 vuforia 目标图像。我想要实现的是测量连接两个目标图像的红轴的角度,相对于我想使用由绿线连接的三个目标图像生成的平面。希望这更容易理解。我将如何使用这三个目标图像生成该平面?

[![][1]][1]

你的问题太广泛了!不过我会试一试:

我假设 "Plane" 你指的是实际可见的 3D 网格。否则,您可以直接使用 3 个给定坐标来创建数学 Plane.

首先,对于您通常使用矩形网格的平面。您的问题不清楚您希望如何从您获得的 3 个坐标构建它。

我只是假设您在这里直接使用角点 ABC 的 3 个坐标创建一个 Parallelogram,然后添加第 4 个坐标D 通过将向量 BC 添加到 A

using UnityEngine;

[RequireComponent(typeof(MeshFilter))]
[RequireComponent(typeof(MeshRenderer))]
public class PlaneController : MonoBehaviour
{
    public Transform target1;
    public Transform target2;
    public Transform target3;

    private MeshFilter meshFilter;
    private Mesh mesh;
    private Vector3[] vertices;

    private void Awake()
    {
        meshFilter = GetComponent<MeshFilter>();
        mesh = meshFilter.mesh;

        vertices = new Vector3[4];

        // Set the 4 triangles (front and backside)
        // using the 4 vertices
        var triangles = new int[3 * 4];

        // triangle 1 - ABC
        triangles[0] = 0;
        triangles[1] = 1;
        triangles[2] = 2;
        // triangle 2 - ACD
        triangles[3] = 0;
        triangles[4] = 2;
        triangles[5] = 3;

        // triangle 3 - BAC
        triangles[6] = 1;
        triangles[7] = 0;
        triangles[8] = 2;
        // triangle 4 - ADC
        triangles[9] = 0;
        triangles[10] = 3;
        triangles[11] = 2;

        mesh.vertices = vertices;
        mesh.triangles = triangles;
    }

    // Update is called once per frame
    void Update()
    {
        // build triangles according to target positions
        vertices[0] = target1.position - transform.position; // A
        vertices[1] = target2.position - transform.position; // B
        vertices[2] = target3.position - transform.position; // C

        // D = A  + B->C
        vertices[3] = vertices[0] + (vertices[2] - vertices[1]);

        // update the mesh vertex positions
        mesh.vertices = vertices;

        // Has to be done in order to update the bounding box culling
        mesh.RecalculateBounds();
    }
}


编辑

在你改变问题之后:在这种情况下,你并不真正需要视觉平面,但如前所述,创建纯数学构造 Plane

平面法线是始终与平面成 90° 的矢量。然后你可以使用例如Vector3.Angle in order to get the angular difference between the normal 和你的红色矢量(假设在 target1target4 之间)。像

#if UNITY_EDITOR
using UnityEditor;
#endif
using UnityEngine;

public class AngleChecker : MonoBehaviour
{
    [Header("Input")]

    public Transform target1;
    public Transform target2;
    public Transform target3;
    public Transform target4;

    [Header("Output")]
    public float AngleOnPlane;

    private void Update()
    {
        AngleOnPlane = GetAngle();
    }

    private float GetAngle()
    {
        // get the plane (green line/area)
        var plane = new Plane(target1.position, target2.position, target3.position);

        // get the red vector
        var vector = target4.position - target1.position;

        // get difference
        var angle = Vector3.Angle(plane.normal, vector);

        // since the normal itself stands 90° on the plane
        return 90 - angle;
    }

#if UNITY_EDITOR
    // ONLY FOR VISUALIZATION
    private void OnDrawGizmos()
    {
        // draw the plane
        var mesh = new Mesh
        {
            vertices = new[]
            {
                target1.position,
                target2.position,
                target3.position,
                target3.position + (target1.position - target2.position)
            },
            triangles = new[] {
                0, 1, 2,
                0, 2, 3,
                1, 0, 2,
                0, 3, 2
            }
        };
        mesh.RecalculateNormals();
        Gizmos.color = Color.white;
        Gizmos.DrawMesh(mesh);

        // draw the normal at target1
        var plane = new Plane(target1.position, target2.position, target3.position);
        Gizmos.color = Color.blue;
        Gizmos.DrawRay(target1.position, plane.normal);

        Handles.Label(target1.position + plane.normal, "plane normal");

        // draw the red vector
        Gizmos.color = Color.red;
        Gizmos.DrawLine(target1.position, target4.position);

        Handles.Label(target4.position , $"Angle to plane: {90 - Vector3.Angle(plane.normal, target4.position - target1.position)}");
    }
#endif
}


在你发表评论后,我看到你想要让它在场景中也可见。 所以我们也回到了我的答案的第一部分,正是在那里完成了这件事。

通过合并前面提到的脚本,将可见平面也简单地添加到场景中。

为了使文本可视化,我建议通过 Unity UI Manual

要使线条出现,有两种选择。您可以使用 LineRenderer (API) 或简单地使用立方体进行相应的旋转和缩放:

// ! UNCOMMENT THIS IF YOU RATHER WANT TO USE THE LINERENDERER !
//#define USE_LINERENDERER

using UnityEngine;
using UnityEngine.UI;

[RequireComponent(typeof(MeshFilter))]
[RequireComponent(typeof(MeshRenderer))]
public class PlaneController : MonoBehaviour
{
    [Header("Input")]

    // Reference these in the Inspector
    public Transform target1;
    public Transform target2;
    public Transform target3;
    public Transform target4;

    [Space]

    public MeshFilter meshFilter;

    [Header("Output")]
    public float AngleOnPlane;

    public Text angleText;
    public float lineWith = 0.05f;

#if USE_LINERENDERER
    public LineRenderer lineRenderer;
#else
    public MeshRenderer cubeLine;
#endif

    private Mesh mesh;
    private Vector3[] vertices;
    private Vector3 redLineDirection;

    // if using line renderer
    private Vector3[] positions = new Vector3[2];

    private void Start()
    {
        InitializePlaneMesh();

#if USE_LINERENDERER
        InitializeLineRenderer();
#endif
    }

    // Update is called once per frame
    private void Update()
    {
        // update the plane mesh
        UpdatePlaneMesh();

        // update the angle value
        UpdateAngle();
#if USE_LINERENDERER
        // update the line either using the line renderer
        UpdateLineUsingLineRenderer();
#else
        // update the line rather using a simple scaled cube instead
        UpdateLineUsingCube();
#endif
    }

    private void InitializePlaneMesh()
    {
        if (!meshFilter) meshFilter = GetComponent<MeshFilter>();

        mesh = meshFilter.mesh;

        vertices = new Vector3[4];

        // Set the 4 triangles (front and backside)
        // using the 4 vertices
        var triangles = new int[3 * 4];

        // triangle 1 - ABC
        triangles[0] = 0;
        triangles[1] = 1;
        triangles[2] = 2;
        // triangle 2 - ACD
        triangles[3] = 0;
        triangles[4] = 2;
        triangles[5] = 3;

        // triangle 3 - BAC
        triangles[6] = 1;
        triangles[7] = 0;
        triangles[8] = 2;
        // triangle 4 - ADC
        triangles[9] = 0;
        triangles[10] = 3;
        triangles[11] = 2;

        mesh.vertices = vertices;
        mesh.triangles = triangles;
    }

#if USE_LINERENDERER
    private void InitializeLineRenderer()
    {
        lineRenderer.positionCount = 2;
        lineRenderer.startWidth = lineWith;
        lineRenderer.endWidth = lineWith;
        lineRenderer.loop = false;
        lineRenderer.alignment = LineAlignment.View;
        lineRenderer.useWorldSpace = true;
    }
#endif

    private void UpdatePlaneMesh()
    {
        // build triangles according to target positions
        vertices[0] = target1.position - transform.position; // A
        vertices[1] = target2.position - transform.position; // B
        vertices[2] = target3.position - transform.position; // C

        // D = A  + B->C
        vertices[3] = vertices[0] + (vertices[2] - vertices[1]);

        // update the mesh vertex positions
        mesh.vertices = vertices;

        // Has to be done in order to update the bounding box culling
        mesh.RecalculateBounds();
    }

    private void UpdateAngle()
    {
        // get the plane (green line/area)
        var plane = new Plane(target1.position, target2.position, target3.position);

        // get the red vector
        redLineDirection = target4.position - target1.position;

        // get difference
        var angle = Vector3.Angle(plane.normal, redLineDirection);

        // since the normal itself stands 90° on the plane
        AngleOnPlane = Mathf.Abs(90 - angle);

        // write the angle value to a UI Text element
        angleText.text = $"Impact Angle = {AngleOnPlane:F1}°";

        // move text to center of red line and a bit above it
        angleText.transform.position = (target1.position + target4.position) / 2f + Vector3.up * 0.1f;

        // make text always face the user (Billboard)
        // using Camera.main here is not efficient! Just for the demo
        angleText.transform.forward = Camera.main.transform.forward;
    }

#if USE_LINERENDERER
    private void UpdateLineUsingLineRenderer()
    {
        positions[0] = target1.position;
        positions[1] = target4.position;

        lineRenderer.SetPositions(positions);
    }
#else
    private void UpdateLineUsingCube()
    {
        // simply rotate the cube so it is facing in the line direction
        cubeLine.transform.forward = redLineDirection.normalized;

        // scale it to match both target positions with its ends
        cubeLine.transform.localScale = new Vector3(lineWith, lineWith, redLineDirection.magnitude);

        // and move it to the center between both targets
        cubeLine.transform.position = (target1.position + target4.position) / 2f;
    }
#endif
}

使用 LineRenderer 的结果

使用看起来几乎相同的立方体的结果