PBR基础模板

1. PBR主体构成

PBR由主要的两个部分组成

  • 直接光照
  • 间接光照

☛直接光照

直接光照部分

1
2
3
4
5
6
//NdotL-光照方向和法线的点积并限制在0,1范围内.
half NdotL = saturate(dot(normalWS, lightDirectionWS));
//光照颜色和强度乘以NdotL就得到了辐射量.
half3 radiance = lightColor * (lightAttenuation * NdotL);
//乘以BRDF函数则得到了最后的PBR颜色.
half3 LightingPhysicallyBased = BRDF * radiance;

其中BRDF, 采用拟合的Cook-Torrance BRDF((Bidirectional Reflective Distribution Function) )(双向反射分布函数)来进行计算. 公式如下:

f(l,v) = kd*fd+ks*fs
  • kd-漫反射
  • fd-漫反射BRDF函数
  • ks-高光反射
  • fs-高光反射BRDF函数

♢fs部分

fs = DGF/4(NdotL)(NdotV)
fs = DVG/4

  • D-法线分布函数
  • G-几何遮蔽函数
  • F-菲涅尔反射函数
  • V-可见函数 = G/(NdotL)(NdotV)

D, G, F这几个函数有很多版本, Brian Karis在制作UE4的BRDF时参考了这些版本, 见specular-brdf-reference

⚐D - 法线分布函数采用GGX (Trowbridge-Reitz) - 决定高光的强度和大小
1
2
3
4
5
6
//D = roughness^2 / ( NoH^2 * (roughness^2 - 1) + 1 )^2 * INVPI
float D_TrowbridgeReitzGGX(half a2,half NoH){
    half nh2 = NoH * NoH;
    half b = nh2 * (a2 - 1) + 1.00001f;
    return a2 * INV_PI / (b * b);//注意, Unity因为光强统一除以PI的原因, 所以采用的是没有除以PI的D项.
}
⚐G - 几何遮蔽函数(UE4)使用GGX(Schlick-Smith) - 决定表面的粗糙度

k = a*sqrt(2/PI), 即k等于a乘以(根号(2/PI)).

  • a为roughness.
  • sqrt(2/PI)=0.797884560803, (另rsqrt(2/PI) = 1.25331413732). 图形见下链接
    但在UE中用
    k = a/2, 来拟合. 即用0.5代替了0.7978.
    又因为本来fs就要除以NdotV*NdotL, 所以最终用V代替了G.

G1(X) = NdotX/((NdotX)*(1-k)+k)
G(L, V) = NdotV*NdotL/((NdotV*(1-k)+k)*(NdotV*(1-k)+k))
V(L, V) = 1/((NdotV*(1-k)+k)*(NdotV*(1-k)+k))

1
2
3
4
5
6
7
//k = roughness * 0.5
//G = NoV*NoL/((NoV*(1-k)+k)*(NoL*(1-k)+k)
float V_SmithGGX(half roughness,NoV,NoL){
    float oneMinusK = 1.0 - 0.5 * roughness;
    float k = 0.5*roughness;
    return 1/(NoV * oneMinusK + k) * 1/(NoL * oneMinusK + k);
}
⚐F - 菲涅尔反射公式(Schlick) - 边缘高光

F(V, H) = F0 + (1-F0) * pow((1-VoH), 5)

1
2
3
half3 F_Schlick(half3 f0,half VoH){
    return f0 + (1 - f0) * pow(1 - VoH,5);
}

以下是Unity在URP中使用的版本, Unity非常鸡贼的在brdfData中定义了一个normalizationTerm的值来影响最终的判断, 导致代码阅读困难.

*另外, 公式中的Roughness并非直接采样结果, 而是采样结果的平方. 贴图采样结果在Unity中定义为"PerceptualRoughness", 感知粗糙度.

albedo = SAMPLER_TEXTURE2D(albedoAlphaMap, sampler_albedoAlphaMap, uv).rgb * _BaseColor.rgb;

half4 kDielectricSpec = half4(0.04, 0.04, 0.04, 1.0 - 0.04);// standard dielectric reflectivity coef at incident angle (= 4%)

// We'll need oneMinusReflectivity, so
//  1-reflectivity = 1-lerp(dielectricSpec, 1, metallic) = lerp(1-dielectricSpec, 0, metallic)
//由于1-dielectricSpec直接存在了kDielectricSpec.a中直接使用即可.
half oneMinusReflectivityMetallic = lerp(kDielectricSpec.a, 0, metallic);
half reflectivity = half(1.0)-oneMinusReflectivityMetallic;
half3 brdfDiffuse = albedo*oneMinusReflectivityMetallic;//金属漫反射为0, 非金属为0.96*albedo
half3 brdfSpecular = lerp(kDielectricSpec.rgb, albedo, metallic)//金属高光颜色为albedo, 非金属高光颜色为half3(0.04)

由以上代码可以看出

♢kd = lerp(1-kDielectricSpec.r, 0, metallic)

♢fd = albedo/pi *pi

♢ks = lerp(kDielectricSpec.rgb, albedo, metallic) = F0 = brdfSpecular

//注, unity用的F是不包含ks的, 所以会乘一个ks, 而通常的F算法是包含ks的,抛弃外乘的ks结果会更接近美术需求. 此处需要继续研究.
//半球积分后, 整个式子都会乘以PI, 而fd正好抵消, fs中的D项也可以抵消分母的PI.
//Unity在光照部分, 将所有的光强度除以了PI, 所以此时无论是漫反射部分, 还是高光部分的D项, 都是为了这个操作才做的简化, 但也造成了需要时刻注意Unity的光照是除以了PI的. 即Unity的光照3.14才等于UE的光照1.
fs比较复杂单独开一段来解析.

// GGX Distribution multiplied by combined approximation of Visibility and Fresnel
    // BRDFspec = (D * V * F) / 4.0
    // D = roughness^2 / ( NoH^2 * (roughness^2 - 1) + 1 )^2
    // V * F = 1.0 / ( LoH^2 * (roughness + 0.5) )
    // See "Optimizing PBR for Mobile" from Siggraph 2015 moving mobile graphics course
    // https://community.arm.com/events/1155

    // Final BRDFspec = roughness^2 / ( NoH^2 * (roughness^2 - 1) + 1 )^2 * (LoH^2 * (roughness + 0.5) * 4.0)
    // We further optimize a few light invariant terms
    // brdfData.normalizationTerm = (roughness + 0.5) * 4.0 rewritten as roughness * 4.0 + 2.0 to a fit a MAD.

注: 整个BRDF中的a, 只是一个与"采样粗糙度roughness"有关的数, Unity中是用采样粗糙度的值的平方来决定a的. 而实际上这部分有待商榷.

虚幻的光照文章中明确的提到了这一点, 是为了让美术调整方便, 让0.5的粗糙度调整起来更符合美术的感知.

☛最终在BRDF.hlsl中加入如下代码

//D_TrowbridgeReitzGGX
float D_TrowbridgeReitzGGX(half a2,half NoH)
{
    half nh2 = NoH * NoH;
    half b = nh2 * (a2 - 1) + 1.00001f;
    // return a2 * INV_PI / (b * b);
    return a2 / (b * b);
}

//V_SmithGGX
float V_SmithGGX(half roughness,half NoV,half NoL){
    float oneMinusK = 1.0 - 0.5 * roughness;
    float k = 0.5*roughness;
    return 1/(NoV * oneMinusK + k) * 1/(NoL * oneMinusK + k);
}

//F_Schlick
half3 F_Schlick_Custom(half3 f0,half VoH){
    return f0 + (1 - f0) * pow(1 - VoH,5);
}

//等效half3 brdfSpecular
half3 F0(half3 albedo, half metallic)
{
   return lerp(kDielectricSpec.rgb, albedo, metallic);
}

//替换用BRDF
half3 DirectBRDFSpecularCustom(BRDFData brdfData, half3 normalWS, half3 lightDirectionWS, half3 viewDirectionWS)
{
    float3 lightDirectionWSFloat3 = float3(lightDirectionWS);
    float3 halfDir = SafeNormalize(lightDirectionWSFloat3 + float3(viewDirectionWS));

    half NoV = dot(normalWS, viewDirectionWS);
    half NoL = dot(normalWS, lightDirectionWS);
    half NdH = dot(normalWS, halfDir);
    half VoH = dot(halfDir, viewDirectionWS);
    half D = D_TrowbridgeReitzGGX(brdfData.roughness2,NdH);
    half V = V_SmithGGX(brdfData.roughness,NoV,NoL);
    // half3 f0 = F0(brdfData.albedo, surfaceData.metallic);
    half3 f0 = brdfData.specular;
    half3 F = F_Schlick_Custom(f0, VoH);

    half3 specularTerm = D*V*F*0.25;

    #if defined (SHADER_API_MOBILE) || defined (SHADER_API_SWITCH)
    specularTerm = specularTerm - HALF_MIN;
    specularTerm = clamp(specularTerm, 0.0, 100.0); // Prevent FP16 overflow on mobiles
    #endif

    return specularTerm;
}

独立可调试版

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
Shader "Render/URP/PBR/Custom_BRDF_Single_Lit"
{
    Properties
    {
        [Enum(UnityEngine.Rendering.CullMode)]_Cull("Cull", Float) = 2.0
        [Enum(UnityEngine.Rendering.BlendMode)]_SrcBlend("SrcBlend", Float) = 5.0
        [Enum(UnityEngine.Rendering.BlendMode)]_DstBlend("DstBlend", Float) = 10.0
        [Enum(Off, 0, On, 1)]_ZWrite("ZWrite", Float) = 1.0
        [Enum(UnityEngine.Rendering.CompareFunction)]_ZTest("ZTest", Float) = 4.0
        [Toggle(_ALPHATEST_ON)]_AlphaClip("AlphaClip", Float) = 0.0
        _Cutoff("Alpha Cutoff", Range(0.0, 1.0)) = 0.5

        [Space(20)]
        [MainTexture] _BaseMap("Albedo", 2D) = "white" {}
        [MainColor] _BaseColor("Color", Color) = (1,1,1,1)

        _Smoothness("Smoothness", Range(0.0, 1.0)) = 0.5

        _Metallic("Metallic", Range(0.0, 1.0)) = 0.0
        _MetallicGlossMap("Metallic", 2D) = "white" {}

        [Toggle(_NORMALMAP)]_UseBumpMap("UseBumpMap", Float) = 0.0
        _BumpScale("BumpScale", Float) = 1.0
        _BumpMap("Normal Map", 2D) = "bump" {}

        [Toggle(_TANGENTMAP)]_UseTangentMap("UseTangentMap", Float) = 0.0
        _TangentScale("TangentScale", Range(0, 5.0)) = 1.0
        _TangentMap("Tangent Map", 2D) = "bump" {}

        _OcclusionStrength("OcclusionStrength", Range(0.0, 1.0)) = 1.0
        _OcclusionMap("Occlusion", 2D) = "white" {}

        [HDR] _EmissionColor("Color", Color) = (0,0,0)
        _EmissionMap("Emission", 2D) = "white" {}

        [Space(20)]
        _Anisotropy("Anisotropy", Range(-1.0, 1.0)) = 0.0
        [KeywordEnum(None,Neubelt,Burley)]_AnisoRoughness("AnisoRoughness", Float) = 0.0

        [Space(20)]
        [KeywordEnum(None,Lambert,Disney)]_DirDiffuse("DirDiffuse", Float) = 1.0
        [KeywordEnum(None,GGX,GGXAniso)]_DirSpec_N("DirSpec_N", Float) = 1.0
        [KeywordEnum(None,SmithGGX_1,SmithGGX_2, SmithGGX_3, SmithGGX_Aniso, KSK)]_DirSpec_G("DirSpec_G", Float) = 1.0
        [KeywordEnum(None,Schlick)]_DirSpec_F("DirSpec_F", Float) = 1.0

        [Space(5)]
        [KeywordEnum(None,URP,Builtin)]_DirSpec_VIS("DirSpecVIS", Float) = 0.0

        [Space(5)]
        [KeywordEnum(None,SH)]_IndirDiff("IndirDiff", Float) = 0.0
        [KeywordEnum(None,Iso,Aniso)]_IndirSpec("IndirSpec", Float) = 0.0

//        [KeywordEnum(None,NDF,G,Fresnel)]_Debug("Debug", Float) = 1.0

    }
    SubShader
    {
        Tags
        {
            "RenderType"="Opaque" "RenderPipeline" = "UniversalPipeline" "UniversalMaterialType" = "Lit" "IgnoreProjector" = "True"
        }
        LOD 100

        Pass
        {
            Name "ForwardLit"
            Tags
            {
                "LightMode" = "UniversalForward"
            }

            Blend[_SrcBlend][_DstBlend]
            ZWrite[_ZWrite]
            Cull[_Cull]

            HLSLPROGRAM
            #pragma shader_feature_local_fragment _ANISOROUGHNESS_NONE _ANISOROUGHNESS_NEUBELT _ANISOROUGHNESS_BURLEY
            #pragma shader_feature_local_fragment _DIRDIFFUSE_NONE _DIRDIFFUSE_LAMBERT _DIRDIFFUSE_DISNEY
            #pragma shader_feature_local_fragment _DIRSPEC_N_NONE _DIRSPEC_N_GGX _DIRSPEC_N_GGXANISO
            #pragma shader_feature_local_fragment _DIRSPEC_G_NONE _DIRSPEC_G_SMITHGGX_1 _DIRSPEC_G_SMITHGGX_2 _DIRSPEC_G_SMITHGGX_3 _DIRSPEC_G_SMITHGGX_ANISO _DIRSPEC_G_KSK
            #pragma shader_feature_local_fragment _DIRSPEC_F_NONE _DIRSPEC_F_SCHLICK
            #pragma shader_feature_local_fragment _DIRSPEC_VIS_NONE _DIRSPEC_VIS_URP _DIRSPEC_VIS_BUILTIN
            #pragma shader_feature_local_fragment _INDIRDIFF_NONE _INDIRDIFF_SH
            #pragma shader_feature_local_fragment _INDIRSPEC_NONE _INDIRSPEC_ISO _INDIRSPEC_ANISO

            #pragma shader_feature_local _NORMALMAP
            #pragma shader_feature_local _TANGENTMAP
            #pragma shader_feature_local_fragment _ALPHATEST_ON

            #pragma multi_compile _ DYNAMICLIGHTMAP_ON
            #pragma multi_compile _ _MAIN_LIGHT_SHADOWS _MAIN_LIGHT_SHADOWS_CASCADE _MAIN_LIGHT_SHADOWS_SCREEN

            #pragma multi_compile_instancing
            #pragma instancing_options renderinglayer
            #pragma multi_compile _ DOTS_INSTANCING_ON


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

            #pragma vertex LitPassVertex
            #pragma fragment LitPassFragment

            struct Attributes
            {
                float4 positionOS : POSITION;
                float3 normalOS : NORMAL;
                float4 tangentOS : TANGENT;
                float2 texcoord : TEXCOORD0;
                float2 staticLightmapUV : TEXCOORD1;
                float2 dynamicLightmapUV : TEXCOORD2;
            };

            struct Varyings
            {
                float2 uv : TEXCOORD0;
                float3 positionWS : TEXCOORD1;

                float3 normalWS : TEXCOORD2;
                half4 tangentWS : TEXCOORD3; // xyz: tangent, w: sign

                DECLARE_LIGHTMAP_OR_SH(staticLightmapUV, vertexSH, 8);

                float4 positionCS : SV_POSITION;
            };

            float _Anisotropy, _TangentScale;

            TEXTURE2D(_TangentMap);
            SAMPLER(sampler_TangentMap);

            Varyings LitPassVertex(Attributes input)
            {
                Varyings output = (Varyings)0;

                float3 positionWS = TransformObjectToWorld(input.positionOS.xyz);
                float3 positionVS = TransformWorldToView(positionWS);
                float4 positionCS = TransformWorldToHClip(positionWS);

                real sign = real(input.tangentOS.w) * GetOddNegativeScale();
                float3 normalWS = TransformObjectToWorldNormal(input.normalOS);
                float3 tangentWS = float3(TransformObjectToWorldDir(input.tangentOS.xyz));
                float3 bitangentWS = real3(cross(normalWS, tangentWS)) * sign;

                float2 mainuv = TRANSFORM_TEX(input.texcoord, _BaseMap);

                OUTPUT_LIGHTMAP_UV(input.staticLightmapUV, unity_LightmapST, output.staticLightmapUV);
                #ifdef DYNAMICLIGHTMAP_ON
                output.dynamicLightmapUV = input.dynamicLightmapUV.xy * unity_DynamicLightmapST.xy + unity_DynamicLightmapST.zw;
                #endif
                OUTPUT_SH(output.normalWS.xyz, output.vertexSH);

                output.uv = mainuv;
                output.positionWS = positionWS;
                output.normalWS = normalWS;
                output.tangentWS = float4(tangentWS, sign);
                output.positionCS = positionCS;

                return output;
            }

            float V_SmithGGXCorrelatedFast(float NoV, float NoL, float roughness)
            {
                float a = roughness;
                float GGXV = NoL * (NoV * (1.0 - a) + a);
                float GGXL = NoV * (NoL * (1.0 - a) + a);
                return 0.5 / (GGXV + GGXL);
            }

            float V_SmithGGXCorrelated(float NoV, float NoL, float a)
            {
                float a2 = a * a;
                float GGXL = NoV * sqrt((-NoL * a2 + NoL) * NoL + a2);
                float GGXV = NoL * sqrt((-NoV * a2 + NoV) * NoV + a2);
                return 0.5 / (GGXV + GGXL);
            }

            half4 LitPassFragment(Varyings i) : SV_Target
            {
                half4 albedoAlpha = SampleAlbedoAlpha(i.uv, TEXTURE2D_ARGS(_BaseMap, sampler_BaseMap));

                half3 albedo = albedoAlpha.rgb * _BaseColor.rgb;
                half alpha = albedoAlpha.a * _BaseColor.a;
                #if defined(_ALPHATEST_ON)
                    clip(alpha - _Cutoff);
                #endif

                half4 metallicSmoothness = SAMPLE_TEXTURE2D(_MetallicGlossMap, sampler_MetallicGlossMap, i.uv);
                half metallic = metallicSmoothness.r * _Metallic;
                half smoothness = metallicSmoothness.a * _Smoothness;

                float3 normalTS = SampleNormal(i.uv, TEXTURE2D_ARGS(_BumpMap, sampler_BumpMap), _BumpScale);

                //SampleOcclusion(uv);
                half occ = SAMPLE_TEXTURE2D(_OcclusionMap, sampler_OcclusionMap, i.uv).g;
                occ = LerpWhiteTo(occ, _OcclusionStrength);

                half3 emission = SAMPLE_TEXTURE2D(_EmissionMap, sampler_EmissionMap, i.uv).rgb * _EmissionColor.rgb;

                float sgn = i.tangentWS.w; // should be either +1 or -1
                float3 bitangent = sgn * cross(i.normalWS.xyz, i.tangentWS.xyz);
                half3x3 tangentToWorld = half3x3(i.tangentWS.xyz, bitangent.xyz, i.normalWS.xyz);
                float3 normalWS = TransformTangentToWorld(normalTS, tangentToWorld);
                normalWS = normalize(normalWS);

                #if _TANGENTMAP
                //采样切线图
                float4 tt = SAMPLE_TEXTURE2D(_TangentMap, sampler_TangentMap, i.uv);
                float3 tangentTS = UnpackNormalmapRGorAG(tt, _TangentScale);
                float3 tangentWS = TransformTangentToWorld(tangentTS, tangentToWorld);
                tangentWS = Orthonormalize(tangentWS, normalWS);
                float3 bitangentWS = cross(normalWS, tangentWS);
                #else
                real sign = real(i.tangentWS.w) * GetOddNegativeScale();
                float3 tangentWS = i.tangentWS.xyz;
                float3 bitangentWS = real3(cross(normalWS, tangentWS)) * sign;
                #endif

                //Reflectivity  = lerp(dielectricSpec, 1, metallic) 金属度越高反射率(高光占反射比例)越高
                half oneMinusReflectivity = OneMinusReflectivityMetallic(metallic);
                half reflectivity = half(1.0) - oneMinusReflectivity;
                half3 brdfDiffuseCol = albedo * oneMinusReflectivity;
                half3 brdfSpecularCol = lerp(kDieletricSpec.rgb, albedo, metallic);

                float perceptualRoughness = PerceptualSmoothnessToPerceptualRoughness(smoothness);
                float roughness = max(PerceptualRoughnessToRoughness(perceptualRoughness), HALF_MIN_SQRT);
                float roughness2 = max(roughness * roughness, HALF_MIN);
                float grazingTerm = saturate(smoothness + reflectivity);
                float normalizationTerm = roughness * half(4.0) + half(2.0);
                float roughness2MinusOne = roughness2 - half(1.0);

                #if _ANISOROUGHNESS_NEUBELT
                float roughnessT = roughness;
                float roughnessB = lerp(0, roughness, 1 - _Anisotropy);
                #elif _ANISOROUGHNESS_BURLEY
                float roughnessT = max(roughness * (1 + _Anisotropy), 1e-4);
                float roughnessB = max(roughness * (1 - _Anisotropy), 1e-4);
                #else
                float roughnessT = roughness;
                float roughnessB = roughness;
                #endif

                half3 lightDirectionWS = half3(_MainLightPosition.xyz);
                half3 lightColor = _MainLightColor.rgb;

                half3 viewDirWS = GetWorldSpaceNormalizeViewDir(i.positionWS);
                half3 viewDirTS = GetViewDirectionTangentSpace(float4(tangentWS, sgn), normalWS, viewDirWS);

                float3 halfDir = normalize(lightDirectionWS + viewDirWS);
                float NoH = saturate(dot(normalWS, halfDir));
                half LoH = saturate(dot(lightDirectionWS, halfDir));
                half NoV = saturate(dot(normalWS, viewDirWS));
                half NoL = saturate(dot(normalWS, lightDirectionWS));
                half LoV = saturate(dot(lightDirectionWS, viewDirWS));

                half ToH = (dot(tangentWS, halfDir));
                half BoH = (dot(bitangentWS, halfDir));
                half ToV = (dot(tangentWS, viewDirWS));
                half BoV = (dot(bitangentWS, viewDirWS));
                half ToL = (dot(tangentWS, lightDirectionWS));
                half BoL = (dot(bitangentWS, lightDirectionWS));

                //直接光
                float4 shadowCoord = TransformWorldToShadowCoord(i.positionWS);
                half lightAttenuation = MainLightRealtimeShadow(shadowCoord);

                half3 brdfDiffuse = brdfDiffuseCol;

                #if _DIRDIFFUSE_LAMBERT
                // NoL在光源那边计算
                brdfDiffuse *= half3(1, 1, 1);
                #elif _DIRDIFFUSE_DISNEY
                brdfDiffuse *= DisneyDiffuseNoPI( NoV, NoL, LoV, perceptualRoughness);
//预留Multiscattering
                #else
                brdfDiffuse *= half3(0, 0, 0);
                #endif


                #if _DIRSPEC_N_GGX
                float d = NoH * NoH * roughness2MinusOne + 1.00001f;
                float ndf = roughness2 / (d * d);
                #elif _DIRSPEC_N_GGXANISO
                //Burley版本
                float ndf = D_GGXAnisoNoPI(ToH, BoH, NoH, roughnessT, roughnessB);
                #else
                float ndf = 1;
                #endif


                #if _DIRSPEC_VIS_NONE
                #if _DIRSPEC_G_SMITHGGX_1
                //unity自己方案
                float vis = V_SmithJointGGX(NoL, NoV, roughness);
                #elif _DIRSPEC_G_SMITHGGX_2
                //unreal4方案
                float vis = V_SmithJointGGXApprox(NoL, NoV, roughness);
                #elif _DIRSPEC_G_SMITHGGX_3
                //Frostbite方案
                float vis = V_SmithGGXCorrelated(NoL, NoV, roughness);
                #elif _DIRSPEC_G_KSK
                //ksk在ppt中不是完整的VIS 它漏了一个/4 看unity源码注释就知道
                float vis = 1 / (LoH * LoH * (1 - roughness2) + roughness2) * 1 / 4;
                #elif _DIRSPEC_G_SMITHGGX_ANISO
                //heitz版本
                float vis = V_SmithJointGGXAniso(ToV, BoV, NoV, ToL, BoL, NoL, roughnessT, roughnessB);
                #else
                float vis = 1;
                #endif


                #if _DIRSPEC_F_SCHLICK
                float f = pow(1 - LoH, 5.0);
                float3 fresnel = f + brdfSpecularCol * (1 - f);
                #else
                float3 fresnel = 1;
                #endif

                float3 vis_f = vis * fresnel;

                #elif _DIRSPEC_VIS_URP
                //计算Fresnel  LoH 就是等于 VoH
                //几何遮蔽用ksk-rough版本  
                //Fresnel_Schlik拟合后乘SpecColor
                half LoH2 = LoH * LoH;
                float3 vis_f = brdfSpecularCol / (max(0.1h, LoH2) * normalizationTerm);
//#elif _DIRSPEC_VIS_BUILTIN 没什么特别就没写了 上面可以拼出来
//V_SmithJointGGX + 传统fresnel(写法很正式)
                #else
                float3 vis_f = 1;
                #endif

                float3 brdfSpec = ndf * vis_f;
                half3 brdf = (brdfDiffuse + brdfSpec) * lightColor * NoL;
                brdf *= lightAttenuation;


                //间接光
                float3 inDirCol = float3(0, 0, 0);

                #if _INDIRDIFF_SH
                half3 indirectDiffuse = SAMPLE_GI(i.staticLightmapUV, i.vertexSH, normalWS) * brdfDiffuseCol;
                inDirCol += indirectDiffuse;
                #endif


                #if _INDIRSPEC_ISO  || _INDIRSPEC_ANISO
                #if _INDIRSPEC_ISO
                half3 reflectVector = reflect(-viewDirWS, normalWS);
                #else
                half3 anisotropicDirection = _Anisotropy >= 0.0 ? bitangentWS : tangentWS;
                half3 anisotropicTangent = cross(anisotropicDirection, viewDirWS);
                half3 anisotropicNormal = cross(anisotropicTangent, bitangentWS);
                half3 bentNormal = normalize(lerp(normalWS, anisotropicNormal, _Anisotropy));
                half3 reflectVector = reflect(-viewDirWS, bentNormal);
                #endif

                half fresnelTerm = Pow4(1.0 - NoV);
                half3 indirectSpecular = GlossyEnvironmentReflection(reflectVector, i.positionWS, perceptualRoughness, 1.0h) * NoV;
                float surfaceReduction = 1.0 / (roughness2 + 1.0);
                inDirCol += indirectSpecular * half3(surfaceReduction * lerp(brdfSpecularCol, grazingTerm, fresnelTerm));
                #endif

                inDirCol *= albedo;

                half4 color = float4(brdf + inDirCol, alpha);
                color.rgb *= occ;
                color.rgb += emission;

                return color;
            }
            ENDHLSL
        }

        Pass
        {
            Name "ShadowCaster"
            Tags
            {
                "LightMode" = "ShadowCaster"
            }

            ZWrite On
            ZTest LEqual
            ColorMask 0
            Cull[_Cull]

            HLSLPROGRAM
            #pragma exclude_renderers gles gles3 glcore
            #pragma target 4.5

            // -------------------------------------
            // Material Keywords
            #pragma shader_feature_local_fragment _ALPHATEST_ON
            #pragma shader_feature_local_fragment _SMOOTHNESS_TEXTURE_ALBEDO_CHANNEL_A

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

            // -------------------------------------
            // Universal Pipeline keywords

            // This is used during shadow map generation to differentiate between directional and punctual light shadows, as they use different formulas to apply Normal Bias
            #pragma multi_compile_vertex _ _CASTING_PUNCTUAL_LIGHT_SHADOW

            #pragma vertex ShadowPassVertex
            #pragma fragment ShadowPassFragment

            #include "Packages/com.unity.render-pipelines.universal/Shaders/LitInput.hlsl"
            #include "Packages/com.unity.render-pipelines.universal/Shaders/ShadowCasterPass.hlsl"
            ENDHLSL
        }

        Pass
        {
            Name "DepthOnly"
            Tags
            {
                "LightMode" = "DepthOnly"
            }

            ZWrite On
            ColorMask 0
            Cull[_Cull]

            HLSLPROGRAM
            #pragma exclude_renderers gles gles3 glcore
            #pragma target 4.5

            #pragma vertex DepthOnlyVertex
            #pragma fragment DepthOnlyFragment

            // -------------------------------------
            // Material Keywords
            #pragma shader_feature_local_fragment _ALPHATEST_ON
            #pragma shader_feature_local_fragment _SMOOTHNESS_TEXTURE_ALBEDO_CHANNEL_A

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

            #include "Packages/com.unity.render-pipelines.universal/Shaders/LitInput.hlsl"
            #include "Packages/com.unity.render-pipelines.universal/Shaders/DepthOnlyPass.hlsl"
            ENDHLSL
        }
    }
}

参考网页:

延展

Edit

Pub: 12 Jul 2023 11:47 UTC

Edit: 18 Dec 2023 02:05 UTC

Views: 95