Sine.

URP PBR Shader 编写指南

介绍在 URP 中编写 PBR Shader 的改写层级,涵盖 SurfaceData、BRDF、GI 与自定义光照结构。

类Surface Shader 的代码

如果只是写一些类似内置渲染管线时,Surface Shader的代码. 仅需要修改以下两点

  1. 重写InputData 初始化函数(可选)

    struct InputData
    {
        float3  positionWS;
        float4  positionCS;
        float3  normalWS;
        half3   viewDirectionWS;
        float4  shadowCoord;
        half    fogCoord;
        half3   vertexLighting;
        half3   bakedGI;
        float2  normalizedScreenSpaceUV;
        half4   shadowMask;
        half3x3 tangentToWorld;
    };

    仅当你需要在片元着色器中修改InputData的值时,才需要重写InputData的初始化函数,对于顶点位置的修改,可以在顶点着色器中完成

  2. 重写SurfaceData 初始化函数(大部分简单效果只需要重写SurfaceData的初始化函数就行,就像我们在Surface Shader 中做的那样)

    struct SurfaceData
    {
        half3 albedo;
        half3 specular;
        half  metallic;
        half  smoothness;
        half3 normalTS;
        half3 emission;
        half  occlusion;
        half  alpha;
        half  clearCoatMask;
        half  clearCoatSmoothness;
    };

    就像是 Surface Shader 中一样,将你修改后的albedo,metallic,smoothness等参数传入SurfaceData 结构

编写需要重构BRDF光照Shader

这个需求通常发生在,

需要简化BRDF光照,以减少光源计算的开销

增强BRDF的光照计算,以达到更准确的效果

需要在BRDF的基础上,叠加新的光照细节,常见于 皮肤,车漆等需要二次高光的材质

在上文的基础上,再重写 BRDF初始化函数及其结构体

  1. 重写 BRDF Data 结构体(可选)

    struct BRDFData
    {
        half3 albedo;
        half3 diffuse;
        half3 specular;
        half reflectivity;
        half perceptualRoughness;
        half roughness;
        half roughness2;
        half grazingTerm;
    
        // We save some light invariant BRDF terms so we don't have to recompute
        // them in the light loop. Take a look at DirectBRDF function for detailed explaination.
        half normalizationTerm;     // roughness * 4.0 + 2.0
        half roughness2MinusOne;    // roughness^2 - 1.0
    };

    如果你只是想要在原本的pbr上再叠加新的光照效果,通常不需要重写BRDF Data,如皮肤或车漆材质的二次高光

    即,如果你还需要正常的PBR效果,就无需修改PBRData结构体

  2. 重写UniversalFragmentPBR函数

    Unity对PBR效果的实现都在这个函数中完成

  3. 重写GlobalIllumination函数

    在GlobalIllumination函数中, 通过GlossyEnvironmentReflection提供反射,EnvironmentBRDF提供直接光照的BRDF

GlobalIllumination 函数

GlobalIllumination //计算全局光照
├─ GlossyEnvironmentReflection //计算间接反射(采样反射探针)
└─ EnvironmentBRDF //将计算出的环境反射结果与间接漫反射(BakedGI)混合,最终输出得到GlobalGI

LightingPhysicallyBased 函数

LightingPhysicallyBased //计算直接光照
└─DirectBRDFSpecular 计算镜面高光反射

编写自定义光照效果的Shader

这个应用是最广泛的,NPR渲染,各向异性的毛发等效果

  1. 直接舍弃BRDF相关的代码
  2. 自己创建相关的光照结构体,例如 NPRData, ToonData,以对应的光照模型命名
  3. 重写 UniversalFragmentPBR函数, 建议改成UniversalFragmentNPR,或UniversalFragmentToon等,以对应的光照模型命名
  4. 重写GlobalIllumination函数

本篇核心

Unity为我们提供了一个比较完善的光照Shader框架,因此我建议沿用他们设计的框架,这有利于我们去兼容一些Unity特性

一些KeyWord

		#pragma multi_compile _ _ADDITIONAL_LIGHTS_VERTEX _ADDITIONAL_LIGHTS
		#pragma multi_compile_fragment _ _ADDITIONAL_LIGHT_SHADOWS
		#pragma multi_compile _ _LIGHT_LAYERS
		#pragma multi_compile _ _FORWARD_PLUS