本文Xray shader是在SurfaceShader基础上写
代码一般都想着复用,我把这个Xray写成一个pass
根据usepass来加Xray shader功能
首先理解一下深度和深度测试
(1)什么是深度?
深度其实就是该像素点在3d世界中距离摄像机的距离。离摄像机越远,则深度值(Z值)越大。
(2)什么是深度缓存?
深度缓存中存储着准备要绘制在屏幕上的像素点的深度值。如果启用了深度缓冲区,在绘制每个像素之前,OpenGL会把该像素的深度值和深度缓存的深度值进行比较。如果新像素深度值<深度缓存深度值,则新像素值会取代原先的;反之,新像素值被遮挡,其颜色值和深度将被丢弃。(深度主要起的是比较的作用)
(3)什么是深度测试?
在深度测试中,默认情况是将要绘制的新像素的z值与深度缓冲区中对应位置的z值进行比较,如果比深度缓存中的值小,那么用新像素的颜色值更新深度缓存中对应像素的颜色值。
(4)为什么需要深度?
在不使用深度测试的时候,如果我们先绘制一个距离较近的物体,再绘制距离较远的物体,则距离远的物体因为后绘制,会把距离近的物体覆盖掉,这样的效果并不是我们所希望的。而有了深度缓冲以后,绘制物体的顺序就不那么重要了,都能按照远近(Z值)正常显示,这很关键。
很显然Xray Shader不需要深度缓存,只需要深度测试,比较像素,shader里面写上这两句
zwrite off 不开启深度
ztest greater 深度测试比较
其他surface shader不想被xray印象可以修改缓存,这样子可以就可以过滤
Stencil{
Ref 254
Comp Always
Pass Replace
ZFail Keep
}
效果图
下面完整的xray Pass
Shader "XRay/XRay"
{
Properties
{
_RayColor("RayColor", Color) = (1, 0, 0, 0.5)
_RayIntensity("Intensity", Range(0, 2)) = 0
}
SubShader
{
pass
{
Name "XRAY"
Blend SrcAlpha One
zwrite off
ztest greater
Stencil
{
Ref 254
Comp NotEqual
Pass Keep
ZFail Keep
}
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#pragma target 3.0
#include "UnityCG.cginc"
fixed4 _RayColor;
float _RayIntensity;
struct v2f
{
float4 pos:POSITION;
float4 color : COLOR;
};
v2f vert(appdata_base v)
{
v2f o;
o.pos = UnityObjectToClipPos(v.vertex);
float3 viewDir = normalize(ObjSpaceViewDir(v.vertex));//计算出顶点到相机的向量
float val = 1 - saturate(dot(v.normal, viewDir));//计算点乘值
o.color = _RayColor * val * (1 + _RayIntensity);//计算强度
return o;
}
float4 frag(v2f IN) :color
{
float4 col = IN.color;
return col;
}
ENDCG
}
}
FallBack "Diffuse"
}
然后我们添加一个SurfaceShader
在属性那里添加
_RayColor("RayColor", Color) = (1, 0, 0, 0.6)
_RayIntensity("Intensity", Range(0, 2)) = 0
这个为了控制pass里面的变量
最后在SubShader添加UsePass "XRay/XRay/XRAY",这样子就可以使用xray
不过我在项目中遇到
Shader "Custom/NewSurfaceShader" {
Properties {
_Color ("Color", Color) = (1,1,1,1)
_MainTex ("Albedo (RGB)", 2D) = "white" {}
_Glossiness ("Smoothness", Range(0,1)) = 0.5
_Metallic ("Metallic", Range(0,1)) = 0.0
_RayColor("RayColor", Color) = (1, 0, 0, 0.6)
_RayIntensity("Intensity", Range(0, 2)) = 0
}
SubShader {
Tags { "RenderType"="Opaque" }
LOD 200
UsePass "XRay/XRay/XRAY"
CGPROGRAM
// Physically based Standard lighting model, and enable shadows on all light types
#pragma surface surf Standard fullforwardshadows
// Use shader model 3.0 target, to get nicer looking lighting
#pragma target 3.0
sampler2D _MainTex;
struct Input {
float2 uv_MainTex;
};
half _Glossiness;
half _Metallic;
fixed4 _Color;
// Add instancing support for this shader. You need to check 'Enable Instancing' on materials that use the shader.
// See https://docs.unity3d.com/Manual/GPUInstancing.html for more information about instancing.
// #pragma instancing_options assumeuniformscaling
UNITY_INSTANCING_BUFFER_START(Props)
// put more per-instance properties here
UNITY_INSTANCING_BUFFER_END(Props)
void surf (Input IN, inout SurfaceOutputStandard o) {
// Albedo comes from a texture tinted by color
fixed4 c = tex2D (_MainTex, IN.uv_MainTex) * _Color;
o.Albedo = c.rgb;
// Metallic and smoothness come from slider variables
o.Metallic = _Metallic;
o.Smoothness = _Glossiness;
o.Alpha = c.a;
}
ENDCG
}
FallBack "Diffuse"
}
不过在项目中遇到过摄像机远的时候XRay生效,靠近障碍物的时候就失效
摄像机远的情况
看看FrameDebug里面的深度
摄像机近的情况
看看深度
然后看看镜头拉近之后遮挡物件和xray的深度
可以看出来还是深度不一致影响深度测试
引起这个问题因为美术那边把多个跨度大的场景物件合成一个引起,解决方案最好把xray物体的渲染队列提高
Shader修改
Tags { "RenderType"="Opaque" "Queue" = "Geometry+100" }
这样子就可以解决这个问题