unity 如何获取到屏幕中间_Unity通用渲染管线Shader日志输出工具
這是侑虎科技第700篇文章,感謝作者鄒春毅供稿。歡迎轉發分享,未經作者授權請勿轉載。如果您有任何獨到的見解或者發現也歡迎聯系我們,一起探討。(QQ群:793972859)
作者主頁:https://www.zhihu.com/people/zou-chun-yi-45,作者也是U Sparkle活動參與者,UWA歡迎更多開發朋友加入U Sparkle開發者計劃,這個舞臺有你更精彩!
在Unity開發過程中,如果需要輸出調試日志只需要在C#中調用Debug.Log即可,但是Shader由于硬件結構上的問題無法像C#一樣輕松地輸出調試日志。因此在Shader編碼過程中調試就成了一個很困難的事情,比如想知道VS中某個中間變量結果是否正確等等。我寫的這個工具就是希望能把Shader中的變量能像C#一樣輸出,解決調試中遇到的困難。當然原理與C#中的日志輸出是完全不同的,針對不同的Shader解決方法也是不同的。一、開發環境Unity 2019.3+URP支持的調試Shader類型:VertexShader、FragmentShader、ComputeShader。二、VertexShader中的日志輸出頂點著色器與像素著色器是兩個必須的著色器,但是不要忘記,這兩者還有一個可選的著色器:幾何著色器(Geometry Shader)。關于幾何著色器就不詳細闡述了,大家可以自行查閱相關資料。由于幾何著色器可以為模型添加新的頂點,并且還沒有經過光柵化,因此我們可以將頂點著色器中需要輸出的變量存儲到紋理通道中,然后在幾何著色階段利用新增的頂點將這個變量的內容畫到屏幕上。1. ?下面直接介紹使用方法:以調試Lit.shader為例(工程中參見LitDebugVertex.shader)。先看下效果:對紅圈內的模型Shader進行日志輸出調試過程中的模型會以Wireframe的模式渲染,點擊某一個頂點會輸出調試的日志2. ?對需要日志輸出的Shader進行簡單改造1)在原先Fragment聲明的地方插入如下代碼,然后注釋掉原先的聲明。
#pragma vertex LitPassVertex
//#pragma fragment LitPassFragment
//1、VertexDebug: 在#pragma fragment xxx后前添加,同時注釋掉此行
#pragma geometry geom //關閉調試注釋此行
#pragma fragment debugFrag //關閉調試注釋此行
#define VERTEX_DEBUG_ENABLE //關閉調試注釋此行
#define VERTEX_DEBUG_INDEX 0 //選取的頂點所在三角形index(0,1,2,3-表示全部檢測)
#include "Packages/com.seasun.graphics/Shaders/Debug/VertexDebug.hlsl"
如果想取消調試,恢復到正常的渲染模式,可以注釋掉上述標記的3行代碼,并恢復原先的Fragment函數聲明。2)因為替換了Fragment函數,所以需要修改原先Vertex函數的名稱。//2、VertexDebug: 修改Vert函數分布傳入4個參數:返回類型,函數名,數據結構體名稱,結構體實例
VERTEX_DEBUG_FUN(Varyings, LitPassVertex, Attributes, input)
//Varyings LitPassVertex(Attributes input)
{
Varyings output = (Varyings)0;
float4 mrtValue = 0;
UNITY_SETUP_INSTANCE_ID(input);
UNITY_TRANSFER_INSTANCE_ID(input, output);
UNITY_INITIALIZE_VERTEX_OUTPUT_STEREO(output);
3)初始化UNITY_SETUP_INSTANCE_ID(input);
UNITY_TRANSFER_INSTANCE_ID(input, output);
UNITY_INITIALIZE_VERTEX_OUTPUT_STEREO(output);
VertexPositionInputs vertexInput = GetVertexPositionInputs(input.positionOS.xyz);
//3、VertexDebug: 初始化,傳遞投影后的坐標值
VERTEX_DEBUG_INIT(vertexInput.positionCS)
4)添加想要輸出的變量#if defined(_MAIN_LIGHT_SHADOWS) && !defined(_RECEIVE_SHADOWS_OFF)
output.shadowCoord = GetShadowCoord(vertexInput);
#endif
output.positionCS = vertexInput.positionCS;
//4、VertexDebug: 根據屏幕采點,自動選擇頂點(可選,也可以自己填寫)
if (VERTEX_DEBUG_AUTO_JUDGE)
{
//5、VertexDebug: 輸入想要調試輸出的變量,支持xy2個參數
VERTEX_DEBUG_VALUE(xy, input.lightmapUV.xy)
}
由于Shader是并行執行,在調試期間會有多個頂點執行同樣的一段代碼,因此這里有兩種方法來指定某一個頂點輸入。一種是像示例中的一樣使用這個宏,然后在場景的運行的時候按住Alt,用鼠標點擊模型的頂點,然后就會輸出選中頂點的日志(如果游戲頂點比較密集,會輸出多個頂點的日志)。另一種方式是自己設置約束,在Shader中指定某一個頂點輸出日志。5)改寫返回//6、VertexDebug: 將原始輸出結構放入宏中
VERTEX_DEBUG_OUTPUT(output)
//return output;
3. ?開始調試在調試場景中,找個任意一個GameObject,掛載VertexDebug.cs腳本,然后啟動游戲。按住Alt,用鼠標點擊待調試模型的頂點。調節攝像機的視角,使待調試的頂點進行放大,避免其他頂點的干擾。三、FragmentShader中的日志輸出像素著色器不像頂點著色器那樣,中間有幾何著色器輔助輸出,因此像素著色器中的調試信息只能存儲到顏色緩沖區中。但是存儲到顏色緩沖區中的內容不僅會影響最終的渲染結果,也會受到后期等因素的影響。假如我們使用MRT,就可以解決上述問題。Unity中的MRT可參見延遲渲染:https://docs.unity3d.com/Manual/RenderTech-DeferredShading.html1. ?對URP進行改造URP由于使用正向渲染,因此并沒有啟用MRT,所以需要稍微改造,已到達支持的目的。具體內容這里就不闡述了,可以在工程中搜索宏FRAGMENG_DEBUG查看改造的內容。2. ?下面直接介紹使用方法:以調試Lit.shader為例(工程中參見LitDebugFragment.shader)。先看下效果:木材Shader為需要調試的,插入代碼后,渲染結果不會受到任何影響按住Ctrl,點擊需要顯示輸出內容的像素點,同時會在屏幕和Console中輸出內容3. ?對需要日志輸出的Shader進行簡單改造1)在HLSLPROGRAM前添加//1、FragmentDebug:添加混合模式
Blend 1 One Zero
指定SV_Target1的混合方式2)在Fragment函數前添加//--------------------------------------
// GPU Instancing
#pragma multi_compile_instancing
//2、FragmentDebug: 在Fragment函數前添加
#pragma multi_compile __ FRAGMENT_DEBUG_ENABLE
#include "Packages/com.seasun.graphics/Shaders/Debug/FragmentDebug.hlsl"
#include "ShaderPass/LitInput.hlsl"
3)改造Fragment函數名和初始化// Used in Standard (Physically Based) shader
//half4 LitPassFragment(Varyings input) : SV_Target
//3、FragmentDebug: 修改Frag函數分別傳入3個參數:函數名、v2f結構體名稱、結構體實例
FRAGMENT_DEBUG_FUN(LitPassFragment, Varyings, input)
{
//4、FragmentDebug: 初始化
FRAGMENT_DEBUG_INIT
4)增添想輸出的變量和改造返回half4 color = UniversalFragmentPBR(inputData, surfaceData.albedo, surfaceData.metallic, surfaceData.specular, surfaceData.smoothness, surfaceData.occlusion, surfaceData.emission, surfaceData.alpha);
color.rgb = MixFog(color.rgb, inputData.fogCoord);
//5、FragmentDebug: 輸入想要調試輸出的變量,支持xyz3個參數
FRAGMENT_DEBUG_VALUE(xyz, surfaceData.albedo)
//6、FragmentDebug: 將原始結果放入宏中
FRAGMENT_DEBUG_OUTPUT(color)
//return color;
4. ?開始調試在PlayerSetting中增添宏FRAGMENG_DEBUG,刪除此宏會自動關掉全部功能,包括對URP的改造。在調試場景中,找個任意一個GameObject,掛載FragmentDebug.cs腳本,然后啟動游戲。按住Ctrl,用鼠標點擊待調試模型的像素點,會在Console和屏幕中輸出日志內容。Ctrl+D可以顯示和隱藏屏幕中的調試窗口。四、ComputeShader中的日志輸出ComputeShader與上面的VS與PS不同,是完全兩套流水線,基于GPGPU設計,天然就支持數據從GPU回傳數據到CPU。這個工具為了更方便地調試輸出,只是對原本的方法進行了一些封裝。1. ?下面直接介紹使用方法:使用示例參見倉庫中的CSTest.cs和CSDebug.compute。2. ?對執行腳本進行改造由于ComputeShader的執行通常有兩種,一種是直接執行,另一種是在CommandBuffer中執行。針對這兩種方法使用上略有差別:1)直接執行private void ExcuteCSManual()
{
CSDebug.ComputeShaderDebugSet("Debug1", m_ComputeShader, kernel);
CSDebug.ComputeShaderDebugSet("Debug2", m_ComputeShader, kernel);
m_ComputeShader.SetTexture(kernel, "Result", m_RenderTexture);
m_ComputeShader.SetTexture(kernel, "Source", m_SrcTexture);
m_ComputeShader.Dispatch(kernel, m_RenderTexture.width, m_RenderTexture.height, 1);
Debug.Log("CS1 : " + CSDebug.ComputeShaderDebugGet("Debug1"));
Debug.Log("CS2 : " + CSDebug.ComputeShaderDebugGet("Debug2"));
CSDebug.ComputeShaderDebugRelease();
}
在Dispatch之前設置變量名,可以根據實際情況設置多個,其中Debug1和Debug2為變量名。在執行完Dispatch之后調用CSDebug.ComputeShaderDebugGet來獲取ComputeShader中輸出的數值。最后執行CSDebug.ComputeShaderDebugRelease()來釋放ComputeBuffer。2)在CommandBuffer中執行private void ExcuteCSCommand(ScriptableRenderContext context, Camera camera)
{
if (camera == Camera.main)
{
if (m_ExcuteCommand)
{
m_ExcuteCommand = false;
}
else
{
return;
}
Debug.Log("CS1 : " + CSDebug.ComputeShaderDebugGet("Debug1"));
Debug.Log("CS2 : " + CSDebug.ComputeShaderDebugGet("Debug2"));
CSDebug.ComputeShaderDebugRelease();
CommandBuffer command = CommandBufferPool.Get("ExcuteCSCommand");
CSDebug.ComputeShaderDebugSet("Debug1", m_ComputeShader, kernel);
CSDebug.ComputeShaderDebugSet("Debug2", m_ComputeShader, kernel);
command.SetComputeTextureParam(m_ComputeShader, kernel, "Result", m_RenderTexture);
command.SetComputeTextureParam(m_ComputeShader, kernel, "Source", m_SrcTexture);
command.DispatchCompute(m_ComputeShader, kernel, m_RenderTexture.width, m_RenderTexture.height, 1);
context.ExecuteCommandBuffer(command);
CommandBufferPool.Release(command);
}
}
與執行直接調用的三個函數一樣,但是由于CommandBuffer不是立即執行,而是延遲執行的,因此DispatchCompute之后ComputeBuffer并沒有真正執行,也就無法獲取調試的內容。CSDebug.ComputeShaderDebugSet使用的位置同直接執行,但是Get和Release兩個方法需要放到Set之前。也就是說,每次Get出來的是上一次執行的結果,第一次執行輸出的內容為0。3. ?對ComputeShader進行改造#pragma kernel CSMain
//1) 在定義前添加
#include "Packages/com.seasun.graphics/Shaders/Debug/CSDebug.hlsl"
RWTexture2D<float4> Result;
Texture2D Source;
//2)定義變量,其中變量名同C#中的定義
DEBUG_DEF(Debug1)
DEBUG_DEF(Debug2)
[numthreads(1, 1, 1)]
void CSMain(uint3 id : SV_DispatchThreadID)
{
int i = id.x;
int j = id.y;
float c = Source[float2(i, j)].x * 0.3 + Source[float2(i, j)].y * 0.2 + Source[float2(i, j)].z * 0.5;
Result[float2(i, j)] = float4(c, c, c, Source[float2(i, j)].w);
if (i == 100 && j == 100)
{
//3)增添想要輸出的變量
DEBUG_VALUE(Debug1, Source[float2(i, j)].x)
DEBUG_VALUE(Debug2, Source[float2(i, j)].y)
}
}
一共3個步驟,這里就不再細說了。4. ?開始調試在PlayerSetting中增添CS_DEBUG宏,然后運行場景。由于ComputeShader不像普通的Shader一樣支持宏編譯和變體,因此ComputeShader中宏的實現采用文件替換的方式間接實現。每次修改完宏之后需要在編輯器模式下執行一次CSDebug中的任意方法才能真正生效(也可以在編輯器模式下調試一次即可)。點擊右上角的兩個按鈕進行測試,結果在Console中輸出五、倉庫地址歡迎大家Clone使用,提出改進意見。https://github.com/zouchunyi/ShaderDebug文末,再次感謝鄒春毅的分享,如果您有任何獨到的見解或者發現也歡迎聯系我們,一起探討。(QQ群:793972859)
也歡迎大家來積極參與U Sparkle開發者計劃,簡稱“US”,代表你和我,代表UWA和開發者在一起!
UWA GPM正式上線!
近期精彩回顧
【厚積薄發】GPU Skinning不生效問題
【萬象更新】GOT Online已支持Unreal最新版本
【學堂上新】DOTS深度研究之原理分析篇
【充電一刻】一分鐘,讀懂UWA性能報告
總結
以上是生活随笔為你收集整理的unity 如何获取到屏幕中间_Unity通用渲染管线Shader日志输出工具的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 联发科技嵌入式_英特尔联手联发科技重回移
- 下一篇: Win7频繁掉线是什么原因