Unity-URP

这篇文章主要记录学习URP的知识点。主要搞清楚这几件事:

  1. 什么是URP,以及URP能干啥。
  2. 相比于传统的Built-In,URP有哪些优势。与此同时在URP管线下需要注意哪些点。
  3. 最实在的,URP管线下的Shader编写规则。
  4. 关于自定义管线,这一块暂时先放下。

一、什么是URP

想要了解什么是URP,那么得先了解什么是SRP。
SRP全称为Scriptable Render Pipeline(可编程渲染管线/脚本化渲染管线),是Unity提供的新渲染系统,可以在Unity通过C#脚本调用一系列API配置和执行渲染命令的方式来实现渲染流程,SRP将这些命令传递给Unity底层图形体系结构,然后再将指令发送给图形API。
说白了就是我们可以用SRP的API来创建自定义的渲染管线,可用来调整渲染流程或修改或增加功能。

URP的全称为Universal Render Pipeline(通用渲染管线), 它是Unity官方基于SRP提供的功能而专门开发出的一个模板。它的前身是LWRP, 在2019.3开始改名为URP,它涵盖了范围广泛的不同平台,性能比内置管线要好,另外可以进行自定义,实现不同风格的渲染。

二、URP三大优势

1. 光照处理

URP是单Pass前向渲染管线,而内置管线是多Pass,可选前向渲染管线和延迟渲染管线。

所谓的前向渲染,就是在渲染物体受点光光照的时候,分别对每个点光对该物体产生的影响进行计算,最后将所有光的渲染结果相加得到最终物体的颜色。

内置渲染管线是多pass的,简单来讲,当场景中有多个光源时,会产生多个pass来渲染光照,在移动端如果有多个光源的话,将会产生巨大的性能消耗。

而URP使用单个pass,这个物体收到的所有光照都只会产生一个pass,所有的光源处理都可以在一个DrawCall中完成,对于性能的消耗就小得多。

2. SRP Bacher

批处理是性能优化的一大法宝,传统内置渲染管线中的批处理有着诸多限制。特别是动态批处理,这在SRP Batcher 中得到了彻底优化。

SRP Batcher 是一个底层渲染优化机制,可通过许多使用同一着色器变体的材质来加快场景中的 CPU 渲染速度。即使是不同的材质球,只要是用一个着色器变体的物体都可以批处理到一起。

3. 可扩展性

利用通用渲染管线提供的接口,技术美术可以轻松实现以往很难实现的效果

二、 URP和传统Built-In最大的不同

1. SRP Batcher

要想使用SRP Batcher,需要在URP Asset中勾选启用,并且要有对应支持的Shder。具体做法是需要将需缓存到CBuffer的变量写到CBUFFER_START(UnityPerMaterial)和CBUFFER_END之间:

CBUFFER_START(UnityPerMaterial)
    float4 _BaseMap_ST;
    float4 _BaseColor;
CBUFFER_END

2. 单Pass

URP管线不再支持多个渲染Pass。在URP中采用的是单Pass的光照计算,在一个Pass中遍历光源进行光照计算:
在这里插入图片描述
现在URP解决了这个问题。实现了一个单PASS的正向渲染。可以支持多盏动态光,但是全部动态灯光都会放在一个Pass里渲染,这样带来的问题是要限制灯光的数量,因为每次Draw Call去画的时候,传给GPU的参数是有限的。目前是支持1盏平行光,每个对象可能只能接受4个动态光。

在Built-In管线里面,我们可以使用多Pass实现描边、毛发等等效果,但是在URP中就不行了。一般来说 Render Feature可以解决大部分问题,可以通过它实现大部分多Pass的效果。

对于毛发这种Pass数量多的,就可以考虑使用多物体单Pass的方式实现:https://github.com/Acshy/FurShaderUnity

接下来看看内置渲染管线和URP各种情况下的光照处理实验对比

以下是分别在四种情况下对比所得出的结论

  • 无光源。 (没区别)
  • 一个平行光,无阴影。(没区别)
  • 一个平行光,一个点光源,无阴影。
    结论:内置渲染管线跟只有一个平行光时比起来Batches将近增加了一倍,而URP的Batches和SetPass calls跟一个平行光时一样,一点都没有增加。
    一个动态光,有阴影。
    结论:在阴影的处理方面URP性能比内置渲染管线好很多。

URP光照处理最终结论:

  • 性能上阴影处理方面比内置渲染管线好很多。
  • URP平行光基础上添加动态光没有带来额外的Batches和SetPass calls性能开销。

三、Shader

基于URP管线下不同的渲染策略,需要改变开发出对应的Shader包。
URP的Shader开发仍然使用的是ShaderLab的语法结构,开发语言和普通的Shader开发不一样在于它使用的是HLSL编程语言。

  • 渲染Pass:
    “LightMode”=“UniversalForward”
    最终输出到帧缓存中,只能有一个,也就是我们说的URP只支持一个Pass
  • 投影Pass:
    “LightMode”=“ShadowCaster”
    用于计算投影
  • 深度Pass:
    “LightMode”=”DepthOnly”
    如果管线设置了生成深度图,会通过这个Pass渲染
  • 其他Pass:
    专门用于烘焙的Pass,专门用于2D渲染的Pass等

四、SRP Batcher

Unity 2018引入了可编程渲染管线SRP,其中包含新的底层渲染循环SRP Batcher批处理器,它可以大幅提高CPU在渲染时的处理速度,根据场景内容的不同,提升效果为原来的1.2~4倍不等。

SRP Batcher:SRP Batcher 是一个底层渲染循环,可通过许多使用同一着色器变体的材质来加快场景中的 CPU 渲染速度。也就是说就算是不同的材质,只要使用同一个shader,都能批处理。

五、URP Shader 对比

URP Shader
“RenderPipeline” = “UniversalPipeline”
名称为 RenderPipeline 的子着色器标签会向 Unity 显示,此子着色器应与哪个渲染管线搭配使用。UniversalPipeline 的值表明 Unity 应将此子着色器与 URP 搭配使用。

目前不建议使用 CGPROGRAM / ENDCG 宏。使用这些宏意味着使用 UnityCG.cginc。如此混合 SRP 和内置渲染管线着色器库,会导致很多问题。

简单URP模板:

Shader "URPCustom/Unlit"
{
    Properties
    {
        _MainTex ("Texture", 2D) = "white" {}
        _BaseColor("Base Color",Color)=(1,1,1,1)
    }
    SubShader  //子着色器1,针对显卡A的着色器,这里是ShaderLab着色器的主要内容
    {
        Tags
        {
            "RenderPipeline"="UniversalPipeline"//这是一个URP Shader!
            "Queue"="Geometry"
            "RenderType"="Opaque"
        }
        HLSLINCLUDE
         //CG中核心代码库 #include "UnityCG.cginc"
        #include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Core.hlsl"
       
        //除了贴图外,要暴露在Inspector面板上的变量都需要缓存到CBUFFER中
        CBUFFER_START(UnityPerMaterial)
            float4 _MainTex_ST;
            half4 _BaseColor;
        CBUFFER_END
        ENDHLSL

        Pass
        {
            Tags{"LightMode"="UniversalForward"}//这个Pass最终会输出到颜色缓冲里

            HLSLPROGRAM //CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag

            TEXTURE2D(_MainTex);//在CG中会写成sampler2D _MainTex;
            SAMPLER(sampler_MainTex);

            struct Attributes//这就是a2v
            {
                float4 positionOS : POSITION;
                float2 uv : TEXCOORD;
            };
            struct Varings//这就是v2f
            {
                float4 positionCS : SV_POSITION;
                float2 uv : TEXCOORD;
            };
                        
            Varings vert(Attributes IN)
            {
                Varings OUT;
                //在CG里面,我们这样转换空间坐标 o.vertex = UnityObjectToClipPos(v.vertex);
                VertexPositionInputs positionInputs = GetVertexPositionInputs(IN.positionOS.xyz);
                OUT.positionCS = positionInputs.positionCS;

                OUT.uv=TRANSFORM_TEX(IN.uv,_MainTex);
                return OUT;
            }

            float4 frag(Varings IN):SV_Target
            {
                //在CG里,我们这样对贴图采样 fixed4 col = tex2D(_MainTex, i.uv);
                half4 baseMap = SAMPLE_TEXTURE2D(_MainTex, sampler_MainTex, IN.uv);                
                return baseMap * _BaseColor;
            }
            ENDHLSL  //ENDCG          
        }
    }
}

一个基础的光照模型代码:

Shader "zxz/URPSimpleLit"
{
    Properties
    {
        _BaseMap ("Base Texture",2D) = "white"{}
        _BaseColor("Base Color",Color) = (1,1,1,1)
        _SpecularColor("SpecularColor",Color)=(1,1,1,1)
        _Smoothness("Smoothness",float)=10
        _Cutoff("Cutoff",float)=0.5
    }
    SubShader
    {
        Tags
        {
            "RenderPipeline"="UniversalPipeline"
            "Queue"="Geometry"
            "RenderType"="Opaque"
        }

        HLSLINCLUDE
        #include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Core.hlsl"

        CBUFFER_START(UnityPerMaterial)
            float4 _BaseMap_ST;
            float4 _BaseColor;
            float4 _SpecularColor;
            float _Smoothness;
            float _Cutoff;
        CBUFFER_END
        
        ENDHLSL
    
        Pass
        {
            Name "URPSimpleLit"  
            Tags{"LightMode"="UniversalForward"}

            HLSLPROGRAM            
            #include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Lighting.hlsl"

            #pragma vertex vert
            #pragma fragment frag

            struct Attributes
            {
                float4 positionOS : POSITION;
                float4 normalOS : NORMAL;
                float2 uv : TEXCOORD0;
            };
            struct Varings
            {
                float4 positionCS : SV_POSITION;
                float2 uv : TEXCOORD0;
                float3 positionWS : TEXCOORD1;
                float3 viewDirWS : TEXCOORD2;
                float3 normalWS : TEXCOORD3;
            };
            
            TEXTURE2D(_BaseMap);
            SAMPLER(sampler_BaseMap);    

            Varings vert(Attributes IN)
            {
                Varings OUT = (Varings)0;
                VertexPositionInputs positionInputs = GetVertexPositionInputs(IN.positionOS.xyz);
                VertexNormalInputs normalInputs = GetVertexNormalInputs(IN.normalOS.xyz);
                OUT.positionCS = positionInputs.positionCS;
                OUT.positionWS = positionInputs.positionWS;
                OUT.viewDirWS = GetCameraPositionWS() - positionInputs.positionWS;
                OUT.normalWS = normalInputs.normalWS;
                OUT.uv=TRANSFORM_TEX(IN.uv,_BaseMap);
                return OUT;
            }
            
            float4 frag(Varings IN):SV_Target
            {                
                half4 baseMap = SAMPLE_TEXTURE2D(_BaseMap, sampler_BaseMap, IN.uv);      
                //计算主光
                Light light = GetMainLight();
                half3 diffuse = LightingLambert(light.color, light.direction, IN.normalWS);
                half3 specular = LightingSpecular(light.color, light.direction, normalize(IN.normalWS), normalize(IN.viewDirWS), _SpecularColor, _Smoothness);
                //计算附加光照
                uint pixelLightCount = GetAdditionalLightsCount();
                for (uint lightIndex = 0; lightIndex < pixelLightCount; ++lightIndex)
                {
                    Light light = GetAdditionalLight(lightIndex, IN.positionWS);
                    diffuse += LightingLambert(light.color, light.direction, IN.normalWS);
                    specular += LightingSpecular(light.color, light.direction, normalize(IN.normalWS), normalize(IN.viewDirWS), _SpecularColor, _Smoothness);
                }

                half3 color=baseMap.xyz*diffuse*_BaseColor+specular;
                clip(baseMap.a-_Cutoff);
                return float4(color,1);
            }
            ENDHLSL            
        }
        
          Pass
        {
            Name "ShadowCaster"
            Tags{"LightMode" = "ShadowCaster"}

            ZWrite On
            ZTest LEqual
            Cull[_Cull]

            HLSLPROGRAM
            // Required to compile gles 2.0 with standard srp library
            #pragma prefer_hlslcc gles
            #pragma exclude_renderers d3d11_9x
            #pragma target 2.0

            // -------------------------------------
            // Material Keywords
            #pragma shader_feature _ALPHATEST_ON
            #pragma shader_feature _GLOSSINESS_FROM_BASE_ALPHA

            //--------------------------------------
            // GPU Instancing
            #pragma multi_compile_instancing

            #pragma vertex ShadowPassVertex
            #pragma fragment ShadowPassFragment


            //由于这段代码中声明了自己的CBUFFER,与我们需要的不一样,所以我们注释掉他
            //#include "Packages/com.unity.render-pipelines.universal/Shaders/SimpleLitInput.hlsl"
            //它还引入了下面2个hlsl文件
            #include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Core.hlsl"
            #include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/SurfaceInput.hlsl"
            #include "Packages/com.unity.render-pipelines.universal/Shaders/ShadowCasterPass.hlsl"
            ENDHLSL
        }

    }
}

URP Shaders默认提供PBR光照(Lit)、简单兰伯特光照(SimpleLit)和无光照(Unlit)等常用Shader

处于性能考虑,URP默认参数中,只有平行光进行逐像素计算,其他光进行顶点计算(我们可以在URP Asset中更改这一设置)。

阴影:
一般在Buit-In管线里,我们只需要最后FallBack返回到系统的Diffuse Shader,管线就会去里面找到他处理阴影的Pass。但是在URP中,一个Shader中的所有Pass需要有一致的CBuffer,否则便会打破SRP Batcher,影响效率。

而系统默认SimpleLit的Shader中的CBuffer内容和我的写的并不一致,所以我们需要把它阴影处理的Pass复制一份,并且删掉其中引用的SimpleLitInput.hlsl(相关CBuffer的声明在这里面)。

CBUFFER_START和CBUFFER_END,对于变量是单个材质独有的时候建议放在这里面,以提高性能。CBUFFER(常量缓冲区)的空间较小,不适合存放纹理贴图这种大量数据的数据类型。HLSL贴图的采样函数和采样器函数,TEXTURE2D (_MainTex)和SAMPLER(sampler_MainTex)。

Shader迁移具体细节

1. LightMode

LightMode里的值改成UniversalForward。如下:

"LightMode"="UniversalForward"

删掉以下内容:

#define UNITY_PASS_FORWARDBASE
#pragma multi_compile_fwdbase

可以使用的灯光模式,有三种:

“UniversalForward”
“LightweightForward”
“SRPDefaultUnlit"

2. 一些CGInclude常用的接口

1、把CGPROGRAM改成HLSLPROGRAM,
把ENDCG改成ENDHLSL。
把CGINCLUDE改成HLSLINCLUDE。

2、把#include “UnityCG.cginc”
改成

#include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Core.hlsl" 
#include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Lighting.hlsl"

3、o.pos = UnityObjectToClipPos( v.vertex );
改成

VertexPositionInputs vertexInput = GetVertexPositionInputs(v.vertex.xyz);
o.pos = vertexInput.positionCS;

4、o.normalDir = UnityObjectToWorldNormal(v.normal);
改成

VertexNormalInputs vertexNormalInput = GetVertexNormalInputs(v.normal);
o.normalDir = vertexNormalInput.normalWS;

5、灯光方向和灯光颜色
_WorldSpaceLightPos0.xyz改成
在vert函数里:

o.shadowCoord = GetShadowCoord(vertexInput);

frag函数里:

Light mainLight = mainLight = GetMainLight(i.shadowCoord);
float3 lightDirection = mainLight.direction;

_LightColor0 改成 mainLight.color

6、把fixed 定义的变量都改成float 的。
比如 fixed4 (finalColor,1)改成float4 (finalColor,1)

7、阴影:https://blog.csdn.net/zakerhero/article/details/106274331?spm=1001.2014.3001.5501
8、 如何获取深度图:https://blog.csdn.net/zakerhero/article/details/115693888?spm=1001.2014.3001.5501
9、_GrabTexture
URP管线下,截图不再使用GrabTexture,而是使用_CameraOpaqueTexture或_CameraColorTexture
删掉GrabPass{ }
sampler2D _GrabTexture; 改成 SAMPLER(_CameraOpaqueTexture);
同时,还得开启URP管线设置的Opaque Texture。
具体参考:Unity URP管线如何截屏,及热扰动(热扭曲)效果的实现
10、 如何使用多Pass:如何在Unity的URP下使用多pass(multi pass)

NN 自定义管线资料:

  1. https://zhuanlan.zhihu.com/p/364701443
  2. https://zhuanlan.zhihu.com/p/451203440

最重要的资料:官方文档

  1. Unity URP/SRP 渲染管线浅入深出:https://zhuanlan.zhihu.com/p/353687806

  2. 系列1:https://zhuanlan.zhihu.com/p/336428407
    系列2:https://zhuanlan.zhihu.com/p/336508199
    系列3:https://zhuanlan.zhihu.com/p/336670858

  3. https://zhuanlan.zhihu.com/p/391437161

  4. 内置管线Shade升级URP详细手册

  5. qq

  6. ee

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值