本文主要是介绍3.1深度测试与模板测试,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!
一、模板测试(Stencil Test)
原理:
应用案例:
1.什么是模板测试
从渲染管线出发
Pixel Ownership Test(像素所有权测试 )
定义:
- Pixel Ownership Test用于确定当前OpenGL上下文是否拥有对应于特定片段的帧缓冲区中的像素。
位置:
- 在图形渲染管线的后处理阶段,特别是在Fragment Shader(片段着色器)之后和Per-Fragment操作期间,会进行Pixel Ownership Test。
作用:
- 当顶点着色器完成其工作并进入Primitive Assembly(原始装配)阶段后,裁剪操作正在进行中。之后,片段会进入Fragment Shader进行处理。在片段着色器处理完片段后,会进行Pixel Ownership Test。
- 如果Pixel Ownership Test确定OpenGL上下文拥有该像素,则片段会进入下一个测试阶段,如模板测试、深度测试等。否则,窗口系统会决定是否丢弃该片段,或者是否使用该片段执行任何进一步的片段操作。
Scissor Test(裁剪测试): 用于限制图形渲染中绘制区域的一个测试。
定义:
- Scissor Test(剪裁测试)是OpenGL中的一个功能,用于在渲染时限制绘制区域。
原理:
- 通过指定一个矩形的剪裁窗口,启用Scissor Test后,只有在这个窗口之内的像素才能被绘制,其它像素则会被丢弃。
Alpha Test(透明度测试):用于基于像素的透明度值来决定是否绘制该像素
定义:
- 通过比较像素的透明度值与设定的阈值(Alpha Cutoff Value)来决定是否绘制该像素。
原理:
- 透明度值通常使用图像的Alpha通道表示,取值范围从0到1,其中0表示完全透明,1表示完全不透明。在进行Alpha Test时,如果像素的透明度值小于等于设定的阈值,则丢弃该像素,不进行绘制;如果透明度值大于阈值,则绘制该像素。
应用:
- 透明物体的渲染控制:当渲染透明物体时,可以使用Alpha Test来控制渲染的可见性。通过设定合适的Alpha Cutoff Value,可以只绘制透明度高于阈值的像素,从而实现精确的透明物体渲染。
- 物体边缘的轮廓渲染:通过对透明度值进行Alpha Test,并在边缘处绘制轮廓效果,可以增强物体的视觉效果,使其在场景中更加突出。
- 镜面反射和折射效果:在镜面反射和折射的计算中,Alpha Test可用于限制渲染的像素,只绘制镜面反射或折射效果所需的区域。
实现:
在Shader中需要设置透明队列(Tags中的"Queue"设置为"AlphaTest"),并在片元着色器中使用clip()
函数根据Alpha值和阈值进行比较,决定是否丢弃像素。
Shader "Custom/AlphaTestShader"
{ Properties { _MainTex ("Texture", 2D) = "white" {} _AlphaCutoff ("Alpha Cutoff", Range(0,1)) = 0.5 } SubShader { Tags { "Queue"="AlphaTest" "RenderType"="TransparentCutout" "IgnoreProjector"="True" } 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; }; sampler2D _MainTex; float _AlphaCutoff; v2f vert (appdata v) { v2f o; o.vertex = UnityObjectToClipPos(v.vertex); o.uv = v.uv; return o; } fixed4 frag (v2f i) : SV_Target { fixed4 col = tex2D(_MainTex, i.uv); clip(col.a - _AlphaCutoff); // Alpha Test return col; } ENDCG } }
}
什么是Alpha通道:
Alpha通道是计算机图形学中用于表示图像透明度的一种通道。
也称为α Channel或Alpha Channel,用于描述图像中每个像素的透明度信息。
Alpha通道保存了图像的透明和半透明度信息。通过调整Alpha通道的值,可以控制图像中不同区域的透明度,从而实现图像的融合、叠加、剪裁等效果。
缺点:只能实现不透明或全透明效果
Stencil Test(模板测试):用于控制像素是否可以写入到帧缓冲区中。
定义:
- 通过与模板缓冲区(Stencil Buffer)中的值进行比较来决定像素是否通过测试。
原理:
- 模板缓冲区是一个与帧缓冲区大小相同的附加缓冲区,用于存储每个像素的模板值。当进行渲染时,模板测试会将像素的模板值与模板缓冲区中的值进行比较,根据比较结果决定是否允许该像素写入帧缓冲区。
应用:
- 遮罩显示:通过模板测试,可以实现遮罩效果,如只绘制具有特定模板值的像素。
- 复杂图形渲染:模板测试可以与深度测试和颜色缓冲区操作结合使用,以实现更精细的图形渲染效果,如镂空效果、轮廓线、阴影效果等。
- 特殊效果:在游戏开发中,模板测试常用于实现一些特殊效果,如隐身怪物的显示(需要多次触发才能显示完全)、迷雾系统等。
Depth Test(深度测试):用于确定在渲染过程中哪些像素应该被绘制到屏幕上。
定义:
- 比较新生成的像素的深度值(通常存储在Z-Buffer中)与屏幕上对应位置已存在的像素的深度值,以确定哪个像素应该被显示在屏幕上。
目的:
- 解决在将三维场景渲染到二维屏幕时,由于物体之间的遮挡关系而产生的视觉混淆问题。通过深度测试,可以确保近处的物体遮挡住远处的物体,从而创建出符合人眼视觉的渲染效果。
原理:
- 深度缓冲区(Z-Buffer):深度缓冲区是一个与屏幕大小相同的内存区域,用于存储每个像素的深度值。深度值通常是一个浮点数,表示像素在三维空间中距离观察者的距离。值越小,表示像素越靠近观察者;值越大,表示像素越远离观察者。
- 深度测试过程:
- 在每个像素的渲染过程中,图形处理器会计算新生成的像素的深度值。
- 然后,图形处理器会将这个深度值与深度缓冲区中对应位置的深度值进行比较。
- 根据比较的结果,图形处理器会决定是否将新生成的像素写入到颜色缓冲区中。
Blending(混合):用于创建平滑的过渡效果。
定义:
- Blending,即混合,是一种将两个或多个纹理或图像融合在一起的技术,以产生一种看起来自然且连续的视觉效果。
原理:
- 混合阶段(Blend Phase):在这个阶段,源图像(Source Image)和目标图像(Destination Image)被混合在一起,以产生一个新的图像。混合通常使用一种颜色空间,如RGBA颜色空间(红、绿、蓝和透明度),其中透明度(Alpha值)用于控制源图像和目标图像的叠加程度。通过调整Alpha值,可以控制源图像在目标图像上的显示方式,从完全覆盖到完全隐藏。
- 采样阶段(Sample Phase):在这个阶段,混合后的图像被采样并应用到目标表面。采样过程通常涉及将混合后的像素值与目标表面的颜色进行比较,以确定最终的颜色值。这个过程确保了融合后的图像与目标表面的外观一致。
Dithering(抖动显示):用于模拟更高颜色分辨率的图像效果,尤其是在颜色深度有限的情况下。
Logic Op(逻辑操作):指的是一系列用于控制像素如何组合或修改的位操作。这些操作通常用于实现复杂的渲染效果,如物体描边、多边形填充、阴影渲染等。
帧缓冲区(Frame Buffer)主要用于存储和处理图像数据。
帧缓冲区,也被称为显存或帧缓存,是用于存储显卡芯片处理过或即将提取的渲染数据的内存区域。
类似于计算机的内存,用于存储要处理的图形信息。帧缓冲区由像素组成的二维数组构成,每一个存储单元对应屏幕上的一个像素,整个帧缓冲对应一帧图像即当前屏幕画面。
从逻辑上理解
referenceValue:是一个预先设定的模板参考值,用于与图形渲染过程中每个像素点的模板缓冲(Stencil Buffer)中的值进行比较。
readmask:读取掩码
通过一定条件来判断是对该片元或片元属性执行抛弃操作还是保留操作
从书面概念上理解
模板缓冲区:模板缓冲区与颜色缓冲区和深度缓冲区类似,模板缓冲区可以为屏幕上的每个像素点保存一个无符号整数值(通常的话是个8位整数)。这个值的具体意义视程序的具体应用而定。
在渲染的过程中,可以用这个值与一个预先设定的参考值相比较,根据比较的结果来决定是否更新相应的像素点的颜色值。这个比较的过程被称为模板测试。
模板测试发生在透明度测试(alpha test)之后,深度测试(depth test)之前。如果模板测试通过,则相应的像素点更新,否则不更新。
2.语法表示
3.ComparisonFunction(比较函数)
4.更新值
5.深入理解
3D卡牌效果
Box效果
6.总结
- 使用模板缓冲区最重要的两个值:当前模板缓冲值(stencilBufferValue)和模板参考值(referenceValue)
- 模板测试主要就是对这两个值使用特定的比较操作:Never,Always,Less ,LEqual,Greater,Equal等等
- 模板测试之后要对模板缓冲区的值(stencilBufferValue)进行更新操作,更新操作包括:Keep,Zero,Replace,IncrSat,DecrSat,Invert等等
- 模板测试之后可以根据结果对模板缓冲区做不同的更新操作,比如模板测试成功操作Pass,模板测试失败操作Fail,深度测试失败操作ZFail,还有正对正面和背面精确更新操作PassBack,PassFront,FailBack等等
7.扩展
Unity Shader: 理解Stencil buffer并将它用于一些实战案例(描边,多边形填充,反射区域限定,阴影体shadow volume阴影渲染)_unity shader stencil-CSDN博客
UnityShader实例09:Stencil Buffer&Stencil Test_unity shader 螺丝孔-CSDN博客
Unity3D Stencil Test模板测试详解-腾讯游戏学堂 (tencent.com)
模板测试 - LearnOpenGL-CN
Extra: Using the stencil buffer effect | Patreon
二、深度测试(Z Test)
1.深度缓冲
画家算法
如果你想了解计算机如何去绘图,就需要了解画家算法,在计算机图形学中,有一个很重要的问题需要解决,这个问题叫做可见性问题,我们需要将一个3D模型投影到一个2D的屏幕上,在这个过程中,哪些模型上的三角形是可见的,哪些是不可见的,都需要正确处理
如上图所示,当一个画家需要画出最右侧图片时,他一定是先从最远的开始画起,而图上从左到右的绘画顺序就是典型的画家算法
计算机图形学就是用画家算法进行绘图,首先将待渲染图形里面的多边形根据深度排序,之后按照顺序进行绘制,当我们从后往前进行绘制时,就不会造成后面遮挡前面的问题,也就解决了可见性问题
逐个像素点排序
画家算法只能解决一些明显的可见性问题,如下图所示,当模型进行穿插时,你分辨不出图形的前后关系,这时候,就需要逐个像素点排序(即一个一个像素点比较)
为了能够确定每个像素点的深度(像素点靠摄像机的距离),我们就需要用到深度缓冲
深度缓冲
深度缓冲将上图像素的深度描绘成不同深浅的红色,我们可以看到上图两幅画面的对比,红色越深的像素,代表它离摄像头越近,也就是它的深度越近
假设物体A,B,C的深度为5,6,7;初始化深度缓冲区中数据为无穷,如果我们先画物体A,A的深度为5,此时5<无穷,因此把A物体的颜色值写入颜色缓冲区中,同时把5写入深度缓冲区中A物体占据的区域
A渲染完成,渲染物体B时,如果有区域重复的地方,则将A与B的深度比较,因为5<6,因此B的深度不会写入屏幕当中
当然,因为我们先绘制的物品不同,物品深度存在差异,深度缓冲的方式也不尽相同,绘制效率也会不同,那么是先渲染物体A,B,C的效率高,还是渲染C,B,A的效率高呢?
答案是渲染A,B,C的顺序效率高
当我们按照C,B,A的顺序进行渲染时,明显可以看出颜色写入和深度写入的次数远多于顺序A,B,C,并且在我们的手机图形硬件中,有一个Eerly-Z的机制,它会把深度测试的时机提前,我们不再进行fragment片源着色,然后进行深度比较,而是先进行深度比较,再进行图形像素点的渲染,因此我们节省的不仅仅是颜色写入和深度写入的时间开销,而是整个像素的渲染开销
2.深度缓冲区(Z-Buffer)
1.什么是深度缓冲:
深度缓冲区与帧缓冲区相对应,用于记录上面每个像素的深度值,通过深度缓冲区,我们可以进行深度测试,从而确定像素的遮挡关系,保证渲染正确。
2.何为深度:
深度其实就是该象素点在3d世界中距离摄象机的距离(绘制坐标),深度缓存中存储着每个象素点(绘制在屏幕上的)的深度值!深度值(Z值)越大,则离摄像机越远。
实际上在unity中,并不是用整数表示的,而是用0~1之间的浮点数表示的,在传统的图形学中对于无穷远用1.0表示,而距离摄像机最近的则用0.0表示
为什么要用0.0、1.0表示呢,这跟三维图形变换有关,我们渲染的物件,是一个一个的3D模型,像图片左上角的立方体所示,它有本身的坐标空间,通过矩阵变换,将模型放在游戏世界的每一个角,落,如上图中第3个方框所示,以摄像机视角观察,得出上图第二排第一个方框所示,经过投影变换后,每一个物体都会经过透视替换,都可以看到这些物件,它的深度值对于opengl都是介于[-1,1]之间,对于D3D都是介于[0,1]之间,如何得到值呢?
就是把能在摄像机中看到的景物经过坐标变换,而变换矩阵会确保能看到的景物深度值在[0,1]区间中
图中near表示离摄像机近的距离,far表示离摄像机远的距离
我们在靠近摄像机的位置和远离摄像机的位置各取两个距离相同的像素点,可以看到它们之间的深度是不同的
可以看出unity中越靠近的物体越精确,距离远的物体的两个像素点的深度值差很小,很难看出它的深度值差
float4 frag(vert_output In)
{float depth=UNITY_SAMPLE_DEPTH(tex2D(_CameraDepthTexture),In.uv);//通过_CameraDepthTexture可取到右图深度纹理float linear01Depth=linear01Depth(depth);//调用linear01Depth(depth)转换为线性的深度值return float4(linear01Depth,0,0,1);//把取到的深度值通过绿色通道linear01Depth,蓝色通道0,红色通道0,设置为不透明得到右图所示深度图
}
3.为什么需要深度:
在不使用深度测试的时候,如果我们先绘制一个距离较近的物体,再绘制距离较远的物体,则距离远的物体因为后绘制,会把距离近的物体覆盖掉,这样的效果并不是我们所希望的。而有了深度缓冲以后,绘制物体的顺序就不那么重要了,都能按照远近(Z值)正常显示。
4.深度缓冲区的定义:
- 深度缓冲是一张二维纹理,用于存储每个像素在观察空间中距离摄像机的距离,即深度
- 它与颜色缓冲区一起组成帧缓冲区(Frame Buffer),用于存储最终渲染的图像
- 深度缓冲区的主要作用是进行深度测试(Depth Test),通过比较像素的深度值来确定像素的可见性
深度缓冲就像颜色缓冲(储存所有的片段颜色:视觉输出)一样,在每个片段中储存了信息,并且(通常)和颜色缓冲有着一样的宽度和高度。
深度缓冲是由窗口系统自动创建的,它会以16、24或32位float的形式储存它的深度值。在大部分的系统中,深度缓冲的精度都是24位的
z-buffer中存储的是当前的深度信息,对于每个像素存储一个深度值
通过 Z Write 和 Z Test 来调用 Z-Buffer,实现想要的渲染结果
3.什么是深度测试
从渲染管线出发
从逻辑上理解
Zwrite On:深度写入开启
currentDepthValue:当前处理的某个像素、片元或数据点的深度值
DepthBufferValue:深度缓冲区值
从书面概念上理解
所谓深度测试,就是针对当前对象在屏幕上(更准确的说是frame buffer)对应的像素点,将对象自身的深度值与当前该像素点缓存的深度值进行比较,如果通过了,本对象在该像素点才会将颜色写入颜色缓冲区,否则否则不会写入颜色缓冲
从发展上理解
4.深度写入(Z Write)
深度写入包括两种状态:ZWrite On与ZWrite Off
ZWrite On:当我们开启深度写入的时候,物体被渲染时针对物体在屏幕(更准确地说是frame buffer)上每个像素的深度都写入到深度缓冲区;
ZWrite Off:如果是ZWrite Off,那么物体的深度就不会写入深度缓冲区。
物体是否会写入深度,除了ZWrite这个状态之外,更重要的是需要深度测试通过,也就是ZTest通过,如果ZTest都没通过,那么也就不会写入深度了
ZTest分为通过和不通过两种情况,ZWrite分为开启和关闭两种情况的话,一共就是四种情况:
1.深度测试通过,深度写入开启:写入深度缓冲区,写入颜色缓冲区;
2.深度测试通过,深度写入关闭:不写深度缓冲区,写入颜色缓冲区;
3.深度测试失败,深度写入开启:不写深度缓冲区,不写颜色缓冲区;
4.深度测试失败,深度写入关闭:不写深度缓冲区,不写颜色缓冲区;
5.ZTest操作比较
默认是ZWrite On 和ZTest Lequal,深度缓存一开始为无穷大
6.渲染队列
Unity中的几种内置的渲染队列:
按照渲染顺序,从先到后进行排序, 队列数越小的,越先渲染,队列数越大的,越后渲染。
Unity中设置渲染队列:Tags { “Queue” = “Transparent”},默认是Geometry
Queue(队列)
- 不透明物体的渲染顺序:从前往后
- 透明物体的渲染顺序:从后往前(OverDraw)
可以在shader的Inspector面板查看相关属性:
7.Eary-Z技术
传统的渲染管线中,ZTest其实是在Blending阶段,这时候进行深度测试,所有对象的像素着色器都会计算一遍,没有什么性能提升,仅仅是为了得出正确的遮挡结果,会造成大量的无用计算,因为每个像素点上肯定重叠了很多计算。
现代GPU中运用了Early-Z的技术,在Vertex阶段和Fragment阶段之间(光栅化之后,fragment之前)进行一次深度测试,如果深度测试失败,就不必进行fragment阶段的计算了,因此在性能上会有很大的提升。但是最终的ZTest仍然需要进行,以保证最终的遮挡关系结果正确。
前面的一次主要是Z-Cull为了裁剪以达到优化的目的,后一次主要是Z-Check,为了检查,如下图:
8.深度值
深度测试 - LearnOpenGL-CN
9.深入理解
1.地图扫描
10.总结
- 使用深度缓冲区最重要的两个值:当前深度缓冲值(ZBufferValue)和深度参考值(referenceValue),并通过比较操作获取理想渲染效果
- Unity中的渲染顺序:先渲染不透明物体,顺序是从前到后;再渲染透明物体,顺序是从后到前
- 通过Zwrite 和Z Test组合使用控制半透明物体的渲染
- 引入early-z技术后的深度测试相关的渲染流程
- 深度缓冲区中存储的深度值为0到1范围的浮点值,且为非线性
11.扩展
Unity Shader-渲染队列,ZTest,ZWrite,Early-Z_unity shader ztest zclip-CSDN博客
深度测试 - LearnOpenGL-CN
ShaderLab: Culling & Depth Testing - Unity 手册 (unity3d.com)
Unity Toon Water Shader 教程 (roystan.net)
这篇关于3.1深度测试与模板测试的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!