立即从 3D 管道访问 2D 框的 Unity 功能?
Posted
技术标签:
【中文标题】立即从 3D 管道访问 2D 框的 Unity 功能?【英文标题】:Unity function to access the 2D box immediately from the 3D pipeline? 【发布时间】:2018-08-18 06:29:49 【问题描述】:在 Unity 中,假设您有一个 3D 对象,
当然,获得AABB是微不足道的,Unity有直接的功能,
(您可能必须以通常的方式“将渲染器的所有边界框相加”,没问题。)
所以 Unity 确实有一个直接功能,可以立即为您提供 3D AABB 盒子,而不是每帧的内部网格/渲染管道。
现在,对于有问题的相机,AABB 确实覆盖了某个 2D 边界框...
事实上......有没有某种内置的直接方法可以在 Unity 中找到橙色的 2D 框??
问题 - Unity 是否有一个功能可以立即从管道中提供 2D 截锥体?
(请注意,要手动执行此操作,您只需为 AABB 的 8 个点制作光线(或使用世界到屏幕空间,就像 Draco 提到的那样);将它们封装在 2D 中以制作橙色框。)
我不需要手动解决方案,我问的是引擎是否在每一帧都以某种方式从管道中给出这个?
有电话吗?
(确实,有这个会更好……)
我的感觉是其中的一个或全部
尤其是遮挡系统 着色器 渲染器肯定会知道橙色框,甚至可能知道管道内的蓝色框,就在显卡旁边,就像它知道给定网格的 AABB 一样。
我们知道 Unity 可以让您在给定网格的每一帧立即点击 AABB 3D 框:事实上,Unity 是否提供了此处所示的“2D 视锥体边界”?
【问题讨论】:
这是一个有趣的问题... @Programmer 我想是的。 我也是。很明显,(1) 整体渲染引擎和/或 (2) 着色器和/或 (3) 遮挡系统,实际上必须立即知道此信息 在每个对象的每一帧内。 ( @Hristo - 该算法实际上不起作用!想一想物体何时旋转。您将立即面临扩展/收缩侧面以包含局部截头体部分断裂的主要工作。A非常困难的问题。) 最后只是 FWIW,@Lotan - 我们想要的是 AABB 的“平板相机盒”。即“橙色方块”。使用 8 个极端手动(正如我解释的那样)相对容易。问题是统一是否内置(可能在金属上)。 (我关于 blue 框的附录完全不同;但谁知道呢,也许 Unity 也提供了。) 【参考方案1】:据我所知,没有内置为此。
但是,自己找到极端确实非常容易。获取网格的边界框(屏幕截图中显示的长方体)就是这样做的,您只是在转换后的空间中进行。
-
遍历网格的所有顶点,执行以下操作:
Transform 从本地空间到世界空间的点(处理缩放和旋转)
Transform从世界空间到屏幕空间的点
确定新点的 X 和 Y 是否高于/低于存储的最小值/最大值,如果是,则使用新值更新存储的最小值/最大值
循环遍历所有顶点后,您将获得 4 个值:min-X、min-Y、max-X 和 max-Y。现在您可以构建边界矩形了
您可能还希望首先执行模型的Gift Wrapping,并且只处理生成的凸包(因为不属于凸包的任何点都不会超出凸包的边界)。如果您打算在模型在屏幕上移动、缩放或旋转时绘制此屏幕空间矩形,并且必须重新计算边界框,那么您需要这样做并缓存结果。
请注意,如果模型动画(例如,如果您的类人机器人站起来并跳起千斤顶),这不起作用。解决动画案例要困难得多,因为您必须将每个动画的每一帧都视为原始网格的一部分,以便进行凸包求解(以确保您的任何动画都不会移动网格的一部分在凸包之外),将复杂度增加一个幂。
【讨论】:
嘿 Draco,游戏引擎在芯片组上计算这些东西是(如问题中所解释的)三个过程的一部分 - 渲染、着色器、遮挡。事实上,我们知道 Unity 公开了 AABB 计算(因此自己计算会很疯狂,对吧?:))。这里的问题是截锥体 是否在 Unity 中暴露(就像 AABB 流一样,每一帧)。 我已经删除了我自己的 cmets 并编辑了答案。 它看到“据我所知,没有内置的。”实际上是正确 - 就是这样!【参考方案2】:3D 边界框
-
获取给定的 GameObject 3D 边界框的中心和大小
计算 8 个角
将位置转换为 GUI 空间(屏幕空间)
函数GUI3dRectWithObject
将返回屏幕上给定游戏对象的3D边界框。
二维边界框
-
遍历给定游戏对象中的每个顶点
将每个顶点的位置转换为世界空间,并转换为 GUI 空间(屏幕空间)
求4个角值:x1,x2,y1,y2
函数GUI2dRectWithObject
将返回屏幕上给定游戏对象的二维边界框。
代码
public static Rect GUI3dRectWithObject(GameObject go)
Vector3 cen = go.GetComponent<Renderer>().bounds.center;
Vector3 ext = go.GetComponent<Renderer>().bounds.extents;
Vector2[] extentPoints = new Vector2[8]
WorldToGUIPoint(new Vector3(cen.x-ext.x, cen.y-ext.y, cen.z-ext.z)),
WorldToGUIPoint(new Vector3(cen.x+ext.x, cen.y-ext.y, cen.z-ext.z)),
WorldToGUIPoint(new Vector3(cen.x-ext.x, cen.y-ext.y, cen.z+ext.z)),
WorldToGUIPoint(new Vector3(cen.x+ext.x, cen.y-ext.y, cen.z+ext.z)),
WorldToGUIPoint(new Vector3(cen.x-ext.x, cen.y+ext.y, cen.z-ext.z)),
WorldToGUIPoint(new Vector3(cen.x+ext.x, cen.y+ext.y, cen.z-ext.z)),
WorldToGUIPoint(new Vector3(cen.x-ext.x, cen.y+ext.y, cen.z+ext.z)),
WorldToGUIPoint(new Vector3(cen.x+ext.x, cen.y+ext.y, cen.z+ext.z))
;
Vector2 min = extentPoints[0];
Vector2 max = extentPoints[0];
foreach (Vector2 v in extentPoints)
min = Vector2.Min(min, v);
max = Vector2.Max(max, v);
return new Rect(min.x, min.y, max.x - min.x, max.y - min.y);
public static Rect GUI2dRectWithObject(GameObject go)
Vector3[] vertices = go.GetComponent<MeshFilter>().mesh.vertices;
float x1 = float.MaxValue, y1 = float.MaxValue, x2 = 0.0f, y2 = 0.0f;
foreach (Vector3 vert in vertices)
Vector2 tmp = WorldToGUIPoint(go.transform.TransformPoint(vert));
if (tmp.x < x1) x1 = tmp.x;
if (tmp.x > x2) x2 = tmp.x;
if (tmp.y < y1) y1 = tmp.y;
if (tmp.y > y2) y2 = tmp.y;
Rect bbox = new Rect(x1, y1, x2 - x1, y2 - y1);
Debug.Log(bbox);
return bbox;
public static Vector2 WorldToGUIPoint(Vector3 world)
Vector2 screenPoint = Camera.main.WorldToScreenPoint(world);
screenPoint.y = (float)Screen.height - screenPoint.y;
return screenPoint;
参考:Is there an easy way to get on-screen render size (bounds)?
【讨论】:
以上是关于立即从 3D 管道访问 2D 框的 Unity 功能?的主要内容,如果未能解决你的问题,请参考以下文章