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日志输出工具相关推荐
- unity 如何获取到屏幕中间_【Unity】屏幕空间位置变换到世界空间位置的方法
屏幕空间像素的位置,是一个二维的浮点数,而世界空间的位置,则是三维的浮点数.实现的基本思路很简单,是世界空间位置变换到屏幕空间位置的逆过程,只是稍微有些区别.如果对图形渲染管线中的坐标变换没有弄清楚, ...
- unity 烘焙参数 设置_Unity通用渲染管线(URP)系列(九)——点光源和聚光灯
200+篇教程总入口,欢迎收藏: 放牛的星星:[教程汇总+持续更新]Unity从入门到入坟--收藏这一篇就够了zhuanlan.zhihu.com 本文重点内容: 1.支持更多类型的灯光 2.包含实 ...
- unity 如何获取到屏幕中间_请问如何获得场景的中心点在屏幕上的坐标?
该楼层疑似违规已被系统折叠 隐藏此楼查看此楼 using UnityEngine; using System.Collections; public class click : MonoBehavio ...
- extjs 渲染之前的方法_Unity通用渲染管线(URP)系列(十一)——后处理(Bloom)...
200+篇教程总入口,欢迎收藏: 放牛的星星:[教程汇总+持续更新]Unity从入门到入坟--收藏这一篇就够了zhuanlan.zhihu.com 本文重点内容: 1.创建简单的post-FX栈 2 ...
- Unity Android平台下插件/SDK开发通用流程
本文主要面向对Android开发不甚了解的Unity开发者,介绍了基于最新的Android Studio的标准Android开发环境与项目结构的配置流程,在此基础上,开发者可以快速的进行SDK的接入与 ...
- unity 自动将文件上传_unity如何存储文件夹
玩转Unity资源,对象和序列化(上) 文章目录[点击展开](?)[+] 这是一系列文章中的第二章,覆盖了Unity5的Assets,Resources和资源管理 本文将从Unity编辑器和运行时两个 ...
- Android适配 获取手机屏幕的分辨率
如何将一个应用程序适配在不同的手机上,虽然这不算是一个技术问题,但是对于刚刚做屏幕的开发人员来说,还真不是一件多么简单的事情. 首先:你需要在AndroidManifest.xml文件的<man ...
- Unity中的UGUI屏幕适配
本文分享Unity中的UGUI屏幕适配 屏幕适配一直是一个老生常谈的问题, 虽然只是项目一开始的时候会用到, 但是还是有很多东西需要学习和了解, 今天给大家分享下一些个人的学习和总结. 各种坐标 屏幕 ...
- Unity - 通用渲染管线(URP)1.渲染、后处理
这是一篇详细讲解URP的文章,涉及具体的使用和原理,翻译自Unity官方的文档. 本文由 祝你万事顺利 出品,转载请注明出处. 简介 URP是一种预置的可编程渲染管线.可以实现快速的渲染而不需要sha ...
最新文章
- Python通过一个网页地址获得网页标题Title
- Qt 2D绘图功能简单总结
- 重磅发布|新一代云原生数据仓库AnalyticDB「SQL智能诊断」功能详解
- 【Android】 Android中Log调试详解
- 脉冲神经网络基础知识,SpikeProp
- 亚马逊最大无人售货超市开张,云端结账随拿随走
- python转义字符_python转义字符
- Java实现对称密钥算法
- 信庭嵌入式工作室-ARM应用技术之体系结构应用(中)
- 如何在 Windows 中删除运行历史记录
- Backstepping反步法控制四旋翼无人机(一)
- C stdlib.h
- 使用 阿里云 播放器播放 .flv 和 hls(.m3u8) 格式的视频流
- 阿蒙森 斯科特_斯科特的单元测试定律
- 一入职就遇上Mysql亿级优化,方案都改了5遍
- 阿里云应用实时监控服务ARMS接入
- Vue3-uniapp上传图片到七牛云(身份证信息)
- Activity工作流入门篇
- 【图像识别】基于BP神经网络实现手写体大写字母识别附matlab代码
- rectangle函数的使用
热门文章
- java获取实体类的属性和值
- x86的32位汇编快速入门
- C++中如何定义动态数组
- base64链接转为地址php,php将图片链接转换为base64编码文件流
- 创建QT项目只有一个pro文件
- 2、AD工程创建步骤
- origin数据平滑_研发工程师必备:20条实用origin技能,让作图效率飞起来
- jcenter和maven下载失败Can't connect to SOCKS proxy:Connection refused: connect
- Android开发之添加QQ群的方法(官方代码)
- 缓存目录的区别getCacheDir()、getFilesDir()、getExternalFilesDir()、getExternalCacheDir()的作用