Unity SRP自定义渲染管线 -- 2.Custom Shaders
本章將接著上一篇文章,在初步實現一個渲染管線后來創建自定義的shader。上一篇文章的鏈接
?https://blog.csdn.net/yinfourever/article/details/90516602。在本章中,將完成以下內容:
- 寫一個HLSL Shader
- 定義constant buffer(常量緩沖區)
- 使用?Render Pipeline Core Library
- 支持動態合批和GPU instancing
本章最后實現的效果如下圖,多個不同顏色球體,只需要一個draw call
256個球體,只需要一個draw call1.Custom Unlit Shader
1.1Creating a Shader
創建一個Shader,刪掉所有自帶的默代碼,寫入以下代碼。
將上一章創建的Unlit Opaque material指定為使用我們創建的這個新shader。
1.2 HLSL
Unity 新的SRP渲染管線使用HLSL,所以我們自定義的渲染管線也使用HLSL,我們使用HLSLPROGRAM?and?ENDHLSL
?一個Shader至少應該包含vertex函數(頂點shader部分)和fragment函數(片元shader部分)。我們命名UnlitPassVertex?for the vertex function and?UnlitPassFragment?for the other。但具體代碼我們不在這個文件中實現,而是分離到一個單獨的hlsl文件中,在這里include進來。
?在生成的Unlit.hlsl文件中,我們使用#ifndef來避免多次引用。之后,我們聲明Vertex函數的輸入參數結構體和輸出參數結構體
在vertex shader中,講輸入參數傳遞給輸出參數,在fragment shader中,返回1,這樣就完成了一個最簡單的shader架構,雖然參數還是有錯誤的(position參數沒有進行坐標變換處理直接從vertex shader傳入到了fragment shader)?
1.3?Transformation Matrices
從模型空間到裁剪空間需要兩步轉換,我們先用unity_ObjectToWorld矩陣轉換到世界坐標空間,再用unity_MatrixVP矩陣轉換到裁剪空間。
?我們可以通過一個小技巧來提高運算的效率,將input的position信息,從三維向量補齊成四維,可以大大加快運算效率。
1.4 Constant Buffers
Unity沒有直接提供MVP矩陣二是拆開成提供兩個矩陣M和VP是因為VP矩陣在一幀中不會改變,可以重復利用。Unity將M矩陣和VP矩陣存入Constant Buffer中以提高運算效率,M矩陣存入的buffer為UnityPerDraw buffer, 也就是針對每個物體的繪制不會改變。VP矩陣則存入的是UnityPerFrame buffer,即每一幀VP矩陣并不會改變。Constant Buffer并不是所有平臺都支持,目前OpenGL就不支持。
使用cbuffer keyworld來引入Constant buffer,constant buffer中還有很多其他的數據,暫時我們用不到,只使用這兩個矩陣
1.5 Core Library
? 因為constant buffer并不是支持所有平臺,所以我們使用宏來代替直接cbuffer keyword,使用CBUFFER_START 和CBUFFER_END?這兩個宏需要使用Core Library,通過package manager可以安裝。? 安裝成功后,在hlsl代碼中引入common.hlsl就可以使用這兩個宏了。
1.6 Compilation Target Level
我們使用#pragma target?指定shader level為3.5 代替默認的2.5,我們不支持OpenGL ES2那些老設備。
?
1.7 Folder Structure
我們調整下文件目錄結構,使引用的文件都放入ShaderLibrary文件夾
2?Dynamic Batching
?現在使用我們新創建的這個shader去渲染大量球體時,可以看到每個球體各自占用一個draw call。在fram debugger中我們可以看到提示沒有合批的原因是我們沒有開啟動態合批。
?
我們根據提示,即使打開了player setting中的Dynamic Batching option選項,依然不會有效果,這是因為player setting中設置的是unity默認的渲染管線,而我們現在使用的是自定義的渲染管線,所以需要代碼手動控制我們自己的這個管線開啟合批功能。
開啟后,我們發現合批依然沒有成功,根據提示,是因為物體的定點數太多,超過了動態合批的限制300。?
把球體換成頂點數很少的方體,動態合批就成功了,可以看到只有一個draw call?
2.2 Colors
當使用不同的material時,是不能動態合批的,為了證明這一點,我們加入color屬性。
?將color屬性存入constant buffer中
復制一份之前的material,將復制的material中的color屬性設置為其他顏色,我們可以看到合批結果為4個draw call。?這是因為對于每個material至少會產生一個draw call,unity中為了避免overdraw,會根絕空間位置信息分組渲染,對于每個material,往往會多于一個draw call。
debugger中可以看到提示不能合批的信息為使用了不同的material。?
2.3 Optional Batching
動態合批是一個提升渲染效率的好方法,但是并不是所有時候都有效,甚至會拖慢渲染效率。當場景中并不存在大量使用相同material的小mesh的時候,動態合批就沒什么用了,反而每幀關于動態合批的運算會降低效率。我們在自定義的渲染管線中加入一個bool值,來作為是否開啟動態合批的開關。
?在MyPipeline的構造函數中,傳入是否開啟動態合批的bool值
?在render函數中根據drawFlags設置動態合批是否開啟
?3 GPU Instancing
除了動態合批外,GPU Instancing也可以用于減少draw call。通過GPU Instancing,CPU告訴GPU在一個draw call中,渲染特定的mesh和material的組合多次。這使得使用相同mesh和material的物體渲染時不再像動態合批需要重新組成一個新的后批后的大mesh,這也就移除了對mesh大小的限制。
3.1?Optional Instancing
和之前的動態合批一樣,我們也引入一個bool值控制是否開啟GPU Instancing
?
?3.2?Material Support
渲染管線開啟了GPU Instancing還不夠,還需要使得material支持GPU Instancing,通過添加#pragma multi_compile_instancing來產生shader 變體,一個支持GPU Instancing一個不支持。在Editor中material的設置中可以看到是否開啟GPU Instancing選項。
3.3?Shader Support
當GPU Instancing開啟時,GPU會使用相同的constant data渲染同一個mesh多次。但是因為每個物體的位置不同,所以M矩陣就不同。為了解決這個問題,會在constant buffer中存入一個數組,用于存儲待渲染的物體的M矩陣。每一個instance根據自身的index,從數組中取用數據。
我們使用unity提供的UNITY_MATRIX_M這個宏,在core library中的這個宏可以支持instancing,在使用矩陣數組的時候可以取出對應的矩陣
?使用該宏需要引用UnityInstancing.hlsl這個文件。
?當時用Instancing時,物體的index或被gpu傳入頂點數據中,UNITY_MATRIX_M這個宏需要使用這個index數據,我們將Index數據加入到Vertex shader函數的輸入結構體中,在Vertex Shader的處理函數中使用UNITY_SETUP_INSTANCE_ID?這個宏來使其生效。
?
?除了?object-to-world matrices, 默認?world-to-object矩陣也存在了constant buffer中。但是目前我們沒有需求使用他們,所以可以通過#pragma instancing_options assumeuniformscaling去掉他們提高性能?
3.4 Many Colors
之前我們想實現一個場景中多個顏色的物體只能使用多個material,但是如果使用類似存儲M矩陣的方式操作color屬性,就可以實現用一個matrial渲染多種顏色物體。
首先創建一個腳本,包含要使用的color數據。
?其次通過MaterialPropertyBlock函數創建一個MaterialPropertyBlock實體,通過它來設置material中的顏色。這些操作寫入了OnValidate函數中,使得在editor中可以看到顏色變化效果。
?通過去掉局部變量,可以優化效率。
這時就可以用一個material渲染多種顏色了,但是到目前為止,每個物體都會產生draw call?
?3.5?Per-Instance Colors
像M矩陣處理的方式一樣,我們也將color屬性以數組形式存入constant buffer中,在使用時通過index從數組中取出。
不同于M矩陣Unity已經幫我們處理好了,color屬性需要我們自己手動創建constant buffer并存入其中
在vertex shader函數的輸出中,也要把index數據傳遞到fragment shader函數中。在fragment shdare的處理函數中,根據index取用color數據
這時,我們只需使用一個material就能實現多顏色物體的渲染了,并且可以實現合并draw call。但是要注意的是constant buffer能存儲多少數據是有限制的,最大數量的Instance取決于每個instance需要多少數據,除此之外,不同平臺最大限制也不同。同時instance draw call的合并依然也要求需要是相同的mesh和material,比如下圖中的方形和圓形雖然使用相同的material,但因為mesh不同所以依然需要不同的draw call進行渲染。
?
創作挑戰賽新人創作獎勵來咯,堅持創作打卡瓜分現金大獎總結
以上是生活随笔為你收集整理的Unity SRP自定义渲染管线 -- 2.Custom Shaders的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 学习笔记50—多重假设检验与Bonfer
- 下一篇: 心知天气调用