你能从 vuforia 目标图像创建一个平面吗?
Can you create a plane from vuforia target images?
如下图所示,我有 4 张 vuforia 目标图像。我想要实现的是测量连接两个目标图像的红轴的角度,相对于我想使用由绿线连接的三个目标图像生成的平面。希望这更容易理解。我将如何使用这三个目标图像生成该平面?
[![][1]][1]
你的问题太广泛了!不过我会试一试:
我假设 "Plane" 你指的是实际可见的 3D 网格。否则,您可以直接使用 3 个给定坐标来创建数学 Plane
.
首先,对于您通常使用矩形网格的平面。您的问题不清楚您希望如何从您获得的 3 个坐标构建它。
我只是假设您在这里直接使用角点 A
、B
和 C
的 3 个坐标创建一个 Parallelogram,然后添加第 4 个坐标D
通过将向量 B
→C
添加到 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
和你的红色矢量(假设在 target1
和 target4
之间)。像
#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 的结果
使用看起来几乎相同的立方体的结果
如下图所示,我有 4 张 vuforia 目标图像。我想要实现的是测量连接两个目标图像的红轴的角度,相对于我想使用由绿线连接的三个目标图像生成的平面。希望这更容易理解。我将如何使用这三个目标图像生成该平面?
[![][1]][1]
你的问题太广泛了!不过我会试一试:
我假设 "Plane" 你指的是实际可见的 3D 网格。否则,您可以直接使用 3 个给定坐标来创建数学 Plane
.
首先,对于您通常使用矩形网格的平面。您的问题不清楚您希望如何从您获得的 3 个坐标构建它。
我只是假设您在这里直接使用角点 A
、B
和 C
的 3 个坐标创建一个 Parallelogram,然后添加第 4 个坐标D
通过将向量 B
→C
添加到 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
和你的红色矢量(假设在 target1
和 target4
之间)。像
#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 的结果
使用看起来几乎相同的立方体的结果