You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在DirectX11中加载动画.gif至着色器资源视图及ID3D11ShaderResourceView数组?

嘿,这个问题我刚好折腾过好几次,DirectX 11本身并没有原生支持GIF动画的加载,所以得拆成几个关键步骤来搞定:先解析GIF的每帧像素数据,再把这些数据转换成DirectX能识别的纹理资源,最后生成对应的Shader Resource View(SRV)或者SRV数组。下面一步步给你讲清楚细节:

第一步:解析GIF动画的帧数据

DirectX 11没有内置的GIF解析器,所以我们得借助第三方库或者Windows自带的GDI+来搞定。我个人推荐用stb_image——这是个轻量的单头文件库,专门处理图像加载,支持GIF动画,集成起来特别方便:

  • 先把stb_image.h加到项目里,然后在包含头文件前定义STBI_ONLY_GIF(只编译GIF相关的代码,减小体积)。
  • 用stb的GIF解析函数获取每帧的像素数据、尺寸、延迟时间等信息,代码片段如下:
#define STBI_ONLY_GIF
#include "stb_image.h"

int width, height, channels;
int num_frames = 0;
int* frame_delays = nullptr;
// frames是二维数组,frames[i]对应第i帧的像素数据,格式为RGBA(带透明)或RGB
unsigned char** frames = stbi_load_gif("your_animation.gif", &width, &height, &channels, &num_frames, &frame_delays);

如果偏爱Windows原生API,也可以用GDI+的Image类加载GIF并遍历帧,但代码会繁琐不少,stb更适合游戏这类性能敏感的场景。

第二步:将单帧转为DirectX纹理并创建单个SRV

拿到每帧的像素数据后,我们需要把它转换成ID3D11Texture2D,再从纹理创建对应的SRV:

  1. 先定义纹理的描述结构,匹配GIF的像素格式:
D3D11_TEXTURE2D_DESC tex_desc = {};
tex_desc.Width = width;
tex_desc.Height = height;
tex_desc.MipLevels = 1; // 不需要mipmap的话设为1即可
tex_desc.ArraySize = 1;
tex_desc.Format = DXGI_FORMAT_R8G8B8A8_UNORM; // 对应RGBA8位像素格式
tex_desc.SampleDesc.Count = 1;
tex_desc.SampleDesc.Quality = 0;
tex_desc.Usage = D3D11_USAGE_DEFAULT;
tex_desc.BindFlags = D3D11_BIND_SHADER_RESOURCE;
tex_desc.CPUAccessFlags = 0; // 静态纹理无需CPU访问权限
tex_desc.MiscFlags = 0;
  1. 创建纹理并写入当前帧的像素数据:
D3D11_SUBRESOURCE_DATA init_data = {};
init_data.pSysMem = frames[0]; // 这里取第一帧的数据
init_data.SysMemPitch = width * channels; // 每行像素的字节数

ID3D11Texture2D* frame_texture = nullptr;
HRESULT hr = pDevice->CreateTexture2D(&tex_desc, &init_data, &frame_texture);
if (FAILED(hr)) { /* 这里要处理创建失败的逻辑 */ }
  1. 从纹理创建SRV:
D3D11_SHADER_RESOURCE_VIEW_DESC srv_desc = {};
srv_desc.Format = tex_desc.Format;
srv_desc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE2D;
srv_desc.Texture2D.MipLevels = tex_desc.MipLevels;
srv_desc.Texture2D.MostDetailedMip = 0;

ID3D11ShaderResourceView* frame_srv = nullptr;
hr = pDevice->CreateShaderResourceView(frame_texture, &srv_desc, &frame_srv);
if (FAILED(hr)) { /* 处理错误 */ }

// 纹理被SRV持有引用,这里可以直接释放纹理
frame_texture->Release();
第三步:创建SRV数组(ID3D11ShaderResourceView*数组)

如果要把所有GIF帧都封装成SRV数组,只需要循环处理每帧,重复上面的纹理和SRV创建步骤,然后把每个SRV指针存入数组即可:

// 初始化SRV数组,大小等于GIF的帧数
ID3D11ShaderResourceView** srv_array = new ID3D11ShaderResourceView*[num_frames];
ZeroMemory(srv_array, sizeof(ID3D11ShaderResourceView*) * num_frames);

for (int i = 0; i < num_frames; ++i) {
    // 填充当前帧的初始化数据
    D3D11_SUBRESOURCE_DATA frame_data = {};
    frame_data.pSysMem = frames[i];
    frame_data.SysMemPitch = width * channels;

    // 创建当前帧的纹理
    ID3D11Texture2D* temp_texture = nullptr;
    HRESULT hr = pDevice->CreateTexture2D(&tex_desc, &frame_data, &temp_texture);
    if (FAILED(hr)) { /* 处理错误,比如清理已创建的资源 */ }

    // 创建当前帧的SRV
    D3D11_SHADER_RESOURCE_VIEW_DESC temp_srv_desc = {};
    temp_srv_desc.Format = tex_desc.Format;
    temp_srv_desc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE2D;
    temp_srv_desc.Texture2D.MipLevels = 1;
    temp_srv_desc.Texture2D.MostDetailedMip = 0;

    hr = pDevice->CreateShaderResourceView(temp_texture, &temp_srv_desc, &srv_array[i]);
    if (FAILED(hr)) { /* 处理错误 */ }

    // 释放临时纹理
    temp_texture->Release();
}

额外注意事项

  • 透明通道处理:如果GIF带透明效果,stb会自动返回RGBA格式,对应DXGI_FORMAT_R8G8B8A8_UNORM就完全兼容;如果是索引色GIF,stb也会自动转换为RGBA格式。
  • 帧延迟管理:记得保存frame_delays数组里的每帧延迟时间,这样播放动画时可以精准控制帧切换的间隔。
  • 资源释放:用完SRV数组后,一定要循环释放每个SRV,再删除数组,同时别忘了释放stb加载的帧数据:
// 释放SRV数组
for (int i = 0; i < num_frames; ++i) {
    if (srv_array[i]) {
        srv_array[i]->Release();
    }
}
delete[] srv_array;

// 释放stb加载的资源
stbi_image_free(frames);
free(frame_delays);
  • 性能优化建议:如果帧数较多,也可以考虑创建纹理数组(把tex_desc.ArraySize设为帧数),然后创建一个对应整个纹理数组的SRV,渲染时通过数组索引切换帧,性能比多个独立SRV更好。不过这和你问的“SRV数组”是不同的实现方式,按需选择即可。

内容的提问来源于stack exchange,提问作者Mase

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.20 07:55:08