如何使用DirectX 11.1直接渲染YUV I420数据?
DirectX 11.1 渲染 YUV I420 数据的实现方案
核心思路
DirectX 11.1 没有原生支持直接渲染 YUV I420 格式,常规做法是将 Y、U、V 三个分量作为独立纹理传入着色器,在像素着色器中完成 YUV 到 RGB 的转换计算。
具体实现步骤
1. 创建对应 Y/U/V 平面的纹理资源
YUV I420 是平面格式:Y 平面尺寸为宽×高,U、V 平面尺寸均为**(宽/2)×(高/2)**。
- 为每个平面创建
ID3D11Texture2D,指定纹理格式为DXGI_FORMAT_R8_UNORM(单通道8位无符号) - 同步创建对应的
ID3D11ShaderResourceView(SRV),供着色器访问纹理数据
示例代码片段:
// 创建Y平面纹理 D3D11_TEXTURE2D_DESC yTexDesc{}; yTexDesc.Width = width; yTexDesc.Height = height; yTexDesc.MipLevels = 1; yTexDesc.ArraySize = 1; yTexDesc.Format = DXGI_FORMAT_R8_UNORM; yTexDesc.SampleDesc.Count = 1; yTexDesc.Usage = D3D11_USAGE_DYNAMIC; yTexDesc.BindFlags = D3D11_BIND_SHADER_RESOURCE; yTexDesc.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE; ID3D11Texture2D* pYTexture = nullptr; pDevice->CreateTexture2D(&yTexDesc, nullptr, &pYTexture); // 创建Y平面SRV D3D11_SHADER_RESOURCE_VIEW_DESC ySrvDesc{}; ySrvDesc.Format = yTexDesc.Format; ySrvDesc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE2D; ySrvDesc.Texture2D.MipLevels = 1; ID3D11ShaderResourceView* pYSrv = nullptr; pDevice->CreateShaderResourceView(pYTexture, &ySrvDesc, &pYSrv);
U、V 平面的创建逻辑一致,仅需将 Width 设为 width/2、Height 设为 height/2。
2. 更新纹理数据
每次获取新的 YUV 数据时,通过 Map 和 Unmap 操作更新三个纹理的内容:
// 更新Y平面数据 D3D11_MAPPED_SUBRESOURCE mappedY; pContext->Map(pYTexture, 0, D3D11_MAP_WRITE_DISCARD, 0, &mappedY); memcpy(mappedY.pData, yData, width * height); pContext->Unmap(pYTexture, 0); // 更新U平面数据 D3D11_MAPPED_SUBRESOURCE mappedU; pContext->Map(pUTexture, 0, D3D11_MAP_WRITE_DISCARD, 0, &mappedU); memcpy(mappedU.pData, uData, (width/2)*(height/2)); pContext->Unmap(pUTexture, 0); // V平面更新逻辑与U平面一致
3. 编写着色器完成YUV转RGB
在像素着色器中采样三个纹理的 Y、U、V 值,使用标准转换公式计算 RGB。注意 U、V 需双线性采样(分辨率为 Y 平面的一半)。
HLSL 像素着色器示例:
Texture2D<float> YTexture : register(t0); Texture2D<float> UTexture : register(t1); Texture2D<float> VTexture : register(t2); SamplerState BilinearSampler : register(s0); float4 PS(float4 pos : SV_POSITION, float2 uv : TEXCOORD0) : SV_TARGET { float y = YTexture.Sample(BilinearSampler, uv).r; float u = UTexture.Sample(BilinearSampler, uv).r - 0.5f; float v = VTexture.Sample(BilinearSampler, uv).r - 0.5f; // BT.601 转换公式(如需BT.709可替换对应系数) float r = y + 1.402f * v; float g = y - 0.344f * u - 0.714f * v; float b = y + 1.772f * u; return float4(r, g, b, 1.0f); }
顶点着色器只需传递 UV 坐标,与你之前渲染 RGB 的顶点着色器逻辑一致。
4. 绑定资源并渲染
渲染前将三个 SRV 和采样器绑定到着色器资源槽:
pContext->PSSetShaderResources(0, 1, &pYSrv); pContext->PSSetShaderResources(1, 1, &pUSrv); pContext->PSSetShaderResources(2, 1, &pVSrv); pContext->PSSetSamplers(0, 1, &BilinearSampler);
后续绘制流程(如绘制全屏四边形)与你之前渲染 RGB 的流程完全相同。
额外优化点
- 若硬件支持,可尝试使用
DXGI_FORMAT_YUV420P相关纹理格式,减少手动转换开销,但需确认显卡兼容性 - 可将 YUV 转 RGB 计算放到计算着色器中提前完成并缓存为 RGB 纹理,但实时像素着色器转换通常更高效
内容的提问来源于stack exchange,提问作者NeNeNe1
相关产品推荐
相关产品推荐

