SDL3 入門(5):紋理渲染

小时了了發表於2024-07-14

建立紋理

有三個 API 可以用來建立紋理:

  • SDL_CreateTexture 引數少,使用方便,適用於建立簡單的紋理
  • SDL_CreateTextureFromSurface
    適用於從已有影像資料建立紋理
  • SDL_CreateTextureWithProperties 可以指定各種屬性,功能強大,用起來也比較複雜,適用於另外兩個 API 無法滿足需求的情況

實際上前兩個 API 內部都是透過呼叫 SDL_CreateTextureWithProperties 實現紋理建立的。這也是 SDL API 設計的特點,對於常用操作有簡潔的 API 實現,同時也有使用複雜但是功能更靈活強大的 API 提供。

這裡我們準備建立一個最小的四種顏色的紋理,畫素尺寸 2x2,也就是總計只有 4 個畫素。首先使用陣列定義影像資料:

uint8_t pixels[4 * 2 * 2] = {
    0,   0,   255, 255,  // b, g, r, a
    0,   255, 0,   255,  //
    255, 0,   0,   255,  //
    0,   255, 255, 255   //
};

SDL 對畫素格式的定義是按照從高位到低位的顏色命名的,所以上面的資料對應的格式是 SDL_PIXELFORMAT_ARGB8888

由於已經有畫素資料,所以我們可以從影像資料建立 Surface 然後呼叫 SDL_CreateTextureFromSurface 從 Surface 建立紋理:

SDL_Surface* surface = SDL_CreateSurfaceFrom(pixels, 2, 2, 4 * 2, SDL_PIXELFORMAT_ARGB8888);
if (!surface) {
    SDL_Log("Create surface failed: %s", SDL_GetError());
    return -1;
}

SDL_Texture* texture = SDL_CreateTextureFromSurface(renderer, surface);
if (!texture) {
    SDL_Log("Create texture failed: %s", SDL_GetError());
    return -1;
}

影像混合模式選擇 None 也就是忽略透明通道,縮放模式選擇臨近點插值:

SDL_SetTextureBlendMode(texture, SDL_BLENDMODE_NONE);
SDL_SetTextureScaleMode(texture, SDL_SCALEMODE_NEAREST);

渲染紋理

渲染紋理使用的 API 是 SDL_RenderTexture,可以透過引數指定渲染紋理的矩形區域,也可以指定目標矩形區域。這裡我們選擇渲染整個紋理到目標中心附近寬高各為視窗一般的矩形區域:

// 計算目標矩形
int width = 0;
int height = 0;
SDL_GetRenderOutputSize(renderer, &width, &height);
SDL_FRect dst_rect{};
dst_rect.w = width * 0.5f;
dst_rect.h = height * 0.5f;
dst_rect.x = (width - dst_rect.w) * 0.5f;
dst_rect.y = (height - dst_rect.h) * 0.5f;

// 渲染
SDL_SetRenderDrawColor(renderer, 16, 0, 16, 255);
SDL_RenderClear(renderer);
SDL_RenderTexture(renderer, texture, nullptr, &dst_rect);
SDL_RenderPresent(renderer);

渲染效果如下:

SDL3 入門(5):紋理渲染

圖中每種顏色對應的是最初的 2x2 影像中的一個畫素,因為前面我們選擇的縮放模式是臨近點插值。實際影像處理中使用更多的是雙線性插值,我們可以修改前面的程式碼看下效果:

SDL_SetTextureScaleMode(texture, SDL_SCALEMODE_LINEAR);

渲染效果:

SDL3 入門(5):紋理渲染

紋理格式

可以使用如下程式碼查詢當前渲染器支援的紋理格式:

void PrintSupportedTextureFormats(SDL_Renderer* renderer)
{
    SDL_PixelFormatEnum* texture_format = static_cast<SDL_PixelFormatEnum*>(SDL_GetProperty(
        SDL_GetRendererProperties(renderer), SDL_PROP_RENDERER_TEXTURE_FORMATS_POINTER, nullptr));
    int index = 0;
    while (texture_format && *texture_format != SDL_PIXELFORMAT_UNKNOWN) {
        SDL_Log("Texture format[%d]: %s", index, SDL_GetPixelFormatName(*texture_format));
        ++texture_format;
        ++index;
    }
}

使用不同的圖形引擎建立渲染器時支援的紋理格式也不相同,下面是在一臺 Windows 11 系統筆記本上的測試結果:

Format direct3d11 direct3d12 direct3d(9) opengl opengles2 vulkan software
SDL_PIXELFORMAT_ARGB8888 Y Y Y Y Y Y Y
SDL_PIXELFORMAT_ABGR8888 - - - Y Y - -
SDL_PIXELFORMAT_XRGB8888 Y Y - Y Y Y Y
SDL_PIXELFORMAT_XBGR8888 - - - Y Y - -
SDL_PIXELFORMAT_XBGR2101010 Y Y - - - Y -
SDL_PIXELFORMAT_RGBA64_FLOAT Y Y - - - Y -
SDL_PIXELFORMAT_YV12 Y Y Y Y Y Y -
SDL_PIXELFORMAT_IYUV Y Y Y Y Y Y -
SDL_PIXELFORMAT_NV12 Y Y - Y Y Y -
SDL_PIXELFORMAT_NV21 Y Y - Y Y Y -
SDL_PIXELFORMAT_P010 Y Y - - - Y -

可以看到 SDL_PIXELFORMAT_ARGB8888 格式被包括軟體實現在內的所有圖形引擎支援,這正是我們前面選擇使用該格式建立紋理的原因,可以方便的選擇各個圖形引擎進行測試。

相關文章