初次嘗試GPU Driver —— 大范圍植被渲染之着色
在《初次嘗試GPU Driven —— 大范圍植被渲染》中實現了草地分布,本文實現草的着色。
本文分四個部分:
- 生成網格
- 隨機調整
- 着色
- 風場
生成草網格
網格形狀通常有矩形和三角形,本文使用三角形的網格。

上圖從左到右依次提高細節。
隨機調整
用上一步生成的網格渲染,會看到這樣的畫面。

很顯然,分布的太整齊了,草叢不是整齊排列的,所以每顆草不能都用一樣的方向和大小渲染,在這一步將草隨機一下。思路是通過草的世界坐標得出隨機值,用該隨機數去旋轉&縮放頂點,因為每顆草的世界坐標是固定的,所以隨機數也是固定的,又因為每顆草的坐標都不一樣,所以隨機數也可能會不一樣(這不是病句)。
隨機數的計算方法有很多,只要讓其盡可能亂就行了,計算出縮放和旋轉后,再加上之前算出來的世界坐標,就可以構建變換矩陣了。
float3 wcoord = _GrassCoords[instanceID];
...
float random(float2 pos)
{
return frac(sin(dot(pos, float2(12.9898, 78.2330))) * 1.9);
}
...
// 平移/縮放/旋轉
float rand = random(wcoord.xz);
// 隨機縮放
float2 scale = lerp(float2(0.2, 0.5),
float2(0.3, 1.0), rand);
// 隨機旋轉
float2 rotate = float2(cos(rand * UNITY_PI * 2),
sin(rand * UNITY_PI * 2));
float4x4 transform = float4x4(float4(scale.x * rotate.x, 0, -rotate.y, wcoord.x),
float4( 0, scale.y, 0, wcoord.y),
float4( -rotate.y, 0, rotate.x, wcoord.z),
float4(0, 0, 0, 1));
...
o.wcoord = mul(transform, v.vertex);
o.vertex = UnityWorldToClipPos(o.wcoord);

草已經被打亂了,但每顆草太直了,接下來壓彎每顆草,思路是將草往前傾斜,同時降低Y軸值,Y軸值越大,則傾斜越大,下壓力越大。
float2 forward = float2(0, 1);
float2 offset = forward * scale.y * _Bend
* v.vertex.y * v.vertex.y;
v.vertex.xz += offset;
v.vertex.y -= length(offset);

把數量翻10倍后,畫面如下:

着色
着色這部分簡單處理,給定兩個基礎顏色,分別表示草的頂部和底部色,隨后用Lambert光照着色。
float3 worldCoord = i.wcoord;
float3 worldNormal = normalize(i.normal);
float3 lightNormal = UnityWorldSpaceLightDir(worldCoord);
float4 color = lerp(_BottomColor, _TopColor, i.vcoord.y);
fixed3 ambient = color * UNITY_LIGHTMODEL_AMBIENT.rgb;
fixed wDotL = max(0.2, dot(worldNormal,lightNormal));
fixed3 diffuse = color * wDotL * _LightColor0.rgb;
color.rgb += ambient;
color.rgb += diffuse;
return color;

風場
這一步加入風的影響,通過風向,風速,風力三個因素定義風,隨時間挪動影響范圍。
本文的風區分微風和強風,微風持續影響,強風按頻率影響,可以抖動一下頻率效果更佳。
本文強風用下圖頻率:

// 基礎風
float3 wind = _WindDirect * _WindPower * v.vertex.y * v.vertex.y;
float windValue = tex2Dlod(_WindMask, float4(wcoord.xz / 64, 0, 0)).r;
// 微風
wcoord.xyz += wind * sin(_Time.y * _WindSpeed + dot(wcoord, _WindDirect)) * 0.3;
// 強風
wcoord.xyz += wind * saturate(sin(_Time.y * _WindSpeed + dot(wcoord, _WindDirect))) * windValue;
wcoord.xyz += wind * saturate(sin(0.75 * _Time.y * _WindSpeed + dot(wcoord, _WindDirect))) * windValue;
wcoord.xyz += wind * saturate(sin(0.25 * _Time.y * _WindSpeed + dot(wcoord, _WindDirect))) * windValue;
到此,渲染部分就結束了,下面展示一段加入高度圖后的最終表現:


