1. 程式人生 > >D3D12渲染技術之地形和水

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