Unity Shader基於視差對映的雲海效果

開發MrsFu123發表於2022-05-21

雲海效果的實現

這裡的雲海shader有RPM和POM兩個版本,最終使用的是RPM。

在迭代次數足夠的情況下,兩種差別不大。

躁波圖的rgb是顏色,a是高度

VS方法

v2f vert (appdata_full v)

{
   v2f o;
   o.pos = UnityObjectToClipPos(v.vertex);
   o.uv = TRANSFORM_TEX(v.texcoord,_MainTex) + frac(_Time.y*_HeightTileSpeed.zw);
   o.uv2 = v.texcoord * _HeightTileSpeed.xy;
   o.posWorld = mul(unity_ObjectToWorld, v.vertex);
   o.normalDir = UnityObjectToWorldNormal(v.normal);
   TANGENT_SPACE_ROTATION;
   o.viewDir = mul(rotation, ObjSpaceViewDir(v.vertex));
   o.color = v.color;
#if USING_FOG
   HeightFog(o.posWorld.xyz,o.fog);
#endif
   UNITY_TRANSFER_FOG(o,o.pos);
   return o;
}

shader計算了兩套uv,uv是高度和顏色貼圖的uv,uv2是做擾動用的uv

viewDir是切線空間下的視線向量,透明度寫在頂點色裡

FS方法裡初始化

float3 viewRay = normalize(-i.viewDir);

viewRay.z = abs(viewRay.z)+0.42;
viewRay.xy *= _Height;

float3 shadeP = float3(i.uv,0);
float3 shadeP2 = float3(i.uv2,0);

0.42的一個hack的值,是為了防止攝像機向量V和法向量N夾角過大。本身精度足夠是不需要這個值的,但是為了減少迭代次數必須加上這個值。

_Height控制雲的凹凸程度。

shadeP、shadeP2 用於記錄迭代的狀態。

shadeP的xy是取樣uv,z是當前深度。

shadeP2的xy是擾動uv,z沒有用到。

視差計算

RPM版

const int linearStep = 2;

const int binaryStep = 5;
float4 T = tex2D(_MainTex, shadeP2.xy);
float h2 = T.a * _HeightAmount;
// linear search
float3 lioffset = viewRay / (viewRay.z * (linearStep+1));
for(int k=0; k<linearStep; k++)
{
   float d = 1.0 - tex2Dlod(_MainTex, float4(shadeP.xy,0,0)).a * h2;
   shadeP += lioffset * step(shadeP.z, d);
}
// binary search
float3 biOffset = lioffset;
for(int j=0; j<binaryStep; j++)
{
   biOffset = biOffset * 0.5;
   float d = 1.0 - tex2Dlod(_MainTex, float4(shadeP.xy,0,0)).a * h2;
   shadeP += biOffset * sign(d - shadeP.z);
}

先線性查詢,再用二分法查詢。

取樣過程中乘上擾動的高度值。

注:專案用的深度圖其實是高度圖,所以取樣和上文有點區別,取的是一個反向的值。

迭代次數越多,效果越好,過少會出現顏色分層。

POM版

float linearStep = 7;

float4 T = tex2D(_MainTex, shadeP2.xy);
float h2 = T.a * _HeightAmount;
float3 lioffset = viewRay / (viewRay.z * linearStep);
float d = 1.0 - tex2Dlod(_MainTex, float4(shadeP.xy,0,0)).a * h2;
float3 prev_d = d;
float3 prev_shadeP = shadeP;
while(d > shadeP.z)
{
   prev_shadeP = shadeP;
   shadeP += lioffset;
   prev_d = d;
   d = 1.0 - tex2Dlod(_MainTex, float4(shadeP.xy,0,0)).a * h2;
}
float d1 = d - shadeP.z;
float d2 = prev_d - prev_shadeP.z;
float w = d1 / (d1 - d2);
shadeP = lerp(shadeP, prev_shadeP, w);

先線性查詢,最後直接對最後兩次查詢做線性插值。

迭代的次數過少會丟失細節。

光照計算

half4 c = tex2D(_MainTex,shadeP.xy) * T * _Color;

half Alpha = i.color.r;
float3 normal = normalize(i.normalDir);
half3 lightDir = UnityWorldSpaceLightDir(i.posWorld);
float NdotL = max(0,dot(normal,lightDir));

#if USING_FOG
   fixed3 viewDir = normalize(UnityWorldSpaceViewDir(i.posWorld));
   float sunFog = saturate( dot(-viewDir,lightDir));
   half3 sunFogColor  = lerp(_HeightFogColor,_sunFogColor,pow(sunFog,2));
   fixed3 finalColor = c.rgb * (NdotL * lightColor + unity_AmbientEquator.rgb * sunFogColor * _LightIntensity);
   unity_FogColor.rgb = lerp(sunFogColor, unity_FogColor.rgb, i.fog.y*i.fog.y);
   finalColor.rgb = lerp(finalColor.rgb,unity_FogColor.rgb, i.fog.x);
#else
   fixed3 finalColor = c.rgb*(NdotL*lightColor + unity_AmbientEquator.rgb);
#endif
UNITY_APPLY_FOG(i.fogCoord, finalColor);

return ColorOutput(fixed4(finalColor.rgb,Alpha));


來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/70016819/viewspace-2895935/,如需轉載,請註明出處,否則將追究法律責任。

相關文章