在上一個教程中,咱們從模型空間到屏幕渲染了一個立方體。 在本教程中,咱們將擴輾轉換的概念並演示能夠經過這些轉換實現的簡單動畫。git
本教程的結果將是圍繞另外一個軌道運行的對象。 展現轉換以及如何將它們組合以實現指望的效果將是有用的。 在咱們介紹新概念時,將來的教程將在此基礎上構建。github
(SDK root)\Samples\C++\Direct3D11\Tutorials\Tutorial05函數
Github動畫
在3D圖形中,變換一般用於對頂點和矢量進行操做。 它還用於將它們在一個空間中轉換爲另外一個空間。 經過與矩陣相乘來執行變換。 一般有三種類型的原始變換能夠在頂點上執行:平移(相對於原點位於空間中),旋轉(相對於x,y,z幀的方向)和縮放(距離 起源)。 除此以外,投影變換用於從視圖空間到投影空間。 XNA Math庫包含的API能夠方便地構建矩陣,用於多種用途,例如平移,旋轉,縮放,世界到視圖轉換,視圖到投影轉換等。 而後,應用程序可使用這些矩陣來轉換其場景中的頂點。 須要對矩陣變換有基本的瞭解。 咱們將簡要介紹下面的一些示例。翻譯
平移是指在空間中移動或移位必定距離。 在3D中,用於翻譯的矩陣具備形式。3d
1 0 0 0 0 1 0 0 0 0 1 0 a b c 1
其中(a,b,c)是定義移動方向和距離的向量。 例如,要沿X軸(負X方向)移動頂點-5單位,咱們能夠將其與此矩陣相乘:orm
1 0 0 0 0 1 0 0 0 0 1 0 -5 0 0 1
若是咱們將此應用於以原點爲中心的立方體對象,則結果是該框向負X軸移動5個單位,如圖5所示,在應用平移以後。對象
圖1.平移的影響blog
在3D中,空間一般由原點和來自原點的三個惟一軸定義:X,Y和Z.計算機圖形中一般使用多個空間:對象空間,世界空間,視圖空間,投影空間和屏幕空間。教程
圖2.在對象空間中定義的立方體
旋轉是指圍繞穿過原點的軸旋轉頂點。 三個這樣的軸是空間中的X,Y和Z軸。 2D中的示例是逆時針旋轉矢量[1 0] 90度。 旋轉的結果是向量[0 1]。 用於圍繞Y軸順時針旋轉1度的矩陣看起來像這樣:
cosΐ 0 -sinΐ 0 0 1 0 0 sinΐ 0 cosΐ 0 0 0 0 1
圖6顯示了圍繞Y軸旋轉以原點爲中心45度的立方體的效果。
圖3.圍繞Y軸旋轉的效果
縮放是指沿軸方向放大或縮小矢量份量的大小。 例如,矢量能夠沿全部方向按比例放大或僅沿X軸按比例縮小。 爲了擴展,咱們一般在下面應用縮放矩陣:
p 0 0 0 0 q 0 0 0 0 r 0 0 0 0 1
其中p,q和r分別是沿X,Y和Z方向的比例因子。 下圖顯示了沿X軸縮放2並沿Y軸縮放0.5的效果。
圖4.縮放的效果
要將多個變換應用於矢量,咱們能夠簡單地將矢量乘以第一個變換矩陣,而後將獲得的矢量乘以第二個變換矩陣,依此類推。 由於向量和矩陣乘法是關聯的,咱們也能夠先將全部矩陣相乘,而後將向量乘以乘積矩陣,獲得相同的結果。 下圖顯示了若是咱們將旋轉和平移轉換結合在一塊兒,立方體將如何結束。
圖5.旋轉和平移的效果
在本教程中,咱們將轉換兩個多維數據集。 第一個將旋轉到位,而第二個將圍繞第一個旋轉,同時在其本身的軸上旋轉。 這兩個立方體將具備與其關聯的本身的世界變換矩陣,而且該矩陣將在渲染的每一個幀中從新應用於該矩陣。
XNA Math中有一些函數能夠幫助建立旋轉,平移和縮放矩陣。
第一個立方體將旋轉到位,並做爲軌道的中心。 立方體沿Y軸旋轉,應用於相關的世界矩陣。 這是經過調用如下代碼中顯示的XMMatrixRotationY函數來完成的。 立方體每幀旋轉必定量。 因爲立方體被假設爲連續旋轉,所以旋轉矩陣所基於的值隨每幀遞增。
// 1st Cube: Rotate around the origin g_World1 = XMMatrixRotationY( t );
第一個立方體將旋轉到位,並做爲軌道的中心。 立方體沿Y軸旋轉,應用於相關的世界矩陣。 這是經過調用如下代碼中顯示的XMMatrixRotationY函數來完成的。 立方體每幀旋轉必定量。 因爲立方體被假設爲連續旋轉,所以旋轉矩陣所基於的值隨每幀遞增。
// 2nd Cube: Rotate around origin XMMATRIX mSpin = XMMatrixRotationZ( -t ); XMMATRIX mOrbit = XMMatrixRotationY( -t * 2.0f ); XMMATRIX mTranslate = XMMatrixTranslation( -4.0f, 0.0f, 0.0f ); XMMATRIX mScale = XMMatrixScaling( 0.3f, 0.3f, 0.3f ); g_World2 = mScale * mSpin * mTranslate * mOrbit;
須要注意的一點是,這些操做不是可交換的。 應用轉換的順序很重要。 試驗轉化順序並觀察結果。
因爲全部變換函數都將根據參數建立新矩陣,所以它們旋轉的量必須遞增。 這是經過更新「時間」變量來完成的。
// Update our time t += XM_PI * 0.0125f;
在進行渲染調用以前,必須爲着色器更新常量緩衝區。 請注意,世界矩陣對於每一個多維數據集都是惟一的,所以會爲每一個傳遞給它的對象進行更改。
// // Update variables for the first cube // ConstantBuffer cb1; cb1.mWorld = XMMatrixTranspose( g_World1 ); cb1.mView = XMMatrixTranspose( g_View ); cb1.mProjection = XMMatrixTranspose( g_Projection ); g_pImmediateContext->UpdateSubresource( g_pConstantBuffer, 0, NULL, &cb1, 0, 0 ); // // Render the first cube // g_pImmediateContext->VSSetShader( g_pVertexShader, NULL, 0 ); g_pImmediateContext->VSSetConstantBuffers( 0, 1, &g_pConstantBuffer ); g_pImmediateContext->PSSetShader( g_pPixelShader, NULL, 0 ); g_pImmediateContext->DrawIndexed( 36, 0, 0 ); // // Update variables for the second cube // ConstantBuffer cb2; cb2.mWorld = XMMatrixTranspose( g_World2 ); cb2.mView = XMMatrixTranspose( g_View ); cb2.mProjection = XMMatrixTranspose( g_Projection ); g_pImmediateContext->UpdateSubresource( g_pConstantBuffer, 0, NULL, &cb2, 0, 0 ); // // Render the second cube // g_pImmediateContext->DrawIndexed( 36, 0, 0 );
本教程還有另一個重要的補充,那就是深度緩衝區。 沒有它,較小的軌道立方體在圍繞後者的後部時仍會被繪製在較大的中心立方體的頂部。 深度緩衝區容許Direct3D跟蹤繪製到屏幕的每一個像素的深度。 Direct3D 11中深度緩衝區的默認行爲是檢查屏幕上繪製的每一個像素與屏幕空間像素的深度緩衝區中存儲的值。 若是正在渲染的像素的深度小於或等於深度緩衝器中已經存在的值,則繪製像素而且將深度緩衝器中的值更新爲新繪製的像素的深度。 另外一方面,若是正在繪製的像素的深度大於深度緩衝器中已經存在的值,則丟棄該像素而且深度緩衝器中的深度值保持不變。
示例中的如下代碼建立深度緩衝區(DepthStencil紋理)。 它還建立深度緩衝區的DepthStencilView,以便Direct3D 11知道將其用做深度模板紋理。
// Create depth stencil texture D3D11_TEXTURE2D_DESC descDepth; ZeroMemory( &descDepth, sizeof(descDepth) ); descDepth.Width = width; descDepth.Height = height; descDepth.MipLevels = 1; descDepth.ArraySize = 1; descDepth.Format = DXGI_FORMAT_D24_UNORM_S8_UINT; descDepth.SampleDesc.Count = 1; descDepth.SampleDesc.Quality = 0; descDepth.Usage = D3D11_USAGE_DEFAULT; descDepth.BindFlags = D3D11_BIND_DEPTH_STENCIL; descDepth.CPUAccessFlags = 0; descDepth.MiscFlags = 0; hr = g_pd3dDevice->CreateTexture2D( &descDepth, NULL, &g_pDepthStencil ); if( FAILED(hr) ) return hr; // Create the depth stencil view D3D11_DEPTH_STENCIL_VIEW_DESC descDSV; ZeroMemory( &descDSV, sizeof(descDSV) ); descDSV.Format = descDepth.Format; descDSV.ViewDimension = D3D11_DSV_DIMENSION_TEXTURE2D; descDSV.Texture2D.MipSlice = 0; hr = g_pd3dDevice->CreateDepthStencilView( g_pDepthStencil, &descDSV, &g_pDepthStencilView ); if( FAILED(hr) ) return hr;
爲了使用這個新建立的深度模板緩衝區,教程必須將其綁定到設備。 這是經過將深度模板視圖傳遞給OMSetRenderTargets函數的第三個參數來完成的。
g_pImmediateContext->OMSetRenderTargets( 1, &g_pRenderTargetView, g_pDepthStencilView );
與渲染目標同樣,咱們還必須在渲染以前清除深度緩衝區。 這可確保先前幀的深度值不會錯誤地丟棄當前幀中的像素。 在下面的代碼中,教程其實是將深度緩衝區設置爲最大量(1.0)。
// // Clear the depth buffer to 1.0 (max depth) //