DirectX将后备缓冲区数据写入文件

3
我正在尝试将DirectX程序的每一帧转换为YUV格式(用于视频编码)。因此,首先需要获取每一帧中每个像素的RGB(A)值。我需要从后备缓冲区(backbuffer)中获取这些值。
由于DirectX中没有 glReadPixels 函数,所以我按照以下步骤进行:
1. 获取后备缓冲区的 renderTargetView 指针,并获取后备缓冲资源。 2. 将该资源转换为 ID3D10Texture2D 类型。 3. 创建一个暂存纹理,并使用 CopyResource 函数将前面步骤中的 texture2D 复制到暂存纹理中。
此时,我可以使用 D3DX10SaveTextureToFile 函数,通过暂存纹理将后备缓冲区正确地保存为图像。
然而,我不想将磁盘用作绕路,我想立即获取RGB数据,因此我执行以下操作:
1. 映射暂存资源。 2. 读取映射纹理的 pData,以获取RGB(A)值。
问题在于:RGB值是垃圾值。这是像素(1,1)的一个示例:
(1, 1) = (-170141183460469230000000000000000000000.000000, -170141183460469230000000000000000000000.000000, -170141183460469230000000000000000000000.000000)
这特别奇怪,因为我在映射另一个暂存纹理(来自另一个离屏渲染目标)时,使用相同的代码就能正常工作。
以下是我的代码:
// Get resource pointer to backbuffer
ID3D10Resource *backbufferRes;
m_D3D->GetRenderTargetView()->GetResource(&backbufferRes);

// Cast backbuffer resource to texture2D
ID3D10Texture2D* tempTexture = 0;
backbufferRes->QueryInterface(__uuidof(ID3D10Texture2D),(LPVOID*) &tempTexture);
backbufferRes->Release();

// Get the descriptor of this texture2D
D3D10_TEXTURE2D_DESC descDefault; 
tempTexture->GetDesc(&descDefault);

// Create a staging texture desc based on the texture of the backbuffer
D3D10_TEXTURE2D_DESC descStaging;
descStaging = descDefault;
descStaging.Usage = D3D10_USAGE_STAGING;
descStaging.CPUAccessFlags = D3D10_CPU_ACCESS_READ;  
descStaging.BindFlags = 0;

// Create the new empty staging texture
ID3D10Texture2D *texture = 0;
m_D3D->GetDevice()->CreateTexture2D( &descStaging, NULL, &texture);

// Copy the backbuffer texture data (tempTexture) to the staging texture (texture)
m_D3D->GetDevice()->CopyResource(texture, tempTexture);

// This call works perfectly, image is correct!
// D3DX10SaveTextureToFile(texture, D3DX10_IFF_BMP, L"D:\\img.bmp");

// We want to avoid disk access, so instead let's map the texture and read its RGB data
D3D10_MAPPED_TEXTURE2D mappedTexture;
hr = texture->Map(D3D10CalcSubresource(0, 0, 1), D3D10_MAP_READ, 0, &mappedTexture);
FLOAT* m_pBits = (FLOAT*) malloc(4 * descStaging.Width * descStaging.Height * sizeof(FLOAT));
if(!FAILED(hr)) {
    memcpy(m_pBits, mappedTexture.pData, 4 * descStaging.Width * descStaging.Height);
    texture->Unmap(D3D10CalcSubresource(0, 0, 1));
}
texture->Release();
tempTexture->Release();

fp = fopen("D:\\output.txt", "a");
for( UINT row = 0; row < descStaging.Height; row++ )
{
    UINT rowStart = row * mappedTexture.RowPitch / 4;
    for( UINT col = 0; col < descStaging.Width; col++ )
    {
        r = m_pBits[rowStart + col*4 + 0]; // Red (X)
        g = m_pBits[rowStart + col*4 + 1]; // Green (Y)
        b = m_pBits[rowStart + col*4 + 2]; // Blue (Z)
        a = m_pBits[rowStart + col*4 + 3]; // Alpha (W)

        // Save pixel values to disk
        fprintf(fp, "%d %d - %f %f %f\n",  col + 1, row + 1, r, g, b);
    }
}
fclose(fp);

有人知道问题可能是什么吗?非常感谢任何帮助。


2
将您的帧渲染到纹理作为渲染目标,然后使用RGB到YUV着色器进行后处理,这样比手动转换快得多,因为GPU具有并行能力。 - Gnietschow
谢谢!这实际上是个好主意,我会试一下。将渲染结果呈现到纹理上是我心中的另一个备选方案(它可以解决问题,但需要再进行一次渲染),但我仍然想知道为什么会出现这些结果。 - Glenn
1个回答

2

虽然这是一个老问题,但我认为这可能是问题所在:

在使用texture->Map()映射纹理后,您尝试一次性将其复制到m_pBits中。除非映射纹理的RowPitch与其宽度相同(请参见此处),否则这样做是行不通的。

相反,您必须逐行复制图像:

BYTE* source = static_cast<BYTE*>(mappedTexture.pData);
BYTE& dest = m_pBits;
for (int i = 0; i < IMAGE_HEIGHT; ++i) {
    memcpy(dest, source, IMAGE_WIDTH * 4); // for 4 bytes per pixel
    source += mappedTexture.RowPitch;
    dest += IMAGE_WIDTH * 4;
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接