DirectX3D cbuffer和tbuffer

cbuffer和tbuffer,Shader Model 4支持的新特性,通過打包數據可以獲得更好的性能。

關於Shader與應用程序間的數據傳遞。要傳遞的數據主要有 constant buffer,SamplerState,Texture2D(resource)。

這裏要先提一下DirectX10中新引入的constant buffer。在DX10中,constant存放於常量緩衝區中,每個常量緩衝區由 4096個常量寄存器組成,共有16個常量緩衝區。

constant buffer會爲 兩種:cbuffer和tbuffer。注意tbuffer是並不是用來存儲紋理的,而是指可以像紋理那樣來訪問其中的數據,對於索引類數據有更好的性能。

 

爲什麼使用cbuffer?

cbuffer通過允許將着色常量組合在一起並同時提交,而不是單獨調用以提交每個常量,從而減少更新着色常量所需的帶寬。

有效使用常量緩衝區的最佳方法是把更新比較頻繁的着色器變量組織爲cbuffer。這允許應用程序最小化更新着色器常量所需的帶寬。

 

HLSL打包規則

HLSL打包規則類似於使用Visual Studio執行pragma pack 4,它將數據打包成4字節的邊界。此外,HLSL打包數據,使其不跨越16字節的邊界。變量被壓縮到一個給定的四分量向量中,直到變量跨越一個四分量邊界;下一個變量將被放到下一個四分量向量。更詳細的討論:https://blog.csdn.net/X_Jun96/article/details/81283268

 

Direct3D 9與Direct3D 10和11之間的差異:

與Direct3D 9中常量的自動分配(不執行打包,而是將每個變量分配給一組float4寄存器)不同,HLSL常量變量遵循Direct3D 10和11中的打包規則。

 

來看實例:

cbuffer

在shader中有如下定義

cbuffer VSConstantBuffer : register(b0)
{
    matrix g_World; 
    matrix g_View;  
    matrix g_Proj;  
    matrix g_WorldInvTranspose;
}

register(bN):b表示constant buffer,N爲input slot (0-15),能使用的爲0-13,預留2個插槽供內部使用 。

即表示Mybuffer存放於b3中。

在VS_3D函數中使用cbuffer中的常量:

// 頂點着色器(3D)
VertexPosHWNormalTex VS_3D(VertexPosNormalTex vIn)
{
    VertexPosHWNormalTex vOut;
    matrix viewProj = mul(g_View, g_Proj);
    float4 posW = mul(float4(vIn.PosL, 1.0f), g_World);

    vOut.PosH = mul(posW, viewProj);
    vOut.PosW = posW.xyz;
    vOut.NormalW = mul(vIn.NormalL, (float3x3) g_WorldInvTranspose);
    vOut.Tex = vIn.Tex;
    return vOut;
}

在應用程序C++代碼中:

// 設置常量緩衝區描述
D3D11_BUFFER_DESC cbd;
ZeroMemory(&cbd, sizeof(cbd));
cbd.Usage = D3D11_USAGE_DYNAMIC;
cbd.ByteWidth = sizeof(VSConstantBuffer);
cbd.BindFlags = D3D11_BIND_CONSTANT_BUFFER;
cbd.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE;
// 新建用於VS和PS的常量緩衝區
m_pd3dDevice->CreateBuffer(&cbd, nullptr, m_pConstantBuffers[0].GetAddressOf());
cbd.ByteWidth = sizeof(PSConstantBuffer);
m_pd3dDevice->CreateBuffer(&cbd, nullptr, m_pConstantBuffers[1].GetAddressOf());

// 初始化用於VS的常量緩衝區的值
m_VSConstantBuffer.world = XMMatrixIdentity();			
m_VSConstantBuffer.view = XMMatrixTranspose(XMMatrixLookAtLH(
		XMVectorSet(0.0f, 0.0f, -5.0f, 0.0f),
		XMVectorSet(0.0f, 0.0f, 0.0f, 0.0f),
		XMVectorSet(0.0f, 1.0f, 0.0f, 0.0f)
	));
m_VSConstantBuffer.proj = XMMatrixTranspose(XMMatrixPerspectiveFovLH(XM_PIDIV2, AspectRatio(), 1.0f, 1000.0f));
m_VSConstantBuffer.worldInvTranspose = XMMatrixIdentity();


// VS常量緩衝區對應HLSL寄存於b0的常量緩衝區
m_pd3dImmediateContext->VSSetConstantBuffers(0, 1, m_pConstantBuffers[0].GetAddressOf());

第一個參數即爲要傳遞的buffer放置的slot起點。類似的函數PSSetConstantBuffers,GSSetConstantBuffers。通過VSSetConstantBuffers方法,爲cbuffer的常量賦值。

// 更新PS常量緩衝區資源
D3D11_MAPPED_SUBRESOURCE mappedData;
m_pd3dImmediateContext->Map(m_pConstantBuffers[1].Get(), 0, D3D11_MAP_WRITE_DISCARD, 0, &mappedData);
memcpy_s(mappedData.pData, sizeof(PSConstantBuffer), &m_PSConstantBuffer, sizeof(PSConstantBuffer));
m_pd3dImmediateContext->Unmap(m_pConstantBuffers[1].Get(), 0);

然後,用Map方法更新頻繁更改的常量。 

 

tbuffer

像紋理Texture那樣來訪問其中的數據,語法爲register(tN), t 表示紋理,N 爲input slot (0-127) 。

紋理並不能直接綁定到着色器中,需要爲紋理創建對應的着色器資源視圖才能夠給着色器使用

例,PS中主要代碼:

Texture2D g_Tex : register(t0);
SamplerState g_SamLinear : register(s0);

// 像素着色器(2D)
float4 PS_2D(VertexPosHTex pIn) : SV_Target
{
    return g_Tex.Sample(g_SamLinear, pIn.Tex);
}

應用程序C++代碼中:

// 創建Texture
CreateDDSTextureFromFile(m_pd3dDevice.Get(), L"Texture\\WoodCrate.dds", nullptr, m_pWoodCrate.GetAddressOf());

// 設置着色器資源
m_pd3dImmediateContext->PSSetShaderResources(0, 1, m_pWoodCrate.GetAddressOf());

通過 PSSetShaderResources 爲HLSL中的g_Tex賦值,然後,調用Texture的採樣方法,進行紋理採樣。

 

SamplerState

語法爲register(sN), s 表示採樣器,s 爲input slot (0-127) 。

例,PS中主要代碼:

g_Tex.Sample函數是採樣方法, 採樣就是根據紋理座標取出紋理中對應位置最接近的像素,返回一個float4的向量。

Texture2D g_Tex : register(t0);
SamplerState g_SamLinear : register(s0);

// 像素着色器(2D)
float4 PS_2D(VertexPosHTex pIn) : SV_Target
{
    return g_Tex.Sample(g_SamLinear, pIn.Tex);
}

應用程序C++代碼中:

// 初始化採樣器狀態描述
D3D11_SAMPLER_DESC sampDesc;
ZeroMemory(&sampDesc, sizeof(sampDesc));
sampDesc.Filter = D3D11_FILTER_MIN_MAG_MIP_LINEAR;//過濾器
sampDesc.AddressU = D3D11_TEXTURE_ADDRESS_WRAP;//尋址模式
sampDesc.AddressV = D3D11_TEXTURE_ADDRESS_WRAP;
sampDesc.AddressW = D3D11_TEXTURE_ADDRESS_WRAP;
sampDesc.ComparisonFunc = D3D11_COMPARISON_NEVER;
sampDesc.MinLOD = 0;
sampDesc.MaxLOD = D3D11_FLOAT32_MAX;
// 創建採樣器狀態
m_pd3dDevice->CreateSamplerState(&sampDesc, m_pSamplerState.GetAddressOf());

// 像素着色階段設置好採樣器
m_pd3dImmediateContext->PSSetSamplers(0, 1, m_pSamplerState.GetAddressOf());

PSSetSamplers第一個參數就是對應register(s0),採樣寄存器的第一個插槽位置。也就是爲對應HLSL的PS階段的shader代碼中的g_SamLinear變量賦值,然後在PS中進行採樣。

 

附錄:DirectX11--深入理解與使用2D紋理資源

https://blog.csdn.net/X_Jun96/article/details/81010611

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章