昨儿吹酱在群里发了一个WebGL 雪地的例子,里面雪地的交互挺有意思。还好它的源代码只是压缩了一下,没有加密;大概扒拉着看了一下,它其实就是不断利用createRadialGradient绘制Bump Map来实现的。
比较有意思的是它在代码的注释里提到了一篇文章Bump Mapping Unparametrized Surfaces on the GPU,看了下挺好玩,于是我搞到Unity里面去了~
它要做的事情其实很简单:最普通的Bump Map只是在顶点处沿着法向量做一个位移,但法向量是保持不变,这样导致的结果就是和新的Mesh对不上了~论文提出了一个方法,通过屏幕空间的手法近似做一个法向量扰动,使得效果更加逼真。对比图见下:左边是只修改pos;右边是同时修改pos和normal。
这个东西很适合拿来做雪地脚印的简单交互~不得不吐槽,一开始我试图用surface shader实现,但是始终没法将最终的worldNormal转回tangent space,因为直接写回o.Normal
就全乱套了……
BumpMap.shaderview rawShader "Kanglai/BumpTest" { Properties { _MainTex ("Base (RGB)", 2D) = "white" {} _BumpMap ("Bump (R)", 2D) = "black" {} _BumpScale ("Bump Scale", Range(-0.3, 0.3)) = 0.01 } SubShader { Pass { CGPROGRAM #pragma vertex vert #pragma fragment frag #pragma glsl #pragma target 3.0
#include "UnityCG.cginc"
sampler2D _MainTex, _BumpMap; float _BumpScale; struct v2f{ float4 pos : SV_POSITION; float4 tex : TEXCOORD0; float4 wpos : TEXCOORD1; float3 normal : TEXCOORD2; };
float3 perturbNormalArb( float3 surf_pos, float3 surf_norm, float2 dHdxy ) { float3 vSigmaX = ddx( surf_pos ); float3 vSigmaY = ddy( surf_pos ); float3 vN = surf_norm;
float3 R1 = cross( vSigmaY, vN ); float3 R2 = cross( vN, vSigmaX );
float fDet = dot( vSigmaX, R1 );
float3 vGrad = sign( fDet ) * ( dHdxy.x * R1 + dHdxy.y * R2 ); return normalize( abs( fDet ) * surf_norm - vGrad ); } v2f vert(appdata_base i){ v2f o; o.normal = mul ((float3x3)UNITY_MATRIX_IT_MV, i.normal); float _Amount = tex2D (_BumpMap, i.texcoord.xy).r * _BumpScale; o.wpos = mul (UNITY_MATRIX_MV, i.vertex); o.wpos.xyz += o.normal * _Amount; o.pos = mul (UNITY_MATRIX_P, o.wpos); o.tex = i.texcoord; return o; } float4 frag(v2f i) : COLOR { float3 albedo = tex2D(_MainTex, i.tex.xy); float2 TexDx = ddx(i.tex.xy); float2 TexDy = ddy(i.tex.xy); float Hll = tex2D(_BumpMap, i.tex.xy).x; float Hlr = tex2D(_BumpMap, i.tex.xy + TexDx).x; float Hul = tex2D(_BumpMap, i.tex.xy + TexDy).x; float dBs = (Hlr - Hll) * _BumpScale ; float dBt = (Hul - Hll) * _BumpScale ; i.normal = perturbNormalArb(i.wpos, i.normal, float2(dBs, dBt)); #ifndef USING_DIRECTIONAL_LIGHT float3 lightDir = _WorldSpaceLightPos0.xyz - i.wpos; #else float3 lightDir = _WorldSpaceLightPos0.xyz; #endif float diff = max (0, dot (i.normal, normalize(lightDir))); return float4(albedo * diff, 1.0); } ENDCG } } FallBack "Diffuse" }
|