1 /*
2  * Copyright (c) 2024 Huawei Device Co., Ltd.
3  * Licensed under the Apache License, Version 2.0 (the "License");
4  * you may not use this file except in compliance with the License.
5  * You may obtain a copy of the License at
6  *
7  *     http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the License is distributed on an "AS IS" BASIS,
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  * See the License for the specific language governing permissions and
13  * limitations under the License.
14  */
15 
16 #ifndef SHADERS__COMMON__3D_DM_BRDF_COMMON_H
17 #define SHADERS__COMMON__3D_DM_BRDF_COMMON_H
18 
19 /*
20 BRDF functions.
21 Follows http://graphicrants.blogspot.com/2013/08/specular-brdf-reference.html
22 Some mobile optimizations from https://google.github.io/filament/Filament.html
23 */
24 #define CORE_BRDF_PI 3.14159265359
25 // Avoid divisions by 0 on devices that do not support denormals (from Filament documentation)
26 #define CORE_BRDF_MIN_ROUGHNESS 0.089
27 #define CORE_BRDF_EPSILON 0.0001
28 
29 #define CORE_HDR_FLOAT_CLAMP_MAX_VALUE 64512.0
30 
dLambert()31 float dLambert()
32 {
33     return (1.0 / CORE_BRDF_PI);
34 }
35 
36 // Sebastien Lagarde and Charles de Rousiers. 2014. Moving Frostbite to PBR.
37 // https://www.ea.com/frostbite/news/moving-frostbite-to-pb
EnvSpecularAo(float ao,float NoV,float roughness)38 float EnvSpecularAo(float ao, float NoV, float roughness)
39 {
40     return clamp(pow(NoV + ao, exp2(-16.0 * roughness - 1.0)) - 1.0 + ao, 0.0, 1.0);
41 }
42 
SpecularHorizonOcclusion(vec3 R,vec3 N)43 float SpecularHorizonOcclusion(vec3 R, vec3 N)
44 {
45     const float horizon = min(1.0 + dot(R, N), 1.0);
46     return horizon * horizon;
47 }
48 
dAshikhmin(float roughness,float NoH)49 float dAshikhmin(float roughness, float NoH)
50 {
51     const float r2 = roughness * roughness;
52     const float cos2h = NoH * NoH;
53     const float sin2h = 1.0 - cos2h;
54     const float sin4h = sin2h * sin2h;
55     return (sin4h + 4.0 * exp(-cos2h / (sin2h * r2))) / (CORE_BRDF_PI * (1.0 + 4.0 * r2) * sin4h);
56 }
57 
58 // includes microfaced BRDF denominator and geometry term
vAshikhmin(float NoV,float NoL)59 float vAshikhmin(float NoV, float NoL)
60 {
61     return 1.0 / (4.0 * (NoL + NoV - NoL * NoL));
62 }
63 
dCharlie(float roughness,float NoH)64 float dCharlie(float roughness, float NoH)
65 {
66     const float invR = 1.0 / roughness;
67     const float cos2h = NoH * NoH;
68     const float sin2h = 1.0 - cos2h; // NOTE: should max to fp16
69     return (2.0 + invR) * pow(sin2h, invR * 0.5) / (2.0 * CORE_BRDF_PI);
70 }
71 
72 // compensation for underlaying surface
f0ClearcoatToSurface(const vec3 f0)73 vec3 f0ClearcoatToSurface(const vec3 f0)
74 {
75     // approximation of ior with value of 1.5
76     return clamp(f0 * (f0 * 0.526868 + 0.529324) - 0.0482256, 0.0, 1.0);
77 }
78 
79 // Approximation for sheen integral on hemisphere
EnvBRDFApproxSheen(float NoV,float alpha)80 float EnvBRDFApproxSheen(float NoV, float alpha)
81 {
82     const float c = 1.0 - NoV;
83     const float c3 = c * c * c;
84     return 0.6558446 * c3 + 1.0 / (4.1652655 + exp(-7.9729136 * alpha + 6.3351689));
85 }
86 
fSchlick(vec3 f0,float VoH)87 vec3 fSchlick(vec3 f0, float VoH)
88 {
89     // optimized for scalars
90     const float p = pow(1.0 - VoH, 5.0);
91     return p + f0 * (1.0 - p);
92 }
93 
fSchlickSingle(float f0,float VoH)94 float fSchlickSingle(float f0, float VoH)
95 {
96     const float p = pow(1.0 - VoH, 5.0);
97     return p + f0 * (1.0 - p);
98 }
99 
100 // f0.xyz = f0, f0.w = f90
fSchlick(vec4 f0,float VoH)101 vec3 fSchlick(vec4 f0, float VoH)
102 {
103     const float p = pow(1.0 - VoH, 5.0);
104     return (f0.w * p) + f0.xyz * (1.0 - p);
105 }
106 
107 // Normal distribution function, GGX (Trowbridge-Reitz), Walter et al. 2007.
dGGX(float alpha2,float NoH)108 float dGGX(float alpha2, float NoH)
109 {
110     const float f = (NoH * alpha2 - NoH) * NoH + 1.0;
111     return alpha2 / (CORE_BRDF_PI * (f * f));
112 }
113 
114 // Normal distribution function for anisotropic based on Burley 2012 (Physically-Based Shading at Disney)
dGGXAnisotropic(float at,float ab,float NoH,float ToH,float BoH,float anisotropy)115 float dGGXAnisotropic(float at, float ab, float NoH, float ToH, float BoH, float anisotropy)
116 {
117     float a2 = at * ab;
118     vec3 d = vec3(ab * ToH, at * BoH, a2 * NoH);
119     float d2 = dot(d, d);
120     if (d2 == 0) {
121         return 0.0;
122     }
123     float w2 = a2 / d2;
124     return a2 * w2 * w2 * (1.0 / CORE_BRDF_PI);
125 }
126 
127 // Geometric shadowing with combined BRDF denominator
vGGXWithCombinedDenominator(float alpha2,float NoV,float NoL)128 float vGGXWithCombinedDenominator(float alpha2, float NoV, float NoL)
129 {
130     const float gv = NoV + sqrt((NoV - NoV * alpha2) * NoV + alpha2);
131     const float gl = NoL + sqrt((NoL - NoL * alpha2) * NoL + alpha2);
132     return min(1.0 / (gv * gl), CORE_HDR_FLOAT_CLAMP_MAX_VALUE);
133 }
134 
135 // "A Microfacet Based Coupled Specular-Matte BRDF Model with Importance Sampling"
136 // Kelemen 2001
vKelemen(float LoH)137 float vKelemen(float LoH)
138 {
139     return min(0.25 / (LoH * LoH), CORE_HDR_FLOAT_CLAMP_MAX_VALUE);
140 }
141 
vGGXAnisotropic(float at,float ab,float NoL,float NoV,float ToL,float ToV,float BoL,float BoV,float anisotropy)142 float vGGXAnisotropic(
143     float at, float ab, float NoL, float NoV, float ToL, float ToV, float BoL, float BoV, float anisotropy)
144 {
145     float gv = NoL * length(vec3(at * ToV, ab * BoV, NoV));
146     float gl = NoV * length(vec3(at * ToL, ab * BoL, NoL));
147     float v = 0.5 / (gv + gl);
148     return clamp(v, 0.0, 1.0);
149 }
150 
microfacedSpecularBrdf(vec3 f0,float alpha2,float NoL,float NoV,float NoH,float VoH)151 vec3 microfacedSpecularBrdf(vec3 f0, float alpha2, float NoL, float NoV, float NoH, float VoH)
152 {
153     // NOTE: test correlate k for "hotness" remapping
154     float D = dGGX(alpha2, NoH);
155     float G = vGGXWithCombinedDenominator(alpha2, NoV, NoL);
156     vec3 F = fSchlick(f0, VoH);
157     return F * (D * G);
158 }
159 
microfacedSpecularBrdfClearcoat(float f0,float alpha2,float NoL,float NoH,float VoH,float clearcoat,out float fcc)160 float microfacedSpecularBrdfClearcoat(
161     float f0, float alpha2, float NoL, float NoH, float VoH, float clearcoat, out float fcc)
162 {
163     // NOTE: test correlate k for "hotness" remapping
164     float D = dGGX(alpha2, NoH);
165     float G = vKelemen(NoH);
166     float F = fSchlickSingle(f0, VoH) * clearcoat;
167     fcc = F;
168     return F * D * G;
169 }
170 
microfacedSpecularBrdfAnisotropic(vec3 f0,float alpha,float NoL,float NoV,float NoH,float VoH,float ToL,float ToV,float ToH,float BoL,float BoV,float BoH,float anisotropy)171 vec3 microfacedSpecularBrdfAnisotropic(vec3 f0, float alpha, float NoL, float NoV, float NoH, float VoH, float ToL,
172     float ToV, float ToH, float BoL, float BoV, float BoH, float anisotropy)
173 {
174     const float at = max(alpha * (1.0 + anisotropy), CORE_BRDF_EPSILON);
175     const float ab = max(alpha * (1.0 - anisotropy), CORE_BRDF_EPSILON);
176 
177     float D = dGGXAnisotropic(at, ab, NoH, ToH, BoH, anisotropy);
178     float V = vGGXAnisotropic(at, ab, NoL, NoV, BoV, ToV, ToL, BoL, anisotropy);
179     vec3 F = fSchlick(f0, VoH);
180     return F * (V * D);
181 }
182 
diffuseCoeff()183 float diffuseCoeff()
184 {
185     return dLambert();
186 }
187 
188 #endif // SHADERS__COMMON__3D_DM_BRDF_COMMON_H
189