Shader学习:能量护盾(边缘发亮)

效果如下:

思路(适用于球体):
1.球体的顶点法线与视线越趋于垂直于则边缘处越亮,是否趋于垂直的结果可以用法线和视线的点积结果来获得
2.当球体与其他物体交汇,边缘处需要发亮。判断方法是当球体的顶点深度离当前场景的深度越近,则越靠近边缘处

步骤:
1.新建一个场景,然后新建一个球体和立方体,让他们重叠,并让摄像机可以看到两者
2.新建一个材质球并选择下方提供的shader,然后再将材质球赋予球体
3.通过材质球面板修改参数查看效果(Depth Threshold参数会受摄像机的近远裁剪屏幕值影响)

Shader文件:

Shader "Custom/EneryShield" {
	Properties {
		_Color ("Color", Color) = (1,1,1,1)
		_EdgeColor ("Edge Color", Color) = (1,1,1,1)
		_EdgeScale ("Edge Scale", Float) = 1
		_DepthThreshold ("Depth Threshold", Range(0, 0.1)) = 0
		_Transparent ("Transparent", Range(0, 1)) = 0
	}
	SubShader {
		Tags { "RenderType"="Transparent" "Queue" = "Transparent" "IgnoreProjector" = "True" "ForceNoShadowCasting" = "True" }
		LOD 300
		
		CGINCLUDE
		#include "UnityCG.cginc"

		fixed4 _Color;
		fixed4 _EdgeColor;
		float _EdgeScale;
		fixed _Transparent;
		fixed _DepthThreshold;
		sampler2D _CameraDepthTexture;

		struct a2v {
			float4 vertex : POSITION;
			float3 normal : NORMAL;
			float2 texcoord : TEXCOORD0;
		};

		struct v2f {
			float4 pos : SV_POSITION;
			float4 srcPos : TEXCOORD0;
			float3 worldNormal : TEXCOORD1;
			float3 worldViewDir : TEXCOORD2;
		};

		v2f vert(a2v v) {
			v2f o;
			o.pos = UnityObjectToClipPos(v.vertex);
			o.srcPos = ComputeScreenPos(o.pos);
			o.worldNormal = UnityObjectToWorldNormal(v.normal);
			o.worldViewDir = UnityWorldSpaceViewDir(v.vertex);
			return o;
		}

		fixed4 frag(v2f i) : SV_TARGET {
			fixed3 worldNormal = normalize(i.worldNormal);
			fixed3 worldViewDir = normalize(i.worldViewDir);	

			// 计算该片元所在像素的线性深度值
			fixed screenDepth = Linear01Depth(SAMPLE_DEPTH_TEXTURE(_CameraDepthTexture, i.srcPos.xy / i.srcPos.w));
			// 计算该片元自身的线性深度值
			fixed selfDepth = Linear01Depth(i.pos.z);
			// 计算两个深度值之间的距离
			fixed deltaD = 1 - smoothstep(0, _DepthThreshold, abs(screenDepth - selfDepth));
			// 计算法线和视线的点积结果
			fixed nv = 1 - abs(dot(worldNormal, worldViewDir));
			// 两者相加用作之后控制显示效果的系数
			fixed edge = saturate((nv + deltaD) * _EdgeScale);

			fixed4 finalColor = fixed4(lerp(_Color.rgb, _EdgeColor.rgb, edge), max(edge, _Transparent));
			return finalColor;
		}

		ENDCG

		Pass {
			Cull Off
			ZWrite Off
			Blend SrcAlpha OneMinusSrcAlpha 

			CGPROGRAM

			#pragma vertex vert
			#pragma fragment frag

			ENDCG
		}
	}
}