Chapter 213 · 3D 图形

相机

相机是一个纯值类型结构体——视图矩阵与投影矩阵都在你的手里。本章覆盖透视/正交投影、LookAt 视图与大世界下的全局版调用。

213

相机的值语义

xge3d_camera_t 只有两个矩阵和近远裁剪面:

typedef struct xge3d_camera_t {
    xge3d_mat4_t view, projection;
    float near_z, far_z;
} xge3d_camera_t;

没有"相机对象"——投影与视图分别由独立函数填充,渲染时整体传给 xge3dRender。想插值相机动画?直接对两个矩阵做变换或对参数做插值后重建,状态完全透明。

透视与正交

xge3d_camera_t cam;

// 透视:垂直视场(弧度)、纵横比、近远裁剪面(米)
xge3dCameraPerspective(&cam, 1.2f, 16.0f/9.0f, 0.1f, 500.0f);

// 正交:以宽高(米)定义视景体,适合 2.5D 与工具视图
xge3dCameraOrthographic(&cam, 128.0f, 72.0f, -100.0f, 100.0f);

// 视图矩阵:眼点、目标点、上方向
xge3dCameraLookAt(&cam,
    (xge3d_vec3_t){ 0, 18, 28 },   // eye
    (xge3d_vec3_t){ 0, 2,  0 },    // target
    (xge3d_vec3_t){ 0, 1,  0 });  // up
提示:窗口尺寸变化时只需用新的纵横比重新调用一次 xge3dCameraPerspective;视图矩阵按帧重建的开销可以忽略。

大世界的全局版 LookAt

当场景设置了双精度原点后,眼点与目标点可以用全局坐标表达——xge3dCameraLookAtGlobal 接受 origin、eye、target,内部完成相对化:

xge3dSceneSetOrigin(pScene, (xge3d_dvec3_t){ 1e9, 0, 1e9 });
xge3dCameraLookAtGlobal(&cam, tOrigin, tGlobalEye, tGlobalTarget,
                          (xge3d_vec3_t){ 0, 1, 0 });

相机相关的查询

相机结构体还参与两类查询:xge3dCameraScreenRay 把屏幕像素反投影为拾取射线(第 223 章展开);xge3dCameraIntersectsAabb 做视锥包围盒相交测试,供自定义裁剪逻辑使用。

本章 API 速查

函数 / 类型说明
xge3d_camera_tview + projection + near/far,纯值类型
xge3dCameraPerspective(out, fov_y, aspect, near, far)透视投影
xge3dCameraOrthographic(out, width, height, near, far)正交投影
xge3dCameraLookAt(cam, eye, target, up)视图矩阵
xge3dCameraLookAtGlobal(cam, origin, eye, target, up)双精度全局坐标版
xge3dCameraScreenRay(cam, x, y, w, h, out)屏幕像素 → 拾取射线
xge3dCameraIntersectsAabb(cam, box)视锥包围盒测试
xge_3d 相机演示
图:本章示例对应 examples/xge_3d——相机、节点层级、共享网格与深度遮挡的最小演示。

要点与常见陷阱

  • fov_y 是垂直视场——换算水平 FOV 时记得乘纵横比。
  • near 不要盲目取 0——过小的近裁剪面会吃掉深度精度,常见做法是 0.1 与目标场景尺度匹配。
  • up 向向量为零会让 LookAt 失败,保证其与视线不平行。

进阶练习

  1. 实现一个跟随角色的第三人称相机:LookAt 目标固定在角色上方 2 米处,眼点随 yaw/pitch 球面分布。
  2. 用 Orthographic 渲染同一场景,对比透视的纵深感。
  3. 每帧打印 xge3dCameraIntersectsAabb 对某包围盒的结果,找出它离开视锥的帧。