Chapter 14 · 渲染管线

相机系统:正交投影

掌握 xge_camera_t 结构体与正交投影相机,实现平移、缩放、旋转的 2D 世界视图。

14

概念:2D 相机

XGE 的相机定义了"世界坐标 → 屏幕坐标"的映射关系。正交投影(Orthographic)是 2D 游戏的标准选择——物体不因距离而缩小,保持像素精确。

相机支持三个自由度:平移(Position)、缩放(Scale)、旋转(Rotation)。

xge_camera_t 结构体

typedef struct xge_camera_t {
    xge_vec2_t tPosition;       // 相机中心(世界坐标)
    xge_vec2_t tScale;          // 缩放因子 (1,1)=原始
    float      fRotation;       // 旋转角度(弧度)
    xge_rect_t tViewport;       // 视口矩形(像素)
    int        iCoordinateMode; // XGE_COORD_Y_DOWN / XGE_COORD_CENTER
    int        iProjectionMode; // XGE_CAMERA_ORTHO / XGE_CAMERA_PERSPECTIVE
    float      fFovY;           // 透视 FOV(正交时忽略)
    float      fNearZ;          // 近裁剪面
    float      fFarZ;           // 远裁剪面
    float      fPerspectiveDistance; // 透视距离
} xge_camera_t;

API 速查

函数说明
xge_camera_t xgeCameraDefault(float fWidth, float fHeight)创建默认正交相机(原点在左上角)
void xgeCameraSet(const xge_camera_t* pCamera)设置当前相机
xge_camera_t xgeCameraGet(void)获取当前相机

编译本章示例

gcc -O2 -I. -o ch14.exe ch14_main.c build\xge.lib -lm -lws2_32 -liphlpapi -lgdi32 -luser32 -lshell32 -lopengl32 -lole32 -lwinmm -lavrt
.\ch14.exe

坐标模式

常量值说明
XGE_COORD_Y_DOWN0原点在左上角,Y 轴向下(默认,适合 UI)
XGE_COORD_CENTER1原点在中心,Y 轴向上(适合游戏世界)

示例 1:默认相机

static int onFrame(void* pUser) {
    xgeClear(0x081114FF);

    // 创建默认正交相机(800×600 视口)
    xge_camera_t cam = xgeCameraDefault(800.0f, 600.0f);
    xgeCameraSet(&cam);

    // 此时 (0,0) 在左上角,(800,600) 在右下角
    xgeShapeRectFill(100, 100, 200, 150, 0x4FD8C2FF);

    return XGE_OK;
}

示例 2:相机跟随(平移)

static float g_fPlayerX = 400, g_fPlayerY = 300;

static int onFrame(void* pUser) {
    xgeClear(0x081114FF);

    // 相机中心跟随玩家
    xge_camera_t cam = xgeCameraDefault(800.0f, 600.0f);
    cam.tPosition.fX = g_fPlayerX;
    cam.tPosition.fY = g_fPlayerY;
    xgeCameraSet(&cam);

    // 世界中的物体用世界坐标绘制
    xgeShapeCircleFill(g_fPlayerX, g_fPlayerY, 20, 0xFFB454FF);
    xgeShapeRectFill(0, 0, 2000, 50, 0x2A4A3FFF);  // 地面

    return XGE_OK;
}

示例 3:缩放与旋转

xge_camera_t cam = xgeCameraDefault(800.0f, 600.0f);

// 放大 2 倍(值越大越放大)
cam.tScale.fX = 2.0f;
cam.tScale.fY = 2.0f;

// 旋转 15 度
cam.fRotation = 15.0f * 3.14159265f / 180.0f;

// 使用中心坐标模式
cam.iCoordinateMode = XGE_COORD_CENTER;

xgeCameraSet(&cam);

示例 4:平滑缩放(滚轮)

static float g_fZoom = 1.0f;

static int onFrame(void* pUser) {
    // 读取滚轮增量
    float wheel = xgeMouseGetWheel().fY;
    g_fZoom += wheel * 0.1f;

    // 限制缩放范围
    if (g_fZoom < 0.25f) g_fZoom = 0.25f;
    if (g_fZoom > 4.0f)  g_fZoom = 4.0f;

    xge_camera_t cam = xgeCameraDefault(800.0f, 600.0f);
    cam.tScale.fX = g_fZoom;
    cam.tScale.fY = g_fZoom;
    xgeCameraSet(&cam);

    xgeClear(0x081114FF);
    // ... 绘制世界 ...
    return XGE_OK;
}

完整代码

将以下代码保存为 ch14_main.c,编译后运行即可体验相机平移与滚轮缩放:

#include "xge.h"

static float g_fZoom = 1.0f;
static float g_fCamX = 400.0f, g_fCamY = 300.0f;

static int onFrame(void* pUser) {
    (void)pUser;

    // 滚轮缩放
    xge_vec2_t wheel = xgeMouseGetWheel();
    g_fZoom += wheel.fY * 0.1f;
    if (g_fZoom < 0.25f) g_fZoom = 0.25f;
    if (g_fZoom > 4.0f)  g_fZoom = 4.0f;

    // 方向键平移相机
    float spd = 5.0f / g_fZoom;
    if (xgeKeyDown(XGE_KEY_LEFT))  g_fCamX -= spd;
    if (xgeKeyDown(XGE_KEY_RIGHT)) g_fCamX += spd;
    if (xgeKeyDown(XGE_KEY_UP))    g_fCamY -= spd;
    if (xgeKeyDown(XGE_KEY_DOWN))  g_fCamY += spd;

    // 设置相机
    xge_camera_t cam = xgeCameraDefault(800.0f, 600.0f);
    cam.tPosition.fX = g_fCamX;
    cam.tPosition.fY = g_fCamY;
    cam.tScale.fX = g_fZoom;
    cam.tScale.fY = g_fZoom;
    xgeCameraSet(&cam);

    xgeClear(0x081114FF);

    // 绘制世界中的物体
    xgeShapeRectFill(0, 0, 2000, 50, 0x2A4A3FFF);
    xgeShapeCircleFill(400, 300, 30, 0xFFB454FF);
    xgeShapeRectFill(100, 100, 120, 80, 0x4FD8C2FF);

    return XGE_OK;
}

int main(void) {
    xge_desc_t d = {0};
    d.iWidth  = 800;
    d.iHeight = 600;
    d.sTitle  = "ch14 - Ortho Camera";
    d.iFlags  = XGE_INIT_VSYNC;
    xgeInit(&d);
    xgeRun(onFrame, NULL);
    xgeUnit();
    return 0;
}

代码讲解

  • xgeCameraDefault(800, 600) 创建默认正交相机,原点在左上角。
  • tPosition 是相机中心看向的世界坐标,方向键平移相机即改变此值。
  • tScale 控制缩放,滚轮增量乘以 0.1 使缩放平滑。
  • 相机是帧状态,每帧必须重新调用 xgeCameraSet。

要点与陷阱

  • 每帧设置:相机是帧状态,xgeBegin 后重置为默认。如需自定义相机,每帧都要调用 xgeCameraSet。
  • Scale 含义:(2,2) 表示世界放大 2 倍显示;(0.5,0.5) 表示缩小到一半。
  • Position 是相机中心:不是左上角。相机看向 Position 指定的世界坐标点。
  • Y_DOWN vs CENTER:Y_DOWN 适合 UI(原点在左上);CENTER 适合游戏(原点在屏幕中心)。
  • 非等比缩放:tScale.fX ≠ tScale.fY 会导致画面拉伸,通常保持两者相等。

进阶练习

  1. 实现一个"小地图":用第二个视口(第 17 章)+ 缩小相机绘制全局缩略图。
  2. 实现相机震动效果:受击时在 Position 上叠加随机偏移,逐帧衰减。
  3. 用 XGE_COORD_CENTER 模式绘制一个旋转的星形,相机 fRotation 随时间匀速旋转。
相机系统:正交投影效果
图:相机系统:正交投影渲染效果