D3D12渲染技術之地形和水
在這個案例中,我們為山和水場景新增紋理, 第一個關鍵問題是我們在地上鋪設草紋理, 因為地網格是一個大的表面,如果我們簡單地在其上拉伸紋理,那麼使用較少的紋理畫素將覆蓋每個三角形。 換句話說,表面沒有足夠的紋理解析度; 我們會得到放大的紋理,因此,我們在地網格上重複草紋理以獲得更高的解析度。 第二個關鍵問題是我們使用時間函式模擬水波紋的滾動, 這種增加的運動使水效果更佳。 下圖顯示了演示的螢幕截圖。
網格紋理座標生成
下圖顯示了xz平面中的m×n網格和歸一化紋理空間域[0,1] 中的對應網格, 從圖中可以清楚地看出,xz平面中第i個網格頂點的紋理座標是紋理空間中第i個網格頂點的座標, 第i個頂點的紋理空間座標是:
xz空間中網格頂點vij的紋理座標由uv空間中的第i個網格頂點Tij給出。
因此,我們使用以下程式碼在GeometryGenerator :: CreateGrid方法中為網格生成紋理座標:
GeometryGenerator::MeshData GeometryGenerator::CreateGrid(float width, float depth, uint32 m, uint32 n) { MeshData meshData; uint32 vertexCount = m*n; uint32 faceCount = (m-1)*(n-1)*2; float halfWidth = 0.5f*width; float halfDepth = 0.5f*depth; float dx = width / (n-1); float dz = depth / (m-1); float du = 1.0f / (n-1); float dv = 1.0f / (m-1); meshData.Vertices.resize(vertexCount); for(uint32 i = 0; i < m; ++i) { float z = halfDepth - i*dz; for(uint32 j = 0; j < n; ++j) { float x = -halfWidth + j*dx; meshData.Vertices[i*n+j].Position = XMFLOAT3(x, 0.0f, z); meshData.Vertices[i*n+j].Normal = XMFLOAT3(0.0f, 1.0f, 0.0f); meshData.Vertices[i*n+j].TangentU = XMFLOAT3(1.0f, 0.0f, 0.0f); // Stretch texture over grid. meshData.Vertices[i*n+j].TexC.x = j*du; meshData.Vertices[i*n+j].TexC.y = i*dv; } }
紋理平鋪
在地形網格上鋪設草紋理, 但到目前為止,我們計算的紋理座標位於單位域[0,1] ; 所以不會發生平鋪。 要平鋪紋理,我們指定wrap地址模式,並使用紋理變換矩陣將紋理座標縮放5倍。 因此,紋理座標被對映到域[0,5] ,以便紋理在平面網格表面上平鋪5×5次:
void TexWavesApp::BuildRenderItems() { auto gridRitem = std::make_unique<RenderItem>(); gridRitem->World = MathHelper::Identity4x4(); XMStoreFloat4x4(&gridRitem->TexTransform, XMMatrixScaling(5.0f, 5.0f, 1.0f)); … }
紋理動畫
要在水面上實現紋理滾動,我們將紋理平面中的紋理座標轉換為AnimateMaterials方法中的時間函式,該方法在每個更新週期呼叫。 如果每幀的位移很小,則會產生平滑動畫的錯覺。 我們使用wrap地址模式和無縫紋理,以便我們可以無縫地轉換紋理空間平面周圍的紋理座標。 以下程式碼顯示了我們如何計算水紋理的偏移向量,以及我們如何構建和設定水的紋理矩陣:
void TexWavesApp::AnimateMaterials(const GameTimer& gt)
{
// Scroll the water material texture coordinates.
auto waterMat = mMaterials["water"].get();
float& tu = waterMat->MatTransform(3, 0);
float& tv = waterMat->MatTransform(3, 1);
tu += 0.1f * gt.DeltaTime();
tv += 0.02f * gt.DeltaTime();
if(tu >= 1.0f)
tu -= 1.0f;
if(tv >= 1.0f)
tv -= 1.0f;
waterMat->MatTransform(3, 0) = tu;
waterMat->MatTransform(3, 1) = tv;
// Material has changed, so need to update cbuffer.
waterMat->NumFramesDirty = gNumFrameResources;
}
總結
1、紋理座標用於定義紋理上的三角形,該三角形對映到3D三角形。
2、為遊戲建立紋理的最流行的方法是讓美術在Photoshop或其他影象編輯器中製作它們,然後將它們儲存為影象檔案,如BMP,DDS,TGA或PNG, 然後遊戲應用程式將載入時的影象資料載入到ID3D12Resource物件中, 對於實時圖形應用程式,DDS(DirectDraw表面格式)影象檔案格式是首選,因為它支援GPU本身理解的各種影象格式; 特別是,它支援可由GPU本機解壓縮的壓縮影象格式。
3、將傳統影象格式轉換為DDS格式有兩種常用方法:使用匯出到DDS的影象編輯器或使用名為texconv的Microsoft命令列工具。
4、我們可以使用CreateDDSTextureFromFile12函式從儲存在磁碟上的影象檔案建立紋理,該函式位於DVD上的Common / DDSTextureLoader.h / .cpp。
5、當我們放大表面並試圖用幾個紋理畫素覆蓋太多螢幕畫素時,會發生放大, 當我們縮小表面並且太多紋理畫素正試圖覆蓋太少的螢幕畫素時,會發生縮小, Mipmap和紋理過濾器是處理放大和縮小的技術。 GPU本身支援三種紋理過濾(按質量最低,質量最低,質量最高,最昂貴):點,線性和各向異性過濾器。
6、定址模式定義了Direct3D應該對[0,1]範圍之外的紋理座標做什麼。 例如,紋理應該是平鋪,映象等。
7、紋理座標可以像其他點一樣縮放,旋轉和平移。 通過每幀少量地逐漸變換紋理座標,我們為紋理設定動畫。
Demo下載地址:連結:https://pan.baidu.com/s/1X0Vikf6qGYGPKU-Nwf-wYA 密碼:h79q