Chapter 21 · 基础图元

点与线

学习 XGE 最基础的绘制图元——点和线,理解世界坐标与像素坐标(Px 后缀)的区别。

21

概念:图元与坐标空间

XGE 的 Shape 系列函数是最底层的即时绘制 API——无需创建对象,直接调用即绘制。每个函数都有两个版本:

  • 无后缀版本(如 xgeShapePoint):坐标在世界空间中,受相机变换影响。
  • Px 后缀版本(如 xgeShapePointPx):坐标在像素空间中,不受相机影响,始终对应屏幕像素。适合 HUD/UI。

API 速查

函数说明
void xgeShapePoint(float fX, float fY, float fSize, uint32_t iColor)绘制点(世界坐标)
void xgeShapePointPx(float fX, float fY, float fSize, uint32_t iColor)绘制点(像素坐标)
void xgeShapeLine(float fX0, float fY0, float fX1, float fY1, float fWidth, uint32_t iColor)绘制线段(世界坐标)
void xgeShapeLinePx(float fX0, float fY0, float fX1, float fY1, float fWidth, uint32_t iColor)绘制线段(像素坐标)

编译本章示例

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

示例 1:绘制点

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

    // 绘制一个 6 像素大小的点
    xgeShapePoint(400, 300, 6.0f, 0x4FD8C2FF);

    // 绘制一排渐变大小的点
    for (int i = 0; i < 20; i++) {
        float x = 100 + i * 30.0f;
        float size = 2.0f + i * 0.5f;
        xgeShapePoint(x, 200, size, 0xFFB454FF);
    }

    return XGE_OK;
}

示例 2:绘制线段

// 基本线段:从 (100,100) 到 (700,500),宽度 3
xgeShapeLine(100, 100, 700, 500, 3.0f, 0xFF6B5EFF);

// 绘制坐标轴
xgeShapeLine(400, 50, 400, 550, 1.0f, 0x6DB3F2FF);  // Y 轴
xgeShapeLine(100, 300, 700, 300, 1.0f, 0x6DB3F2FF);  // X 轴

// 绘制星形连线
float cx = 400, cy = 300, r = 150;
for (int i = 0; i < 5; i++) {
    float a0 = i * 4 * 3.14159265f / 5.0f - 3.14159265f/2;
    float a1 = (i+1) * 4 * 3.14159265f / 5.0f - 3.14159265f/2;
    xgeShapeLine(
        cx + r*cosf(a0), cy + r*sinf(a0),
        cx + r*cosf(a1), cy + r*sinf(a1),
        2.0f, 0xFFB454FF
    );
}

示例 3:Px 版本(HUD 不受相机影响)

// 设置缩放相机
xge_camera_t cam = xgeCameraDefault(800, 600);
cam.tScale.fX = cam.tScale.fY = 2.0f;
xgeCameraSet(&cam);

// 世界坐标线:受缩放影响,变粗变长
xgeShapeLine(100, 100, 200, 100, 2.0f, 0xFF6B5EFF);

// 像素坐标线:始终 2px 宽,固定在屏幕位置
xgeShapeLinePx(100, 50, 300, 50, 2.0f, 0x4FD8C2FF);

完整代码

将以下代码保存为 ch21_main.c,编译后运行即可看到点与线的绘制效果:

#include "xge.h"
#include <math.h>

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

    // 绘制一排渐变大小的点
    for (int i = 0; i < 20; i++) {
        float x = 100 + i * 30.0f;
        float size = 2.0f + i * 0.5f;
        xgeShapePoint(x, 150, size, 0xFFB454FF);
    }

    // 绘制坐标轴
    xgeShapeLine(400, 220, 400, 550, 1.0f, 0x6DB3F240);
    xgeShapeLine(100, 380, 700, 380, 1.0f, 0x6DB3F240);

    // 绘制五角星连线
    float cx = 400, cy = 380, r = 120;
    for (int i = 0; i < 5; i++) {
        float a0 = i * 4 * 3.14159265f / 5.0f - 3.14159265f / 2;
        float a1 = (i + 1) * 4 * 3.14159265f / 5.0f - 3.14159265f / 2;
        xgeShapeLine(
            cx + r * cosf(a0), cy + r * sinf(a0),
            cx + r * cosf(a1), cy + r * sinf(a1),
            2.0f, 0x4FD8C2FF
        );
    }

    return XGE_OK;
}

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

代码讲解

  • xgeShapePoint(x, y, size, color) 绘制一个直径为 size 的圆点。
  • xgeShapeLine(x0, y0, x1, y1, width, color) 绘制从起点到终点的线段。
  • 五角星用“隔点连线”算法:每次前进 4π/5 角度,5 次后回到起点形成星形。
  • 坐标轴使用低 Alpha(0x6DB3F240)作为背景参考线。

要点与陷阱

  • 点不是 1 像素:fSize 控制点的直径。Size=1 时是一个像素,Size=10 时是一个直径 10 的圆。
  • 线宽单位:fWidth 在世界坐标版本中受相机缩放影响;Px 版本中始终是屏幕像素。
  • 线段有端帽:默认使用圆角端帽(Round Cap),线段两端会超出坐标点半个线宽。
  • 抗锯齿:所有 Shape 绘制默认开启抗锯齿(AA),边缘平滑。
  • 性能:点和线是最轻量的图元。但数万次调用仍有开销,大量粒子考虑 SpriteBatch。

进阶练习

  1. 用线段绘制一个动态正弦波:每帧更新 Y 坐标,形成波浪动画。
  2. 实现"鼠标画笔":记录鼠标轨迹点,用线段连接相邻点。
  3. 用点绘制一个粒子喷泉:每帧生成随机位置的点,逐帧下落并淡出。
点与线效果
图:点与线渲染效果