深入URP之Shader篇5: SimpleLit Shader分析(1)

SimpleLit.shader

本篇开始分析simple lit shader。我们通过分析unlit shader了解了URP shader的结构,以及一些基础功能。而simple lit shader包含了更多的内容。本篇开始,就不会特别仔细的逐行去分析shader文件了,只会针对pass的功能中比较有意思且之前没看到过的内容进行分析。

ForwardLit

名字为ForwardLit的pass,它的Tags为:

Tags {
    
     "LightMode" = "UniversalForward" }

根据URP的文档:

UniversalForward : The Pass renders object geometry and evaluates all light contributions. URP uses this tag value in the Forward Rendering Path.

说明这是一个前向渲染路径的pass,虽然URP(至少是10.8.1版本)还没有正式支持Deferred Rendering,但URP已经为它打了很多基础,所以会特意标明这个pass是前向渲染还是延迟渲染,如果是延迟渲染则会使用LightModeUniversalGBuffer,SimpleLit shader也有这个pass,但是暂时不会研究它。

支持的关键字

看一下ForwardLit支持的关键字,就可以大概看出实现了哪些功能,当然本身的功能就是光照不必说。

            // -------------------------------------
            // Material Keywords
            #pragma shader_feature_local_fragment _ALPHATEST_ON
            #pragma shader_feature_local_fragment _ALPHAPREMULTIPLY_ON
            #pragma shader_feature_local_fragment _ _SPECGLOSSMAP _SPECULAR_COLOR
            #pragma shader_feature_local_fragment _GLOSSINESS_FROM_BASE_ALPHA
            #pragma shader_feature_local _NORMALMAP
            #pragma shader_feature_local_fragment _EMISSION
            #pragma shader_feature_local _RECEIVE_SHADOWS_OFF

            // -------------------------------------
            // Universal Pipeline keywords
            #pragma multi_compile _ _MAIN_LIGHT_SHADOWS
            #pragma multi_compile _ _MAIN_LIGHT_SHADOWS_CASCADE
            #pragma multi_compile _ _ADDITIONAL_LIGHTS_VERTEX _ADDITIONAL_LIGHTS
            #pragma multi_compile_fragment _ _ADDITIONAL_LIGHT_SHADOWS
            #pragma multi_compile_fragment _ _SHADOWS_SOFT
            #pragma multi_compile _ LIGHTMAP_SHADOW_MIXING
            #pragma multi_compile _ SHADOWS_SHADOWMASK
            #pragma multi_compile_fragment _ _SCREEN_SPACE_OCCLUSION

            // -------------------------------------
            // Unity defined keywords
            #pragma multi_compile _ DIRLIGHTMAP_COMBINED
            #pragma multi_compile _ LIGHTMAP_ON
            #pragma multi_compile_fog

            //--------------------------------------
            // GPU Instancing
            #pragma multi_compile_instancing
            #pragma multi_compile _ DOTS_INSTANCING_ON

可以看到,这些关键字分了几类。比如Material Keywords都是使用shader feature定义的,这些是材质上可以开关选择的关键字,比如我们之前看到过的alpha test, alpha premultiply;而simpeLit增加的有Specular GlossMap/Specular Color, Normalmap, Emission以及ReceiveShadowsOff,除了最后一个,都是和计算光照相关的选项,涉及到高光,法线贴图和自发光,我们会在shader里面看到它们的实现。
Universal Pipeline keywords这一组则是URP的一些全局设置相关的,这里的全局只是相对于材质来说,也不是local/global keywords那个全局。比如_MAIN_LIGHT_SHADOWS决定了是否使用主光阴影,_MAIN_LIGHT_SHADOWS_CASCADE是是否主光阴影使用级联阴影。Additional Lights相关的关键字是控制附加灯相关的光照和阴影。Lightmap shadow mixing和shadow mask是Unity的混合光照相关。
Unity defined keywords这一组顾名思义就是Unity定义的关键字了,比如lightmap开关和深度雾。
此外还有GPU Instancing这组。

引用的HLSL

#include "Packages/com.unity.render-pipelines.universal/Shaders/SimpleLitInput.hlsl"
#include "Packages/com.unity.render-pipelines.universal/Shaders/SimpleLitForwardPass.hlsl"

这也是URP的惯例了,一个Pass自身的hlsl代码分为Input和Pass两部分。

SimpleLitInput.hlsl

在分析unlit shader时,我没有太关注unlit input,但是这次我打算详细的分析一下了。
SimpleLit input hlsl包含了shader使用的uniform的定义,uniform可能是以CBuffer的形式定义,也可能是shader全局变量的形式。使用CBUffer形式的往往是为了兼容SRPBatcher或者Instancing,打包进CBuffer的uniform往往是要经常变化的,比如随材质变化或者随instance变化;而全局变量的uniform就是全局改变的。
有几个内容需要关注:

  • SRP Batcher相关的CBuffer
  • Instancing相关的CBuffer
  • 什么是DOTS Instancing
    相关内容篇幅会比较大,因此会分为几篇,以SimpleLit Shader为例子说明。

SimpleLitForwardPass.hlsl

这个文件就是SimpleLit前向渲染pass的主要代码了。首先它一开始就引用了"Packages/com.unity.render-pipelines.universal/ShaderLibrary/Lighting.hlsl"文件,这个文件非常重要,值得好好研读。这里面几乎包含了URP光照相关的全部shader库了,在相关shader引用到里面的内容时会做说明。回到SimpleLit,定义了如下的顶点属性:

struct Attributes
{
    
    
    float4 positionOS    : POSITION;
    float3 normalOS      : NORMAL;
    float4 tangentOS     : TANGENT;
    float2 texcoord      : TEXCOORD0;
    float2 lightmapUV    : TEXCOORD1;
    UNITY_VERTEX_INPUT_INSTANCE_ID
};

坐标,法线,切线,两套uv,基本全了,毕竟要算光照都不能少,除了没用顶点色。再看VS的输出以及FS的输入:

struct Varyings
{
    
    
    float2 uv                       : TEXCOORD0;
    DECLARE_LIGHTMAP_OR_SH(lightmapUV, vertexSH, 1);

    float3 posWS                    : TEXCOORD2;    // xyz: posWS

#ifdef _NORMALMAP
    float4 normal                   : TEXCOORD3;    // xyz: normal, w: viewDir.x
    float4 tangent                  : TEXCOORD4;    // xyz: tangent, w: viewDir.y
    float4 bitangent                : TEXCOORD5;    // xyz: bitangent, w: viewDir.z
#else
    float3  normal                  : TEXCOORD3;
    float3 viewDir                  : TEXCOORD4;
#endif

    half4 fogFactorAndVertexLight   : TEXCOORD6; // x: fogFactor, yzw: vertex light

#if defined(REQUIRES_VERTEX_SHADOW_COORD_INTERPOLATOR)
    float4 shadowCoord              : TEXCOORD7;
#endif

    float4 positionCS               : SV_POSITION;
    UNITY_VERTEX_INPUT_INSTANCE_ID
    UNITY_VERTEX_OUTPUT_STEREO
};

VS输出啥其实是看FS的计算需要啥,首先uv, 世界坐标,法线,视线方向等计算Blinn-Phong模型光照所需的经典角色一个不少;如果启用了法线贴图,还需要输出切线坐标系;另外似乎还有所谓的vertex light;当然clip sapce postition是必须的。

本篇总结

由于篇幅所限,本篇只是起了个头,结果挖了好几个坑。至少我们现在对SimpeLit的功能有了大体的了解,光照部分应该是使用了Blinn-Phong模型,支持法线贴图。下篇将继续分析前向渲染路径的光照。之后会有几篇介绍SRP Batcher和Instancing。然后是Shadow Caster等等。基本上一个SimpleLit就可以挖出很多东西了。

猜你喜欢

转载自blog.csdn.net/n5/article/details/128183653