塞爾達風之杖技術分析-水體渲染

提要

nn

在上一篇塞爾技術分析中,我們學習了角色的卡通渲染方法和生動的表情系統,今天要學習的是塞爾達中非常風格化的水體渲染。

nn

地牢中的水

nn

大海的水

nn

海洋表現

nn

首先是水面的mesh。

nn

可以用程序生成,這裡為了方便直接在Blender裡面拉一個面出來。

nn

首先添加一個Plane,然後通過subdivide來分割。

nn

導出之前要把uv分出來

nn

海平面用到一張貼圖,遊戲中的水面的貼圖大小是1024*1024,同時用了4級的mipmap

nn

直接鋪到mesh上是這樣的

nn

通過調整水面貼圖的uv坐標來達到波紋的效果。

nn

fixed4 frag (v2f i) : SV_Targetn{ntfloat2 uv = i.uv * 10.0 + float2(_Time.y * -0.05, _Time.y * -0.05);nntuv.y += 0.01 * (sin(uv.x * 3.5 + _Time.y * 0.35) + sin(uv.x * 4.8 + _Time.y * 1.05) + sin(uv.x * 7.3 + _Time.y * 0.45)) / 3.0;ntuv.x += 0.12 * (sin(uv.y * 4.0 + _Time.y * 0.5) + sin(uv.y * 6.8 + _Time.y * 0.75) + sin(uv.y * 11.3 + _Time.y * 0.2)) / 3.0;ntuv.y += 0.12 * (sin(uv.x * 4.2 + _Time.y * 0.64) + sin(uv.x * 6.3 + _Time.y * 1.65) + sin(uv.x * 8.2 + _Time.y * 0.45)) / 3.0;nntfixed4 tex1 = tex2D(_MainTex, uv * 1.0);ntfixed4 tex2 = tex2D(_MainTex, uv * 1.0 + float2(0.2, 0.2));nntfloat tmp = tex1.a * 0.9 - tex2.a * 0.02;ntfloat4 col = fixed4(_MainColor.rgb + fixed3(tmp, tmp, tmp), 1.0);ntreturn col;n}n

nn

采了兩次貼圖,一個是白色的,一個是深藍色的,用來表現層次感。

nn

接下來要實現的是海平面的高低起伏。

nn

主要的思路就是隨機且連續的變換mesh上頂點的位置,有幾種做法吧,一種是用雜訊來處理,一種是疊加多個sin函數。

nn

這裡採用疊加sin函數的方式來處理

nn

float calculateSurface(float x, float z, float scale)n{ntfloat y = 0.0;nty += (sin(x * 1.0 / scale + _Time.y * 1.0) + sin(x * 2.3 / scale + _Time.y * 1.5) + sin(x * 3.3 / scale + _Time.y * 0.4)) / 3.0;nty += (sin(z * 0.2 / scale + _Time.y * 1.8) + sin(z * 1.8 / scale + _Time.y * 1.8) + sin(z * 2.8 / scale + _Time.y * 0.8)) / 3.0;ntreturn y;n}nnv2f vert (appdata v)n{ntv2f o;ntv.vertex.z += _WaveStrength * calculateSurface(v.vertex.x, v.vertex.y, _WaveScale);nto.vertex = mul(UNITY_MATRIX_MVP, v.vertex);nto.uv = TRANSFORM_TEX(v.uv, _MainTex);ntUNITY_TRANSFER_FOG(o, o.vertex);ntreturn o;n}n

通過下面兩個參數,可以調整波的振幅和頻率

nn

_WaveScale("Wave Scale", Float) = 0.1n_WaveStrength("Wave Strength", Float) = 0.1n

還有個簡單的技術,其實link在開船的時候,船和海洋是不往前開的,動的是兩旁的建築和上面的小鳥,但在視覺上是發現不了的,這樣就規避了海平面動態生成和銷毀的問題。

nn

河流瀑布

nn

首先要實現的是河流主體,就是最簡單的UV動畫。

貼圖是長這樣的

nn

Mesh直接用一個Unity自帶的quad。

nn

Shader的關鍵代碼

fixed4 frag(v2f i) : SV_Targetn{nt// sample the texturentfixed4 col = tex2D(_MainTex, i.uv + fixed2(0, _FlowSpeed * _Time.y));ntcol = fixed4(_MainColor.rgb + 0.2f* fixed3(col.a, col.a, col.a), 1.0);ntreturn col;n}n

nn

接著來看一下岸邊的水花

nn

主要分兩個部分,一部分是豎著,貼在岸上的部分,一部分是貼在水面上的部分。Mesh還是用Unity的Quad,進行適當的縮放和旋轉。

nn

然而僅僅是uv動畫很難有那麼好的效果。為了添加層次感和動態,首先是在shader中采兩次貼圖,將結果進行疊加,

nn

fixed4 frag (v2f i) : SV_Targetn{ntfixed flowX = _FlowSpeed * _Time.y;ntfixed4 col = tex2D(_MainTex, i.uv - float2(flowX, 0))*_MainColor;ntfixed4 col2 = tex2D(_MainTex, i.uv - float2(flowX *2, 0))*_MainColor;ntreturn col + col2;n}n

接下來還要做的是動態地調整貼圖的Tiling,具體的實現就是在VS中利用_MainTex_ST算出兩個uv值,傳到fs中

nn

o.uv = v.uv.xy * (_MainTex_ST.xy + fixed2(0, 0.17 * sin(_Time.y))) + _MainTex_ST.zw;no.uv2 = v.uv.xy * (_MainTex_ST.xy + fixed2(0, 0.13 * sin(_Time.y * 2)))+ _MainTex_ST.zw;n

對於float4 _MainTex_ST:

Yes, indeed for any texture property, Unity provides value for float4 with "_ST" suffix. The x,y contains texture scale, and z,w contains translation (offset).

在fs中采出來

nn

fixed4 frag (v2f i) : SV_Targetn{ntfixed flowX = _FlowSpeed * _Time.y;ntfixed4 col = tex2D(_MainTex, i.uv - float2(flowX, 0))*_MainColor;ntfixed4 col2 = tex2D(_MainTex, i.uv2 - float2(flowX *1.4, 0))*_MainColor;ntreturn col + col2;n}n

調一個結果

nn

小結

nn

可以看出在wind walker中,水體的表現主要是通過uv動畫的方式來處理,外加一些sin函數來處理隨機,這使得在Game Cube這樣的低性能機器上也能得到很好的卡通水體的渲染效。

nn

參考

nn

Shadertoy

Intuitive Understanding of Sine Waves

Chapter 1. Effective Water Simulation from Physical Models

[UDK] Zelda Water Shader Help

The Legend of Zelda: Wind waker styled water

medium.com/@gordonnl/th


推薦閱讀:

淺談使用NGUI的界面架構(二)關於NData
Unity3D熱更新LuaFramework入門實戰(7)——PureMVC
Unity自動化構建之iOS打包(另有Android篇)
微表面模型-PBR渲染管線的材質

TAG:Unity游戏引擎 | 游戏开发 | 独立游戏开发 |