FFT Ocean

最近讀了利用GPU實現大規模動畫角色的渲染有感,終於想起來把一個兩年前的坑FFT Ocean Part 1給填啦。之前做的時候思路有點問題,只考慮了用Scale/Offset來序列幀的情況,搞的自己很繁瑣;現在開闊腦洞直接用vertex ID就方便多了。

左邊是CPU FFT,右邊是將頂點信息Bake到貼圖之後利用vertex texture fetch實現的版本。

Shader參考了今年Advances in Real-Time Rendering in Games里的Crest: Novel Ocean Rendering Techniques in an Open Source Framework實現。

從teaser其實就能看出來怎麼做的,橫坐標就是vertex id,縱坐標就是時間。具體的使用方式和嘉棟的代碼一樣:

float x = (vid + 0.5) * _PosTex_TexelSize.x;nfloat y = (_Time.y + 0.5) * _PosTex_TexelSize.y;nfloat4 uv = float4(x, y, 0, 0);nnv.vertex = float4(tex2Dlod(_PosTex, uv).xyz, 1);nv.normal = tex2Dlod(_NmlTex, uv).xyz;n

具體實現的時候也遇到了不少的坑。

周期性

從最原始的公式上來說,這玩意兒其實是不好做序列幀的因為沒有準確的周期。但實際實現的時候tilde{w(k)}=[[frac{w(k)}{w_0}]]w_0 也就是說 frac{2pi}{w_0} 一定是一個周期。

這裡還可以進一步優化,譬如找到這些角速度的最大公約數,來獲得一個更小的周期。

精度

有了周期大小之後,只要劃分若干段採樣就行了。相鄰兩段之間是使用的線性插值,然而FFT計算並不是線性的,所以如果每段步進過大,那麼插值出來的結果就會和正確值差很多…我第一次做的時候發現採樣出來的非常『丑』,後來調整參數就好了多了。

FOAM

這是從Crest里學到的一個小技巧,通過計算det來得到浪花的強度。我之前只知道利用這貨來判斷是否出現overlap…

這裡如果直接使用坐標的y來作為強度是有問題的,因為可能會出現比較高的波谷、也可能出現比較低的波峰;正確的做法是考慮考慮周圍一格的高度值作為對比。

渲染

這部分在網上參考了好幾個實現,基本上要考慮幾個方面

  • 海水顏色,一般是用一個ramp,或者簡單點就是深水和淺水根據y插值
  • 反射,特別是Skybox部分的貢獻
  • 折射,水下部分
  • 浪花,基本都是一個uv動畫的貼圖
  • 法線貼圖,我發現光FFT生成的Geometry Normal還是不夠用,貼上兩張Bump作為細節紋理效果會好很多
  • SSS/太陽平行光等加分項

TODO

目前實現的這個版本只能作為原型,如果以後實用化的話還有很多工程細節要完成。

  • Grid拼接
  • LOD處理
  • GLES2 Fallback
  • 使用RGBA8888來encode輸入貼圖,節約精度

能不能用上還是看項目驅動了(逃

推薦閱讀:

【UnrealEngine4】藍圖-材質的數據傳輸與偽ComputePipeline
【《Real-Time Rendering 3rd》 提煉總結】(二) 第二章 · 圖形渲染管線 The Graphics Rendering Pipeline
反向路徑採樣在MLT演算法中的運用

TAG:Unity游戏引擎 | 渲染 |