2013-03-10 76 views
2

我試圖將我的DirectX程序的每個幀都轉換爲YUV(用於視頻編碼)。因此,我需要首先在每個幀上的每個像素的RGB(A)值。我需要從後緩衝器中獲得這些。DirectX將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 2013-03-10 19:59:14

+0

謝謝!這實際上是一個好主意,我會嘗試一下。渲染到紋理是我想到的另一種選擇(它以另一個渲染通道爲代價來解決問題),但我仍然想知道爲什麼我會得到這些結果。 – Glenn 2013-03-11 00:10:31

+1

它像一個魅力。再次感謝! – Glenn 2013-03-11 14:26:20

回答

1

老問題,但我認爲這可能是問題:

後您texture->Map()地圖紋理,你嘗試在整個複製到m_pBits都一氣呵成。除非映射紋理的RowPitch與寬度相同,否則這將不起作用(請參見here)。

相反,你必須通過圖像複製一行接一行:

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; 
}