天天看點

D3d/opengl texture yuv yuv420p nv12 yv12 等等 顯示 以及傳入shaderresource

為何要用nv12 或nv21?

官方解釋是友善渲染使用。個人了解 :友善将nv12(DXGI_FORMAT_NV12)或nv21(DXGI_FORMAT_NV21)資料放到一個texture中,然後通過shader進行渲染。nv12與nv21差別是u與v的存放空間的位置問題,一個在前一個在後。

下面是msdn中對nv12 aligned 格式的圖例描述(y:width,height  ;uv:width,height/2)memory(width*height*3/2)

D3d/opengl texture yuv yuv420p nv12 yv12 等等 顯示 以及傳入shaderresource

類似的還有DXGI_FORMAT_P010(YV12 10bit)和DXGI_FORMAT_P016(YV12 16bit),DXGI_FORMAT_420_OPAQUEY(YV12 8bit)。

下面是msdn中對yv12 aligned 格式的圖例描述(y:width,height ;u:width/2,height/2 ;v:width/2,height/2)  memory(width*height*2)

D3d/opengl texture yuv yuv420p nv12 yv12 等等 顯示 以及傳入shaderresource

從上面的截圖可以看出,nv12相比于yv12的優勢在空間使用率更高。因為空間都是成塊的申請的,很顯然上面的yv12中,在u和v的後面部分被空餘了。

如果原始資料在cpu記憶體中是420p(YV12),一般存儲是連續的,而不是aligned 的 ,如果要放到texture中,需要做aligned 操作和必要的轉換。(aligned的目的是使記憶體塊按行列對齊,使可以通過橫縱坐标來通路。迎合texture的這一個基本的需求)

nv12 nv21 最終都是需要轉換成rgb或rgba的。一般在自己寫的shader中轉換。

參考:

https://docs.microsoft.com/zh-cn/windows/desktop/api/dxgiformat/ne-dxgiformat-dxgi_format

https://www.gamedev.net/forums/topic/678034-how-to-read-dxgi-format-nv12-or-dxgi-format-p010-texture-in-direct3d-shader/

https://social.msdn.microsoft.com/Forums/vstudio/en-US/f8b73c84-c205-4da5-b7bc-982ccc9ad1f8/nv12-textures-not-working-in-directx-111?forum=wpdevelop

在D3d9中可以通過surface來顯示

測試成功案例:https://blog.csdn.net/balijinyi/article/details/80284561

在D3D11中可以通過ID3D11VideoDecoderOutputView 來顯示。

msdn中關于yuv的詳細介紹:

https://docs.microsoft.com/zh-cn/windows/desktop/medfound/recommended-8-bit-yuv-formats-for-video-rendering

裡面介紹nv12 yv12 隻能算是一種texture array,但是又比較特殊的array

對于想将nv12 傳入shader resource的同學 ,坑定 會 遇到nv12 作為shader resource傳入的問題。通過ShaderResourceView 可以巧妙的讓y和uv分開顯示。msdn的說法就是y和uv需要放到兩個view中顯示。

D3d/opengl texture yuv yuv420p nv12 yv12 等等 顯示 以及傳入shaderresource

可通過下面方式進行操作:

一個nv12 texture 可以直接放到srv數組中。這樣在shader中被當做兩個紋理來使用。(可以将nv12 texture了解為一個特殊的紋理數組)

c++代碼中:
void init(){
    hr = g_pd3dDevice->CreateSamplerState( &sampDesc, &g_pSamplerLinear[0] );
    if( FAILED( hr ) )
        return hr;
	hr = g_pd3dDevice->CreateSamplerState(&sampDesc, &g_pSamplerLinear[1]);
	if (FAILED(hr))
		return hr;
    SRVDesc.Format = DXGI_FORMAT_R8_UNORM;//y
    SRVDesc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE2D;
    SRVDesc.Texture2D.MipLevels = 1;
    hr = g_pd3dDevice->CreateShaderResourceView(tex, &SRVDesc, &g_pTextureRV[0]);//srv[0]映射到y的位址上去了
    SRVDesc.Format = DXGI_FORMAT_R8G8_UNORM;//DXGI_FORMAT_R8G8_UNORM //uv 
    hr = g_pd3dDevice->CreateShaderResourceView(tex, &SRVDesc, &g_pTextureRV[1]); //srv[1] 映射到了uv的位址上去了
}

void update()
{
    g_pImmediateContext->PSSetShaderResources( 0, 2, g_pTextureRV ); //将srv數組傳入
}



PS中
Texture2D txDiffuse : register( t0 ); //指向了srv數組的的第一個成員(實際是y)
SamplerState samLinear : register( s0 );
Texture2D txDiffuse1 : register(t1);//指向了srv數組的第二個成員(實際是uv)
SamplerState samLinear1 : register(s1);
           

可參考https://blog.csdn.net/qiushangren/article/details/89244745來測試。記得設定bindflag為D3D11_BIND_SHADER_RESOURCE

yuv轉換函數

float4 yuvtorgb(float y,float u,float v){

    y = y*255;u = u*255;v = v*255;

    float4 rgba;

    rgba.r = clamp((y + 1.402*(v - 128))/255.0f,0,1.0f);

    rgba.g = clamp((y - 0.34414*(u - 128) - 0.71414*(v - 128))/255.0f,0,1.0f);

    rgba.b = clamp((y + 1.772*(u - 128))/255.0f,0,1.0f);

    rgba.a = 1.0f;

    return rgba;

}

參考:https://social.msdn.microsoft.com/Forums/en-US/e19906f4-afd5-455b-82ba-92c63addabec/how-to-convert-dxgiformatnv12-to-dxgiformatr8g8b8a8unorm

good luck。

繼續閱讀