一条约定同时覆盖 2D 与 3D、单个对象与实例: 位置是锚点的世界位置,而 宽、高与深是目标尺寸 — 不是缩放系数。 锚点是对象自身包围盒的几何中心。
| 成员 | 含义 |
|---|---|
PosX / PosY / PosZ | 锚点的世界位置。左手坐标系;在默认相机下 +X 是屏幕右侧。 |
Width / Height / Depth | 期望的世界尺寸。引擎从资产自身的边界推导缩放。它们是 float? — null 意味着“尚未设置”。 |
Rotation | 在 Model 上是绕 Y 轴的弧度。Mesh3D 与实例类型接受四元数。 |
Alpha | 不透明度,也是不移除某物而隐藏它的常用方式。 |
LocalSize | 只读。资产缩放前自身的尺寸,如果你需要按其单位推理的话。 |
ComputedScale | 只读。引擎推导出的逐轴缩放:目标尺寸除以局部尺寸。 |
AnchorLocal | 只读。模型空间中的锚点 — 原始包围盒的中心。 |
AnchorWorldOffset | 只读。当你需要放置模型的原点而不是中心时使用它;见下文。 |
回报是你可以为导入的资产设定尺寸,而不必知道美术是怎么导出的。
Width = 1f 就是一米宽,无论 GLB 声称什么,而且
两个来自两位美术的模型并排放置时,真的是一样高。
每个 3D 控件都以同样的方式构建它的矩阵,而顺序正是全部要点:
Matrix4x4.CreateTranslation(-AnchorLocal)
* Matrix4x4.CreateScale(ComputedScale)
* GetRotationMatrix()
* Matrix4x4.CreateTranslation(PosX, PosY, PosZ)
在行向量约定下,那就是
((p - A) * S) * R + Pos。锚点
A 精确落在
Pos 上,旋转枢轴就是锚点,因此旋转
一个对象既不改变它的位置也不改变它的尺寸。正是这个性质让参考应用中的
对象编辑器无需任何补偿数学就能工作。
如果缩放在前,求值就会退化成
(p * S - A) * R + Pos:锚点将不再落在
Pos 上,世界中心会漂移
A * (1 - S),于是渲染、拾取与高亮边界
会彼此悄悄地不一致。锚点曾经也是一个角
(Min.X, Max.Y, Min.Z),直到它被改成
包围盒中心,因为角锚定给出有偏的旋转枢轴,让
放置难以思考。
有时你想把模型自身的原点钉在一个世界点上 — 例如,
一个导出时站在地面平面上的角色。恒等式
p*S*R + t == ((p-A)*S)*R + Pos 直接给你转换:
// Pin the model's local origin to a world-space target.
var target = new Vector3(4f, 0f, 12f);
var offset = robot.AnchorWorldOffset; // recompute each frame
robot.PosX = target.X + offset.X;
robot.PosY = target.Y + offset.Y;
robot.PosZ = target.Z + offset.Z;
每一帧重新计算偏移量而不是缓存它:它已经把当前缩放和 旋转折叠进去了,所以对象变化时恒等式依然成立。先设置尺寸和旋转, 再设置位置。
Width、
Height 或 Depth
都会从 LocalSize 填入,所以一个没有设置尺寸的
模型会以它导出的比例出现,而不是塌缩成空。
GetWorldBounds() 返回用于
剔除和阴影的保守盒,为蒙皮运动做了膨胀。
GetWorldBoundsRaw() 与实际渲染的身体匹配。拾取和
选择框使用原始的那个;把保守盒用作选择框会在
模型上下留下可见的空白。
BaseApp.Camera 是一个单一的
Camera3D,被每个后端和每个通道共享,它是
视图、投影与视锥的唯一事实来源。
CameraPos 和
CameraTarget 转发到
Camera.Position 和
Camera.Target。
CameraTarget = new Vector3(0f, 3.5f, -2.5f);
CameraPos = CameraTarget + new Vector3(0f, 0.5f, -3.5f);
Camera.FovY = MathF.PI / 4f; // radians
Camera.Near = 0.1f;
Camera.Far = 1300f;
| 成员 | 说明 |
|---|---|
Position、Target、Up | 左手系 look-at。写入相同的值不会弄脏矩阵。 |
FovY、Near、Far | 投影,运行时可变。Far 还钳制太阳级联延伸的距离。 |
Aspect | 只读;引擎每帧从表面提供它。 |
View、Projection、ViewProjection | 只在有东西变化时重建,这就是为什么每帧触碰相机是免费的。 |
Frustum | 剔除测试的对象。可供你做自己的粗略可见性检查。 |
ProjectionJittered、PrevViewProjection、JitterPixels | 归时间抗锯齿所有。如果你在调试 TAA 可以读它们;不要写它们。 |
当 CameraPos.Z 为负时,相机朝原点看,
世界 +X 是屏幕右侧 — 每个示例都用这个布置,2D
叠加坐标系也与它一致。