在上一個教程中,咱們爲項目引入了照明。 如今咱們將經過向咱們的立方體添加紋理來構建它。 此外,咱們將介紹常量緩衝區的概念,並解釋如何使用緩衝區經過最小化帶寬使用來加速處理。git
本教程的目的是修改中心立方體以將紋理映射到其上。github
(SDK root)\Samples\C++\Direct3D11\Tutorials\Tutorial07函數
Github佈局
紋理映射是指2D圖像在3D幾何體上的投影。 咱們能夠把它想象成包裝禮物,將裝飾紙放在一個平淡無奇的盒子上。 爲此,咱們必須指定幾何體表面上的點如何與2D圖像對應。3d
訣竅是正確地將模型的座標與紋理對齊。 對於複雜模型,很難手動肯定紋理的座標。 所以,3D建模包一般將導出具備相應紋理座標的模型。 因爲咱們的示例是一個立方體,所以很容易肯定匹配紋理所需的座標。 紋理座標在頂點處定義,而後針對曲面上的各個像素進行插值。對象
紋理是從文件中檢索並用於建立着色器資源視圖的2D圖像,以即可以從着色器中讀取它。blog
hr = D3DX11CreateShaderResourceViewFromFile( g_pd3dDevice, L"seafloor.dds", NULL, NULL, &g_pTextureRV, NULL );
咱們還須要建立一個採樣器狀態來控制着色器如何處理過濾,MIP和尋址。 在本教程中,咱們將啓用簡單的採樣器狀態,以啓用線性過濾和換行尋址。 要建立採樣器狀態,咱們將使用ID3D11Device :: CreateSamplerState()。教程
// Create the sample state 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; hr = g_pd3dDevice->CreateSamplerState( &sampDesc, &g_pSamplerLinear );
在咱們將圖像映射到立方體以前,咱們必須首先在立方體的每一個頂點上定義紋理座標。 因爲圖像能夠是任何大小,所以使用的座標系已標準化爲[0,1]。 紋理的左上角對應於(0,0),右下角對應於(1,1)。資源
在這個例子中,咱們將整個紋理分佈在立方體的每一側。 這簡化了座標的定義,沒有混淆。 可是,它徹底能夠指定要在全部六個面上拉伸的紋理,儘管定義點更加困難,而且它會顯得拉伸和扭曲。get
首先,咱們更新了用於定義頂點的結構,以包含紋理座標。
struct SimpleVertex { XMFLOAT3 Pos; XMFLOAT2 Tex; };
接下來,咱們將更新輸入佈局爲了讓着色器包含這些座標。
// Define the input layout D3D11_INPUT_ELEMENT_DESC layout[] = { { "POSITION", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 0, D3D11_INPUT_PER_VERTEX_DATA, 0 }, { "TEXCOORD", 0, DXGI_FORMAT_R32G32_FLOAT, 0, 12, D3D11_INPUT_PER_VERTEX_DATA, 0 }, };
因爲輸入佈局已更改,所以還必須修改相應的頂點着色器輸入以匹配添加。
struct VS_INPUT { float4 Pos : POSITION; float2 Tex : TEXCOORD; };
最後,咱們準備在咱們在教程4中定義的頂點中包含紋理座標。注意第二個參數輸入是包含紋理座標的D3DXVECTOR2。 立方體上的每一個頂點都對應於紋理的一角。 這將建立一個簡單的映射,其中每一個頂點獲得(0,0)(0,1)(1,0)或(1,1)做爲座標。
// Create vertex buffer SimpleVertex vertices[] = { { XMFLOAT3( -1.0f, 1.0f, -1.0f ), XMFLOAT2( 0.0f, 0.0f ) }, { XMFLOAT3( 1.0f, 1.0f, -1.0f ), XMFLOAT2( 1.0f, 0.0f ) }, { XMFLOAT3( 1.0f, 1.0f, 1.0f ), XMFLOAT2( 1.0f, 1.0f ) }, { XMFLOAT3( -1.0f, 1.0f, 1.0f ), XMFLOAT2( 0.0f, 1.0f ) }, { XMFLOAT3( -1.0f, -1.0f, -1.0f ), XMFLOAT2( 0.0f, 0.0f ) }, { XMFLOAT3( 1.0f, -1.0f, -1.0f ), XMFLOAT2( 1.0f, 0.0f ) }, { XMFLOAT3( 1.0f, -1.0f, 1.0f ), XMFLOAT2( 1.0f, 1.0f ) }, { XMFLOAT3( -1.0f, -1.0f, 1.0f ), XMFLOAT2( 0.0f, 1.0f ) }, { XMFLOAT3( -1.0f, -1.0f, 1.0f ), XMFLOAT2( 0.0f, 0.0f ) }, { XMFLOAT3( -1.0f, -1.0f, -1.0f ), XMFLOAT2( 1.0f, 0.0f ) }, { XMFLOAT3( -1.0f, 1.0f, -1.0f ), XMFLOAT2( 1.0f, 1.0f ) }, { XMFLOAT3( -1.0f, 1.0f, 1.0f ), XMFLOAT2( 0.0f, 1.0f ) }, { XMFLOAT3( 1.0f, -1.0f, 1.0f ), XMFLOAT2( 0.0f, 0.0f ) }, { XMFLOAT3( 1.0f, -1.0f, -1.0f ), XMFLOAT2( 1.0f, 0.0f ) }, { XMFLOAT3( 1.0f, 1.0f, -1.0f ), XMFLOAT2( 1.0f, 1.0f ) }, { XMFLOAT3( 1.0f, 1.0f, 1.0f ), XMFLOAT2( 0.0f, 1.0f ) }, { XMFLOAT3( -1.0f, -1.0f, -1.0f ), XMFLOAT2( 0.0f, 0.0f ) }, { XMFLOAT3( 1.0f, -1.0f, -1.0f ), XMFLOAT2( 1.0f, 0.0f ) }, { XMFLOAT3( 1.0f, 1.0f, -1.0f ), XMFLOAT2( 1.0f, 1.0f ) }, { XMFLOAT3( -1.0f, 1.0f, -1.0f ), XMFLOAT2( 0.0f, 1.0f ) }, { XMFLOAT3( -1.0f, -1.0f, 1.0f ), XMFLOAT2( 0.0f, 0.0f ) }, { XMFLOAT3( 1.0f, -1.0f, 1.0f ), XMFLOAT2( 1.0f, 0.0f ) }, { XMFLOAT3( 1.0f, 1.0f, 1.0f ), XMFLOAT2( 1.0f, 1.0f ) }, { XMFLOAT3( -1.0f, 1.0f, 1.0f ), XMFLOAT2( 0.0f, 1.0f ) }, };
當咱們對紋理進行採樣時,咱們須要使用下面幾何體的材質顏色對其進行調製。
紋理和採樣器狀態是咱們在前面的教程中看到的常量緩衝區之類的對象。 在着色器可使用它們以前,須要使用ID3D11DeviceContext :: PSSetSamplers()和ID3D11DeviceContext :: PSSetShaderResources()API進行設置。
g_pImmediateContext->PSSetShaderResources( 0, 1, &g_pTextureRV ); g_pImmediateContext->PSSetSamplers( 0, 1, &g_pSamplerLinear );
好了,如今咱們已準備好在着色器中使用紋理。
要在幾何體頂部映射紋理,咱們將在像素着色器中調用紋理查找功能。 函數Sample將執行2D紋理的紋理查找,而後返回採樣的顏色。 下面顯示的像素着色器調用此函數並將其乘以底層網格顏色(或材質顏色),而後輸出最終顏色。
// Pixel Shader float4 PS( PS_INPUT input) : SV_Target { return txDiffuse.Sample( samLinear, input.Tex ) * vMeshColor; }
還有另外一件事情咱們必須記住,就是要經過頂點着色器傳遞紋理座標。 若是不這樣作,數據在到達像素着色器時就會丟失。 在這裏,咱們只需將輸入的座標複製到輸出中,而後讓硬件處理其他部分。
// Vertex Shader PS_INPUT VS( VS_INPUT input ) { PS_INPUT output = (PS_INPUT)0; output.Pos = mul( input.Pos, World ); output.Pos = mul( output.Pos, View ); output.Pos = mul( output.Pos, Projection ); output.Tex = input.Tex; return output; }
在Direct3D 11中,應用程序可使用常量緩衝區來設置着色器常量(着色器變量)。 使用相似於C風格結構的語法聲明常量緩衝區。 常量緩衝區經過容許將着色器常量組合在一塊兒並同時提交來減小更新着色器常量所需的帶寬,而不是單獨調用單獨提交每一個常量。
在前面的教程中,咱們使用單個常量緩衝區來保存咱們須要的全部着色器常量。 可是,有效使用常量緩衝區的最佳方法是根據更新頻率將着色器變量組織到常量緩衝區中。 這容許應用程序最小化更新着色器常量所需的帶寬。 例如,本教程將常量分爲三個結構:一個用於更改每一個幀的變量,一個用於僅在窗口大小更改時更改的變量,另外一個用於設置一次而後不更改的變量。
本教程的.fx文件中定義瞭如下常量緩衝區。
cbuffer cbNeverChanges { matrix View; }; cbuffer cbChangeOnResize { matrix Projection; }; cbuffer cbChangesEveryFrame { matrix World; float4 vMeshColor; };
要使用這些常量緩衝區,您須要爲每一個緩衝區建立一個ID3D11Buffer對象。 而後,您能夠調用ID3D11DeviceContext :: UpdateSubresource()來在須要時更新每一個常量緩衝區,而不會影響其餘常量緩衝區。
// // Update variables that change once per frame // CBChangesEveryFrame cb; cb.mWorld = XMMatrixTranspose( g_World ); cb.vMeshColor = g_vMeshColor; g_pImmediateContext->UpdateSubresource( g_pCBChangesEveryFrame, 0, NULL, &cb, 0, 0 );