【躬行】-深度缓冲和模板缓冲是怎么存储的?
概述
最近在工作中需要實現(xiàn)一個功能,用到了模板測試。但奇怪的是,模板測試竟然不起作用!在解決問題的過程中,發(fā)現(xiàn)了一些有趣的知識點。通過本文,可以了解在unity中,深度緩沖和模板緩沖到底是怎么存儲的。
測試環(huán)境的搭建
Unity版本:2021.3.16f1
URP版本:12.1.8
RenderDoc:1.29
需要注意的是,URP的版本迭代,代碼改動較大,最好與上面的版本一致。否則,可能會因為版本不同,產(chǎn)生無謂的麻煩。
后面的實驗需要使用到RenderDoc。關于怎么在Unity中使用RenderDoc,可以查看最后的參考文獻部分。
-
由于后續(xù)需要修改URP的源碼進行測試,所以需要移動URP源碼的路徑。新建URP項目,源碼的路徑是類似這種:xxx\Library\PackageCache(xxx是URP項目的文件夾名)。需要將以下兩個URP源碼文件夾移動到xxx\Packages文件夾下:
移動后,Packages文件夾類似這樣:
-
實現(xiàn)一個基礎的Shader,包含了深度測試和模板測試。代碼很簡單,就不贅述了。如下所示:
Shader "Test/Hello World" { Properties { _Color ("Main Color", Color) = (1,1,1,1) [Header(Stencil)] [Enum(UnityEngine.Rendering.CompareFunction)]_StencilComp ("Stencil Comparison", Float) = 8 [IntRange]_Stencil ("Stencil ID", Range(0,255)) = 0 [Enum(UnityEngine.Rendering.StencilOp)]_StencilPass ("Stencil Pass", Float) = 0 } SubShader { Tags { "Queue" = "Geometry" "RenderType" = "Opaque" "RenderPipeline" = "UniversalPipeline" } Pass { Tags { "LightMode" = "UniversalForward" } Cull Off ZTest LEqual ZWrite On Stencil { Ref [_Stencil] Comp [_StencilComp] Pass [_StencilPass] } HLSLPROGRAM #include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Core.hlsl" #pragma vertex vert #pragma fragment frag struct Attributes { float4 positionOS: POSITION; }; struct Varyings { float4 vertex: SV_POSITION; }; half4 _Color; Varyings vert(Attributes input) { Varyings output = (Varyings)0; output.vertex = TransformObjectToHClip(input.positionOS.xyz); return output; } half4 frag(Varyings input): SV_Target { return _Color; } ENDHLSL } } } -
需要設置一下測試的場景環(huán)境。使用上面的Shader新建兩個材質(zhì)球:Far和Near,如下設置:
Far材質(zhì)球,設置為總是通過模板測試,替換模板值3,Render Queue設為2000。
Near材質(zhì)球,設置模板緩沖值為3時才通過,保留模板緩沖值,Render Queue設為2010。
通過上面的設置,會先渲染Far材質(zhì)球,寫入模板緩沖3。然后再渲染Near材質(zhì)球,只有模板緩沖中值為3的區(qū)域才會渲染。
使用Frame Debugger查看渲染流程,可以發(fā)現(xiàn),確實是先渲染Far,再渲染Near。整體的渲染流程如下:
注意上圖中紅框中的部分,是顏色緩沖紋理的名稱。在代碼中使用全局搜索,可以找到如下部分:
通過觀察分析,可以發(fā)現(xiàn),深度緩沖和模板緩沖,主要是受到下面代碼的影響:
colorDescriptor.depthBufferBits的代碼注釋如下:
這個值代表渲染紋理的深度緩沖精度比特值,支持0,16,24,32這四個值。
下面,分別把colorDescriptor.depthBufferBits設為上面的四個值,查看效果。
實驗
實驗一 設為0
colorDescriptor.depthBufferBits = (useDepthRenderBuffer) ? 0 : 0;-
場景效果
-
Frame Debugger
-
RenderDoc
分析:從場景效果看,只渲染了天空盒,沒有顯示出Far或Near。但從Frame Debugger上看,流程并沒有改變,還是先渲染Far,再渲染Near,接著再渲染天空盒。只是天空盒將Far和Near都覆蓋了。從RenderDoc看,只有顏色紋理RT0。從這些內(nèi)容分析以下,應該是因為沒有了深度緩沖和模板緩沖,導致深度測試和模板測試不起作用了。
-
實驗二 設為16
colorDescriptor.depthBufferBits = (useDepthRenderBuffer) ? 16 : 0;
-
場景效果
-
Frame Debugger 與上面相同,略
-
RenderDoc
分析:從場景效果看,顯示出Far和Near,但是模板測試并沒有起作用,因為完整的渲染出了Near。從Frame Debugger上看,流程并沒有改變。從RenderDoc看,除了顏色紋理RT0,還多渲染了一張紋理DS(從名字看,應該是Depth Stencil)。在RT0中右鍵選中Far范圍內(nèi)的一點,再切換到DS,可以在RenderDoc的底部看到選中點的深度、模板信息。從上圖可以看出,DS紋理的格式是R16,后面的值是選中點的深度緩沖值。這樣,可以推測,有了深度緩沖,深度測試應該是起作用了,但是模板緩沖還是沒有起作用,因為沒有模板緩沖。
實驗三 設為24
colorDescriptor.depthBufferBits = (useDepthRenderBuffer) ? 24 : 0;
-
場景效果
-
Frame Debugger 與上面相同,略
-
RenderDoc
分析:從場景效果和Frame Debugger上看,效果和流程與開始實驗前完全一樣。從RenderDoc看,與設為16時一樣,都有RT0和DS兩張紋理。但DS紋理的格式和內(nèi)容是不同的,在上圖底部可以發(fā)現(xiàn),DS的格式是D32S8,后面還有深度緩沖值和模板緩沖值。與設為16時相比,DS紋理的格式不同,紋理的信息中,還多了模板緩沖值。這樣,可以推測,深度緩沖和模板緩沖都有了,深度測試和模板測試也都起作用了。
實驗四 設為32
colorDescriptor.depthBufferBits = (useDepthRenderBuffer) ? 32 : 0;
-
場景效果與上面相同,略
-
Frame Debugger 與上面相同,略
-
RenderDoc與上面相同,略
可以發(fā)現(xiàn),設為32時,與設為24時的效果完全相同。這是為什么呢?
colorDescriptor.depthBufferBits的源碼如下:
public int depthBufferBits
{
get => GraphicsFormatUtility.GetDepthBits(this.depthStencilFormat);
set => this.depthStencilFormat = RenderTexture.GetDepthStencilFormatLegacy(value, this.graphicsFormat);
}
設為24時,單步調(diào)試的結果如下:
設為32時,單步調(diào)試的結果如下:
從上面可以發(fā)現(xiàn),設置depthBufferBits的int值,并不會向一般的屬性那樣直接存儲int值。而是經(jīng)過計算之后,存儲到GraphicsFormat類型的變量中。而當設置的值是24和32時,保存的GraphicsFormat類型的變量都是D32_SFloat_S8_UInt。這也就解釋了為什么設為24和32時,RenderDoc中完全一致的問題。
實驗結論
上面的實驗結果,可以用下面的圖表簡潔表達:
| DS紋理 | 深度測試 | 模板測試 | |
|---|---|---|---|
| 0 | ? | ? | ? |
| 16 | ? | ? | ? |
| 24 | ? | ? | ? |
| 32 | ? | ? | ? |
回到最初遇到的問題:模板測試不起作用。根據(jù)上面的表格,在項目中查了一下,是因為depthBufferBits設為了0,導致深度測試和模板測試都不起作用了。思路延伸一下:從性能優(yōu)化的角度考慮,如果某種情況下不需要深度測試或模板測試,可以賦予depthBufferBits一個比較低的值,這樣,DS紋理占用的內(nèi)存會比較小,甚至不需要申請DS紋理的內(nèi)存。
參考
- [1] RenderDoc Integration
總結
以上是生活随笔為你收集整理的【躬行】-深度缓冲和模板缓冲是怎么存储的?的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Python 文件处理指南:打开、读取、
- 下一篇: Go 接口-契约介绍