Chapter 95 · 图像与纹理

采样器与纹理更新

Sampler 控制纹理的过滤和寻址方式;UpdateRGBA 支持局部更新纹理像素,适合动态内容。

95

概念

采样器(Sampler)决定 GPU 如何从纹理中取值:线性过滤(平滑)或最近邻(像素风), 以及 UV 超出 [0,1] 时的寻址模式(Clamp/Repeat/Mirror)。 UpdateRGBA 允许只更新纹理的一个子矩形,避免整张重新上传。

API 速查

函数说明
xge_sampler_t xgeSamplerDefault(void)获取默认采样器(线性 + Clamp)
int xgeTextureSetSampler(xge_texture pTexture, const xge_sampler_t* pSampler)设置纹理采样器
xge_sampler_t xgeTextureGetSampler(xge_texture pTexture)获取当前采样器
int xgeTextureUpdateRGBA(xge_texture pTexture, int iX, int iY, int iWidth, int iHeight, const void* pPixels, int iStride)局部更新像素

编译本章示例

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

示例:像素风渲染 + 局部更新

// 设置最近邻采样(像素风格)
xge_sampler_t sampler = xgeSamplerDefault();
sampler.iFilter = XGE_FILTER_NEAREST;
sampler.iWrapU = XGE_WRAP_CLAMP;
sampler.iWrapV = XGE_WRAP_CLAMP;
xgeTextureSetSampler(&spriteTex, &sampler);

// 局部更新:只刷新纹理的一小块区域
uint8_t patch[32 * 32 * 4];
// ... 填充 patch 像素 ...
xgeTextureUpdateRGBA(&tex,
    64, 64,   // 目标 x, y
    32, 32,   // 宽, 高
    patch, 32 * 4); // 像素 + stride

// 平铺寻址(用于背景重复)
sampler.iWrapU = XGE_WRAP_REPEAT;
sampler.iWrapV = XGE_WRAP_REPEAT;
xgeTextureSetSampler(&bgTex, &sampler);

完整代码

将以下代码保存为 ch95_main.c,编译后运行即可看到像素风渲染与局部更新效果:

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

#define TEX_SIZE 64

static xge_texture_t g_tex;
static float g_time = 0.0f;
static xge_font g_font = NULL;

static int onFrame(void* pUser) {
    (void)pUser;
    xgeClear(0x081114FF);
    g_time += 0.016f;

    // 局部更新:动态移动的小方块
    uint8_t patch[8 * 8 * 4];
    memset(patch, 0, sizeof(patch));
    for (int i = 0; i < 8*8; i++) {
        patch[i*4+0] = 255;
        patch[i*4+1] = (uint8_t)(128 + 127*sinf(g_time*3.0f));
        patch[i*4+2] = 0;
        patch[i*4+3] = 255;
    }
    int px = (int)(g_time * 20.0f) % (TEX_SIZE - 8);
    xgeTextureUpdateRGBA(&g_tex, px, 28, 8, 8,
        patch, 8 * 4);

    // 放大绘制(像素风)
    xgeDraw(&g_tex, 100.0f, 100.0f);

    xgeTextDraw(g_font, "NEAREST filter + partial update",
        100.0f, 400.0f, 0xFFFFFFFF)

    return XGE_OK;
}

int main(void) {
    xge_desc_t d = {0};
    d.iWidth  = 800;
    d.iHeight = 600;
    d.sTitle  = "ch95 - Pixel Art & Partial Update";
    d.iFlags  = XGE_INIT_VSYNC;
    xgeInit(&d);

    xgeFontLoad(&g_font, "Roboto.ttf", 18.0f);

    // 创建棋盘格纹理
    uint8_t pixels[TEX_SIZE * TEX_SIZE * 4];
    for (int y = 0; y < TEX_SIZE; y++)
        for (int x = 0; x < TEX_SIZE; x++) {
            int i = (y * TEX_SIZE + x) * 4;
            uint8_t v = ((x/8 + y/8) % 2) ? 200 : 60;
            pixels[i] = pixels[i+1] = pixels[i+2] = v;
            pixels[i+3] = 255;
        }
    xgeTextureCreateRGBA(&g_tex, TEX_SIZE, TEX_SIZE, pixels);

    // 设置最近邻采样(像素风格)
    xge_sampler_t sampler = xgeSamplerDefault();
    sampler.iFilter = XGE_FILTER_NEAREST;
    sampler.iWrapU = XGE_WRAP_CLAMP;
    sampler.iWrapV = XGE_WRAP_CLAMP;
    xgeTextureSetSampler(&g_tex, &sampler);

    xgeRun(onFrame, NULL);

    xgeTextureFree(&g_tex);
    xgeFontFree(g_font);
    xgeUnit();
    return 0;
}

代码讲解

  • NEAREST 采样:放大时不插值,保持像素边缘锐利,适合像素画/复古游戏。
  • UpdateRGBA:只更新纹理的一小块区域,比重新创建整张纹理快得多。
  • 动态方块:每帧计算新位置并更新对应区域,实现动态 HUD/小地图效果。
  • 棋盘格:程序化生成经典像素风棋盘格背景。

要点与陷阱

  • NEAREST:像素画/复古游戏必用,避免模糊。
  • LINEAR:默认值,适合大多数场景。
  • UpdateRGBA:区域不能超出纹理边界,否则返回错误。
  • 性能:局部更新比整张重新创建快得多,适合动态 HUD/小地图。

进阶练习

  1. 实现动态小地图:每帧 UpdateRGBA 更新玩家位置像素。
  2. 对比 NEAREST vs LINEAR:同一纹理放大 4 倍观察差异。
采样器与纹理更新效果
图:采样器与纹理更新渲染效果