34

移动端下雪系统的实现-腾讯游戏学院

 5 years ago
source link: http://gad.qq.com/article/detail/288896
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
先上最终效果:



 

做完下雨做下雪,(下雨文章地址:http://gad.qq.com/article/detail/288769)下雪其实和下雨有很多类似的地方。类似的部分我就不再仔细说明,主要还是看不同的部分。个人认为下雪比下雨更加困难。

 

首先还是从雪粒子开始,雪粒子无法像雨一样根据深度来直接判断阻挡,因为雨是垂直下落的,但雪不是,雪是会有各种方向的,所以这里需要做实实在在的碰撞来处理遮挡。

      
 private void OnTriggerEnter(Collider other)
{
        SnowManager.inst.reclaim(this);
}

就是用了一个触发器,就可以看到雪被挡住了。因为自带的物理引擎性能也很好,这样做还可以接受。

5c1aecd1363d1.png

然后要开始处理积雪。第一步还是要实现一个雪的材质,起码要看上去像雪。

基本原理就是雪是白色的,然后加上一个法线,模拟雪表面的纹理。

                           
VertexOutputBaseSimple1 vertForwardBase(VertexInput1 v)
{ UNITY_SETUP_INSTANCE_ID(v); VertexOutputBaseSimple1 o; UNITY_INITIALIZE_OUTPUT(VertexOutputBaseSimple1, o); float4 posWorld = mul(unity_ObjectToWorld, v.vertex); o.pos = UnityObjectToClipPos(v.vertex); o.pack0.xy = TRANSFORM_TEX(v.texcoord, _MainTex); o.pack0.zw = TRANSFORM_TEX(v.texcoord, _BumpMap); float3 worldPos = mul(unity_ObjectToWorld, v.vertex).xyz; float3 worldNormal = UnityObjectToWorldNormal(v.normal); float3 worldTangent = UnityObjectToWorldDir(v.tangent.xyz); float tangentSign = v.tangent.w * unity_WorldTransformParams.w; float3 worldBinormal = cross(worldNormal, worldTangent) * tangentSign; o.tSpace0 = float4(worldTangent.x, worldBinormal.x, worldNormal.x, worldPos.x); o.tSpace1 = float4(worldTangent.y, worldBinormal.y, worldNormal.y, worldPos.y); o.tSpace2 = float4(worldTangent.z, worldBinormal.z, worldNormal.z, worldPos.z); o.vlight = ShadeSH9(float4(worldNormal, 1)); o.depthuv = mul(depthMat, float4(worldPos, 1)); TRANSFER_SHADOW(o); UNITY_TRANSFER_FOG(o, o.pos); return o; }
float3 H = normalize(_WorldSpaceLightPos0.xyz + viewDir); float NdotH = max(0, dot(worldNormal, H)); float nl = dot(float3(i.tSpace0.z, i.tSpace1.z, i.tSpace1.z), _WorldSpaceLightPos0.xyz); float NdotL = dot(worldNormal, _WorldSpaceLightPos0.xyz); float NdotV = dot(worldNormal, viewDir); float3 shadow = atten * _LightColor0.rgb; float y = NdotL * shadow; float2 uv_ramp = float2(_Strength * NdotV, y); float3 ramp = tex2D(_SnowTex, uv_ramp.xy); half ssatten = 1.0; half3 specular = saturate(pow(NdotH, _Shininess * 128.0) * _Specular); float shadow1 = SHADOW_ATTENUATION(i); c.rgb = lerp(albedo.rgb * _Color * shadow * nl * _LightColor0.rgb + albedo.rgb * _Color * 0.5, i.vlight, coverage) + coverage * (ramp + albedo * (specular) * shadow); c.rgb *= shadow1;
这样就有了基本的感觉。

5c1aed36793e9.png

然后是处理雪的逐渐覆盖,首先想到的就是利用柏林噪声生成fbm,来逐步覆盖雪地。

                                 
float f;
float3 q = worldPos;
f = 0.5000*noise(q);
q = q * 2.02;
f += 0.2500*noise(q);
q = q * 2.03;
f += 0.1250*noise(q);
q = q * 2.01;
f += 0.0625*noise(q);
float NdotD = saturate(dot(normal, _WorldSpaceLightPos0.xyz));
float coverage = NdotD - lerp(1, -1, _cover);
coverage = saturate(coverage);
coverage = f * _noise - lerp(1, -1, coverage) + max(-0.1,i.tSpace1.z * _cover);

5c1aed81e3717.png

可以看到地面和球体都有部分被雪覆盖的痕迹。 接下来依然要处理遮挡部分,和下雨是一样的。

5c1aec4cca402.png

最后是运动痕迹,和以前的草思路一样。


                                
  
float4 grassuv1 = mul(MoveMatrix, float4(worldPos, 1));
float2 grassuv2 = grassuv1.xy / grassuv1.w * 0.5 + 0.5;
#if UNITY_UV_STARTS_AT_TOP
       grassuv2.y = 1 - grassuv2.y;
#endif
float4 n = tex2D(_MoveTex, grassuv2);
n.xz = (n.xz - 0.5) * 2;
n.y *= _cover;
UNITY_LIGHT_ATTENUATION(atten, i, worldPos)
float4 c = 0;
float3 worldNormal;
worldNormal.x = dot(i.tSpace0.xyz, normal);
worldNormal.y = dot(i.tSpace1.xyz, normal);
worldNormal.z = dot(i.tSpace2.xyz, normal);
worldNormal = normalize(worldNormal);
c.rgb += albedo.rgb * i.vlight;
float3 H = normalize(_WorldSpaceLightPos0.xyz + viewDir);
float NdotH = max(0, dot(worldNormal, H));
float nl = dot(float3(i.tSpace0.z, i.tSpace1.z, i.tSpace1.z), _WorldSpaceLightPos0.xyz);
float NdotL = dot(worldNormal, _WorldSpaceLightPos0.xyz);
float NdotV = dot(worldNormal, viewDir);
float3 shadow = atten * _LightColor0.rgb;
float y = NdotL * shadow;
float2 uv_ramp = float2(_Strength * NdotV, y);
float3 ramp = tex2D(_SnowTex, uv_ramp.xy);
half ssatten = 1.0;
half3 specular = saturate(pow(NdotH, _Shininess * 128.0) * _Specular);
float shadow1 = SHADOW_ATTENUATION(i);
c.rgb = lerp(albedo.rgb * _Color * shadow * nl * _LightColor0.rgb + albedo.rgb * _Color * 0.5, i.vlight, coverage) + min(1.03, coverage * (coverage * 0.05 + 1)) * (ramp + albedo * (specular) * shadow);
c.rgb *= shadow1;
if (needLine > 0.5 && coverage > 0.7 && n.y > 0.1)
{
         c.rgb *= (1 - pow(1 - n.y * f, 5) * 0.3  * pow(coverage, 5));
}

5c1aedd86d76a.png

然后说下这个雪的问题。

顶点片段着色器并不复杂,而且可以适当减少噪声计算来获得好的帧率。问题大的反而是为了处理雪的碰撞,以及雪的速度较慢,会积累大量的粒子在屏幕里。

所以改良措施就是直接用雪粒子特效代替,因为粒子特效是多线程优化过的,也支持碰撞,性能会好不少。

其他应该和下雨类似,需要自己整合到项目中。

最后是插件地址:

https://assetstore.unity.com/packages/vfx/shaders/mobilesnowsystem-135279

About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK