GitHub项目地址
实现原理:
1、边缘发光
2、相交高亮,主要指能量场和别的物体相交的地方是高亮显示
3、扭曲效果,指物体被能量场包围出现的扭曲
1、边缘发光
边缘发光效果,在我前面的文章中有介绍。
//vert
float3 worldNormal = UnityObjectToWorldNormal(v.normal);
float4 worldPos = mul(unity_ObjectToWorld, v.vertex);
float3 worldView = normalize(UnityWorldSpaceViewDir(worldPos));
o.rimLight = 1 - abs(dot(worldNormal, worldView));
//frag
float rim = pow(i.rimLight, _RimPower);
fixed3 rimColor = _RimColor.rgb * rim;
2、相交高亮
相交高亮,需要获取深度图和能量场的深度。
(1)获取深度图
//C#通知摄像机,获取深度图
Camera.main.depthTextureMode |= DepthTextureMode.Depth;
//shader获取深度图
sampler2D _CameraDepthTexture;
//vert计算屏幕坐标,范围[0, w],未齐次除法
o.screenPos = ComputeScreenPos(o.vertex);
//frag计算观察空间的深度值
float depth = SAMPLE_DEPTH_TEXTURE_PROJ(_CameraDepthTexture, UNITY_PROJ_COORD(i.screenPos));
float sceneZ = LinearEyeDepth(depth);
(2)求能量场的深度值
o.vertex = UnityObjectToClipPos(v.vertex);
//计算观察空间的深度值z,并储存到o.screenPos.z
COMPUTE_EYEDEPTH(o.screenPos.z);
得到这两个深度值后,就可以计算相交
float partZ = i.screenPos.z;
float diff = sceneZ - partZ;
float intersect = pow(1 - diff, _IntersectPower);
float rim = pow(i.rimLight, _RimPower);
float glow = max(intersect, rim);
fixed3 rimColor = _RimColor.rgb * glow;
2、扭曲效果
扭曲效果的实现,我们需要抓取屏幕图像,有两种方式:
(1)渲染纹理RenderTexture+额外摄像机(效率更高)
(2)GrabPass(实现简单)
这里我使用GrabPass的方式,GrabPass通常用于渲染透明物体,需要把物体的渲染队列设置成透明队列(即"Queue"="Transparent")。这样才可以保证当渲染该物体时,所有的不透明物体都已经被绘制在屏幕上,从而获取正确的屏幕图像。
//获取屏幕图像
GrabPass
{
"_GrabTempTex"
}
sampler2D _GrabTempTex;
o.grabPos = ComputeGrabScreenPos(o.vertex);
fixed4 color = tex2Dproj(_GrabTempTex, i.grabPos);
能量球范围内的屏幕图像,随时间调整uv的偏移来模拟扭曲的效果。
//frag
float4 offset = tex2D(_NoiseTex, i.uv - Time.xy) * _DistortTimeFactor;
i.grabPos.xy -= offset.xy * _DistortStrength;
fixed4 color = tex2Dproj(_GrabTempTex, i.grabPos);
扭曲效果
注意事项:
使用两个Pass渲染
上图中,左边的效果使用了一个Pass渲染,右边的效果使用了两个Pass渲染。
造成这样的区别,是因为使用一个Pass渲染,深度测试ZTest会把背后的颜色值覆盖掉。
网友评论