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__INPLANE_LIGHTING_COMMON_H
17 #define SHADERS__COMMON__INPLANE_LIGHTING_COMMON_H
18 
19 #include "3d/shaders/common/3d_dm_brdf_common.h"
20 #include "3d/shaders/common/3d_dm_shadowing_common.h"
21 #include "3d/shaders/common/3d_dm_lighting_common.h"
22 // NOTE: inplace
23 #include "3d/shaders/common/3d_dm_inplace_sampling_common.h"
24 
25 /*
26  * Needs to be included after the descriptor sets are bound.
27  */
28 
CalculateLightInplace(uint currLightIdx,vec3 materialDiffuseBRDF,vec3 L,float NoL,ShadingDataInplace sd,ClearcoatShadingVariables ccsv,SheenShadingVariables ssv)29 vec3 CalculateLightInplace(uint currLightIdx, vec3 materialDiffuseBRDF, vec3 L, float NoL, ShadingDataInplace sd,
30     ClearcoatShadingVariables ccsv, SheenShadingVariables ssv)
31 {
32     const vec3 H = normalize(L + sd.V);
33     const float VoH = clamp(dot(sd.V, H), 0.0, 1.0);
34     const float NoH = clamp(dot(sd.N, H), 0.0, 1.0);
35 
36     float extAttenuation = 1.0;
37     vec3 calculatedColor = vec3(0.0);
38     if ((CORE_MATERIAL_FLAGS & CORE_MATERIAL_SHEEN_BIT) == CORE_MATERIAL_SHEEN_BIT) {
39         const float sheenD = dCharlie(ssv.sheenRoughness, NoH);
40         const float sheenV = vAshikhmin(sd.NoV, NoL);
41         const vec3 sheenSpec = ssv.sheenColor * (sheenD * sheenV); // F = 1.0
42 
43         extAttenuation *= (1.0 - (ssv.sheenColorMax * ssv.sheenBRDFApprox));
44         calculatedColor += (sheenSpec * NoL);
45     }
46     if ((CORE_MATERIAL_FLAGS & CORE_MATERIAL_CLEARCOAT_BIT) == CORE_MATERIAL_CLEARCOAT_BIT) {
47         const float ccNoL = clamp(dot(ccsv.ccNormal, L), CORE3D_PBR_LIGHTING_EPSILON, 1.0);
48         const float ccNoH = clamp(dot(ccsv.ccNormal, H), CORE3D_PBR_LIGHTING_EPSILON, 1.0);
49         const float ccLoH = clamp(dot(L, H), CORE3D_PBR_LIGHTING_EPSILON, 1.0);
50         const float ccNoV = clamp(dot(ccsv.ccNormal, sd.V), CORE3D_PBR_LIGHTING_EPSILON, 1.0);
51         const float ccf0 = 0.04;
52 
53         const float ccD = dGGX(ccsv.ccAlpha2, ccNoH);
54         const float ccG = vKelemen(ccLoH);
55         const float ccF = fSchlickSingle(ccf0, ccNoV) * ccsv.cc; // NOTE: cc in ccF
56         const float ccSpec = ccF * ccD * ccG;
57 
58         extAttenuation *= (1.0 - ccF);
59         calculatedColor += vec3(ccSpec * ccNoL);
60     }
61     const float D = dGGX(sd.alpha2, NoH);
62     const float G = vGGXWithCombinedDenominator(sd.alpha2, sd.NoV, NoL);
63     const vec3 F = fSchlick(sd.f0, VoH);
64     const vec3 specContrib = F * (D * G);
65 
66     // KHR_materials_specular: "In the diffuse component we have to account for the fact that F is now an RGB value.",
67     // but it already was? const vec3 diffuseContrib = (1.0 - max(F.x, (max(F.y, F.z)))) * materialDiffuseBRDF;
68     const vec3 diffuseContrib = (1.0 - F.xyz) * materialDiffuseBRDF;
69     calculatedColor += (diffuseContrib + specContrib * extAttenuation) * extAttenuation * NoL;
70     calculatedColor *= uLightData.lights[currLightIdx].color.xyz;
71     return calculatedColor;
72 }
73 
CheckLightLayerMask(uint currLightIdx,uvec2 layers)74 bool CheckLightLayerMask(uint currLightIdx, uvec2 layers) {
75     // Check that the light is enabled for this specific object.
76     // TODO: It seems like the mask bits are in .wz order when it should be .zw?
77     const uvec2 lightLayerMask = uLightData.lights[currLightIdx].indices.wz;
78     // If any of the layer bits match the light layer mask -> return true (i.e. use this light).
79     return (layers & lightLayerMask) != uvec2(0, 0);
80 }
81 
CalculateLightingInplace(ShadingDataInplace sd,ClearcoatShadingVariables ccsv,SheenShadingVariables ssv)82 vec3 CalculateLightingInplace(ShadingDataInplace sd, ClearcoatShadingVariables ccsv, SheenShadingVariables ssv)
83 {
84     const vec3 materialDiffuseBRDF = sd.diffuseColor * diffuseCoeff();
85     vec3 color = vec3(0.0);
86     const uint directionalLightCount = uLightData.directionalLightCount;
87     const uint directionalLightBeginIndex = uLightData.directionalLightBeginIndex;
88     const vec4 atlasSizeInvSize = uLightData.atlasSizeInvSize;
89     for (uint lightIdx = 0; lightIdx < directionalLightCount; ++lightIdx) {
90         const uint currLightIdx = directionalLightBeginIndex + lightIdx;
91 
92         if (!CheckLightLayerMask(currLightIdx, sd.layers)) {
93             continue;
94         }
95 
96         const vec3 L = -uLightData.lights[currLightIdx].dir.xyz; // normalization already done in c-code
97         const float NoL = clamp(dot(sd.N, L), 0.0, 1.0);
98         // NOTE: could check for NoL > 0.0 and NoV > 0.0
99         CORE_RELAXEDP float shadowCoeff = 1.0;
100         if ((CORE_MATERIAL_FLAGS & CORE_MATERIAL_SHADOW_RECEIVER_BIT) == CORE_MATERIAL_SHADOW_RECEIVER_BIT) {
101             const uvec4 lightFlags = uLightData.lights[currLightIdx].flags;
102             if ((lightFlags.x & CORE_LIGHT_USAGE_SHADOW_LIGHT_BIT) == CORE_LIGHT_USAGE_SHADOW_LIGHT_BIT) {
103                 const vec4 shadowCoord = GetShadowMatrix(lightFlags.y) * vec4(sd.pos.xyz, 1.0);
104                 const vec4 shadowFactors = uLightData.lights[currLightIdx].shadowFactors;
105                 if ((CORE_LIGHTING_FLAGS & CORE_LIGHTING_SHADOW_TYPE_VSM_BIT) == CORE_LIGHTING_SHADOW_TYPE_VSM_BIT) {
106                     shadowCoeff = CalcVsmShadow(
107                         uSampColorShadow, shadowCoord, NoL, shadowFactors, atlasSizeInvSize, lightFlags.zw);
108                 } else {
109                     shadowCoeff = CalcPcfShadow(
110                         uSampDepthShadow, shadowCoord, NoL, shadowFactors, atlasSizeInvSize, lightFlags.zw);
111                 }
112             }
113         }
114         color += CalculateLightInplace(currLightIdx, materialDiffuseBRDF, L, NoL, sd, ccsv, ssv) * shadowCoeff;
115     }
116 
117     if ((CORE_LIGHTING_FLAGS & CORE_LIGHTING_SPOT_ENABLED_BIT) == CORE_LIGHTING_SPOT_ENABLED_BIT) {
118         const uint spotLightCount = uLightData.spotLightCount;
119         const uint spotLightLightBeginIndex = uLightData.spotLightBeginIndex;
120         for (uint spotIdx = 0; spotIdx < spotLightCount; ++spotIdx) {
121             const uint currLightIdx = spotLightLightBeginIndex + spotIdx;
122 
123             if (!CheckLightLayerMask(currLightIdx, sd.layers)) {
124                 continue;
125             }
126 
127             const vec3 pointToLight = uLightData.lights[currLightIdx].pos.xyz - sd.pos.xyz;
128             const float dist = length(pointToLight);
129             const vec3 L = pointToLight / dist;
130             const float NoL = clamp(dot(sd.N, L), 0.0, 1.0);
131             // NOTE: could check for NoL > 0.0 and NoV > 0.0
132             CORE_RELAXEDP float shadowCoeff = 1.0;
133             if ((CORE_MATERIAL_FLAGS & CORE_MATERIAL_SHADOW_RECEIVER_BIT) == CORE_MATERIAL_SHADOW_RECEIVER_BIT) {
134                 const uvec4 lightFlags = uLightData.lights[currLightIdx].flags;
135                 if ((lightFlags.x & CORE_LIGHT_USAGE_SHADOW_LIGHT_BIT) == CORE_LIGHT_USAGE_SHADOW_LIGHT_BIT) {
136                     const vec4 shadowCoord = GetShadowMatrix(lightFlags.y) * vec4(sd.pos.xyz, 1.0);
137                     const vec4 shadowFactors = uLightData.lights[currLightIdx].shadowFactors;
138                     if ((CORE_LIGHTING_FLAGS & CORE_LIGHTING_SHADOW_TYPE_VSM_BIT) ==
139                         CORE_LIGHTING_SHADOW_TYPE_VSM_BIT) {
140                         shadowCoeff = CalcVsmShadow(
141                             uSampColorShadow, shadowCoord, NoL, shadowFactors, atlasSizeInvSize, lightFlags.zw);
142                     } else {
143                         shadowCoeff = CalcPcfShadow(
144                             uSampDepthShadow, shadowCoord, NoL, shadowFactors, atlasSizeInvSize, lightFlags.zw);
145                     }
146                 }
147             }
148 
149             const float lightAngleScale = uLightData.lights[currLightIdx].spotLightParams.x;
150             const float lightAngleOffset = uLightData.lights[currLightIdx].spotLightParams.y;
151             // See: https://github.com/KhronosGroup/glTF/tree/master/extensions/2.0/Khronos/KHR_lights_punctual
152             const float cd = dot(uLightData.lights[currLightIdx].dir.xyz, -L);
153             const float angularAttenuation = clamp(cd * lightAngleScale + lightAngleOffset, 0.0, 1.0);
154 
155             const float range = uLightData.lights[currLightIdx].dir.w;
156             const float attenuation = max(min(1.0 - pow(dist / range, 4.0), 1.0), 0.0) / (dist * dist);
157             color += CalculateLightInplace(currLightIdx, materialDiffuseBRDF, L, NoL, sd, ccsv, ssv) *
158                      (angularAttenuation * angularAttenuation * attenuation) * shadowCoeff;
159         }
160     }
161     if ((CORE_LIGHTING_FLAGS & CORE_LIGHTING_POINT_ENABLED_BIT) == CORE_LIGHTING_POINT_ENABLED_BIT) {
162         const uint pointLightCount = uLightData.pointLightCount;
163         const uint pointLightBeginIndex = uLightData.pointLightBeginIndex;
164         for (uint pointIdx = 0; pointIdx < pointLightCount; ++pointIdx) {
165             const uint currLightIdx = pointLightBeginIndex + pointIdx;
166 
167             if (!CheckLightLayerMask(currLightIdx, sd.layers)) {
168                 continue;
169             }
170 
171             const vec3 pointToLight = uLightData.lights[currLightIdx].pos.xyz - sd.pos.xyz;
172             const float dist = length(pointToLight);
173             if (dist == 0) {
174                 return color;
175             }
176             const vec3 L = pointToLight / dist;
177             const float NoL = clamp(dot(sd.N, L), 0.0, 1.0);
178             const float range = uLightData.lights[currLightIdx].dir.w;
179             const float attenuation = max(min(1.0 - pow(dist / range, 4.0), 1.0), 0.0) / (dist * dist);
180             // NOTE: could check for NoL > 0.0 and NoV > 0.0
181             color += CalculateLightInplace(currLightIdx, materialDiffuseBRDF, L, NoL, sd, ccsv, ssv) * attenuation;
182         }
183     }
184 
185     return color;
186 }
187 
188 #endif // SHADERS__COMMON__INPLANE_LIGHTING_COMMON_H
189