UnityShader之遮挡透明
UnityShader之遮挡透明
好久没写博客了,最近在学shader,不得不说,shader真的非常美妙,我沉迷其中无法自拔= =
之前做过一个遮挡透明的功能,当物体遮挡住主角时,该物体会变成半透明显示出主角。这次同样是遮挡透明的功能,不过,变透明的刚刚相反,是主角变成半透明,更严谨的说是主角被遮挡的那一部分变成半透明。
先放出结果图:
当被遮挡时,遮挡部分透明处理,那么需要涉及渲染深度的知识。引擎是如何判断哪个物体在前面哪个物体在后面呢?
深度:每个像素有自己的深度值,离摄像机近的深度小,远的深度大
深度缓冲区:存储每个像素的深度
颜色缓冲区:存储每个像素的颜色
过程:首先比较像素的深度与深度缓冲区同一位置的深度,如果前者小于后者,则未通过深度测试;否则,通过深度测试,将前者写入后者,将该像素的颜色写入到颜色缓冲区。将颜色缓冲区像素颜色显示到屏幕上。
通过这个过程即可把深度小的像素剔除掉,将深度大的显示到屏幕上,从而实现物体的前后顺序。
UnityShader提供了ZWrite 和 ZTest对应深度写入和深度测试。
调整ZWrite可以控制是否将深度写入到深度缓冲区,当然,前提是深度测试通过,如果没通过测试,那么肯定是无法写入的
调整ZTest可以定义上述中前者与后者的比较关系,默认为LEqual即小于等于时通过测试
那么可以得到一种实现思路,用两个PASS:
第一个PASS:ZTest 为 Greater,ZWrite 为 Off,当该像素被遮挡即深度大于深度缓冲区对应位置深度时执行该PASS,那么就可以在该PASS中实现被遮挡像素的效果。
第二个PASS:ZTest为LEqual,ZWrite 为 On,这个PASS与上述PASS是互斥的,在这个PASS中实现未被遮挡像素的效果。
设置ZWrite 是为了防止两个PASS都执行,如果第一个PASS的ZWrite为On,某一像素未被遮挡时,执行第一个PASS,将像素深度写入深度缓冲区,然后轮到第二个PASS进行深度测试时也会通过,因为小于等于嘛。
被遮挡像素透明实现用了边缘光使得更炫酷。边缘光公式大概如下:
fixed rim=1-saturate(dot(worldNormalDir,worldViewDir));
fixed3 finalCol=_RimColor.xyz*pow(rim,_RimPower)*_RimIntensity
通过第一个式子可以得到一个参数rim,顶点法线方向与视角方向契合度越高则rim越小,否则rim越大,即越靠近边缘rim越大
第二个式子中pow是为了提高边缘光硬度
代码:
1 // Upgrade NOTE: replaced '_Object2World' with 'unity_ObjectToWorld'
2
3 Shader "MyShader/Rim/RimShader" {
4 Properties{
5 _RimColor("Rim Color",Color)=(1.0,1.0,1.0,1.0)//边缘光颜色
6 _RimPower("Rim Power",Range(0.1,10))=3.0//Pow参数
7 _RimIntensity("Rim Intensity",Range(0,100))=10//边缘光强度
8
9 _MainTex("Base (RGB)",2D)="white"{}
10 }
11 SubShader{
12 //当所有不透明物体渲染后开始渲染此物体
13 Tags{"Queue"="Geometry+50" "RenderType"="Opaque"}
14
15 Pass{
16 Blend SrcAlpha OneMinusSrcAlpha
17 Cull Off
18 ZWrite Off
19 ZTest Greater
20
21 CGPROGRAM
22 #pragma vertex vert
23 #pragma fragment frag
24 #include "UnityCG.cginc"
25
26 fixed4 _RimColor;
27 float _RimPower;
28 float _RimIntensity;
29
30 struct a2v{
31 float4 vertex:POSITION;
32 float3 normal:NORMAL;
33 };
34
35 struct v2f{
36 float4 pos:SV_POSITION;
37 float4 worldPos:TEXCOORD0;
38 float3 worldNormal:TEXCOORD1;
39 };
40
41 v2f vert(a2v v){
42 v2f o;
43 o.pos=mul(UNITY_MATRIX_MVP,v.vertex);
44 o.worldPos=mul(unity_ObjectToWorld,v.vertex);
45 o.worldNormal=UnityObjectToWorldNormal(v.normal);
46 return o;
47 }
48
49 fixed4 frag(v2f i):SV_TARGET{
50 fixed3 worldNormalDir=normalize(i.worldNormal);
51 fixed3 worldViewDir=normalize(UnityWorldSpaceViewDir(i.worldPos));
52 fixed rim=1-saturate(dot(worldNormalDir,worldViewDir));
53
54 fixed3 col=_RimColor.xyz*pow(rim,_RimPower)*_RimIntensity;
55 return fixed4(col,0.3);
56 }
57 ENDCG
58 }
59
60 Pass{
61 Tags{"LightMode"="ForwardBase"}
62 ZWrite On
63 ZTest LEqual
64 CGPROGRAM
65 #pragma vertex vert
66 #pragma fragment frag
67 #include "UnityCG.cginc"
68 #include "Lighting.cginc"
69 #include "AutoLight.cginc"
70
71 sampler2D _MainTex;
72 float4 _MainTex_ST;
73
74
75 struct a2v{
76 float4 vertex:POSITION;
77 float2 texcoord:TEXCOORD0;
78 };
79
80 struct v2f{
81 float4 pos:SV_POSITION;
82 float2 uv:TEXCOORD0;
83 };
84
85 v2f vert(a2v v){
86 v2f o;
87 o.pos=mul(UNITY_MATRIX_MVP,v.vertex);
88 o.uv=v.texcoord*_MainTex_ST.xy+_MainTex_ST.zw;
89 return o;
90 }
91
92 fixed4 frag(v2f i):SV_TARGET{
93 fixed3 col=tex2D(_MainTex,i.uv).rgb;
94
95 return fixed4(col,1);
96 }
97
98 ENDCG
99 }
100
101
102 }
103 FallBack "Diffuse"
104 }