Shader学习:全息投影效果

效果如下:

原理:
根据射线和法线的点积(几何上两个向量点积代表一个向量在另一个向量上的投影。如果两个向量垂直,点积结果便是0。如果两个向量平行,点积结果将达到最大)来决定颜色的亮暗程度
扫描线则是纹理在屏幕中的Y坐标越靠近扫描线的Y坐标,则越亮,反之越暗
利用_Time.y(当前时间)来控制扫描线的移动和模型闪烁效果
最后在渲染的时候,由于是半透明的效果并要渲染模型的两面。所以先渲染背面再渲染正面来保证不会出现层级问题。并且在渲染正面时使用Blend SrcAlpha One来让叠加的部位更加明亮

操作步骤:
保存以下shader,并将模型的材质选择为该shader即可。通过材质球面板修改参数改变效果

Shader文件:

// Upgrade NOTE: replaced 'mul(UNITY_MATRIX_MVP,*)' with 'UnityObjectToClipPos(*)'

Shader "Custom/Hologram" {
	Properties {
		_Color ("Color", Color) = (1,1,1,1)
		_ColorScale ("Scale", Float) = 1 
		_ScaningAmount ("Scaning Amount", Float) = 10
		_ScaningSpeed ("Scaning Speed", Float) = 1
		_SplashLevel ("Splash Level", Range(0, 1)) = 0
		_SplashFrequency ("Splash Frequency", Float) = 1
	}
	SubShader {
		Tags { "RenderType"="Transparent" "Queue" = "Transparent" "IgnoreProjector" = "True" "ForceNoShadowCasting" = "True" }
		LOD 300

		CGINCLUDE
		#include "UnityCG.cginc"

		fixed4 _Color;
		float _ColorScale;
		float _ScaningAmount;
		float _ScaningSpeed;
		fixed _SplashLevel;
		float _SplashFrequency;

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

		v2f vert(appdata_base 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);

			// 视线和法线越平行,看到的全息投影越亮
			fixed3 hologram = _Color.rgb * abs(dot(worldNormal, worldViewDir)) * _ColorScale;

			// 计算扫描线的亮暗
			float t = _Time.y;
			fixed tDecimal = t - floor(t);
			float splashFrequency = tDecimal * _SplashFrequency;

			half y = (i.srcPos.y * _ScaningAmount) / i.srcPos.w + tDecimal * _ScaningSpeed;
			half scaninglineY = floor(y);
			half scaningScale = lerp(0, 1, abs((y - scaninglineY) - 0.5) * 2);

			fixed4 finalColor = fixed4(hologram, _Color.a * scaningScale * lerp(1 - _SplashLevel, 1, splashFrequency - floor(splashFrequency)));
			return finalColor;
		}

		ENDCG

		Pass {
			ZWrite Off
			Blend SrcAlpha OneMinusSrcAlpha 
			Cull Front

			CGPROGRAM

			#pragma vertex vert
			#pragma fragment frag

			ENDCG
		}

		Pass {
			ZWrite Off
			Blend SrcAlpha One
			Cull Back

			CGPROGRAM

			#pragma vertex vert
			#pragma fragment frag

			ENDCG
		}
	}
}