概述
3D游戏中主角会经常被墙壁之内的东西挡住,此时为了达到突出主角的效果,会做一些特殊处理让主角显示出来。
比如如下效果:
虽然网上有类似的代码,但是如果我们想扩展达到其他更好的效果,还是得了解其运行原理。
代码不是很多,使用的函数包括ZTest,ZWrite,Blend等,看起来都是些内置参数,但是很多人不一定了解其意义和原理。
光栅化阶段的后期,会有一个深度测试和颜色混合的过程,此效果就是针对这个过程进行的一种特殊处理。
问题1:什么是深度?
深度就是该像素点在3D世界中距离摄像机的距离,离摄像机越近,深度值越小。
问题2:什么是深度测试?
首先要有【深度缓存】这个名词的概念,想象屏幕上每一个点,都存放在一个缓存列表中。如果启用深度缓存【ZWrite On】,那么在绘制每个像素前,底层会将当前点的深度值和已经存储在这个位置的点的深度值进行比较。如果新点的深度值小于原来点的深度值,则新的点会代替原来的点。反之新的点会被遮挡,其颜色值和深度值会被丢弃。
知道了上面的两个答案后,我们就可以分析,怎样实现遮挡显示的效果?
1.定义2个pass,一个输出纯色,一个输出模型色
2.当对象没有被挡住时,先执行的纯色pass输出了纯色,然后模型的pass输出了模型色,最终纯色被替换显示正常模型。
3.当对象被墙壁挡住时,如果我们什么也不做,纯色pass输出的颜色和模型pass输出的颜色都会被墙壁代替【因为它们的深度值都比墙壁深度值大】。
4.当对象被墙壁挡住时,我们想要的效果是显示纯色,那么我们可以关闭纯色的【ZWrite】,就是不将它的深度值写入【深度缓存】,此时它的深度测试参数ZTest默认是LEqual,而它的深度值是大于墙壁(存于当前深度缓存)的,所以也不会显示。
这时设置ZTest为GEqual,就是说此pass的输出的颜色深度值大于当前的屏幕的深度值就会显示,显然纯色深度值是大于墙壁色深度值的,所以最终显示了纯色。
5.被墙壁挡住时,模型色已经被剔除了,最终的颜色混合为
Blend SrcAlpha OneMinusSrcAlpha
最终色 = 纯色rgb * 纯色a + 墙壁rgb * (1-纯色a)
代码如下:
- Shader
"Custom/XRay" - {
-
Properties -
{ -
_Color("Color", Color) = (1,1,1,1) -
_MainTex("Albedo", 2D) = "white" {} -
_AfterColor ("After Color", Color) = (0.435, 0.851, 1, 0.419) -
} -
-
SubShader -
{ - Tags { "Queue" = "Geometry+1" "RenderType"="Opaque" }
-
LOD 300 -
-
Blend SrcAlpha OneMinusSrcAlpha // 源RGB*源A + 背景RGB*1-源A -
-
// 1.没有被墙挡住时,先执行纯色的pass,然后执行模型的pass,纯色会被模型颜色替换 -
// 2.1被墙挡住时,模型pass因为开启[ZWrite],[ZTest]为默认的LEqual,因为墙的Z值更小,模型颜色被墙替换 -
// 2.2而纯色没有写入【深度缓存】,【ZTest】为GEqual,而纯色是在墙后面,Z值大于墙,所以显示 -
// 3.此时再融合,纯色RGB*纯色A + 墙RGB*(1-纯色A) -
pass -
{ -
// 深度测试的Pass -
ZTest GEqual // 深度测试 大于等于当前最小【深度缓存】中的值时,就会显示 -
ZWrite Off // 不写入到【深度缓存】 -
CGPROGRAM -
#pragma vertex vert -
#pragma fragment frag -
float4 _AfterColor; -
struct appdata -
{ -
float4 vertex : POSITION; -
}; -
struct v2f -
{ -
float4 pos : POSITION; -
}; -
v2f vert (appdata v) -
{ -
v2f o; -
o.pos = mul(UNITY_MATRIX_MVP,v.vertex); -
return o; -
} -
float4 frag (v2f i) : COLOR -
{ -
return _AfterColor; -
} -
ENDCG -
} -
pass -
{ -
//深度测试默认值,即当前的Z值小于等于【深度缓存】中的值,则显示当前Z -
//没有被墙挡住时它的Z值最小即正常显示,被墙挡住时,墙的Z值更小 -
ZTest LEqual -
CGPROGRAM -
#pragma vertex vert -
#pragma fragment frag -
sampler2D _MainTex; -
float4 _MainTex_ST; -
struct appdata -
{ -
float4 vertex : POSITION; -
float4 texcoord : TEXCOORD0; -
}; -
struct v2f -
{ -
float4 pos : POSITION; -
float4 uv : TEXCOORD0; -
}; -
v2f vert (appdata v) -
{ -
v2f o; -
o.pos = mul(UNITY_MATRIX_MVP,v.vertex); -
o.uv = v.texcoord; -
return o; -
} -
float4 frag (v2f i) : COLOR -
{ -
float4 texCol = tex2D(_MainTex, i.uv); -
return texCol; -
} -
ENDCG -
} -
} - }
原文:http://blog.sina.com.cn/s/blog_89d90b7c0102vvd9.html
最后
以上就是欢呼手机为你收集整理的shader实例(四十二)遮挡显示的全部内容,希望文章能够帮你解决shader实例(四十二)遮挡显示所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
发表评论 取消回复