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_LIGHTING_COMMON_H
17 #define SHADERS__COMMON__3D_DM_LIGHTING_COMMON_H
18
19 #include "3d/shaders/common/3d_dm_brdf_common.h"
20 #include "3d/shaders/common/3d_dm_indirect_lighting_common.h"
21 #include "3d/shaders/common/3d_dm_shadowing_common.h"
22 #include "render/shaders/common/render_compatibility_common.h"
23
24 /*
25 * NOTE: Inplace data uCameras from descriptor sets
26 * Needs to be included after the descriptor sets are bound.
27 */
28
29 #define CORE3D_PBR_LIGHTING_EPSILON 0.0001
30 #define CORE_GEOMETRIC_SPECULAR_AA_ROUGHNESS 1
31
32 struct InputBrdfData {
33 vec4 f0;
34 vec3 diffuseColor;
35 float roughness;
36 float alpha2;
37 };
38
39 struct ShadingData {
40 vec3 pos;
41 vec3 N;
42 float NoV;
43 vec3 V;
44 float alpha2;
45 vec4 f0; // f0 = f0.xyz, f90 = f0.w
46 vec3 diffuseColor;
47 };
48
49 struct ShadingDataInplace {
50 vec3 pos;
51 vec3 N;
52 float NoV;
53 vec3 V;
54 float alpha2;
55 vec4 f0; // f0 = f0.xyz, f90 = f0.w
56 vec3 diffuseColor;
57 uint materialFlags;
58 uvec2 layers;
59 };
60
61 struct ClearcoatShadingVariables {
62 vec3 ccNormal;
63 float cc;
64 CORE_RELAXEDP float ccRoughness;
65 float ccAlpha2;
66 };
67
68 struct SheenShadingVariables {
69 CORE_RELAXEDP vec3 sheenColor;
70 CORE_RELAXEDP float sheenRoughness;
71 CORE_RELAXEDP float sheenColorMax;
72 CORE_RELAXEDP float sheenBRDFApprox;
73 };
74
75 struct AnisotropicShadingVariables {
76 float roughness;
77 float alpha;
78 float anisotropy;
79 float ToV;
80 float BoV;
81 vec3 anisotropicT;
82 vec3 anisotropicB;
83 };
84
85 struct SubsurfaceScatterShadingVariables {
86 vec3 scatterColor;
87 float scatterDistance;
88 float thickness;
89 };
90
91 #define CORE3D_GEOMETRIC_SPECULAR_AA_ROUGHNESS 1
92
GetFinalCorrectedRoughness(CORE_RELAXEDP in vec3 polygonNormal,CORE_RELAXEDP inout float roughness)93 void GetFinalCorrectedRoughness(CORE_RELAXEDP in vec3 polygonNormal, CORE_RELAXEDP inout float roughness)
94 {
95 #if (CORE_GEOMETRIC_SPECULAR_AA_ROUGHNESS == 1)
96 // reduce shading aliasing by increasing roughness based on the curvature of the geometry
97 const CORE_RELAXEDP vec3 normalDFdx = dFdx(polygonNormal);
98 const CORE_RELAXEDP vec3 normalDdFdy = dFdy(polygonNormal);
99 const CORE_RELAXEDP float geometricRoughness =
100 pow(clamp(max(dot(normalDFdx, normalDFdx), dot(normalDdFdy, normalDdFdy)), 0.0, 1.0), 0.333);
101 roughness = max(roughness, geometricRoughness);
102 #endif
103 roughness = clamp(roughness, CORE_BRDF_MIN_ROUGHNESS, 1.0);
104 }
105
AppendIndirectSheen(in SheenShadingVariables ssv,in CORE_RELAXEDP vec3 radianceSample,in CORE_RELAXEDP float alpha,inout CORE_RELAXEDP vec3 irradiance,inout CORE_RELAXEDP vec3 radiance)106 void AppendIndirectSheen(in SheenShadingVariables ssv, in CORE_RELAXEDP vec3 radianceSample,
107 in CORE_RELAXEDP float alpha, inout CORE_RELAXEDP vec3 irradiance, inout CORE_RELAXEDP vec3 radiance)
108 {
109 const vec3 sheenF = ssv.sheenColor * ssv.sheenBRDFApprox;
110 const float sheenAttenuation = 1.0 - (ssv.sheenColorMax * ssv.sheenBRDFApprox);
111 // energy compensation
112 irradiance *= sheenAttenuation;
113 radiance *= sheenAttenuation;
114
115 radiance += sheenF * radianceSample;
116 }
117
AppendIndirectClearcoat(in ClearcoatShadingVariables ccsv,in CORE_RELAXEDP vec3 radianceSample,in CORE_RELAXEDP float alpha,in vec3 V,inout CORE_RELAXEDP vec3 irradiance,inout CORE_RELAXEDP vec3 radiance)118 void AppendIndirectClearcoat(in ClearcoatShadingVariables ccsv, in CORE_RELAXEDP vec3 radianceSample,
119 in CORE_RELAXEDP float alpha, in vec3 V, inout CORE_RELAXEDP vec3 irradiance, inout CORE_RELAXEDP vec3 radiance)
120 {
121 const float ccNoV = clamp(dot(ccsv.ccNormal, V), CORE3D_PBR_LIGHTING_EPSILON, 1.0);
122
123 GetFinalCorrectedRoughness(ccsv.ccNormal, ccsv.ccAlpha2);
124
125 float ccRadianceFactor = EnvBRDFApproxNonmetal(ccsv.ccAlpha2, ccNoV);
126 float ccAttenuation = 1.0 - ccRadianceFactor;
127
128 // energy compensation
129 irradiance *= ccAttenuation;
130 radiance *= ccAttenuation;
131
132 // add clear coat radiance
133 radiance += radianceSample * ccRadianceFactor;
134 }
135
AppendIndirectTransmission(in CORE_RELAXEDP vec3 radianceSample,in CORE_RELAXEDP vec3 baseColor,in CORE_RELAXEDP float transmission,inout CORE_RELAXEDP vec3 irradiance)136 void AppendIndirectTransmission(in CORE_RELAXEDP vec3 radianceSample, in CORE_RELAXEDP vec3 baseColor,
137 in CORE_RELAXEDP float transmission, inout CORE_RELAXEDP vec3 irradiance)
138 {
139 // Approximate double refraction by assuming a solid sphere beneath the point.
140 const CORE_RELAXEDP vec3 Ft = radianceSample * baseColor.rgb;
141 irradiance *= (1.0 - transmission);
142 irradiance = mix(irradiance, Ft, transmission);
143 }
144
GetFinalSampledBrdfGeometricCorrection(in vec3 polygonNormal,inout InputBrdfData ibd)145 void GetFinalSampledBrdfGeometricCorrection(in vec3 polygonNormal, inout InputBrdfData ibd)
146 {
147 // NOTE: diffuse color is already premultiplied
148 CORE_RELAXEDP float roughness = ibd.roughness;
149 GetFinalCorrectedRoughness(polygonNormal, roughness);
150
151 const CORE_RELAXEDP float alpha = roughness * roughness;
152 ibd.alpha2 = alpha * alpha;
153 ibd.roughness = roughness;
154 }
155
GetFinalSampledBrdfGeometricCorrection(inout InputBrdfData ibd)156 void GetFinalSampledBrdfGeometricCorrection(inout InputBrdfData ibd)
157 {
158 const CORE_RELAXEDP float roughness = ibd.roughness;
159 const CORE_RELAXEDP float alpha = roughness * roughness;
160 ibd.alpha2 = alpha * alpha;
161 }
162
CalcBRDFMetallicRoughness(CORE_RELAXEDP vec4 baseColor,vec3 polygonNormal,CORE_RELAXEDP vec4 material)163 InputBrdfData CalcBRDFMetallicRoughness(CORE_RELAXEDP vec4 baseColor, vec3 polygonNormal, CORE_RELAXEDP vec4 material)
164 {
165 InputBrdfData bd;
166 {
167 const CORE_RELAXEDP float metallic = clamp(material.b, 0.0, 1.0);
168 bd.f0.xyz = mix(vec3(material.a), baseColor.rgb, metallic); // f0 reflectance
169 bd.f0.w = 1.0; // f90
170 // spec and glTF-Sample-Viewer don't use f0 here, but reflectance:
171 bd.diffuseColor = mix(baseColor.rgb * (1.0 - bd.f0.xyz), vec3(0.0), vec3(metallic));
172 bd.roughness = material.g;
173 }
174 // NOTE: diffuse color is already premultiplied
175 GetFinalSampledBrdfGeometricCorrection(polygonNormal, bd);
176
177 return bd;
178 }
179
CalcBRDFMetallicRoughness(CORE_RELAXEDP vec4 baseColor,CORE_RELAXEDP vec4 material)180 InputBrdfData CalcBRDFMetallicRoughness(CORE_RELAXEDP vec4 baseColor, CORE_RELAXEDP vec4 material)
181 {
182 InputBrdfData bd;
183 {
184 const CORE_RELAXEDP float metallic = clamp(material.b, 0.0, 1.0);
185 bd.f0.xyz = mix(vec3(material.a), baseColor.rgb, metallic); // f0 reflectance
186 bd.f0.w = 1.0; // f90
187 // spec and glTF-Sample-Viewer don't use f0 here, but reflectance:
188 bd.diffuseColor = mix(baseColor.rgb * (1.0 - bd.f0.xyz), vec3(0.0), vec3(metallic));
189 bd.roughness = material.g;
190 }
191 // NOTE: diffuse color is already premultiplied
192 GetFinalSampledBrdfGeometricCorrection(bd);
193
194 return bd;
195 }
196
CalcBRDFSpecularGlossiness(CORE_RELAXEDP vec4 baseColor,vec3 polygonNormal,CORE_RELAXEDP vec4 material)197 InputBrdfData CalcBRDFSpecularGlossiness(CORE_RELAXEDP vec4 baseColor, vec3 polygonNormal, CORE_RELAXEDP vec4 material)
198 {
199 InputBrdfData bd;
200 {
201 bd.f0.xyz = material.xyz; // f0 reflectance
202 bd.f0.w = 1.0; // f90
203 bd.diffuseColor = baseColor.rgb * (1.0 - max(bd.f0.x, max(bd.f0.y, bd.f0.z)));
204 bd.roughness = 1.0 - clamp(material.a, 0.0, 1.0);
205 }
206 // NOTE: diffuse color is already premultiplied
207 GetFinalSampledBrdfGeometricCorrection(polygonNormal, bd);
208
209 return bd;
210 }
211
CalcBRDFSpecular(CORE_RELAXEDP vec4 baseColor,vec3 polygonNormal,CORE_RELAXEDP vec4 material,CORE_RELAXEDP vec4 specular)212 InputBrdfData CalcBRDFSpecular(
213 CORE_RELAXEDP vec4 baseColor, vec3 polygonNormal, CORE_RELAXEDP vec4 material, CORE_RELAXEDP vec4 specular)
214 {
215 InputBrdfData bd;
216 {
217 const CORE_RELAXEDP float metallic = clamp(material.b, 0.0, 1.0);
218 // start with metal-rough dielectricSpecularF0:
219 bd.f0.xyz = mix(vec3(material.a), baseColor.rgb, metallic); // f0 reflectance
220
221 // tint by specular color and multiply by strength:
222 bd.f0.xyz = min(bd.f0.xyz * specular.rgb, vec3(1.0)) * specular.a;
223
224 // spec: f0, glTF-Sample-Viewer: dielectricSpecularF0. later would be closer to the metal-rough case
225 bd.diffuseColor = mix(baseColor.rgb * (1.0 - max(bd.f0.x, max(bd.f0.y, bd.f0.z))), vec3(0.0), vec3(metallic));
226
227 // final f0
228 bd.f0.xyz = mix(bd.f0.xyz, baseColor.rgb, metallic);
229 bd.f0.w = mix(specular.a, 1.0, metallic); // f90
230 bd.roughness = material.g;
231 }
232 // NOTE: diffuse color is already premultiplied
233 GetFinalSampledBrdfGeometricCorrection(polygonNormal, bd);
234
235 return bd;
236 }
237
CalcTbnMatrix(in vec3 polygonNormal,in vec4 tangentW)238 mat3 CalcTbnMatrix(in vec3 polygonNormal, in vec4 tangentW)
239 {
240 const vec3 tangent = normalize(tangentW.xyz);
241 const vec3 bitangent = cross(polygonNormal, tangent.xyz) * tangentW.w;
242 return mat3(tangent.xyz, bitangent.xyz, polygonNormal);
243 }
244
CalcFinalNormal(in mat3 tbn,in vec3 normal,in float normalScale)245 vec3 CalcFinalNormal(in mat3 tbn, in vec3 normal, in float normalScale)
246 {
247 vec3 n = normalize((2.0 * normal - 1.0) * vec3(normalScale, normalScale, 1.0f));
248 return normalize(tbn * n);
249 }
250
GetAnistropicReflectionVector(const vec3 V,const vec3 N,AnisotropicShadingVariables asv)251 vec3 GetAnistropicReflectionVector(const vec3 V, const vec3 N, AnisotropicShadingVariables asv)
252 {
253 // bent towards aniso direction should be re-evaluated with reference (based on filament)
254 const vec3 anisoDir = asv.anisotropy >= 0.0 ? asv.anisotropicB : asv.anisotropicT;
255 const vec3 anisoTangent = cross(anisoDir, V);
256 const vec3 anisoNormal = cross(anisoTangent, anisoDir);
257 // creates low bend factors for smooth surfaces
258 const float bendFactor = abs(asv.anisotropy) * clamp(asv.roughness * 5.0, 0.0, 1.0);
259 const vec3 bentNormal = normalize(mix(N, anisoNormal, bendFactor));
260
261 return reflect(-V, bentNormal);
262 }
263
GetShadowMatrix(const uint shadowCamIdx)264 mat4 GetShadowMatrix(const uint shadowCamIdx)
265 {
266 // NOTE: uCameras needs to be visible in the main shader which includes this file
267 return uCameras[shadowCamIdx].shadowViewProj;
268 }
269
CalculateLight(uint currLightIdx,vec3 materialDiffuseBRDF,vec3 L,float NoL,ShadingData sd,const uint materialFlags)270 vec3 CalculateLight(
271 uint currLightIdx, vec3 materialDiffuseBRDF, vec3 L, float NoL, ShadingData sd, const uint materialFlags)
272 {
273 const vec3 H = normalize(L + sd.V);
274 const float VoH = clamp(dot(sd.V, H), 0.0, 1.0);
275 const float NoH = clamp(dot(sd.N, H), 0.0, 1.0);
276
277 float extAttenuation = 1.0;
278 vec3 calculatedColor = vec3(0.0);
279 const float D = dGGX(sd.alpha2, NoH);
280 const float G = vGGXWithCombinedDenominator(sd.alpha2, sd.NoV, NoL);
281 const vec3 F = fSchlick(sd.f0, VoH);
282 const vec3 specContrib = F * (D * G);
283
284 // KHR_materials_specular: "In the diffuse component we have to account for the fact that F is now an RGB value.",
285 // but it already was? const vec3 diffuseContrib = (1.0 - max(F.x, (max(F.y, F.z)))) * materialDiffuseBRDF;
286 const vec3 diffuseContrib = (1.0 - F.xyz) * materialDiffuseBRDF;
287 calculatedColor += (diffuseContrib + specContrib * extAttenuation) * extAttenuation * NoL;
288 calculatedColor *= uLightData.lights[currLightIdx].color.xyz;
289 return calculatedColor;
290 }
291
CalculateLight(uint currLightIdx,vec3 materialDiffuseBRDF,vec3 L,float NoL,ShadingData sd,ClearcoatShadingVariables ccsv,SheenShadingVariables ssv,const uint materialFlags)292 vec3 CalculateLight(uint currLightIdx, vec3 materialDiffuseBRDF, vec3 L, float NoL, ShadingData sd,
293 ClearcoatShadingVariables ccsv, SheenShadingVariables ssv, const uint materialFlags)
294 {
295 const vec3 H = normalize(L + sd.V);
296 const float VoH = clamp(dot(sd.V, H), 0.0, 1.0);
297 const float NoH = clamp(dot(sd.N, H), 0.0, 1.0);
298
299 float extAttenuation = 1.0;
300 vec3 calculatedColor = vec3(0.0);
301 if ((materialFlags & CORE_MATERIAL_SHEEN_BIT) == CORE_MATERIAL_SHEEN_BIT) {
302 const float sheenD = dCharlie(ssv.sheenRoughness, NoH);
303 const float sheenV = vAshikhmin(sd.NoV, NoL);
304 const vec3 sheenSpec = ssv.sheenColor * (sheenD * sheenV); // F = 1.0
305
306 extAttenuation *= (1.0 - (ssv.sheenColorMax * ssv.sheenBRDFApprox));
307 calculatedColor += (sheenSpec * NoL);
308 }
309 if ((materialFlags & CORE_MATERIAL_CLEARCOAT_BIT) == CORE_MATERIAL_CLEARCOAT_BIT) {
310 const float ccNoL = clamp(dot(ccsv.ccNormal, L), CORE3D_PBR_LIGHTING_EPSILON, 1.0);
311 const float ccNoH = clamp(dot(ccsv.ccNormal, H), CORE3D_PBR_LIGHTING_EPSILON, 1.0);
312 const float ccLoH = clamp(dot(L, H), CORE3D_PBR_LIGHTING_EPSILON, 1.0);
313 const float ccNoV = clamp(dot(ccsv.ccNormal, sd.V), CORE3D_PBR_LIGHTING_EPSILON, 1.0);
314 const float ccf0 = 0.04;
315
316 const float ccD = dGGX(ccsv.ccAlpha2, ccNoH);
317 const float ccG = vKelemen(ccLoH);
318 const float ccF = fSchlickSingle(ccf0, ccNoV) * ccsv.cc; // NOTE: cc in ccF
319 const float ccSpec = ccF * ccD * ccG;
320
321 extAttenuation *= (1.0 - ccF);
322 calculatedColor += vec3(ccSpec * ccNoL);
323 }
324 const float D = dGGX(sd.alpha2, NoH);
325 const float G = vGGXWithCombinedDenominator(sd.alpha2, sd.NoV, NoL);
326 const vec3 F = fSchlick(sd.f0, VoH);
327 const vec3 specContrib = F * (D * G);
328
329 // KHR_materials_specular: "In the diffuse component we have to account for the fact that F is now an RGB value.",
330 // but it already was? const vec3 diffuseContrib = (1.0 - max(F.x, (max(F.y, F.z)))) * materialDiffuseBRDF;
331 const vec3 diffuseContrib = (1.0 - F.xyz) * materialDiffuseBRDF;
332 calculatedColor += (diffuseContrib + specContrib * extAttenuation) * extAttenuation * NoL;
333 calculatedColor *= uLightData.lights[currLightIdx].color.xyz;
334 return calculatedColor;
335 }
336
CalculateLighting(ShadingData sd,const uint materialFlags)337 vec3 CalculateLighting(ShadingData sd, const uint materialFlags)
338 {
339 const vec3 materialDiffuseBRDF = sd.diffuseColor * diffuseCoeff();
340 vec3 color = vec3(0.0);
341 const uint directionalLightCount = uLightData.directionalLightCount;
342 const uint directionalLightBeginIndex = uLightData.directionalLightBeginIndex;
343 const vec4 atlasSizeInvSize = uLightData.atlasSizeInvSize;
344 for (uint lightIdx = 0; lightIdx < directionalLightCount; ++lightIdx) {
345 const uint currLightIdx = directionalLightBeginIndex + lightIdx;
346 const vec3 L = -uLightData.lights[currLightIdx].dir.xyz; // normalization already done in c-code
347 const float NoL = clamp(dot(sd.N, L), 0.0, 1.0);
348 // NOTE: could check for NoL > 0.0 and NoV > 0.0
349 CORE_RELAXEDP float shadowCoeff = 1.0;
350 if ((materialFlags & CORE_MATERIAL_SHADOW_RECEIVER_BIT) == CORE_MATERIAL_SHADOW_RECEIVER_BIT) {
351 const uvec4 lightFlags = uLightData.lights[currLightIdx].flags;
352 if ((lightFlags.x & CORE_LIGHT_USAGE_SHADOW_LIGHT_BIT) == CORE_LIGHT_USAGE_SHADOW_LIGHT_BIT) {
353 const vec4 shadowCoord = GetShadowMatrix(lightFlags.y) * vec4(sd.pos.xyz, 1.0);
354 const vec4 shadowFactors = uLightData.lights[currLightIdx].shadowFactors;
355 if ((CORE_LIGHTING_FLAGS & CORE_LIGHTING_SHADOW_TYPE_VSM_BIT) == CORE_LIGHTING_SHADOW_TYPE_VSM_BIT) {
356 shadowCoeff = CalcVsmShadow(
357 uSampColorShadow, shadowCoord, NoL, shadowFactors, atlasSizeInvSize, lightFlags.zw);
358 } else {
359 shadowCoeff = CalcPcfShadow(
360 uSampDepthShadow, shadowCoord, NoL, shadowFactors, atlasSizeInvSize, lightFlags.zw);
361 }
362 }
363 }
364 color += CalculateLight(currLightIdx, materialDiffuseBRDF, L, NoL, sd, materialFlags) * shadowCoeff;
365 }
366
367 if ((CORE_LIGHTING_FLAGS & CORE_LIGHTING_SPOT_ENABLED_BIT) == CORE_LIGHTING_SPOT_ENABLED_BIT) {
368 const uint spotLightCount = uLightData.spotLightCount;
369 const uint spotLightLightBeginIndex = uLightData.spotLightBeginIndex;
370 for (uint spotIdx = 0; spotIdx < spotLightCount; ++spotIdx) {
371 const uint currLightIdx = spotLightLightBeginIndex + spotIdx;
372
373 const vec3 pointToLight = uLightData.lights[currLightIdx].pos.xyz - sd.pos.xyz;
374 const float dist = length(pointToLight);
375 const vec3 L = pointToLight / dist;
376 const float NoL = clamp(dot(sd.N, L), 0.0, 1.0);
377 // NOTE: could check for NoL > 0.0 and NoV > 0.0
378 CORE_RELAXEDP float shadowCoeff = 1.0;
379 if ((materialFlags & CORE_MATERIAL_SHADOW_RECEIVER_BIT) == CORE_MATERIAL_SHADOW_RECEIVER_BIT) {
380 const uvec4 lightFlags = uLightData.lights[currLightIdx].flags;
381 if ((lightFlags.x & CORE_LIGHT_USAGE_SHADOW_LIGHT_BIT) == CORE_LIGHT_USAGE_SHADOW_LIGHT_BIT) {
382 const vec4 shadowCoord = GetShadowMatrix(lightFlags.y) * vec4(sd.pos.xyz, 1.0);
383 const vec4 shadowFactors = uLightData.lights[currLightIdx].shadowFactors;
384 if ((CORE_LIGHTING_FLAGS & CORE_LIGHTING_SHADOW_TYPE_VSM_BIT) ==
385 CORE_LIGHTING_SHADOW_TYPE_VSM_BIT) {
386 shadowCoeff = CalcVsmShadow(
387 uSampColorShadow, shadowCoord, NoL, shadowFactors, atlasSizeInvSize, lightFlags.zw);
388 } else {
389 shadowCoeff = CalcPcfShadow(
390 uSampDepthShadow, shadowCoord, NoL, shadowFactors, atlasSizeInvSize, lightFlags.zw);
391 }
392 }
393 }
394
395 const float lightAngleScale = uLightData.lights[currLightIdx].spotLightParams.x;
396 const float lightAngleOffset = uLightData.lights[currLightIdx].spotLightParams.y;
397 // See: https://github.com/KhronosGroup/glTF/tree/master/extensions/2.0/Khronos/KHR_lights_punctual
398 const float cd = dot(uLightData.lights[currLightIdx].dir.xyz, -L);
399 const float angularAttenuation = clamp(cd * lightAngleScale + lightAngleOffset, 0.0, 1.0);
400
401 const float range = uLightData.lights[currLightIdx].dir.w;
402 const float attenuation = max(min(1.0 - pow(dist / range, 4.0), 1.0), 0.0) / (dist * dist);
403 color += CalculateLight(currLightIdx, materialDiffuseBRDF, L, NoL, sd, materialFlags) *
404 (angularAttenuation * angularAttenuation * attenuation) * shadowCoeff;
405 }
406 }
407
408 if ((CORE_LIGHTING_FLAGS & CORE_LIGHTING_POINT_ENABLED_BIT) == CORE_LIGHTING_POINT_ENABLED_BIT) {
409 const uint pointLightCount = uLightData.pointLightCount;
410 const uint pointLightBeginIndex = uLightData.pointLightBeginIndex;
411 for (uint pointIdx = 0; pointIdx < pointLightCount; ++pointIdx) {
412 const uint currLightIdx = pointLightBeginIndex + pointIdx;
413
414 const vec3 pointToLight = uLightData.lights[currLightIdx].pos.xyz - sd.pos.xyz;
415 const float dist = length(pointToLight);
416 const vec3 L = pointToLight / dist;
417 const float NoL = clamp(dot(sd.N, L), 0.0, 1.0);
418 const float range = uLightData.lights[currLightIdx].dir.w;
419 const float attenuation = max(min(1.0 - pow(dist / range, 4.0), 1.0), 0.0) / (dist * dist);
420 // NOTE: could check for NoL > 0.0 and NoV > 0.0
421 color += CalculateLight(currLightIdx, materialDiffuseBRDF, L, NoL, sd, materialFlags) * attenuation;
422 }
423 }
424
425 return color;
426 }
427
CalculateLighting(ShadingData sd,ClearcoatShadingVariables ccsv,SheenShadingVariables ssv,const uint materialFlags)428 vec3 CalculateLighting(
429 ShadingData sd, ClearcoatShadingVariables ccsv, SheenShadingVariables ssv, const uint materialFlags)
430 {
431 const vec3 materialDiffuseBRDF = sd.diffuseColor * diffuseCoeff();
432 vec3 color = vec3(0.0);
433 const uint directionalLightCount = uLightData.directionalLightCount;
434 const uint directionalLightBeginIndex = uLightData.directionalLightBeginIndex;
435 const vec4 atlasSizeInvSize = uLightData.atlasSizeInvSize;
436 for (uint lightIdx = 0; lightIdx < directionalLightCount; ++lightIdx) {
437 const uint currLightIdx = directionalLightBeginIndex + lightIdx;
438 const vec3 L = -uLightData.lights[currLightIdx].dir.xyz; // normalization already done in c-code
439 const float NoL = clamp(dot(sd.N, L), 0.0, 1.0);
440 // NOTE: could check for NoL > 0.0 and NoV > 0.0
441 CORE_RELAXEDP float shadowCoeff = 1.0;
442 if ((materialFlags & CORE_MATERIAL_SHADOW_RECEIVER_BIT) == CORE_MATERIAL_SHADOW_RECEIVER_BIT) {
443 const uvec4 lightFlags = uLightData.lights[currLightIdx].flags;
444 if ((lightFlags.x & CORE_LIGHT_USAGE_SHADOW_LIGHT_BIT) == CORE_LIGHT_USAGE_SHADOW_LIGHT_BIT) {
445 const vec4 shadowCoord = GetShadowMatrix(lightFlags.y) * vec4(sd.pos.xyz, 1.0);
446 const vec4 shadowFactors = uLightData.lights[currLightIdx].shadowFactors;
447 if ((CORE_LIGHTING_FLAGS & CORE_LIGHTING_SHADOW_TYPE_VSM_BIT) == CORE_LIGHTING_SHADOW_TYPE_VSM_BIT) {
448 shadowCoeff = CalcVsmShadow(
449 uSampColorShadow, shadowCoord, NoL, shadowFactors, atlasSizeInvSize, lightFlags.zw);
450 } else {
451 shadowCoeff = CalcPcfShadow(
452 uSampDepthShadow, shadowCoord, NoL, shadowFactors, atlasSizeInvSize, lightFlags.zw);
453 }
454 }
455 }
456 color += CalculateLight(currLightIdx, materialDiffuseBRDF, L, NoL, sd, ccsv, ssv, materialFlags) * shadowCoeff;
457 }
458
459 if ((CORE_LIGHTING_FLAGS & CORE_LIGHTING_SPOT_ENABLED_BIT) == CORE_LIGHTING_SPOT_ENABLED_BIT) {
460 const uint spotLightCount = uLightData.spotLightCount;
461 const uint spotLightLightBeginIndex = uLightData.spotLightBeginIndex;
462 for (uint spotIdx = 0; spotIdx < spotLightCount; ++spotIdx) {
463 const uint currLightIdx = spotLightLightBeginIndex + spotIdx;
464
465 const vec3 pointToLight = uLightData.lights[currLightIdx].pos.xyz - sd.pos.xyz;
466 const float dist = length(pointToLight);
467 const vec3 L = pointToLight / dist;
468 const float NoL = clamp(dot(sd.N, L), 0.0, 1.0);
469 // NOTE: could check for NoL > 0.0 and NoV > 0.0
470 CORE_RELAXEDP float shadowCoeff = 1.0;
471 if ((materialFlags & CORE_MATERIAL_SHADOW_RECEIVER_BIT) == CORE_MATERIAL_SHADOW_RECEIVER_BIT) {
472 const uvec4 lightFlags = uLightData.lights[currLightIdx].flags;
473 if ((lightFlags.x & CORE_LIGHT_USAGE_SHADOW_LIGHT_BIT) == CORE_LIGHT_USAGE_SHADOW_LIGHT_BIT) {
474 const vec4 shadowCoord = GetShadowMatrix(lightFlags.y) * vec4(sd.pos.xyz, 1.0);
475 const vec4 shadowFactors = uLightData.lights[currLightIdx].shadowFactors;
476 if ((CORE_LIGHTING_FLAGS & CORE_LIGHTING_SHADOW_TYPE_VSM_BIT) ==
477 CORE_LIGHTING_SHADOW_TYPE_VSM_BIT) {
478 shadowCoeff = CalcVsmShadow(
479 uSampColorShadow, shadowCoord, NoL, shadowFactors, atlasSizeInvSize, lightFlags.zw);
480 } else {
481 shadowCoeff = CalcPcfShadow(
482 uSampDepthShadow, shadowCoord, NoL, shadowFactors, atlasSizeInvSize, lightFlags.zw);
483 }
484 }
485 }
486
487 const float lightAngleScale = uLightData.lights[currLightIdx].spotLightParams.x;
488 const float lightAngleOffset = uLightData.lights[currLightIdx].spotLightParams.y;
489 // See: https://github.com/KhronosGroup/glTF/tree/master/extensions/2.0/Khronos/KHR_lights_punctual
490 const float cd = dot(uLightData.lights[currLightIdx].dir.xyz, -L);
491 const float angularAttenuation = clamp(cd * lightAngleScale + lightAngleOffset, 0.0, 1.0);
492
493 const float range = uLightData.lights[currLightIdx].dir.w;
494 const float attenuation = max(min(1.0 - pow(dist / range, 4.0), 1.0), 0.0) / (dist * dist);
495 color += CalculateLight(currLightIdx, materialDiffuseBRDF, L, NoL, sd, ccsv, ssv, materialFlags) *
496 (angularAttenuation * angularAttenuation * attenuation) * shadowCoeff;
497 }
498 }
499 if ((CORE_LIGHTING_FLAGS & CORE_LIGHTING_POINT_ENABLED_BIT) == CORE_LIGHTING_POINT_ENABLED_BIT) {
500 const uint pointLightCount = uLightData.pointLightCount;
501 const uint pointLightBeginIndex = uLightData.pointLightBeginIndex;
502 for (uint pointIdx = 0; pointIdx < pointLightCount; ++pointIdx) {
503 const uint currLightIdx = pointLightBeginIndex + pointIdx;
504
505 const vec3 pointToLight = uLightData.lights[currLightIdx].pos.xyz - sd.pos.xyz;
506 const float dist = length(pointToLight);
507 const vec3 L = pointToLight / dist;
508 const float NoL = clamp(dot(sd.N, L), 0.0, 1.0);
509 const float range = uLightData.lights[currLightIdx].dir.w;
510 const float attenuation = max(min(1.0 - pow(dist / range, 4.0), 1.0), 0.0) / (dist * dist);
511 // NOTE: could check for NoL > 0.0 and NoV > 0.0
512 color +=
513 CalculateLight(currLightIdx, materialDiffuseBRDF, L, NoL, sd, ccsv, ssv, materialFlags) * attenuation;
514 }
515 }
516
517 return color;
518 }
519
CalculateLight(uint currLightIdx,vec3 materialDiffuseBRDF,vec3 L,float NoL,ShadingData sd,AnisotropicShadingVariables asv,ClearcoatShadingVariables ccsv,SheenShadingVariables ssv,const uint materialFlags)520 vec3 CalculateLight(uint currLightIdx, vec3 materialDiffuseBRDF, vec3 L, float NoL, ShadingData sd,
521 AnisotropicShadingVariables asv, ClearcoatShadingVariables ccsv, SheenShadingVariables ssv,
522 const uint materialFlags)
523 {
524 const vec3 H = normalize(L + sd.V);
525 const float VoH = dot(sd.V, H);
526 const float NoH = dot(sd.N, H);
527
528 vec3 calculatedColor = vec3(0.0);
529 float extAttenuation = 1.0;
530 if ((materialFlags & CORE_MATERIAL_SHEEN_BIT) == CORE_MATERIAL_SHEEN_BIT) {
531 const float sheenD = dCharlie(ssv.sheenRoughness, NoH);
532 const float sheenV = vAshikhmin(sd.NoV, NoL);
533 const vec3 sheenSpec = ssv.sheenColor * (sheenD * sheenV); // F = 1.0
534
535 extAttenuation *= (1.0 - (ssv.sheenColorMax * ssv.sheenBRDFApprox));
536 calculatedColor += (sheenSpec * NoL);
537 }
538 if ((materialFlags & CORE_MATERIAL_CLEARCOAT_BIT) == CORE_MATERIAL_CLEARCOAT_BIT) {
539 const float ccNoL = clamp(dot(ccsv.ccNormal, L), CORE3D_PBR_LIGHTING_EPSILON, 1.0);
540 const float ccNoH = clamp(dot(ccsv.ccNormal, H), 0.0, 1.0);
541 const float ccf0 = 0.04;
542
543 const float ccD = dGGX(ccsv.ccAlpha2, ccNoH);
544 const float ccG = vKelemen(ccNoH);
545 const float ccF = fSchlickSingle(ccf0, VoH) * ccsv.cc;
546 const float ccSpec = ccF * ccD * ccG;
547
548 extAttenuation *= (1.0 - ccF);
549 calculatedColor += vec3(ccSpec * ccNoL);
550 }
551
552 const float ToL = dot(asv.anisotropicT, L);
553 const float ToH = dot(asv.anisotropicT, H);
554 const float BoL = dot(asv.anisotropicB, L);
555 const float BoH = dot(asv.anisotropicB, H);
556 const float at = max(asv.alpha * (1.0 + asv.anisotropy), CORE3D_PBR_LIGHTING_EPSILON);
557 const float ab = max(asv.alpha * (1.0 - asv.anisotropy), CORE3D_PBR_LIGHTING_EPSILON);
558
559 const float D = dGGXAnisotropic(at, ab, NoH, ToH, BoH, asv.anisotropy);
560 const float G = vGGXAnisotropic(at, ab, NoL, sd.NoV, ToL, asv.ToV, BoL, asv.BoV, asv.anisotropy);
561 const vec3 F = fSchlick(sd.f0, VoH);
562 const vec3 specContrib = F * (D * G);
563
564 // KHR_materials_specular: "In the diffuse component we have to account for the fact that F is now an RGB value.",
565 // but it already was? const vec3 diffuseContrib = (1.0 - max(F.x, (max(F.y, F.z)))) * materialDiffuseBRDF;
566 const vec3 diffuseContrib = (1.0 - F.xyz) * materialDiffuseBRDF;
567 calculatedColor += (diffuseContrib + specContrib * extAttenuation) * extAttenuation * NoL;
568 calculatedColor *= uLightData.lights[currLightIdx].color.xyz;
569 return calculatedColor;
570 }
571
CalculateLighting(ShadingData sd,AnisotropicShadingVariables asv,ClearcoatShadingVariables ccsv,SheenShadingVariables ssv,const uint materialFlags)572 vec3 CalculateLighting(ShadingData sd, AnisotropicShadingVariables asv, ClearcoatShadingVariables ccsv,
573 SheenShadingVariables ssv, const uint materialFlags)
574 {
575 const vec3 materialDiffuseBRDF = sd.diffuseColor * diffuseCoeff();
576 vec3 color = vec3(0.0);
577 const uint directionalLightCount = uLightData.directionalLightCount;
578 const uint directionalLightBeginIndex = uLightData.directionalLightBeginIndex;
579 const vec4 atlasSizeInvSize = uLightData.atlasSizeInvSize;
580 for (uint lightIdx = 0; lightIdx < directionalLightCount; ++lightIdx) {
581 const uint currLightIdx = directionalLightBeginIndex + lightIdx;
582 const vec3 L = -uLightData.lights[currLightIdx].dir.xyz; // normalization already done in c-code
583 const float NoL = clamp(dot(sd.N, L), 0.0, 1.0);
584 // NOTE: could check for NoL > 0.0 and NoV > 0.0
585 CORE_RELAXEDP float shadowCoeff = 1.0;
586 if ((materialFlags & CORE_MATERIAL_SHADOW_RECEIVER_BIT) == CORE_MATERIAL_SHADOW_RECEIVER_BIT) {
587 const uvec4 lightFlags = uLightData.lights[currLightIdx].flags;
588 if ((lightFlags.x & CORE_LIGHT_USAGE_SHADOW_LIGHT_BIT) == CORE_LIGHT_USAGE_SHADOW_LIGHT_BIT) {
589 const vec4 shadowCoord = GetShadowMatrix(lightFlags.y) * vec4(sd.pos.xyz, 1.0);
590 const vec4 shadowFactors = uLightData.lights[currLightIdx].shadowFactors;
591 if ((CORE_LIGHTING_FLAGS & CORE_LIGHTING_SHADOW_TYPE_VSM_BIT) == CORE_LIGHTING_SHADOW_TYPE_VSM_BIT) {
592 shadowCoeff = CalcVsmShadow(
593 uSampColorShadow, shadowCoord, NoL, shadowFactors, atlasSizeInvSize, lightFlags.zw);
594 } else {
595 shadowCoeff = CalcPcfShadow(
596 uSampDepthShadow, shadowCoord, NoL, shadowFactors, atlasSizeInvSize, lightFlags.zw);
597 }
598 }
599 }
600 color +=
601 CalculateLight(currLightIdx, materialDiffuseBRDF, L, NoL, sd, asv, ccsv, ssv, materialFlags) * shadowCoeff;
602 }
603
604 if ((CORE_LIGHTING_FLAGS & CORE_LIGHTING_SPOT_ENABLED_BIT) == CORE_LIGHTING_SPOT_ENABLED_BIT) {
605 const uint spotLightCount = uLightData.spotLightCount;
606 const uint spotLightLightBeginIndex = uLightData.spotLightBeginIndex;
607 for (uint spotIdx = 0; spotIdx < spotLightCount; ++spotIdx) {
608 const uint currLightIdx = spotLightLightBeginIndex + spotIdx;
609
610 const vec3 pointToLight = uLightData.lights[currLightIdx].pos.xyz - sd.pos.xyz;
611 const float dist = length(pointToLight);
612 const vec3 L = pointToLight / dist;
613 const float NoL = clamp(dot(sd.N, L), 0.0, 1.0);
614 // NOTE: could check for NoL > 0.0 and NoV > 0.0
615 CORE_RELAXEDP float shadowCoeff = 1.0;
616 if ((materialFlags & CORE_MATERIAL_SHADOW_RECEIVER_BIT) == CORE_MATERIAL_SHADOW_RECEIVER_BIT) {
617 const uvec4 lightFlags = uLightData.lights[currLightIdx].flags;
618 if ((lightFlags.x & CORE_LIGHT_USAGE_SHADOW_LIGHT_BIT) == CORE_LIGHT_USAGE_SHADOW_LIGHT_BIT) {
619 const vec4 shadowCoord = GetShadowMatrix(lightFlags.y) * vec4(sd.pos.xyz, 1.0);
620 const vec4 shadowFactors = uLightData.lights[currLightIdx].shadowFactors;
621 if ((CORE_LIGHTING_FLAGS & CORE_LIGHTING_SHADOW_TYPE_VSM_BIT) ==
622 CORE_LIGHTING_SHADOW_TYPE_VSM_BIT) {
623 shadowCoeff = CalcVsmShadow(
624 uSampColorShadow, shadowCoord, NoL, shadowFactors, atlasSizeInvSize, lightFlags.zw);
625 } else {
626 shadowCoeff = CalcPcfShadow(
627 uSampDepthShadow, shadowCoord, NoL, shadowFactors, atlasSizeInvSize, lightFlags.zw);
628 }
629 }
630 }
631
632 const float lightAngleScale = uLightData.lights[currLightIdx].spotLightParams.x;
633 const float lightAngleOffset = uLightData.lights[currLightIdx].spotLightParams.y;
634 // See: https://github.com/KhronosGroup/glTF/tree/master/extensions/2.0/Khronos/KHR_lights_punctual
635 const float cd = dot(uLightData.lights[currLightIdx].dir.xyz, -L);
636 const float angularAttenuation = clamp(cd * lightAngleScale + lightAngleOffset, 0.0, 1.0);
637
638 const float range = uLightData.lights[currLightIdx].dir.w;
639 const float attenuation = max(min(1.0 - pow(dist / range, 4.0), 1.0), 0.0) / (dist * dist);
640 color += CalculateLight(currLightIdx, materialDiffuseBRDF, L, NoL, sd, asv, ccsv, ssv, materialFlags) *
641 (angularAttenuation * angularAttenuation * attenuation) * shadowCoeff;
642 }
643 }
644 if ((CORE_LIGHTING_FLAGS & CORE_LIGHTING_POINT_ENABLED_BIT) == CORE_LIGHTING_POINT_ENABLED_BIT) {
645 const uint pointLightCount = uLightData.pointLightCount;
646 const uint pointLightBeginIndex = uLightData.pointLightBeginIndex;
647 for (uint pointIdx = 0; pointIdx < pointLightCount; ++pointIdx) {
648 const uint currLightIdx = pointLightBeginIndex + pointIdx;
649
650 const vec3 pointToLight = uLightData.lights[currLightIdx].pos.xyz - sd.pos.xyz;
651 const float dist = length(pointToLight);
652 const vec3 L = pointToLight / dist;
653 const float NoL = clamp(dot(sd.N, L), 0.0, 1.0);
654 const float range = uLightData.lights[currLightIdx].dir.w;
655 const float attenuation = max(min(1.0 - pow(dist / range, 4.0), 1.0), 0.0) / (dist * dist);
656 // NOTE: could check for NoL > 0.0 and NoV > 0.0
657 color += CalculateLight(currLightIdx, materialDiffuseBRDF, L, NoL, sd, asv, ccsv, ssv, materialFlags) *
658 attenuation;
659 }
660 }
661
662 return color;
663 }
664
CalculateLight(uint currLightIdx,vec3 materialDiffuseBRDF,vec3 L,float NoL,ShadingData sd,SubsurfaceScatterShadingVariables ssssv,const uint materialFlags)665 vec3 CalculateLight(uint currLightIdx, vec3 materialDiffuseBRDF, vec3 L, float NoL, ShadingData sd,
666 SubsurfaceScatterShadingVariables ssssv, const uint materialFlags)
667 {
668 const vec3 H = normalize(L + sd.V);
669 const float VoH = clamp(dot(sd.V, H), 0.0, 1.0);
670 const float NoH = clamp(dot(sd.N, H), 0.0, 1.0);
671
672 float extAttenuation = 1.0;
673 vec3 calculatedColor = vec3(0.0);
674 const float D = dGGX(sd.alpha2, NoH);
675 const float G = vGGXWithCombinedDenominator(sd.alpha2, sd.NoV, NoL);
676 const vec3 F = fSchlick(sd.f0, VoH);
677 const vec3 specContrib = F * (D * G);
678
679 const vec3 diffuseContrib = (1.0 - F.xyz) * materialDiffuseBRDF;
680 calculatedColor += (diffuseContrib + specContrib * extAttenuation) * extAttenuation * NoL;
681
682 // subsurface scattering
683 // Use a spherical gaussian approximation of pow() for forwardScattering
684 // We could include distortion by adding shading_normal * distortion to light.l
685 float scatterVoH = clamp(dot(sd.V, -L), 0.0, 1.0);
686 // map distance to gaussian sharpness, the lower the distance, the higher the sharpness
687 float sharpness = 10000.0 - 10000.0 * ssssv.scatterDistance;
688 float forwardScatter = exp2(scatterVoH * sharpness - sharpness);
689 float backScatter = clamp(NoL * ssssv.thickness + (1.0 - ssssv.thickness), 0.0, 1.0) * 0.5;
690 float subsurface = mix(backScatter, 1.0, forwardScatter) * (1.0 - ssssv.thickness);
691 calculatedColor += ssssv.scatterColor * (subsurface * dLambert());
692
693 // TODO: apply attenuation to the transmitted light, i.e. uLightData.lights[currLightIdx].attenuation
694 return (calculatedColor * uLightData.lights[currLightIdx].color.rgb);
695 }
696
CalculateLighting(ShadingData sd,SubsurfaceScatterShadingVariables sssv,const uint materialFlags)697 vec3 CalculateLighting(ShadingData sd, SubsurfaceScatterShadingVariables sssv, const uint materialFlags)
698 {
699 const vec3 materialDiffuseBRDF = sd.diffuseColor * diffuseCoeff();
700 vec3 color = vec3(0.0);
701 const uint directionalLightCount = uLightData.directionalLightCount;
702 const uint directionalLightBeginIndex = uLightData.directionalLightBeginIndex;
703 const vec4 atlasSizeInvSize = uLightData.atlasSizeInvSize;
704 for (uint lightIdx = 0; lightIdx < directionalLightCount; ++lightIdx) {
705 const uint currLightIdx = directionalLightBeginIndex + lightIdx;
706 const vec3 L = -uLightData.lights[currLightIdx].dir.xyz; // normalization already done in c-code
707 const float NoL = clamp(dot(sd.N, L), 0.0, 1.0);
708 // NOTE: could check for NoL > 0.0 and NoV > 0.0
709 CORE_RELAXEDP float shadowCoeff = 1.0;
710 if ((materialFlags & CORE_MATERIAL_SHADOW_RECEIVER_BIT) == CORE_MATERIAL_SHADOW_RECEIVER_BIT) {
711 const uvec4 lightFlags = uLightData.lights[currLightIdx].flags;
712 if ((lightFlags.x & CORE_LIGHT_USAGE_SHADOW_LIGHT_BIT) == CORE_LIGHT_USAGE_SHADOW_LIGHT_BIT) {
713 const vec4 shadowCoord = GetShadowMatrix(lightFlags.y) * vec4(sd.pos.xyz, 1.0);
714 const vec4 shadowFactors = uLightData.lights[currLightIdx].shadowFactors;
715 if ((CORE_LIGHTING_FLAGS & CORE_LIGHTING_SHADOW_TYPE_VSM_BIT) == CORE_LIGHTING_SHADOW_TYPE_VSM_BIT) {
716 shadowCoeff = CalcVsmShadow(
717 uSampColorShadow, shadowCoord, NoL, shadowFactors, atlasSizeInvSize, lightFlags.zw);
718 } else {
719 shadowCoeff = CalcPcfShadow(
720 uSampDepthShadow, shadowCoord, NoL, shadowFactors, atlasSizeInvSize, lightFlags.zw);
721 }
722 }
723 }
724 color += CalculateLight(currLightIdx, materialDiffuseBRDF, L, NoL, sd, sssv, materialFlags) * shadowCoeff;
725 }
726
727 if ((CORE_LIGHTING_FLAGS & CORE_LIGHTING_SPOT_ENABLED_BIT) == CORE_LIGHTING_SPOT_ENABLED_BIT) {
728 const uint spotLightCount = uLightData.spotLightCount;
729 const uint spotLightLightBeginIndex = uLightData.spotLightBeginIndex;
730 for (uint spotIdx = 0; spotIdx < spotLightCount; ++spotIdx) {
731 const uint currLightIdx = spotLightLightBeginIndex + spotIdx;
732
733 const vec3 pointToLight = uLightData.lights[currLightIdx].pos.xyz - sd.pos.xyz;
734 const float dist = length(pointToLight);
735 const vec3 L = pointToLight / dist;
736 const float NoL = clamp(dot(sd.N, L), 0.0, 1.0);
737 // NOTE: could check for NoL > 0.0 and NoV > 0.0
738 CORE_RELAXEDP float shadowCoeff = 1.0;
739 if ((materialFlags & CORE_MATERIAL_SHADOW_RECEIVER_BIT) == CORE_MATERIAL_SHADOW_RECEIVER_BIT) {
740 const uvec4 lightFlags = uLightData.lights[currLightIdx].flags;
741 if ((lightFlags.x & CORE_LIGHT_USAGE_SHADOW_LIGHT_BIT) == CORE_LIGHT_USAGE_SHADOW_LIGHT_BIT) {
742 const vec4 shadowCoord = GetShadowMatrix(lightFlags.y) * vec4(sd.pos.xyz, 1.0);
743 const vec4 shadowFactors = uLightData.lights[currLightIdx].shadowFactors;
744 if ((CORE_LIGHTING_FLAGS & CORE_LIGHTING_SHADOW_TYPE_VSM_BIT) ==
745 CORE_LIGHTING_SHADOW_TYPE_VSM_BIT) {
746 shadowCoeff = CalcVsmShadow(
747 uSampColorShadow, shadowCoord, NoL, shadowFactors, atlasSizeInvSize, lightFlags.zw);
748 } else {
749 shadowCoeff = CalcPcfShadow(
750 uSampDepthShadow, shadowCoord, NoL, shadowFactors, atlasSizeInvSize, lightFlags.zw);
751 }
752 }
753 }
754
755 const float lightAngleScale = uLightData.lights[currLightIdx].spotLightParams.x;
756 const float lightAngleOffset = uLightData.lights[currLightIdx].spotLightParams.y;
757 // See: https://github.com/KhronosGroup/glTF/tree/master/extensions/2.0/Khronos/KHR_lights_punctual
758 const float cd = dot(uLightData.lights[currLightIdx].dir.xyz, -L);
759 const float angularAttenuation = clamp(cd * lightAngleScale + lightAngleOffset, 0.0, 1.0);
760
761 const float range = uLightData.lights[currLightIdx].dir.w;
762 const float attenuation = max(min(1.0 - pow(dist / range, 4.0), 1.0), 0.0) / (dist * dist);
763 color += CalculateLight(currLightIdx, materialDiffuseBRDF, L, NoL, sd, sssv, materialFlags) *
764 (angularAttenuation * angularAttenuation * attenuation) * shadowCoeff;
765 }
766 }
767 if ((CORE_LIGHTING_FLAGS & CORE_LIGHTING_POINT_ENABLED_BIT) == CORE_LIGHTING_POINT_ENABLED_BIT) {
768 const uint pointLightCount = uLightData.pointLightCount;
769 const uint pointLightBeginIndex = uLightData.pointLightBeginIndex;
770 for (uint pointIdx = 0; pointIdx < pointLightCount; ++pointIdx) {
771 const uint currLightIdx = pointLightBeginIndex + pointIdx;
772
773 const vec3 pointToLight = uLightData.lights[currLightIdx].pos.xyz - sd.pos.xyz;
774 const float dist = length(pointToLight);
775 const vec3 L = pointToLight / dist;
776 const float NoL = clamp(dot(sd.N, L), 0.0, 1.0);
777 const float range = uLightData.lights[currLightIdx].dir.w;
778 const float attenuation = max(min(1.0 - pow(dist / range, 4.0), 1.0), 0.0) / (dist * dist);
779 // NOTE: could check for NoL > 0.0 and NoV > 0.0
780 color += CalculateLight(currLightIdx, materialDiffuseBRDF, L, NoL, sd, sssv, materialFlags) * attenuation;
781 }
782 }
783
784 return color;
785 }
786
787 #endif // SHADERS__COMMON__3D_DM_LIGHTING_COMMON_H
788