【Unity URP】Rendering Debugger和可视化MipMap方案

写在前面

最近开始学习Unity性能优化,是结合了《Unity游戏优化》这本书和教程《Unity性能优化》第叁节——静态资源优化(3)——纹理的基础概念一起学习。在学习纹理优化部分时候遇到了问题,固定管线下Unity的Scene窗口有一个可视化Mipmap的渲染模式:

而这批Miscellaneous模式的选项在URP下相同位置没了:

又比较需要这个便捷的查看方法!于是搜一下想看看有没有出一些插件之类的,突然搜到了大概20年也有小伙伴提出这个问题:

Missing Scene view Draw Modes (Mipmaps, Overdraw, etc.) - Unity Forum

底下Unity技术人员回复了,大概意思就是会加入Rendering Debugger来实现之前固定管线下的D

rawMode的一些功能: 

于是就发现了Rnedering Debugger功能,What's new in URP 12 (Unity 2021.2) | Universal RP | 12.0.0

之前URP下做东西的时候完全没有注意和使用过!一起来简单看看:

Rendering Debugger

非常方便了!比如Material的一些查看,可以单独看albedo的效果:

比如OverDraw:

曾经固定管线下的级联阴影、Alpha通道等的一键预览都有了!但是还是没有Mipmap。

其他版本的URP该怎么办?

小插曲,在Rendering Debugger没出来前,这位大佬Scene View Debug Modes in the Unity URP — John Austin甚至自己做了个插件:

虽然后续URP已经提供了rendering debugger,但是这个把shader添加进debug里的整个框架我们是可以参考过来的。 

讨论Build-in下Scene视图的Mipmaps

其实Mipmap可视化已经有人总结过了:Re0-TA成长笔记 01关于Mipmap问题

而且asset store有免费的类似插件:Colored Mipmap Texture - Visualized Checker

还有其他的方法,要么方法我看不太懂,,要么颜色太多我感觉也不需要那么多颜色,只是想把Mipmap视图作为优化纹理的小工具。我希望在URP下也能实现固定管线那种简单的蓝/红色的Mipmap显示效果,实现方法尽量的简单一点,固定管线下就这样简单:

场景很简陋(之前学习入门精要的build-in项目~),也算体现了大概吧,当纹理大小刚刚好的时候展示的就是纹理的样子,纹理太小了精度不够就会是蓝色,纹理太大精度过大就会是红色!

因为比较好奇,build-in下的Scene视图的Mipmap到底是怎么运行的?物体shader里的贴图一定不只有一张主纹理,他的法线纹理、金属度等纹理都会参与Mipmap,这个大小是综合所有贴图判断的吗?还是只关注主要的albedo贴图?

我们测试一下,拿之前学习法线映射的shader为例,shader有两张纹理,albedo和normal,大小均为2048,显然都过大了,所以Mipmap视图下整体是红色:

现在把albedo贴图改为32,变成蓝色了!:

如果我们只改normal的大小为32,albedo那张不变呢?没有变化!还是红色:

这就初步说明,固定管线下这个Mipmap level判断依据,是根据shader中的MainTex(主纹理)来判断的,而且物体shader的SubShader的RenderType一定要有,才能参与这个Scene视图下Mipmap的红蓝判断:

还需要确定一点,就是这个Mipmap渲染的shader到底怎么获取纹理的?我们再测试一下,如果shader里如果主纹理命名不是默认的_MainTex,而是_BaseTex的话:

场景中之前的那个物体又变蓝了!

好了,到这里我们可以初步推断:

Build-in管线下,Scene View里的Mipmaps可视化,只针对shader主纹理命名为_MainTex的物体有效,且只关注_MainTex一张贴图的大小是否合适,其余的法线纹理等其他贴图不受关注

探讨URP下Mipmap可视化方案

初步猜测:如果我只想要简单的红蓝检测效果,且实践方法尽量简单,我必须要在URP下能够访问所有项目的主纹理,然后单独做一个ViewDebug_Mipmaps的shader,挂在View视图窗口。

紧接着,找资料突然就看到了11年的时候就有人提出了一种可视化Mipmap方案:

A way to visualize mip levels · Aras' website (aras-p.info)

有意思的是在查看Build-in的Debug.hlsl文件时,发现Unity的可视化方案也是参考了之前的那个文章(Unity的Debug.hlsl文件):

上面那个人的主要shader如下:

struct v2f {
    float4 pos : SV_POSITION;
    float2 uv : TEXCOORD0;
    float2 mipuv : TEXCOORD1;
};
float2 mainTextureSize;
v2f vert (float4 vertex : POSITION, float2 uv : TEXCOORD0)
{
    v2f o;
    o.pos = mul (matrix_mvp, vertex);
    o.uv = uv;
    o.mipuv = uv * mainTextureSize / 8.0;
    return o;
}
half4 frag (v2f i) : COLOR0
{
    half4 col = tex2D (mainTexture, i.uv);
    half4 mip = tex2D (mipColorsTexture, i.mipuv);
    half4 res;
    res.rgb = lerp (col.rgb, mip.rgb, mip.a);
    res.a = col.a;
    return res;    
}

OHHHH,他的实现思路差不多就是我的意思!shader里的mainTextureSize,实际上就是URP下shader里我们能默认获取得到的BaseMap_TextureSize,其余的操作只是为了能根据距离动态判断纹理大小是否合适,根据判断结果给上红蓝色。

那我们就开始吧!首先是确定,怎么获取纹理?想起来了,可以从URP下自带的Rendering Debugger入手:

看看Rendering Debugger的逻辑

简单一点,我们打开URP的文件夹,搜索Debug

成功定位到Debugging3d.hlsl:

Debugging3D.hlsl

具体内容就不一句一句来了,单独挑一个,Material的:

你会发现!原来Debugger的逻辑并不是重新自己有一套shader,而是直接拿SurfaceData的东西用,输出想要的项就行了。

SurfaceData

之前扒URP的PBR Shader的时候就已经见过他了,SurfaceData是在SurfaceData.hlsl定义的结构体:

然后在LitInput.hlsl中定义了一个InitializeStandardLitSurfaceData()函数,去初始化SurfaceData:

尝试输出Surface.albedo

刚好我作为联系我有想要优化的项目,那就直接在项目中写个shader看看能不能成功提取SurfaceData.albedo吧!写个shader:

Shader "Debug/Debug Albedo"
{
    SubShader
    {
        Tags{"RenderType" = "Opaque" "RenderPipeline" = "UniversalPipeline"}

        Pass
        {
            HLSLPROGRAM
            
            #pragma vertex LitPassVertex
            #pragma fragment frag

            #include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Core.hlsl"
            #include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/SurfaceData.hlsl"
            #include "Packages/com.unity.render-pipelines.universal/Shaders/LitInput.hlsl"
            #include "Packages/com.unity.render-pipelines.universal/Shaders/LitForwardPass.hlsl"

            float4 frag(Varyings input) : SV_TARGET {

                UNITY_SETUP_INSTANCE_ID(input);
                UNITY_SETUP_STEREO_EYE_INDEX_POST_VERTEX(input);

                #if defined(_PARALLAXMAP)
                #if defined(REQUIRES_TANGENT_SPACE_VIEW_DIR_INTERPOLATOR)
                    half3 viewDirTS = input.viewDirTS;
                #else
                    half3 viewDirTS = GetViewDirectionTangentSpace(input.tangentWS, input.normalWS, input.viewDirWS);
                #endif
                    ApplyPerPixelDisplacement(viewDirTS, input.uv);
                #endif

                SurfaceData surfaceData;
                InitializeStandardLitSurfaceData(input.uv, surfaceData);
                
                return float4(surfaceData.albedo, 1);
                
            }
          
            ENDHLSL
        }
    }

}

参考上面自己Debug的框架,给他做成开关放在Scene下的DrawMode里:

结果,,,Scene是全黑的,并没有单独展示Albedo:

哪里出错了?想起来,不会是因为BaseMap命名和采样的问题吧。为了测试我的想法是否正确,场景中拖个正方体,给他一个简单的shader,shader严格按照BaseMap来:

同样的DrawMode选择Albedo,居然出现了!

果然,验证了我的猜想。检查一下项目里的shader,发现shader都是拿ASE实现的,,,转化的命名什么的都有点混乱,,,

再拿个之前搭的模型为例吧:

同样的Shader:

成功了!调出了albedo,啊,可视化的途径我们算是找到了,接下来就是实现了!

URP下实现Mipmap可视化

给texture每个mip层赋值

这其实是最关键的一步——我们需要有图可采样成颜色。

Tutorial: Creating and modifying custom mipmaps - Texture Tools - NVIDIA Developer Forums

我尝试过Unity创建纹理,也尝试过PS、GIMP生成dds图,但是这俩好像只能打开dds却不能编辑每一个Mip层的颜色!!!

于是另寻他路:dds的目的是给每个mipLevel给一个颜色值,那我们直接获取mipmap的level,根据这个level值输出对应的颜色不就好了,也就是shader里实现这行采样代码:

half4 mip = tex2D (mipColorsTexture, i.mipuv);

这里注意一下,tex2D的本质是什么?这是Unity提供的一个采样器,输入需要采样的纹理及对应的uv值,会进行采样,并根据纹理的设置生成mipmap。这里要关注另一个采样函数:tex2Dlod,这个是可以根据传入uv的.w分量信息指定mipmap层的。随便搜一下tex2Dlod的原理,就能明白了,比如这篇文章:MipMap的LOD实现原理 - 知乎里写的:

float mipmapLevel(float2 uv, float2 textureSize) 
{    
 float dx = ddx(uv * textureSize.x);    
 float dy = ddy(uv * textureSize.y);   
 float d = max(dot(dx, dx), dot(dy, dy));
 return 0.5 * log2(d);//0.5是技巧,本来是d的平方。
} 

我直接在fragment shader里计算:

// 直接自己计算lod,参考tex2DLod
                float2 mipUV = o.uv * o.mainTextureSize/ 8; // 参考文章的方案
                float dx = ddx(mipUV); // 
                float dy = ddy(mipUV);
                float px = 32 * dx;
                float py = 32 * dy;
                float lod = 0.5 * log2(max(dot(px, px), dot(py, py)));

再写个函数,根据传入的lod值lerp我的颜色,颜色取值来自上面的那篇文章:

// 根据当前纹理的Mip层值返回给定颜色
            float4 GetCurMipColorByManualColor(float mipLevel)
            {

                if(mipLevel==0) // 纹理压根没有开启mipmap
                {
                    return real4(0.0,0.0,1.0,1); // 给.a为0,即baseMap
                }
                else
                {
                    if(mipLevel < 1 ) // 代表着纹理太小了,给个蓝色
                    {
                        return lerp(real4(0.0,0.0,1.0,1),real4(0.0,0.0,1.0,0.8),mipLevel);
                    }
                    else if (mipLevel <2)
                    {
                        return lerp(real4(0.0,0.0,1.0,0.8),real4(1,1,1,0), mipLevel-1);
                    }
                    else if(mipLevel <3) // mip的正正好,于是.a值给0,意味着纹理此时恰到好处
                    {
                        return lerp(real4(1,1,1,0),real4(1.0,0.7,0.0,0.2),mipLevel-2);
                    }
                    else if(mipLevel <4)
                    {
                        return lerp(real4(1.0,0.7,0.0,0.2),real4(1.0,0.3,0.0,0.6),mipLevel-3);
                    }
                    else if(mipLevel <5)  // mip太多了吧,意味着纹理太大了!因此给个更红的颜色
                    {
                        return lerp(real4(1.0,0.3,0.0,0.6),real4(1.0,0.0,0.0,0.8),mipLevel-4);
                    }
                    else
                    {
                        return real4(1.0,0.0,0.0,0.8); // mip了超大,直接给正红色,.a直接点满,完全为debug的颜色
                    }
                }
            }

接着fragment shader里:

float3 baseColor = surfaceData.albedo;
                float4 debugColor = GetCurMipColorByManualColor(lod);
                float4 res;
                
                res.rgb =lerp(baseColor.rgb, debugColor.rgb, debugColor.a); //由debugColor.a控制插值
                res.a = 1;
                return res;

就是说,到这里就已经实现了!具体怎么把他搬进Scene视图下,前面列举了一个URP下别人实现Albedo可视化的框架,我是直接拿过来用了,他给了源码,所以脚本这里就不过多的介绍。

效果展示

放上一个跟固定管线里的相同模型、贴图的场景的mipmap可视化对比,左边是固定管线下,右边是我实现的:

可以看出在显示上是有一定偏差的,因为Unity内部到底是怎么做mipmap可视化,颜色如何规定的?颜色和mip层的关系是怎样的?没在源码中找到太多的依据。

但基本上算是实现了!虽然shader用了很多个if,但是只是一个debug环节,不用太考虑效率问题。总的来说这个方案学习过程中自己使用起来还是足够的!

文章写的很潦草,涉及到的内容太扩散了,其实也是我实现过程中的心路历程,希望看到这里的你能看懂!!!!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/16022.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

PMP/高项 05-项目进度管理

项目进度管理 概念 项目进度管理&#xff08;Schedule Management) 项目进度管理又叫项目工期管理&#xff08;Duration Management)或项目的时间管理(Time Management) 是一种为管理项目按时完成项目所需的各个过程 进度管理过程 规划进度管理 定义活动 排列活动顺序 估算活…

【LeetCode】 309.最佳买卖股票时机含冷冻期

309.最佳买卖股票时机含冷冻期&#xff08;中等&#xff09; 思路 状态定义 一、很容易想到四种状态&#xff1a; a.今天买入&#xff1b;b.今天卖出&#xff1b;c.昨天卖出&#xff0c;今天处于冷冻期&#xff0c;无法进行操作&#xff1b;d.今天不操作&#xff0c;处于持有…

太酷了,库昊

昨天晚上凌晨3点30&#xff0c;勇士和国王的第7场比赛开打。 在上一局在勇士主场干翻勇士后&#xff0c;国王队的信心倍增&#xff0c;他们用自己的节奏一次次击溃勇士&#xff0c;特别是今天的前两节&#xff0c;国王能能够回应勇士的进球&#xff0c;防守也更有侵略性。今天不…

图扑数字孪生助力智慧冷链园区实现大数据实时监控

前言 近年来&#xff0c;业界学者及企业就智慧冷链物流展开深入研究&#xff0c;2010 年 IBM 发布的《智慧的未来供应链》研究报告中提出智慧供应链概念&#xff0c;并由此延伸出智慧物流概念&#xff0c;即智慧物流是以信息化为依托并广泛应用物联网、人工智能、大数据、云计…

【2023 年第十三届 MathorCup 高校数学建模挑战赛】D 题 航空安全风险分析和飞行技术评估问题 27页论文及代码

【2023 年第十三届 MathorCup 高校数学建模挑战赛】D 题 航空安全风险分析和飞行技术评估问题 27页论文及代码 1 题目 D 题 航空安全风险分析和飞行技术评估问题 飞行安全是民航运输业赖以生存和发展的基础。随着我国民航业的快速发展&#xff0c;针对飞行安全问题的研究显得…

巧用千寻位置GNSS软件| 桥台锥坡放样操作技巧

桥台锥坡放样是针对道路施工中&#xff0c;路桥结合部桥台圆锥形斜坡面进行放样设计的专用程序。本期将给大家介绍如何使用千寻位置GNSS软件实现快速完成桥台锥坡放样。 点击【测量】->【桥台锥坡放样】&#xff0c;从线路库中选择桥台经过的线路或是单独增加桥台 锥坡放样&…

QML动画分组(Grouped Animations)

通常使用的动画比一个属性的动画更加复杂。例如你想同时运行几个动画并把他们连接起来&#xff0c;或者在一个一个的运行&#xff0c;或者在两个动画之间执行一个脚本。动画分组提供了很好的帮助&#xff0c;作为命名建议可以叫做一组动画。有两种方法来分组&#xff1a;平行与…

【无功功率控制】连接到无限电网的小型风电场的无功功率控制(Simulink)

&#x1f4a5;&#x1f4a5;&#x1f49e;&#x1f49e;欢迎来到本博客❤️❤️&#x1f4a5;&#x1f4a5; &#x1f3c6;博主优势&#xff1a;&#x1f31e;&#x1f31e;&#x1f31e;博客内容尽量做到思维缜密&#xff0c;逻辑清晰&#xff0c;为了方便读者。 ⛳️座右铭&a…

jvm之字节码

写在前面 java字节码由单字节的指令(也叫做操作码)组成&#xff0c;但一个 byte 最多能够存储 256 个指令&#xff0c;够用吗&#xff1f;截止到目前是够的&#xff0c;因为指令的个数是200多一点&#xff0c;指令分为如下四类&#xff1a; 1&#xff1a;栈操作指令&#xff…

SmartEngine流程引擎之Custom模式

目录 一、为什么选用SmartEngine 二、各类流程引擎框架简单对比 1、流程设计器推荐 2、什么是BPMN 流程定义解释说明 三、SmartEngine之Custom实操 1、引入依赖 2、典型的初始化代码如下 3、节点如何流转以及流程实例存储问题 4、定义Delegation 关键类 一、为什么选用…

Java 基础入门篇(四)—— 方法的重载与参数传递机制

文章目录 一、方法的定义二、方法的参数传递机制 ★2.1 基本类型的参数传递2.2 引用类型的参数传递 三、方法重载 一、方法的定义 方法的作用&#xff1a;封装一段代码的语法结构&#xff0c;可以被重复调用&#xff0c;以此提高代码的复用性&#xff0c;提高开发效率&#xf…

ChatGPT Plus价格太贵,可以约上三五知己一起上车体验一下,这个项目就能帮到你

对于想体验ChatGPT PLus的小伙伴&#xff0c;可能觉得自己一个人一个月花费20美元&#xff0c;相对于人民币每月137多&#xff0c;确实是一个不少的开支&#xff0c;如果&#xff0c;几个人合作一个账号&#xff0c;这样负担就减少了。刚好&#xff0c;最近逛github发现刚好有一…

小记Java调用C++开发的动态链接库(DLL)

一、背景 五一快乐吖&#xff01;死肥宅正趁着五一这段时间&#xff0c;努力提升自己&#xff01; 最近使用Java拦截Windows系统中一些默认事件时&#xff0c;发现了一些瓶颈。 我用Java操作浏览器、用Java最小化其他应用窗口&#xff0c;但是我发现这个操作&#xff0c;他都…

几十个简要的游戏案例分析

文章目录 一、 介绍二、 影响游戏体验的因素三、 游戏能爆火的因素1.影响游戏爆火因素的排名2.玩游戏的两种经典心理3.经典案例分析Qq农场植物大战僵尸水果忍者召唤神龙羊了个羊 4.游戏公司可借鉴的经验 四、 几十款游戏的多方面分析FC红白游戏机十二人街霸热血高校系列魂斗罗系…

趣说数据结构(练习1) —— 顺序表/链表力扣刷题

练习 1 —— 顺序表/链表力扣刷题 1. 合并两个有序链表 力扣题目地址&#xff1a;https://leetcode.cn/problems/merge-two-sorted-lists/ 问题描述&#xff1a;将两个升序链表合并为一个新的 升序 链表并返回。新链表是通过拼接给定的两个链表的所有节点组成的。 示例&#x…

Java——Java面向对象

该系列博文会告诉你如何从入门到进阶&#xff0c;一步步地学习Java基础知识&#xff0c;并上手进行实战&#xff0c;接着了解每个Java知识点背后的实现原理&#xff0c;更完整地了解整个Java技术体系&#xff0c;形成自己的知识框架。 概述&#xff1a; Java是面向对象的程序…

@Autowired与@Resource原理知识点详解

文章目录 前言springIOC依赖注入的三种方式属性注入&#xff08;字段注入&#xff09;构造方法注入setter注入用哪个&#xff1f; Autowired实现原理 Resource实现原理结论 Autowired与Resource的不同来源不同参数不同使用不同装配顺序 前言 现在spring可以说是一统天下了&…

【Unity-UGUI控件全面解析】| Canvas 画布组件详解

🎬【Unity-UGUI控件全面解析】| Canvas 画布组件详解一、组件介绍1.1 绘制元素的顺序二、组件属性面板2.1 Canvas :画布,控制UI的渲染模式2.2 Canvas Scaler:画布缩放器,控制UI画布的放大缩放的比例2.3 Graphic Raycaster:图形射线投射器,控制是否让UI响应射线点击三、…

【干货分享】一文说透分布式一致性协议(上)

本文首发自「慕课网」&#xff0c;想了解更多IT干货内容&#xff0c;程序员圈内热闻&#xff0c;欢迎关注"慕课网"&#xff01; 作者&#xff1a;大熊老师 | 慕课网讲师 在常见的分布式系统中&#xff0c;总会发生诸如机器宕机或网络异常&#xff08;包括消息的延迟…

数据备份系列:Rsync 备份详解(一)

一、Rsync 简介 1.1 Rsync 是一个远程增量文件备份软件工具 1.2 Rsync 的特性 支持拷贝特殊文件&#xff0c;如连接文件、设备等。可以有排除指定文件或目录同步的功能&#xff0c;相当于打包命令 tar 的排除功能。可以做到保持原文件或目录的权限、时间、软硬链接、属主、组…