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

基于DirectX 12的Oculus Rift图像查看器图片加载问题求助

在DirectX 12中加载图片并上传到GPU(适配Oculus Rift开发)

嘿,我来帮你搞定DirectX 12里的图片加载和上传问题——毕竟给Oculus Rift做图像查看器,这步可是基础中的基础。先得说清楚:DX12不像旧版DX那样有现成的图片加载API,得自己走「读文件→转格式→传GPU」这几步,下面我给你一步步拆解,连代码示例都给你备好:

1. 先把图片文件解码成CPU可处理的像素数据

最省心的方式是用WIC(Windows Imaging Component),它能解码PNG、JPG这类常见格式,还能转换成DXGI兼容的像素格式。记得先初始化COM环境(WIC是COM组件),不然会报错。

核心代码片段:

// 初始化COM环境,放在程序入口处
CoInitializeEx(nullptr, COINIT_MULTITHREADED);

// 创建WIC工厂
ComPtr<IWICImagingFactory> wicFactory;
CoCreateInstance(CLSID_WICImagingFactory2, nullptr, CLSCTX_INPROC_SERVER, IID_PPV_ARGS(&wicFactory));

// 打开目标图片文件(注意路径!VS里可以把图片设为「复制到输出目录」)
ComPtr<IWICBitmapDecoder> decoder;
wicFactory->CreateDecoderFromFilename(L"your_image.png", nullptr, GENERIC_READ, WICDecodeMetadataCacheOnLoad, &decoder);

// 获取图片第一帧(静态图只有一帧)
ComPtr<IWICBitmapFrameDecode> frame;
decoder->GetFrame(0, &frame);

// 转换成DX12常用的RGBA格式
ComPtr<IWICFormatConverter> converter;
wicFactory->CreateFormatConverter(&converter);
converter->Initialize(frame, GUID_WICPixelFormat32bppRGBA, WICBitmapDitherTypeNone, nullptr, 0.f, WICBitmapPaletteTypeCustom);

// 获取图片尺寸,读取像素数据到内存
UINT width, height;
converter->GetSize(&width, &height);
UINT stride = width * 4; // RGBA每个像素占4字节
std::vector<BYTE> pixelData(stride * height);
converter->CopyPixels(nullptr, stride, pixelData.size(), pixelData.data());

如果VS里提示找不到文件,先检查:要么用绝对路径测试,要么右键图片文件→属性→复制到输出目录,选「始终复制」。

2. 创建GPU资源:上传堆+默认堆

DX12里CPU不能直接写GPU的高效资源,得先搞个上传堆(CPU可写,用来临时存数据),再把数据复制到默认堆(GPU直接访问的资源,适合渲染)。

代码示例:

// 定义纹理资源描述
D3D12_RESOURCE_DESC texDesc = {};
texDesc.Dimension = D3D12_RESOURCE_DIMENSION_TEXTURE2D;
texDesc.Width = width;
texDesc.Height = height;
texDesc.DepthOrArraySize = 1;
texDesc.MipLevels = 1;
texDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM; // 和WIC转换的格式对应
texDesc.SampleDesc.Count = 1;
texDesc.Layout = D3D12_TEXTURE_LAYOUT_UNKNOWN;
texDesc.Flags = D3D12_RESOURCE_FLAG_NONE;

// 创建默认堆资源(GPU可直接访问,初始设为复制目标状态)
ComPtr<ID3D12Resource> defaultTexture;
device->CreateCommittedResource(
    &CD3DX12_HEAP_PROPERTIES(D3D12_HEAP_TYPE_DEFAULT),
    D3D12_HEAP_FLAG_NONE,
    &texDesc,
    D3D12_RESOURCE_STATE_COPY_DEST,
    nullptr,
    IID_PPV_ARGS(&defaultTexture)
);

// 创建上传堆资源(CPU可写,用来中转数据)
ComPtr<ID3D12Resource> uploadTexture;
device->CreateCommittedResource(
    &CD3DX12_HEAP_PROPERTIES(D3D12_HEAP_TYPE_UPLOAD),
    D3D12_HEAP_FLAG_NONE,
    &CD3DX12_RESOURCE_DESC::Buffer(stride * height),
    D3D12_RESOURCE_STATE_GENERIC_READ,
    nullptr,
    IID_PPV_ARGS(&uploadTexture)
);

3. 把像素数据上传到GPU

先把内存里的像素数据拷贝到上传堆,再用命令列表把数据复制到默认堆,最后切换默认堆的状态到「像素着色器可访问」——这样渲染时就能用这张图了。

代码示例:

// 映射上传堆到CPU内存,复制像素数据
BYTE* pUploadData;
CD3DX12_RANGE readRange(0, 0); // 只写不读,范围设为空
uploadTexture->Map(0, &readRange, reinterpret_cast<void**>(&pUploadData));
memcpy(pUploadData, pixelData.data(), pixelData.size());
uploadTexture->Unmap(0, nullptr);

// 记录复制命令
commandList->CopyTextureRegion(
    &CD3DX12_TEXTURE_REGION(0, 0, 0, 0, 0, 0, width, height, 1),
    defaultTexture.Get(), 0,
    uploadTexture.Get(), 0
);

// 切换资源状态为像素着色器可用
CD3DX12_RESOURCE_BARRIER barrier = CD3DX12_RESOURCE_BARRIER::Transition(
    defaultTexture.Get(),
    D3D12_RESOURCE_STATE_COPY_DEST,
    D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE
);
commandList->ResourceBarrier(1, &barrier);

// 执行命令列表(需要你提前初始化好命令队列、同步fence等)
commandList->Close();
ID3D12CommandList* ppCommandLists[] = { commandList.Get() };
commandQueue->ExecuteCommandLists(_countof(ppCommandLists), ppCommandLists);

适配Oculus Rift的额外小提示

  • Oculus的渲染目标通常是双缓冲,格式可能用DXGI_FORMAT_R8G8B8A8_UNORM_SRGB,所以你的图片格式最好和它匹配,避免额外转换开销。
  • 如果图片分辨率和Oculus单眼分辨率(比如1080x1200)不匹配,可以在像素着色器里做缩放,或者用DX12的CopyTextureRegion配合缩放参数处理。

内容的提问来源于stack exchange,提问作者Francisco Díaz

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.15 07:42:42