笑狗图
效果原理及实现步骤
利用深度图获取两个模型相交位置并进行处理
【实现步骤】
- 获取屏幕xyz坐标
- 根据屏幕坐标找到缓冲中已存在的像素的深度信息
- 对比当前像素的深度信息很缓冲中像素的深度信息,如果在一定范围内,就说明有重合
- 修改颜色。
shader代码
Shader "Kaima/Depth/ForceField"
{
Properties
{
_RimColor("Rim Color", Color) = (1,1,1,1)
_IntersectColor("Intersect Color", Color) = (1,1,1,1)
_RimPower("Rim Power", Range(0, 1)) = 1
_IntersectionPower("Intersect Power", Range(0, 1)) = 0
}
SubShader
{
Tags { "Queue"="Transparent" "RenderType"="Transparent" }
Pass
{
ZWrite Off
Blend SrcAlpha OneMinusSrcAlpha
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc"
struct appdata
{
float4 vertex : POSITION;
float2 uv : TEXCOORD0;
float3 normal : NORMAL;
};
struct v2f
{
float4 vertex : SV_POSITION;
float3 worldNormal : TEXCOORD0;
float3 worldViewDir : TEXCOORD1;
float4 screenPos : TEXCOORD2;
float2 uv : TEXCOORD4;
};
sampler2D _CameraDepthTexture;
fixed4 _RimColor;
fixed4 _IntersectColor;
float _RimPower;
float _IntersectionPower;
v2f vert (appdata v)
{
v2f o;
// 顶点坐标转为裁剪空间
o.vertex = UnityObjectToClipPos(v.vertex);
// 顶点坐标转为世界空间
float4 worldPos = mul(unity_ObjectToWorld, v.vertex);
// 法线转为世界方向
o.worldNormal = UnityObjectToWorldDir(v.normal);
// 视口方向转为世界空间
o.worldViewDir = UnityWorldSpaceViewDir(worldPos);
// 计算片段着色器的屏幕位置
o.screenPos = ComputeScreenPos(o.vertex);
// 该方法用于获取当前物体的透明像素中获取深度Z信息并存入screenPos.z
COMPUTE_EYEDEPTH(o.screenPos.z);
o.uv = v.uv;
return o;
}
fixed4 frag (v2f i) : SV_Target
{
// 世界法线
float3 worldNormal = normalize(i.worldNormal);
// 世界视口方向
float3 worldViewDir = normalize(i.worldViewDir);
// 获取视角值(saturate会将值限制在0,1区间,在这里先当与只获取最大范围0,1的面向相机的角度值,所有背向相机的都只取0)
float3 forceViewAngle = saturate(dot(worldNormal, worldViewDir));
// 能量场的边缘宽度 = 法线与视口方向的角度值 1-是为了取反
float rim = 1 - forceViewAngle * _RimPower;
// 将齐次空间的屏幕坐标 转为 屏幕空间坐标 获取指定屏幕坐标下的,缓冲中的像素信息
float4 screepos = UNITY_PROJ_COORD(i.screenPos);
// 通过计算 获得指定屏幕坐标像素的线性深度图值
float screenDepth = LinearEyeDepth(SAMPLE_DEPTH_TEXTURE_PROJ(_CameraDepthTexture, screepos));
// 判断是否有交集
// 获取 屏幕深度 与 深度图深度 的 距离。如果距离一致就算是交点, 用1 - 是因为了获取位置。
float depthDestance = 1 - (screenDepth - i.screenPos.z);
// 相交力度
float intersect = depthDestance * _IntersectionPower;
// 区分能量罩 和 重合边界显示颜色
fixed4 color;
if(rim > intersect){
//能量场的颜色
color = rim * _RimColor;
}else{
//重合边界的颜色
color = intersect * _IntersectColor;
}
return color;
}
ENDCG
}
}
}