1. 程式人生 > >遊戲中水的渲染技術系列一

遊戲中水的渲染技術系列一

筆者介紹:姜雪偉,IT公司技術合夥人,IT高階講師,CSDN社群專家,特邀編輯,暢銷書作者,國家專利發明人;已出版書籍:《手把手教你架構3D遊戲引擎》電子工業出版社和《Unity3D實戰核心技術詳解》電子工業出版社等。

水的渲染一直是圖形學需要解決的問題,網上也有很多關於這方面的技術實現,本部落格的系列文章也是給讀者做一個總結,本篇部落格主要介紹用傅立葉變換演算法實現的水反射,也是一種假反射效果,目的是優化效率。實現的效果如下圖所示:


使用傅立葉係數來表示地形高度的假反射效果,

在我們開發的遊戲中使用水著色器,告訴讀者我將如何利用引擎處理水的反射,我們自己開發水渲染效果,需要在優化方面考慮,計算每幀渲染時間。

我們要處理渲染簡化的低解析度反射地圖,因為我們還要渲染對於流動的水平面。

演算法的實現其實都是源於生活,大家如果平時出去旅遊,經常會看到山丘的反射,近處反射的比較清晰,但在遠處它只是一個黑色的斑點。試想一下,如果我們能夠記錄每個水點周圍的水面上方的地形的角度,然後我可以在水著色器中使用這個反射光線,它應該是從“天空”過渡到“地形”的點。

Spherical harmonics球面諧波是一種眾所周知的技術,通常用於全域性照明。非常簡要地總結:每個頂點儲存一組預先計算的係數,這允許我們重建擊中物件的環境光。這些係數基本上儲存從每個方向照射該點的光的對映圖。反射/環境光通常是非常低的頻率,因此這是這些係數如何包含這麼“多”的資訊原因。

我決定用我引擎中的水嘗試類似的技術,每個頂點儲存一組小系數,描述地形在水上方圍繞該點的每個方向上升的角度。這可以用一個四元系列

的係數來描述 - 基本上是球面諧波的2d方程。當在頂點之間插值時,這些傅立葉係數就可以計算出來了,給讀者展示一下效果圖吧。


在水上的一個點周圍的取樣方向上的各種角度。

我們計算每個水頂點的係數,這涉及每個頂點的操作:

1、在點周圍選擇  k個均勻間隔的取樣方向。k的值隻影響計算,因此您可以將其設定為一定

的高以實現其模擬度,我目前使用13。


2、對於每個取樣方向,執行光線跟蹤。一次執行一個高度地影象素,測量水面上方的地形角度。

你想要精確的反射取決於你離岸的距離,在本文的示例應用程式中,我目前使用5個畫素。

如果你的遊戲涉及從低水平面的不同的水觀察視角,你將需要使用更多(後面更多)。


3、現在我們有一個函式(每2π迴圈)表示點周圍的地形高度。


4、為了獲得表示該函式的傅立葉係數,我們需要對每個係數的表示式進行積分計算,確切的表示式可以在網

上找到。我使用數值積分,解析度為400(例如每個函式400個樣本),使用的數字僅影響計算。


 5、我計算前8個係數,這個數字直接影響效果的品質和效能。8對我的目的來說肯定夠好了,當然我們會盡量降低。

我把我的係數作為16位浮點儲存在我的頂點結構中(因此每個頂點佔用16個位元組)。

在水著色器中,我使用反射向量來確定我設定的角度,程式碼如下:

float3 reflectionRay = reflect(worldPosition - CameraPosition, normal);
float angle = atan2(-reflectionRay.z, -reflectionRay.x) + PI;
//這給出了0和2π之間的角度,然後我們可以使用它來查詢地形高度。

本文實現的傅立葉評估函式看起來像這樣(t是角度):
float EvaluateFourier(float t, float4 coefs1, float4 coefs2)
{
  float4 sins;
  float4 coses;
  sincos(float4(t, 2 * t, 3 * t, 4 * t), sins, coses);
  float value = coefs1.r; // a0
  value += coefs1.g * coses.r; // a1
  value += coefs1.b * sins.r; // b1
  value += coefs1.a * coses.g; // a2
  value += coefs2.r * sins.g; // b2
  value += coefs2.g * coses.b; // a3
  value += coefs2.b * sins.b; // b3
  value += coefs2.a * coses.a; // a4
  return value;
}

方程給了我一個角度,這也是演算法與程式設計結合的函式實現,然後我可以比較水面上的反射光線的角度,以確認我們是否應該繪製天空或反射的地形,

目前只是使用黑色的反射地形,效果似乎滿足需求如果我們想要更好的效果,還可以儲存地形的顏色,除了高度。當然這將使所需的資料量增加

四倍。

那它是如何工作的呢?

您可以檢視本文頂部的照片作為示例。這裡有一個版本的頂點網格繪製。每個頂點儲存16位元組的資料在我當前的實現。


上圖顯示了我使用的頂點解析度效果。

在水面上使用的法線貼圖有助於實現這種假反射效果,實現的效果如下所示:


以上實現的效果在效能方面也給讀者分析一下,這也有助於讀者優化Shader的渲染效果:

上面給讀者實現了一種假反射,以避免渲染昂貴的反射貼圖,因此它需要具有高效能。不幸的是,這需要大

量的著色器指令在我當前的實現中評估。atan2約有20條指令。HLSL產生4個標量sincos指令,其

實際上每個佔用8個指令槽。總共,它為畫素著色器添加了約64個指令槽。

針對上述問題,我們的下一步任務是找到一種減少指令數量的方法。可以使用

atan,然後是正弦和餘弦,我可以通過做一些三角取代來減少這一點。或者我可以考慮使用e與虛數的冪的和來評估該系列。

當然我們還可以減少係數的數量。

另外,我將看到我是否可以儲存每個係數在單個位元組而不是16位浮點。

最後總結一下,對於具有更多不同檢視的遊戲,這可能不是一個很好的選擇。還有這個技術的一個問題是它只反射靜態物件,

地形,以及你決定在你的射線檢測演算法中包含的任何其他遊戲元素。