刚好在demo中测试了一下深度图实现的扫描效果,就把这篇当笔记记录一下了,免得集成到我的游戏那会赶时间来不及写,csdn博客已经变成了我的有道云笔记,嘿巴扎黑。
场景扫描这个效果其实很实用,塞尔达传说荒野之息就将三大技能的前置效果都用上了深度图扫描。
原理也比较好理解,特别是了解了深度图以后。首先我们手上有一张camera主纹理渲染图,一张camera深度图,它们出自同一个camera所以uv映射匹配,那么我们可以得到深度图中每个像素的深度(当然要经过Linear01Depth处理成线性深度值),同时我们使用一个_Scan扫描深度值,对每个像素深度进行“扫描查询”,也就是进行约等于判断,当约等于时,就混合一个_ScanColor,那么就实现了我们想要的深度扫描效果,shader代码如下:
Shader "Unlit/DepthScanShader"
{
Properties
{
_MainTex ("Texture", 2D) = "white" {}
_Scan("Scan",Range(0,1)) = 0
_Appro("Scan Approximate",Range(0,0.05)) = 0.01
_ScanColor("Scan Color",Color) = (0,1,0,1)
_Weight("Scan Color Weight",Range(0,1)) = 0
}
SubShader
{
Tags { "RenderType"="Opaque" }
LOD 100
Pass
{
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc"
struct appdata
{
float4 vertex : POSITION;
float2 uv : TEXCOORD0;
};
struct v2f
{
float2 uv : TEXCOORD0;
float4 vertex : SV_POSITION;
};
uniform sampler2D _CameraDepthTexture;
sampler2D _MainTex; /*接受camera的渲染主纹理*/
float4 _MainTex_ST;
float _Scan; /*扫描的当前深度值*/
float _Appro; /*我们不能直接用值相等判断,要用约等于近似判断,这里是近似值*/
float4 _ScanColor; /*扫描到的深度的混合颜色*/
float _Weight; /*混合权重*/
v2f vert (appdata v)
{
v2f o;
o.vertex = UnityObjectToClipPos(v.vertex);
o.uv = TRANSFORM_TEX(v.uv, _MainTex);
return o;
}
//约等于判断
bool isScanApproximate(float depth, float scan, float appro)
{
if (depth<scan + appro && depth>scan - appro)
return true;
return false;
}
//颜色权重混合
fixed4 blendColor(fixed4 col, fixed4 scol, float wei)
{
return col * (1 - wei) + wei * scol;
}
fixed4 frag (v2f i) : SV_Target
{
//采样camera渲染主纹理
fixed4 col = tex2D(_MainTex, i.uv);
//获取线性0-1的depth值
float depth = Linear01Depth(tex2D(_CameraDepthTexture, i.uv));
//判断当前片段的depth值约等于_Scan值,则给当前颜色叠加一个_ScanColor
if (isScanApproximate(depth, _Scan, _Appro))
{
col = blendColor(col, _ScanColor, _Weight);
}
return col;
}
ENDCG
}
}
}
c#代码则和上一篇博客中深度图渲染使用一样,我就不贴了。shader中_MainTex和_CameraDepthTexture的贴图unity都会帮我们自动传递camera渲染出来的主纹理和深度纹理,且uv匹配,直接使用即可,然后在片段函数中进行“深度判断”,约等于当前_Scan的深度区域则混合我们设置的_ScanColor。简单明了,效果如下:
接下来我实现一下塞尔达的效果简版,因为塞尔达的那种扫描效果本身是很多效果的叠加,我这里只针对深度图的扫描应用实现,如下:
Shader "Unlit/DepthZeldaShader"
{
Properties
{
_MainTex("Texture", 2D) = "white" {}
_Scan("Scan",Range(0,1)) = 0
_Appro("Scan Approximate",Range(0,0.3)) = 0.01
_ScanColor("Scan Color",Color) = (0,1,0,1)
_Weight("Scan Color Weight",Range(0,1)) = 0
}
SubShader
{
Tags { "RenderType" = "Opaque" }
LOD 100
Pass
{
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc"
struct appdata
{
float4 vertex : POSITION;
float2 uv : TEXCOORD0;
};
struct v2f
{
float2 uv : TEXCOORD0;
float4 vertex : SV_POSITION;
};
uniform sampler2D _CameraDepthTexture;
sampler2D _MainTex;
float4 _MainTex_ST;
float _Scan;
float _Appro;
float4 _ScanColor;
float _Weight;
v2f vert(appdata v)
{
v2f o;
o.vertex = UnityObjectToClipPos(v.vertex);
o.uv = TRANSFORM_TEX(v.uv, _MainTex);
return o;
}
bool isScanApproximate(float depth, float scan, float appro)
{
if (depth <= scan && depth > (scan - appro))
return true;
return false;
}
fixed4 blendColor(fixed4 col, fixed4 scol, float wei)
{
return col * (1 - wei) + wei * scol;
}
fixed4 frag(v2f i) : SV_Target
{
fixed4 col = tex2D(_MainTex, i.uv);
float depth = Linear01Depth(tex2D(_CameraDepthTexture, i.uv));
if (depth < 1)
{
col = blendColor(col, _ScanColor, _Weight);
}
if (isScanApproximate(depth, _Scan, _Appro))
{
col = blendColor(col, _ScanColor, _Weight * (1 - (_Scan - depth) / _Appro));
}
return col;
}
ENDCG
}
}
}
效果如下:
当然了,深度图还可以实现很多种其他的实用效果,后面我再写几篇深度图的应用。