playcanvas 2.24.0-beta.34 → 2.24.0-beta.35

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (72) hide show
  1. package/build/playcanvas/src/core/core.js +2 -2
  2. package/build/playcanvas/src/framework/lightmapper/lightmapper.js +3 -0
  3. package/build/playcanvas/src/scene/lighting/lighting-params.d.ts +58 -0
  4. package/build/playcanvas/src/scene/lighting/lighting-params.js +35 -1
  5. package/build/playcanvas/src/scene/lighting/lights-buffer.d.ts +7 -1
  6. package/build/playcanvas/src/scene/lighting/lights-buffer.js +6 -4
  7. package/build/playcanvas/src/scene/lighting/world-clusters.js +2 -2
  8. package/build/playcanvas/src/scene/materials/lit-material-options-builder.js +4 -1
  9. package/build/playcanvas/src/scene/materials/standard-material-options-builder.js +5 -2
  10. package/build/playcanvas/src/scene/shader-lib/glsl/chunks/lit/frag/clusteredLight.d.ts +1 -1
  11. package/build/playcanvas/src/scene/shader-lib/glsl/chunks/lit/frag/clusteredLight.js +58 -32
  12. package/build/playcanvas/src/scene/shader-lib/glsl/chunks/lit/frag/lighting/lightFunctionLight.d.ts +1 -1
  13. package/build/playcanvas/src/scene/shader-lib/glsl/chunks/lit/frag/lighting/lightFunctionLight.js +3 -3
  14. package/build/playcanvas/src/scene/shader-lib/glsl/chunks/lit/frag/ltc.d.ts +1 -1
  15. package/build/playcanvas/src/scene/shader-lib/glsl/chunks/lit/frag/ltc.js +13 -2
  16. package/build/playcanvas/src/scene/shader-lib/glsl/chunks/lit/frag/pass-forward/litForwardBackend.d.ts +1 -1
  17. package/build/playcanvas/src/scene/shader-lib/glsl/chunks/lit/frag/pass-forward/litForwardBackend.js +6 -4
  18. package/build/playcanvas/src/scene/shader-lib/programs/lit-shader-options.d.ts +8 -0
  19. package/build/playcanvas/src/scene/shader-lib/programs/lit-shader-options.js +11 -1
  20. package/build/playcanvas/src/scene/shader-lib/programs/lit-shader.js +10 -1
  21. package/build/playcanvas/src/scene/shader-lib/wgsl/chunks/lit/frag/clusteredLight.d.ts +1 -1
  22. package/build/playcanvas/src/scene/shader-lib/wgsl/chunks/lit/frag/clusteredLight.js +59 -32
  23. package/build/playcanvas/src/scene/shader-lib/wgsl/chunks/lit/frag/lighting/lightFunctionLight.d.ts +1 -1
  24. package/build/playcanvas/src/scene/shader-lib/wgsl/chunks/lit/frag/lighting/lightFunctionLight.js +3 -3
  25. package/build/playcanvas/src/scene/shader-lib/wgsl/chunks/lit/frag/ltc.d.ts +1 -1
  26. package/build/playcanvas/src/scene/shader-lib/wgsl/chunks/lit/frag/ltc.js +13 -2
  27. package/build/playcanvas/src/scene/shader-lib/wgsl/chunks/lit/frag/pass-forward/litForwardBackend.d.ts +1 -1
  28. package/build/playcanvas/src/scene/shader-lib/wgsl/chunks/lit/frag/pass-forward/litForwardBackend.js +6 -4
  29. package/build/playcanvas.d.ts +73 -1
  30. package/build/playcanvas.dbg/src/core/core.js +2 -2
  31. package/build/playcanvas.dbg/src/framework/lightmapper/lightmapper.js +3 -0
  32. package/build/playcanvas.dbg/src/scene/lighting/lighting-params.js +83 -1
  33. package/build/playcanvas.dbg/src/scene/lighting/lights-buffer.js +12 -4
  34. package/build/playcanvas.dbg/src/scene/lighting/world-clusters.js +2 -2
  35. package/build/playcanvas.dbg/src/scene/materials/lit-material-options-builder.js +4 -1
  36. package/build/playcanvas.dbg/src/scene/materials/standard-material-options-builder.js +5 -2
  37. package/build/playcanvas.dbg/src/scene/shader-lib/glsl/chunks/lit/frag/clusteredLight.js +75 -39
  38. package/build/playcanvas.dbg/src/scene/shader-lib/glsl/chunks/lit/frag/lighting/lightFunctionLight.js +4 -3
  39. package/build/playcanvas.dbg/src/scene/shader-lib/glsl/chunks/lit/frag/ltc.js +17 -2
  40. package/build/playcanvas.dbg/src/scene/shader-lib/glsl/chunks/lit/frag/pass-forward/litForwardBackend.js +10 -6
  41. package/build/playcanvas.dbg/src/scene/shader-lib/programs/lit-shader-options.js +18 -1
  42. package/build/playcanvas.dbg/src/scene/shader-lib/programs/lit-shader.js +10 -1
  43. package/build/playcanvas.dbg/src/scene/shader-lib/wgsl/chunks/lit/frag/clusteredLight.js +76 -39
  44. package/build/playcanvas.dbg/src/scene/shader-lib/wgsl/chunks/lit/frag/lighting/lightFunctionLight.js +4 -3
  45. package/build/playcanvas.dbg/src/scene/shader-lib/wgsl/chunks/lit/frag/ltc.js +17 -2
  46. package/build/playcanvas.dbg/src/scene/shader-lib/wgsl/chunks/lit/frag/pass-forward/litForwardBackend.js +10 -6
  47. package/build/playcanvas.dbg.js +339 -113
  48. package/build/playcanvas.dbg.mjs +339 -113
  49. package/build/playcanvas.js +226 -95
  50. package/build/playcanvas.min.js +254 -175
  51. package/build/playcanvas.min.mjs +256 -177
  52. package/build/playcanvas.mjs +226 -95
  53. package/build/playcanvas.prf/src/core/core.js +2 -2
  54. package/build/playcanvas.prf/src/framework/lightmapper/lightmapper.js +3 -0
  55. package/build/playcanvas.prf/src/scene/lighting/lighting-params.js +35 -1
  56. package/build/playcanvas.prf/src/scene/lighting/lights-buffer.js +6 -4
  57. package/build/playcanvas.prf/src/scene/lighting/world-clusters.js +2 -2
  58. package/build/playcanvas.prf/src/scene/materials/lit-material-options-builder.js +4 -1
  59. package/build/playcanvas.prf/src/scene/materials/standard-material-options-builder.js +5 -2
  60. package/build/playcanvas.prf/src/scene/shader-lib/glsl/chunks/lit/frag/clusteredLight.js +58 -32
  61. package/build/playcanvas.prf/src/scene/shader-lib/glsl/chunks/lit/frag/lighting/lightFunctionLight.js +3 -3
  62. package/build/playcanvas.prf/src/scene/shader-lib/glsl/chunks/lit/frag/ltc.js +13 -2
  63. package/build/playcanvas.prf/src/scene/shader-lib/glsl/chunks/lit/frag/pass-forward/litForwardBackend.js +6 -4
  64. package/build/playcanvas.prf/src/scene/shader-lib/programs/lit-shader-options.js +11 -1
  65. package/build/playcanvas.prf/src/scene/shader-lib/programs/lit-shader.js +10 -1
  66. package/build/playcanvas.prf/src/scene/shader-lib/wgsl/chunks/lit/frag/clusteredLight.js +59 -32
  67. package/build/playcanvas.prf/src/scene/shader-lib/wgsl/chunks/lit/frag/lighting/lightFunctionLight.js +3 -3
  68. package/build/playcanvas.prf/src/scene/shader-lib/wgsl/chunks/lit/frag/ltc.js +13 -2
  69. package/build/playcanvas.prf/src/scene/shader-lib/wgsl/chunks/lit/frag/pass-forward/litForwardBackend.js +6 -4
  70. package/build/playcanvas.prf.js +226 -95
  71. package/build/playcanvas.prf.mjs +226 -95
  72. package/package.json +1 -1
@@ -1,5 +1,5 @@
1
- const version = "2.24.0-beta.34";
2
- const revision = "eb5f1c2";
1
+ const version = "2.24.0-beta.35";
2
+ const revision = "597f7e9";
3
3
  function extend(target, ex) {
4
4
  for (const prop in ex) {
5
5
  const copy = ex[prop];
@@ -130,6 +130,9 @@ class Lightmapper {
130
130
  lightingParams.cookiesEnabled = srcParams.cookiesEnabled;
131
131
  lightingParams.cookieAtlasResolution = srcParams.cookieAtlasResolution;
132
132
  lightingParams.areaLightsEnabled = srcParams.areaLightsEnabled;
133
+ lightingParams.areaLightRectEnabled = srcParams.areaLightRectEnabled;
134
+ lightingParams.areaLightDiskEnabled = srcParams.areaLightDiskEnabled;
135
+ lightingParams.areaLightSphereEnabled = srcParams.areaLightSphereEnabled;
133
136
  lightingParams.cells = new Vec3(3, 3, 3);
134
137
  lightingParams.maxLightsPerCell = 4;
135
138
  this.worldClusters = new WorldClusters(device);
@@ -14,6 +14,12 @@ export class LightingParams {
14
14
  /** @private */
15
15
  private _areaLightsEnabled;
16
16
  /** @private */
17
+ private _areaLightRectEnabled;
18
+ /** @private */
19
+ private _areaLightDiskEnabled;
20
+ /** @private */
21
+ private _areaLightSphereEnabled;
22
+ /** @private */
17
23
  private _cells;
18
24
  /** @private */
19
25
  private _maxLightsPerCell;
@@ -174,5 +180,57 @@ export class LightingParams {
174
180
  * @type {Vec3}
175
181
  */
176
182
  get cells(): Vec3;
183
+ /**
184
+ * Sets whether clustered lighting supports area lights of rectangular shape
185
+ * ({@link LIGHTSHAPE_RECT}). The lit shaders only include the code of the enabled area light
186
+ * shapes, so disabling the shapes a scene does not use makes them faster to compile, most
187
+ * noticeably on Windows. Area lights of a disabled shape are rendered as punctual lights. Only
188
+ * used when {@link LightingParams#areaLightsEnabled} is true. Defaults to true.
189
+ *
190
+ * @type {boolean}
191
+ */
192
+ set areaLightRectEnabled(value: boolean);
193
+ /**
194
+ * Gets whether clustered lighting supports area lights of rectangular shape.
195
+ *
196
+ * @type {boolean}
197
+ */
198
+ get areaLightRectEnabled(): boolean;
199
+ /**
200
+ * Sets whether clustered lighting supports area lights of disk shape ({@link LIGHTSHAPE_DISK}).
201
+ * Area lights of a disabled shape are rendered as punctual lights, see
202
+ * {@link LightingParams#areaLightRectEnabled}. Defaults to true.
203
+ *
204
+ * @type {boolean}
205
+ */
206
+ set areaLightDiskEnabled(value: boolean);
207
+ /**
208
+ * Gets whether clustered lighting supports area lights of disk shape.
209
+ *
210
+ * @type {boolean}
211
+ */
212
+ get areaLightDiskEnabled(): boolean;
213
+ /**
214
+ * Sets whether clustered lighting supports area lights of sphere shape
215
+ * ({@link LIGHTSHAPE_SPHERE}). Area lights of a disabled shape are rendered as punctual lights,
216
+ * see {@link LightingParams#areaLightRectEnabled}. Defaults to true.
217
+ *
218
+ * @type {boolean}
219
+ */
220
+ set areaLightSphereEnabled(value: boolean);
221
+ /**
222
+ * Gets whether clustered lighting supports area lights of sphere shape.
223
+ *
224
+ * @type {boolean}
225
+ */
226
+ get areaLightSphereEnabled(): boolean;
227
+ /**
228
+ * Bit mask (1 << shape) of the area light shapes the clustered lighting supports, 0 when area
229
+ * lights are disabled.
230
+ *
231
+ * @type {number}
232
+ * @ignore
233
+ */
234
+ get areaLightShapes(): number;
177
235
  }
178
236
  import { Vec3 } from '../../core/math/vec3.js';
@@ -1,8 +1,11 @@
1
1
  import { math } from "../../core/math/math.js";
2
2
  import { Vec3 } from "../../core/math/vec3.js";
3
- import { SHADOW_PCF3_32F } from "../constants.js";
3
+ import { LIGHTSHAPE_DISK, LIGHTSHAPE_RECT, LIGHTSHAPE_SPHERE, SHADOW_PCF3_32F } from "../constants.js";
4
4
  class LightingParams {
5
5
  _areaLightsEnabled = false;
6
+ _areaLightRectEnabled = true;
7
+ _areaLightDiskEnabled = true;
8
+ _areaLightSphereEnabled = true;
6
9
  _cells = new Vec3(10, 3, 10);
7
10
  _maxLightsPerCell = 255;
8
11
  _maxLights = 255;
@@ -90,6 +93,37 @@ class LightingParams {
90
93
  get areaLightsEnabled() {
91
94
  return this._areaLightsEnabled;
92
95
  }
96
+ set areaLightRectEnabled(value) {
97
+ if (this._areaLightRectEnabled !== value) {
98
+ this._areaLightRectEnabled = value;
99
+ this._dirtyLightsFnc();
100
+ }
101
+ }
102
+ get areaLightRectEnabled() {
103
+ return this._areaLightRectEnabled;
104
+ }
105
+ set areaLightDiskEnabled(value) {
106
+ if (this._areaLightDiskEnabled !== value) {
107
+ this._areaLightDiskEnabled = value;
108
+ this._dirtyLightsFnc();
109
+ }
110
+ }
111
+ get areaLightDiskEnabled() {
112
+ return this._areaLightDiskEnabled;
113
+ }
114
+ set areaLightSphereEnabled(value) {
115
+ if (this._areaLightSphereEnabled !== value) {
116
+ this._areaLightSphereEnabled = value;
117
+ this._dirtyLightsFnc();
118
+ }
119
+ }
120
+ get areaLightSphereEnabled() {
121
+ return this._areaLightSphereEnabled;
122
+ }
123
+ get areaLightShapes() {
124
+ if (!this._areaLightsEnabled) return 0;
125
+ return (this._areaLightRectEnabled ? 1 << LIGHTSHAPE_RECT : 0) | (this._areaLightDiskEnabled ? 1 << LIGHTSHAPE_DISK : 0) | (this._areaLightSphereEnabled ? 1 << LIGHTSHAPE_SPHERE : 0);
126
+ }
93
127
  set shadowsEnabled(value) {
94
128
  if (this._shadowsEnabled !== value) {
95
129
  this._shadowsEnabled = value;
@@ -1,6 +1,12 @@
1
1
  export class LightsBuffer {
2
2
  constructor(device: any);
3
- areaLightsEnabled: boolean;
3
+ /**
4
+ * Bit mask (1 << shape) of the area light shapes the lit shaders support, 0 when area lights
5
+ * are disabled.
6
+ *
7
+ * @type {number}
8
+ */
9
+ areaLightShapes: number;
4
10
  /**
5
11
  * Texture storing properties of all lights, one row of pixels per light.
6
12
  *
@@ -49,7 +49,7 @@ const lightBufferDefines = `
49
49
  ${buildShaderDefines(enums, "")}
50
50
  `;
51
51
  class LightsBuffer {
52
- areaLightsEnabled = false;
52
+ areaLightShapes = 0;
53
53
  lightsTexture = null;
54
54
  _maxLights = 0;
55
55
  constructor(device) {
@@ -58,7 +58,7 @@ class LightsBuffer {
58
58
  ShaderChunks.get(device, SHADERLANGUAGE_WGSL).set("lightBufferDefinesPS", lightBufferDefines);
59
59
  this.cookiesEnabled = false;
60
60
  this.shadowsEnabled = false;
61
- this.areaLightsEnabled = false;
61
+ this.areaLightShapes = 0;
62
62
  this._lightsTextureId = this.device.scope.resolve("lightsTexture");
63
63
  this.maxLights = 256;
64
64
  this.invMaxColorValue = 0;
@@ -133,7 +133,7 @@ class LightsBuffer {
133
133
  const isSpot = light._type === LIGHTTYPE_SPOT;
134
134
  const hasAtlasViewport = light.atlasViewportAllocated;
135
135
  const isCookie = this.cookiesEnabled && !!light._cookie && hasAtlasViewport;
136
- const isArea = this.areaLightsEnabled && light.shape !== LIGHTSHAPE_PUNCTUAL;
136
+ const isArea = (this.areaLightShapes & 1 << light.shape) !== 0;
137
137
  const castShadows = this.shadowsEnabled && light.castShadows && hasAtlasViewport;
138
138
  const pos = light._node.getPosition();
139
139
  let lightProjectionMatrix = null;
@@ -174,7 +174,9 @@ class LightsBuffer {
174
174
  dataFloat[dataFloatStart + 4 * TextureIndexFloat.DIRECTION_FLAGS + 1] = tempVec3.y;
175
175
  dataFloat[dataFloatStart + 4 * TextureIndexFloat.DIRECTION_FLAGS + 2] = tempVec3.z;
176
176
  }
177
- dataUint[dataFloatStart + 4 * TextureIndexFloat.DIRECTION_FLAGS + 3] = light.getClusteredFlags(castShadows, isCookie);
177
+ let flags = light.getClusteredFlags(castShadows, isCookie);
178
+ if (!isArea) flags &= ~(3 << 28);
179
+ dataUint[dataFloatStart + 4 * TextureIndexFloat.DIRECTION_FLAGS + 3] = flags;
178
180
  if (lightProjectionMatrix) {
179
181
  const matData = lightProjectionMatrix.data;
180
182
  for (let m = 0; m < 16; m++) {
@@ -104,7 +104,7 @@ class WorldClusters {
104
104
  this.maxLights = lightingParams.maxLights;
105
105
  this.lightsBuffer.cookiesEnabled = lightingParams.cookiesEnabled;
106
106
  this.lightsBuffer.shadowsEnabled = lightingParams.shadowsEnabled;
107
- this.lightsBuffer.areaLightsEnabled = lightingParams.areaLightsEnabled;
107
+ this.lightsBuffer.areaLightShapes = lightingParams.areaLightShapes;
108
108
  }
109
109
  }
110
110
  updateCells() {
@@ -226,7 +226,7 @@ class WorldClusters {
226
226
  updateClusters(lightingParams) {
227
227
  this.counts.fill(0);
228
228
  this.clusters.fill(0);
229
- this.lightsBuffer.areaLightsEnabled = lightingParams ? lightingParams.areaLightsEnabled : false;
229
+ this.lightsBuffer.areaLightShapes = lightingParams ? lightingParams.areaLightShapes : 0;
230
230
  const divX = this._cells.x;
231
231
  const divZ = this._cells.z;
232
232
  const counts = this.counts;
@@ -51,12 +51,15 @@ class LitMaterialOptionsBuilder {
51
51
  litOptions.clusteredLightingCookiesEnabled = scene.lighting.cookiesEnabled;
52
52
  litOptions.clusteredLightingShadowsEnabled = scene.lighting.shadowsEnabled;
53
53
  litOptions.clusteredLightingShadowType = scene.lighting.shadowType;
54
- litOptions.clusteredLightingAreaLightsEnabled = scene.lighting.areaLightsEnabled;
54
+ const areaLightShapes = scene.lighting.areaLightShapes;
55
+ litOptions.clusteredLightingAreaLightsEnabled = areaLightShapes !== 0;
56
+ litOptions.clusteredLightingAreaLightShapes = areaLightShapes;
55
57
  } else {
56
58
  litOptions.clusteredLightingEnabled = false;
57
59
  litOptions.clusteredLightingCookiesEnabled = false;
58
60
  litOptions.clusteredLightingShadowsEnabled = false;
59
61
  litOptions.clusteredLightingAreaLightsEnabled = false;
62
+ litOptions.clusteredLightingAreaLightShapes = 0;
60
63
  }
61
64
  }
62
65
  static updateMaterialOptions(litOptions, material) {
@@ -86,12 +86,15 @@ class StandardMaterialOptionsBuilder {
86
86
  options.litOptions.clusteredLightingCookiesEnabled = scene.lighting.cookiesEnabled;
87
87
  options.litOptions.clusteredLightingShadowsEnabled = scene.lighting.shadowsEnabled;
88
88
  options.litOptions.clusteredLightingShadowType = scene.lighting.shadowType;
89
- options.litOptions.clusteredLightingAreaLightsEnabled = scene.lighting.areaLightsEnabled;
89
+ const areaLightShapes = scene.lighting.areaLightShapes;
90
+ options.litOptions.clusteredLightingAreaLightsEnabled = areaLightShapes !== 0;
91
+ options.litOptions.clusteredLightingAreaLightShapes = areaLightShapes;
90
92
  } else {
91
93
  options.litOptions.clusteredLightingEnabled = false;
92
94
  options.litOptions.clusteredLightingCookiesEnabled = false;
93
95
  options.litOptions.clusteredLightingShadowsEnabled = false;
94
96
  options.litOptions.clusteredLightingAreaLightsEnabled = false;
97
+ options.litOptions.clusteredLightingAreaLightShapes = 0;
95
98
  }
96
99
  }
97
100
  _updateUVOptions(options, stdMat, objDefs, vertexFormat, minimalOptions, cameraShaderParams) {
@@ -192,7 +195,7 @@ class StandardMaterialOptionsBuilder {
192
195
  options.clearCoatGloss = !!stdMat.clearCoatGloss;
193
196
  options.clearCoatPackedNormal = isPackedNormalMap(stdMat.clearCoatNormalMap);
194
197
  options.iorTint = !equalish(stdMat.refractionIndex, DEFAULT_REFRACTION_INDEX);
195
- options.iridescenceTint = stdMat.iridescence !== 1;
198
+ options.iridescenceTint = true;
196
199
  options.glossInvert = stdMat.glossInvert;
197
200
  options.sheenGlossInvert = stdMat.sheenGlossInvert;
198
201
  options.clearCoatGlossInvert = stdMat.clearCoatGlossInvert;
@@ -1,2 +1,2 @@
1
- declare const _default: "\n\n#include \"lightBufferDefinesPS\"\n\n// include this before shadow / cookie code\n#include \"clusteredLightUtilsPS\"\n\n#ifdef CLUSTER_COOKIES\n #include \"clusteredLightCookiesPS\"\n#endif\n\n#ifdef CLUSTER_SHADOWS\n #include \"clusteredLightShadowsPS\"\n#endif\n\nuniform highp usampler2D clusterWorldTexture;\nuniform highp sampler2D lightsTexture;\n\n#ifdef CLUSTER_SHADOWS\n // TODO: when VSM shadow is supported, it needs to use sampler2D in webgl2\n uniform sampler2DShadow shadowAtlasTexture;\n#endif\n\n#ifdef CLUSTER_COOKIES\n uniform sampler2D cookieAtlasTexture;\n#endif\n\nuniform int clusterMaxCells;\n\n// number of lights in the cluster structure\nuniform int numClusteredLights;\n\n// width of the cluster texture\nuniform int clusterTextureWidth;\n\nuniform vec3 clusterCellsCountByBoundsSize;\nuniform vec3 clusterBoundsMin;\nuniform vec3 clusterBoundsDelta;\nuniform ivec3 clusterCellsDot;\nuniform ivec3 clusterCellsMax;\nuniform vec2 shadowAtlasParams;\n\n// structure storing light properties of a clustered light. Vectors and scalars are interleaved\n// so each vec3 packs with an adjacent 4-byte field into a 16-byte slot, minimizing padding for\n// compilers that don't reorder struct members.\nstruct ClusterLightData {\n\n // world space position\n vec3 position;\n\n // light index in the lights texture\n int lightIndex;\n\n // world space direction (spot light only)\n vec3 direction;\n\n // area light shape\n uint shape;\n\n // color\n vec3 color;\n\n // 0.0 if the light doesn't cast shadows\n float shadowIntensity;\n\n // range of the light\n float range;\n\n // compressed biases, two half-floats stored in a float\n float biasesData;\n\n // intensity of the cookie\n float cookieIntensity;\n\n // true for spot lights\n bool isSpot;\n\n // light follow mode\n bool falloffModeLinear;\n\n // light mask (mutually exclusive)\n bool isDynamic;\n bool isLightmapped;\n};\n\n// Spot light cone angles, decoded on demand only when the light is a spot light.\nstruct ClusterLightSpotData {\n float innerConeAngleCos;\n float outerConeAngleCos;\n};\n\n// Area light dimensions and orientation, decoded on demand only for non-punctual lights.\nstruct ClusterLightAreaData {\n vec3 halfWidth;\n vec3 halfHeight;\n};\n\n// Shadow bias parameters, decoded on demand only when the light casts shadows.\nstruct ClusterLightShadowData {\n float shadowBias;\n float shadowNormalBias;\n};\n\n// Note: on some devices (tested on Pixel 3A XL), this matrix when stored inside the light struct has lower precision compared to\n// when stored outside, so we store it outside to avoid spot shadow flickering. This might need to be done to other / all members\n// of the structure if further similar issues are observed. See https://github.com/KhronosGroup/WebGL/issues/3351\n\n// shadow (spot light only) / cookie projection matrix\nmat4 lightProjectionMatrix;\n\n// NOTE: On some Samsung devices, these values can suffer precision / corruption issues when stored\n// as members of ClusterLightData. Keep them as module-scope temporaries instead. See issue #7800.\nuint clusterLightData_flags; // 32bit of flags\nfloat clusterLightData_anglesData; // compressed angles, two half-floats stored in a float\nuint clusterLightData_colorBFlagsData; // blue color component and angle flags (as uint for efficient bit operations)\n\nvec4 sampleLightTextureF(int lightIndex, int index) {\n return texelFetch(lightsTexture, ivec2(index, lightIndex), 0);\n}\n\nClusterLightData decodeClusterLightCore(int lightIndex) {\n ClusterLightData clusterLightData;\n\n // light index\n clusterLightData.lightIndex = lightIndex;\n\n // sample data encoding half-float values into 32bit uints\n vec4 halfData = sampleLightTextureF(lightIndex, {CLUSTER_TEXTURE_COLOR_ANGLES_BIAS});\n\n // store values needed by later decode steps (anglesData / colorBFlagsData live outside the\n // struct due to Samsung precision issues - see #7800)\n clusterLightData_anglesData = halfData.z;\n clusterLightData.biasesData = halfData.w;\n clusterLightData_colorBFlagsData = floatBitsToUint(halfData.y);\n\n // decompress color half-floats\n vec2 colorRG = unpackHalf2x16(floatBitsToUint(halfData.x));\n vec2 colorB_flags = unpackHalf2x16(clusterLightData_colorBFlagsData);\n clusterLightData.color = vec3(colorRG, colorB_flags.x) * {LIGHT_COLOR_DIVIDER};\n\n // position and range, full floats\n vec4 lightPosRange = sampleLightTextureF(lightIndex, {CLUSTER_TEXTURE_POSITION_RANGE});\n clusterLightData.position = lightPosRange.xyz;\n clusterLightData.range = lightPosRange.w;\n\n // spot direction & flags data\n vec4 lightDir_Flags = sampleLightTextureF(lightIndex, {CLUSTER_TEXTURE_DIRECTION_FLAGS});\n\n // spot light direction\n clusterLightData.direction = lightDir_Flags.xyz;\n\n // 32bit flags (kept outside the struct, see #7800)\n clusterLightData_flags = floatBitsToUint(lightDir_Flags.w);\n clusterLightData.isSpot = (clusterLightData_flags & (1u << 30u)) != 0u;\n clusterLightData.shape = (clusterLightData_flags >> 28u) & 0x3u;\n clusterLightData.falloffModeLinear = (clusterLightData_flags & (1u << 27u)) == 0u;\n clusterLightData.shadowIntensity = float((clusterLightData_flags >> 0u) & 0xFFu) / 255.0;\n clusterLightData.cookieIntensity = float((clusterLightData_flags >> 8u) & 0xFFu) / 255.0;\n clusterLightData.isDynamic = (clusterLightData_flags & (1u << 22u)) != 0u;\n clusterLightData.isLightmapped = (clusterLightData_flags & (1u << 21u)) != 0u;\n\n return clusterLightData;\n}\n\nClusterLightSpotData decodeClusterLightSpot() {\n // decompress spot light angles\n uint angleFlags = (clusterLightData_colorBFlagsData >> 16u) & 0xFFFFu; // Extract upper 16 bits as integer\n\n vec2 angleValues = unpackHalf2x16(floatBitsToUint(clusterLightData_anglesData));\n float innerVal = angleValues.x;\n float outerVal = angleValues.y;\n\n // decode based on flags (branch-free)\n float innerIsVersine = float(angleFlags & 1u); // bit 0: inner angle format\n float outerIsVersine = float((angleFlags >> 1u) & 1u); // bit 1: outer angle format\n\n return ClusterLightSpotData(\n mix(innerVal, 1.0 - innerVal, innerIsVersine),\n mix(outerVal, 1.0 - outerVal, outerIsVersine)\n );\n}\n\nvec3 decodeClusterLightOmniAtlasViewport(int lightIndex) {\n return sampleLightTextureF(lightIndex, {CLUSTER_TEXTURE_PROJ_MAT_0}).xyz;\n}\n\nClusterLightAreaData decodeClusterLightAreaData(int lightIndex) {\n return ClusterLightAreaData(\n sampleLightTextureF(lightIndex, {CLUSTER_TEXTURE_AREA_DATA_WIDTH}).xyz,\n sampleLightTextureF(lightIndex, {CLUSTER_TEXTURE_AREA_DATA_HEIGHT}).xyz\n );\n}\n\nmat4 decodeClusterLightProjectionMatrixData(int lightIndex) {\n // shadow matrix\n vec4 m0 = sampleLightTextureF(lightIndex, {CLUSTER_TEXTURE_PROJ_MAT_0});\n vec4 m1 = sampleLightTextureF(lightIndex, {CLUSTER_TEXTURE_PROJ_MAT_1});\n vec4 m2 = sampleLightTextureF(lightIndex, {CLUSTER_TEXTURE_PROJ_MAT_2});\n vec4 m3 = sampleLightTextureF(lightIndex, {CLUSTER_TEXTURE_PROJ_MAT_3});\n return mat4(m0, m1, m2, m3);\n}\n\nClusterLightShadowData decodeClusterLightShadowData(float biasesData) {\n // shadow biases\n vec2 biases = unpackHalf2x16(floatBitsToUint(biasesData));\n return ClusterLightShadowData(biases.x, biases.y);\n}\n\nvec4 decodeClusterLightCookieData() {\n // extract channel mask from flags\n uint cookieFlags = (clusterLightData_flags >> 23u) & 0x0Fu; // 4bits, each bit enables a channel\n vec4 mask = vec4(uvec4(cookieFlags) & uvec4(1u, 2u, 4u, 8u));\n return step(1.0, mask); // Normalize to 0.0 or 1.0\n}\n\nvoid evaluateLight(\n ClusterLightData light, \n vec3 worldNormal, \n vec3 viewDir, \n vec3 reflectionDir,\n#if defined(LIT_CLEARCOAT)\n vec3 clearcoatReflectionDir,\n#endif\n float gloss, \n vec3 specularity, \n vec3 geometricNormal, \n mat3 tbn, \n#if defined(LIT_IRIDESCENCE)\n vec3 iridescenceFresnel,\n#endif\n vec3 clearcoat_worldNormal,\n float clearcoat_gloss,\n float sheen_gloss,\n float iridescence_intensity\n) {\n\n vec3 cookieAttenuation = vec3(1.0);\n float diffuseAttenuation = 1.0;\n float falloffAttenuation = 1.0;\n\n #ifdef LIT_DIFFUSE_TRANSMISSION\n // the whole attenuation of the light arriving at the back of the surface, which the diffuse\n // transmission lets through\n float transmissionAttenuation = 0.0;\n #endif\n\n // evaluate omni part of the light\n vec3 lightDirW = evalOmniLight(light.position);\n vec3 lightDirNormW = normalize(lightDirW);\n\n #ifdef CLUSTER_AREALIGHTS\n\n // distance attenuation\n if (light.shape != {LIGHTSHAPE_PUNCTUAL}) { // area light\n\n // area lights\n ClusterLightAreaData areaData = decodeClusterLightAreaData(light.lightIndex);\n\n // handle light shape\n if (light.shape == {LIGHTSHAPE_RECT}) {\n calcRectLightValues(light.position, areaData.halfWidth, areaData.halfHeight);\n } else if (light.shape == {LIGHTSHAPE_DISK}) {\n calcDiskLightValues(light.position, areaData.halfWidth, areaData.halfHeight);\n } else { // sphere\n calcSphereLightValues(light.position, areaData.halfWidth, areaData.halfHeight);\n }\n\n falloffAttenuation = getFalloffWindow(light.range, lightDirW);\n\n } else\n\n #endif\n\n { // punctual light\n\n if (light.falloffModeLinear)\n falloffAttenuation = getFalloffLinear(light.range, lightDirW);\n else\n falloffAttenuation = getFalloffInvSquared(light.range, lightDirW);\n }\n\n if (falloffAttenuation > 0.00001) {\n\n #ifdef CLUSTER_AREALIGHTS\n\n if (light.shape != {LIGHTSHAPE_PUNCTUAL}) { // area light\n\n // handle light shape\n if (light.shape == {LIGHTSHAPE_RECT}) {\n diffuseAttenuation = getRectLightDiffuse(worldNormal, viewDir, lightDirW, lightDirNormW) * 16.0;\n #ifdef LIT_DIFFUSE_TRANSMISSION\n transmissionAttenuation = getRectLightDiffuse(-worldNormal, viewDir, lightDirW, lightDirNormW) * 16.0;\n #endif\n } else if (light.shape == {LIGHTSHAPE_DISK}) {\n diffuseAttenuation = getDiskLightDiffuse(worldNormal, viewDir, lightDirW, lightDirNormW) * 16.0;\n #ifdef LIT_DIFFUSE_TRANSMISSION\n transmissionAttenuation = getDiskLightDiffuse(-worldNormal, viewDir, lightDirW, lightDirNormW) * 16.0;\n #endif\n } else { // sphere\n diffuseAttenuation = getSphereLightDiffuse(worldNormal, viewDir, lightDirW, lightDirNormW) * 16.0;\n #ifdef LIT_DIFFUSE_TRANSMISSION\n transmissionAttenuation = getSphereLightDiffuse(-worldNormal, viewDir, lightDirW, lightDirNormW) * 16.0;\n #endif\n }\n\n #ifdef LIT_DIFFUSE_TRANSMISSION\n transmissionAttenuation *= falloffAttenuation;\n #endif\n\n } else\n\n #endif\n\n {\n // diffuse transmission, evaluated before the diffuse lighting multiplies into the\n // falloff\n #ifdef LIT_DIFFUSE_TRANSMISSION\n transmissionAttenuation = falloffAttenuation * getLightDiffuse(-worldNormal, viewDir, lightDirNormW);\n #endif\n\n falloffAttenuation *= getLightDiffuse(worldNormal, viewDir, lightDirNormW);\n }\n\n // spot light falloff\n if (light.isSpot) {\n ClusterLightSpotData spotData = decodeClusterLightSpot();\n float spotEffect = getSpotEffect(light.direction, spotData.innerConeAngleCos, spotData.outerConeAngleCos, lightDirNormW);\n falloffAttenuation *= spotEffect;\n\n #ifdef LIT_DIFFUSE_TRANSMISSION\n transmissionAttenuation *= spotEffect;\n #endif\n }\n\n #if defined(CLUSTER_COOKIES) || defined(CLUSTER_SHADOWS)\n\n // with diffuse transmission, the light can also reach the surface from behind\n #ifdef LIT_DIFFUSE_TRANSMISSION\n if (max(falloffAttenuation, transmissionAttenuation) > 0.00001) {\n #else\n if (falloffAttenuation > 0.00001) {\n #endif\n\n // shadow / cookie\n if (light.shadowIntensity > 0.0 || light.cookieIntensity > 0.0) {\n\n vec3 omniAtlasViewport = vec3(0.0);\n\n // shared shadow / cookie data depends on light type\n if (light.isSpot) {\n lightProjectionMatrix = decodeClusterLightProjectionMatrixData(light.lightIndex);\n } else {\n omniAtlasViewport = decodeClusterLightOmniAtlasViewport(light.lightIndex);\n }\n\n float shadowTextureResolution = shadowAtlasParams.x;\n float shadowEdgePixels = shadowAtlasParams.y;\n\n #ifdef CLUSTER_COOKIES\n\n // cookie\n if (light.cookieIntensity > 0.0) {\n vec4 cookieChannelMask = decodeClusterLightCookieData();\n\n if (light.isSpot) {\n cookieAttenuation = getCookie2DClustered(TEXTURE_PASS(cookieAtlasTexture), lightProjectionMatrix, vPositionW, light.cookieIntensity, cookieChannelMask);\n } else {\n cookieAttenuation = getCookieCubeClustered(TEXTURE_PASS(cookieAtlasTexture), lightDirW, light.cookieIntensity, cookieChannelMask, shadowTextureResolution, shadowEdgePixels, omniAtlasViewport);\n }\n }\n\n #endif\n\n #ifdef CLUSTER_SHADOWS\n\n // shadow\n if (light.shadowIntensity > 0.0) {\n ClusterLightShadowData shadowData = decodeClusterLightShadowData(light.biasesData);\n\n vec4 shadowParams = vec4(shadowTextureResolution, shadowData.shadowNormalBias, shadowData.shadowBias, 1.0 / light.range);\n\n // The normal offset moves the sample off the surface, towards the light. A\n // surface with diffuse transmission is also lit from behind, so the side\n // facing the light is used\n #ifdef LIT_DIFFUSE_TRANSMISSION\n vec3 shadowNormal = faceforward(geometricNormal, lightDirNormW, geometricNormal);\n #else\n vec3 shadowNormal = geometricNormal;\n #endif\n\n if (light.isSpot) {\n\n // spot shadow\n vec3 shadowCoord = getShadowCoordPerspZbufferNormalOffset(lightProjectionMatrix, shadowParams, shadowNormal);\n\n #if defined(CLUSTER_SHADOW_TYPE_PCF1)\n float shadow = getShadowSpotClusteredPCF1(SHADOWMAP_PASS(shadowAtlasTexture), shadowCoord, shadowParams);\n #elif defined(CLUSTER_SHADOW_TYPE_PCF3)\n float shadow = getShadowSpotClusteredPCF3(SHADOWMAP_PASS(shadowAtlasTexture), shadowCoord, shadowParams);\n #elif defined(CLUSTER_SHADOW_TYPE_PCF5)\n float shadow = getShadowSpotClusteredPCF5(SHADOWMAP_PASS(shadowAtlasTexture), shadowCoord, shadowParams);\n #elif defined(CLUSTER_SHADOW_TYPE_PCSS)\n float shadow = getShadowSpotClusteredPCSS(SHADOWMAP_PASS(shadowAtlasTexture), shadowCoord, shadowParams);\n #endif\n falloffAttenuation *= mix(1.0, shadow, light.shadowIntensity);\n\n #ifdef LIT_DIFFUSE_TRANSMISSION\n transmissionAttenuation *= mix(1.0, shadow, light.shadowIntensity);\n #endif\n\n } else {\n\n // omni shadow\n vec3 dir = normalOffsetPointShadow(shadowParams, light.position, lightDirW, lightDirNormW, shadowNormal); // normalBias adjusted for distance\n\n #if defined(CLUSTER_SHADOW_TYPE_PCF1)\n float shadow = getShadowOmniClusteredPCF1(SHADOWMAP_PASS(shadowAtlasTexture), shadowParams, omniAtlasViewport, shadowEdgePixels, dir);\n #elif defined(CLUSTER_SHADOW_TYPE_PCF3)\n float shadow = getShadowOmniClusteredPCF3(SHADOWMAP_PASS(shadowAtlasTexture), shadowParams, omniAtlasViewport, shadowEdgePixels, dir);\n #elif defined(CLUSTER_SHADOW_TYPE_PCF5)\n float shadow = getShadowOmniClusteredPCF5(SHADOWMAP_PASS(shadowAtlasTexture), shadowParams, omniAtlasViewport, shadowEdgePixels, dir);\n #endif\n falloffAttenuation *= mix(1.0, shadow, light.shadowIntensity);\n\n #ifdef LIT_DIFFUSE_TRANSMISSION\n transmissionAttenuation *= mix(1.0, shadow, light.shadowIntensity);\n #endif\n }\n }\n\n #endif\n }\n }\n\n #endif\n\n // diffuse / specular / clearcoat\n #ifdef CLUSTER_AREALIGHTS\n\n if (light.shape != {LIGHTSHAPE_PUNCTUAL}) { // area light\n\n // area light diffuse\n {\n vec3 areaDiffuse = (diffuseAttenuation * falloffAttenuation) * light.color * cookieAttenuation;\n\n #if defined(LIT_SPECULAR)\n areaDiffuse = mix(areaDiffuse, vec3(0), dLTCSpecFres);\n #endif\n\n // area light diffuse - it does not mix diffuse lighting into specular attenuation\n dDiffuseLight += areaDiffuse;\n\n #ifdef LIT_DIFFUSE_TRANSMISSION\n vec3 areaTransmission = transmissionAttenuation * light.color * cookieAttenuation;\n\n #if defined(LIT_SPECULAR)\n areaTransmission = mix(areaTransmission, vec3(0), dLTCSpecFres);\n #endif\n\n dDiffuseTransmissionLight += areaTransmission;\n #endif\n }\n\n // specular and clear coat are material settings and get included by a define based on the material\n #ifdef LIT_SPECULAR\n\n // area light specular\n float areaLightSpecular;\n\n if (light.shape == {LIGHTSHAPE_RECT}) {\n areaLightSpecular = getRectLightSpecular(worldNormal, viewDir);\n } else if (light.shape == {LIGHTSHAPE_DISK}) {\n areaLightSpecular = getDiskLightSpecular(worldNormal, viewDir);\n } else { // sphere\n areaLightSpecular = getSphereLightSpecular(worldNormal, viewDir);\n }\n\n dSpecularLight += dLTCSpecFres * areaLightSpecular * falloffAttenuation * light.color * cookieAttenuation;\n\n #ifdef LIT_CLEARCOAT\n\n // area light specular clear coat\n float areaLightSpecularCC;\n\n if (light.shape == {LIGHTSHAPE_RECT}) {\n areaLightSpecularCC = getRectLightSpecular(clearcoat_worldNormal, viewDir);\n } else if (light.shape == {LIGHTSHAPE_DISK}) {\n areaLightSpecularCC = getDiskLightSpecular(clearcoat_worldNormal, viewDir);\n } else { // sphere\n areaLightSpecularCC = getSphereLightSpecular(clearcoat_worldNormal, viewDir);\n }\n\n ccSpecularLight += ccLTCSpecFres * areaLightSpecularCC * falloffAttenuation * light.color * cookieAttenuation;\n\n #endif\n\n #endif\n\n } else\n\n #endif\n\n { // punctual light\n\n // punctual light diffuse\n {\n vec3 punctualDiffuse = falloffAttenuation * light.color * cookieAttenuation;\n\n #if defined(CLUSTER_AREALIGHTS)\n #if defined(LIT_SPECULAR)\n punctualDiffuse = mix(punctualDiffuse, vec3(0), specularity);\n #endif\n #endif\n\n dDiffuseLight += punctualDiffuse;\n\n #ifdef LIT_DIFFUSE_TRANSMISSION\n vec3 punctualTransmission = transmissionAttenuation * light.color * cookieAttenuation;\n\n #if defined(CLUSTER_AREALIGHTS)\n #if defined(LIT_SPECULAR)\n punctualTransmission = mix(punctualTransmission, vec3(0), specularity);\n #endif\n #endif\n\n dDiffuseTransmissionLight += punctualTransmission;\n #endif\n }\n \n // specular and clear coat are material settings and get included by a define based on the material\n #ifdef LIT_SPECULAR\n\n vec3 halfDir = normalize(-lightDirNormW + viewDir);\n \n // specular\n #ifdef LIT_SPECULAR_FRESNEL\n dSpecularLight += \n getLightSpecular(halfDir, reflectionDir, worldNormal, viewDir, lightDirNormW, gloss, tbn) * falloffAttenuation * light.color * cookieAttenuation * \n getFresnel(\n dot(viewDir, halfDir), \n gloss, \n specularity\n #if defined(LIT_IRIDESCENCE)\n , iridescenceFresnel,\n iridescence_intensity\n #endif\n );\n #else\n dSpecularLight += getLightSpecular(halfDir, reflectionDir, worldNormal, viewDir, lightDirNormW, gloss, tbn) * falloffAttenuation * light.color * cookieAttenuation * specularity;\n #endif\n\n #ifdef LIT_CLEARCOAT\n #ifdef LIT_SPECULAR_FRESNEL\n ccSpecularLight += getLightSpecular(halfDir, clearcoatReflectionDir, clearcoat_worldNormal, viewDir, lightDirNormW, clearcoat_gloss, tbn) * falloffAttenuation * light.color * cookieAttenuation * getFresnelCC(dot(viewDir, halfDir));\n #else\n ccSpecularLight += getLightSpecular(halfDir, clearcoatReflectionDir, clearcoat_worldNormal, viewDir, lightDirNormW, clearcoat_gloss, tbn) * falloffAttenuation * light.color * cookieAttenuation; \n #endif\n #endif\n\n #ifdef LIT_SHEEN\n sSpecularLight += getLightSpecularSheen(halfDir, worldNormal, viewDir, lightDirNormW, sheen_gloss) * falloffAttenuation * light.color * cookieAttenuation;\n #endif\n\n #endif\n }\n }\n\n // Write to global attenuation values (for lightmapper)\n dAtten = falloffAttenuation;\n dLightDirNormW = lightDirNormW;\n}\n\nvoid evaluateClusterLight(\n int lightIndex, \n vec3 worldNormal, \n vec3 viewDir, \n vec3 reflectionDir, \n#if defined(LIT_CLEARCOAT)\n vec3 clearcoatReflectionDir,\n#endif\n float gloss, \n vec3 specularity, \n vec3 geometricNormal, \n mat3 tbn, \n#if defined(LIT_IRIDESCENCE)\n vec3 iridescenceFresnel,\n#endif\n vec3 clearcoat_worldNormal,\n float clearcoat_gloss,\n float sheen_gloss,\n float iridescence_intensity\n) {\n\n // decode core light data from textures\n ClusterLightData clusterLightData = decodeClusterLightCore(lightIndex);\n\n // evaluate light if it uses accepted light mask\n #ifdef CLUSTER_MESH_DYNAMIC_LIGHTS\n bool acceptLightMask = clusterLightData.isDynamic;\n #else\n bool acceptLightMask = clusterLightData.isLightmapped;\n #endif\n\n if (acceptLightMask)\n evaluateLight(\n clusterLightData, \n worldNormal, \n viewDir, \n reflectionDir, \n#if defined(LIT_CLEARCOAT)\n clearcoatReflectionDir, \n#endif\n gloss, \n specularity, \n geometricNormal, \n tbn, \n#if defined(LIT_IRIDESCENCE)\n iridescenceFresnel,\n#endif\n clearcoat_worldNormal,\n clearcoat_gloss,\n sheen_gloss,\n iridescence_intensity\n );\n}\n\nvoid addClusteredLights(\n vec3 worldNormal, \n vec3 viewDir, \n vec3 reflectionDir, \n#if defined(LIT_CLEARCOAT)\n vec3 clearcoatReflectionDir,\n#endif\n float gloss, \n vec3 specularity, \n vec3 geometricNormal, \n mat3 tbn, \n#if defined(LIT_IRIDESCENCE)\n vec3 iridescenceFresnel,\n#endif\n vec3 clearcoat_worldNormal,\n float clearcoat_gloss,\n float sheen_gloss,\n float iridescence_intensity\n) {\n\n // skip if no lights (index 0 is reserved for 'no light')\n if (numClusteredLights <= 1)\n return;\n\n // world space position to 3d integer cell coordinates in the cluster structure\n ivec3 cellCoords = ivec3(floor((vPositionW - clusterBoundsMin) * clusterCellsCountByBoundsSize));\n\n // no lighting when cell coordinate is out of range\n if (!(any(lessThan(cellCoords, ivec3(0))) || any(greaterThanEqual(cellCoords, clusterCellsMax)))) {\n\n // cell index (mapping from 3d cell coordinates to linear memory)\n int cellIndex = cellCoords.x * clusterCellsDot.x + cellCoords.y * clusterCellsDot.y + cellCoords.z * clusterCellsDot.z;\n\n // convert cell index to uv coordinates\n int clusterV = cellIndex / clusterTextureWidth;\n int clusterU = cellIndex - clusterV * clusterTextureWidth;\n\n // loop over maximum number of light cells\n for (int lightCellIndex = 0; lightCellIndex < clusterMaxCells; lightCellIndex++) {\n\n // using a single channel texture with data in red channel\n uint lightIndex = texelFetch(clusterWorldTexture, ivec2(clusterU + lightCellIndex, clusterV), 0).x;\n\n if (lightIndex == 0u)\n break;\n\n evaluateClusterLight(\n int(lightIndex), \n worldNormal, \n viewDir, \n reflectionDir,\n#if defined(LIT_CLEARCOAT)\n clearcoatReflectionDir,\n#endif\n gloss, \n specularity, \n geometricNormal, \n tbn, \n#if defined(LIT_IRIDESCENCE)\n iridescenceFresnel,\n#endif\n clearcoat_worldNormal,\n clearcoat_gloss,\n sheen_gloss,\n iridescence_intensity\n ); \n }\n }\n}\n";
1
+ declare const _default: "\n\n#include \"lightBufferDefinesPS\"\n\n// include this before shadow / cookie code\n#include \"clusteredLightUtilsPS\"\n\n#ifdef CLUSTER_COOKIES\n #include \"clusteredLightCookiesPS\"\n#endif\n\n#ifdef CLUSTER_SHADOWS\n #include \"clusteredLightShadowsPS\"\n#endif\n\nuniform highp usampler2D clusterWorldTexture;\nuniform highp sampler2D lightsTexture;\n\n#ifdef CLUSTER_SHADOWS\n // TODO: when VSM shadow is supported, it needs to use sampler2D in webgl2\n uniform sampler2DShadow shadowAtlasTexture;\n#endif\n\n#ifdef CLUSTER_COOKIES\n uniform sampler2D cookieAtlasTexture;\n#endif\n\nuniform int clusterMaxCells;\n\n// number of lights in the cluster structure\nuniform int numClusteredLights;\n\n// width of the cluster texture\nuniform int clusterTextureWidth;\n\nuniform vec3 clusterCellsCountByBoundsSize;\nuniform vec3 clusterBoundsMin;\nuniform vec3 clusterBoundsDelta;\nuniform ivec3 clusterCellsDot;\nuniform ivec3 clusterCellsMax;\nuniform vec2 shadowAtlasParams;\n\n// structure storing light properties of a clustered light. Vectors and scalars are interleaved\n// so each vec3 packs with an adjacent 4-byte field into a 16-byte slot, minimizing padding for\n// compilers that don't reorder struct members.\nstruct ClusterLightData {\n\n // world space position\n vec3 position;\n\n // light index in the lights texture\n int lightIndex;\n\n // world space direction (spot light only)\n vec3 direction;\n\n // area light shape\n uint shape;\n\n // color\n vec3 color;\n\n // 0.0 if the light doesn't cast shadows\n float shadowIntensity;\n\n // range of the light\n float range;\n\n // compressed biases, two half-floats stored in a float\n float biasesData;\n\n // intensity of the cookie\n float cookieIntensity;\n\n // true for spot lights\n bool isSpot;\n\n // light follow mode\n bool falloffModeLinear;\n\n // light mask (mutually exclusive)\n bool isDynamic;\n bool isLightmapped;\n};\n\n// Spot light cone angles, decoded on demand only when the light is a spot light.\nstruct ClusterLightSpotData {\n float innerConeAngleCos;\n float outerConeAngleCos;\n};\n\n// Area light dimensions and orientation, decoded on demand only for non-punctual lights.\nstruct ClusterLightAreaData {\n vec3 halfWidth;\n vec3 halfHeight;\n};\n\n// Shadow bias parameters, decoded on demand only when the light casts shadows.\nstruct ClusterLightShadowData {\n float shadowBias;\n float shadowNormalBias;\n};\n\n// Note: on some devices (tested on Pixel 3A XL), this matrix when stored inside the light struct has lower precision compared to\n// when stored outside, so we store it outside to avoid spot shadow flickering. This might need to be done to other / all members\n// of the structure if further similar issues are observed. See https://github.com/KhronosGroup/WebGL/issues/3351\n\n// shadow (spot light only) / cookie projection matrix\nmat4 lightProjectionMatrix;\n\n// NOTE: On some Samsung devices, these values can suffer precision / corruption issues when stored\n// as members of ClusterLightData. Keep them as module-scope temporaries instead. See issue #7800.\nuint clusterLightData_flags; // 32bit of flags\nfloat clusterLightData_anglesData; // compressed angles, two half-floats stored in a float\nuint clusterLightData_colorBFlagsData; // blue color component and angle flags (as uint for efficient bit operations)\n\nvec4 sampleLightTextureF(int lightIndex, int index) {\n return texelFetch(lightsTexture, ivec2(index, lightIndex), 0);\n}\n\nClusterLightData decodeClusterLightCore(int lightIndex) {\n ClusterLightData clusterLightData;\n\n // light index\n clusterLightData.lightIndex = lightIndex;\n\n // sample data encoding half-float values into 32bit uints\n vec4 halfData = sampleLightTextureF(lightIndex, {CLUSTER_TEXTURE_COLOR_ANGLES_BIAS});\n\n // store values needed by later decode steps (anglesData / colorBFlagsData live outside the\n // struct due to Samsung precision issues - see #7800)\n clusterLightData_anglesData = halfData.z;\n clusterLightData.biasesData = halfData.w;\n clusterLightData_colorBFlagsData = floatBitsToUint(halfData.y);\n\n // decompress color half-floats\n vec2 colorRG = unpackHalf2x16(floatBitsToUint(halfData.x));\n vec2 colorB_flags = unpackHalf2x16(clusterLightData_colorBFlagsData);\n clusterLightData.color = vec3(colorRG, colorB_flags.x) * {LIGHT_COLOR_DIVIDER};\n\n // position and range, full floats\n vec4 lightPosRange = sampleLightTextureF(lightIndex, {CLUSTER_TEXTURE_POSITION_RANGE});\n clusterLightData.position = lightPosRange.xyz;\n clusterLightData.range = lightPosRange.w;\n\n // spot direction & flags data\n vec4 lightDir_Flags = sampleLightTextureF(lightIndex, {CLUSTER_TEXTURE_DIRECTION_FLAGS});\n\n // spot light direction\n clusterLightData.direction = lightDir_Flags.xyz;\n\n // 32bit flags (kept outside the struct, see #7800)\n clusterLightData_flags = floatBitsToUint(lightDir_Flags.w);\n clusterLightData.isSpot = (clusterLightData_flags & (1u << 30u)) != 0u;\n clusterLightData.shape = (clusterLightData_flags >> 28u) & 0x3u;\n clusterLightData.falloffModeLinear = (clusterLightData_flags & (1u << 27u)) == 0u;\n clusterLightData.shadowIntensity = float((clusterLightData_flags >> 0u) & 0xFFu) / 255.0;\n clusterLightData.cookieIntensity = float((clusterLightData_flags >> 8u) & 0xFFu) / 255.0;\n clusterLightData.isDynamic = (clusterLightData_flags & (1u << 22u)) != 0u;\n clusterLightData.isLightmapped = (clusterLightData_flags & (1u << 21u)) != 0u;\n\n return clusterLightData;\n}\n\nClusterLightSpotData decodeClusterLightSpot() {\n // decompress spot light angles\n uint angleFlags = (clusterLightData_colorBFlagsData >> 16u) & 0xFFFFu; // Extract upper 16 bits as integer\n\n vec2 angleValues = unpackHalf2x16(floatBitsToUint(clusterLightData_anglesData));\n float innerVal = angleValues.x;\n float outerVal = angleValues.y;\n\n // decode based on flags (branch-free)\n float innerIsVersine = float(angleFlags & 1u); // bit 0: inner angle format\n float outerIsVersine = float((angleFlags >> 1u) & 1u); // bit 1: outer angle format\n\n return ClusterLightSpotData(\n mix(innerVal, 1.0 - innerVal, innerIsVersine),\n mix(outerVal, 1.0 - outerVal, outerIsVersine)\n );\n}\n\nvec3 decodeClusterLightOmniAtlasViewport(int lightIndex) {\n return sampleLightTextureF(lightIndex, {CLUSTER_TEXTURE_PROJ_MAT_0}).xyz;\n}\n\nClusterLightAreaData decodeClusterLightAreaData(int lightIndex) {\n return ClusterLightAreaData(\n sampleLightTextureF(lightIndex, {CLUSTER_TEXTURE_AREA_DATA_WIDTH}).xyz,\n sampleLightTextureF(lightIndex, {CLUSTER_TEXTURE_AREA_DATA_HEIGHT}).xyz\n );\n}\n\nmat4 decodeClusterLightProjectionMatrixData(int lightIndex) {\n // shadow matrix\n vec4 m0 = sampleLightTextureF(lightIndex, {CLUSTER_TEXTURE_PROJ_MAT_0});\n vec4 m1 = sampleLightTextureF(lightIndex, {CLUSTER_TEXTURE_PROJ_MAT_1});\n vec4 m2 = sampleLightTextureF(lightIndex, {CLUSTER_TEXTURE_PROJ_MAT_2});\n vec4 m3 = sampleLightTextureF(lightIndex, {CLUSTER_TEXTURE_PROJ_MAT_3});\n return mat4(m0, m1, m2, m3);\n}\n\nClusterLightShadowData decodeClusterLightShadowData(float biasesData) {\n // shadow biases\n vec2 biases = unpackHalf2x16(floatBitsToUint(biasesData));\n return ClusterLightShadowData(biases.x, biases.y);\n}\n\nvec4 decodeClusterLightCookieData() {\n // extract channel mask from flags\n uint cookieFlags = (clusterLightData_flags >> 23u) & 0x0Fu; // 4bits, each bit enables a channel\n vec4 mask = vec4(uvec4(cookieFlags) & uvec4(1u, 2u, 4u, 8u));\n return step(1.0, mask); // Normalize to 0.0 or 1.0\n}\n\nvoid evaluateLight(\n ClusterLightData light, \n vec3 worldNormal, \n vec3 viewDir, \n vec3 reflectionDir,\n#if defined(LIT_CLEARCOAT)\n vec3 clearcoatReflectionDir,\n#endif\n float gloss, \n vec3 specularity, \n vec3 geometricNormal, \n mat3 tbn, \n#if defined(LIT_IRIDESCENCE)\n vec3 iridescenceFresnel,\n#endif\n vec3 clearcoat_worldNormal,\n float clearcoat_gloss,\n float sheen_gloss,\n float iridescence_intensity\n) {\n\n vec3 cookieAttenuation = vec3(1.0);\n float diffuseAttenuation = 1.0;\n float falloffAttenuation = 1.0;\n\n #ifdef CLUSTER_AREALIGHTS\n // LTC specular terms of an area light, evaluated together with its diffuse term\n float areaLightSpecular = 0.0;\n float areaLightSpecularCC = 0.0;\n #endif\n\n #ifdef LIT_DIFFUSE_TRANSMISSION\n // the whole attenuation of the light arriving at the back of the surface, which the diffuse\n // transmission lets through\n float transmissionAttenuation = 0.0;\n #endif\n\n // evaluate omni part of the light\n vec3 lightDirW = evalOmniLight(light.position);\n vec3 lightDirNormW = normalize(lightDirW);\n\n #ifdef CLUSTER_AREALIGHTS\n\n // distance attenuation\n if (light.shape != {LIGHTSHAPE_PUNCTUAL}) { // area light\n\n // area lights\n ClusterLightAreaData areaData = decodeClusterLightAreaData(light.lightIndex);\n\n // handle light shape, only the enabled shapes are compiled (a disk uses the rect coordinates)\n #ifdef CLUSTER_AREALIGHT_SPHERE\n if (light.shape == {LIGHTSHAPE_SPHERE}) {\n calcSphereLightValues(light.position, areaData.halfWidth, areaData.halfHeight);\n } else\n #endif\n {\n calcRectLightValues(light.position, areaData.halfWidth, areaData.halfHeight);\n }\n\n falloffAttenuation = getFalloffWindow(light.range, lightDirW);\n\n } else\n\n #endif\n\n { // punctual light\n\n if (light.falloffModeLinear)\n falloffAttenuation = getFalloffLinear(light.range, lightDirW);\n else\n falloffAttenuation = getFalloffInvSquared(light.range, lightDirW);\n }\n\n if (falloffAttenuation > 0.00001) {\n\n #ifdef CLUSTER_AREALIGHTS\n\n if (light.shape != {LIGHTSHAPE_PUNCTUAL}) { // area light\n\n // All LTC evaluations of the light - diffuse (0), diffuse transmission (1), specular (2)\n // and clear coat specular (3) - go through a single loop, so the large LTC functions are\n // emitted once instead of once per term and shape. Shader compilers inline all calls,\n // and on Windows (FXC / DXC) the compile time of the inlined copies dominates.\n bool areaIsRect = light.shape == {LIGHTSHAPE_RECT};\n bool areaIsDisk = light.shape == {LIGHTSHAPE_DISK};\n\n // LTC matrices of the specular and the clear coat specular, from their own gloss and normal\n #ifdef LIT_SPECULAR\n mat3 areaSpecularInvMat = getLTCLightInvMat(dLTCUV);\n #endif\n #ifdef LIT_CLEARCOAT\n mat3 areaClearcoatInvMat = getLTCLightInvMat(ccLTCUV);\n #endif\n\n for (int lobe = 0; lobe < 4; lobe++) {\n\n #ifndef LIT_DIFFUSE_TRANSMISSION\n if (lobe == 1) continue;\n #endif\n #ifndef LIT_SPECULAR\n if (lobe == 2) break;\n #endif\n #ifndef LIT_CLEARCOAT\n if (lobe == 3) break;\n #endif\n\n vec3 lobeNormal = lobe == 1 ? -worldNormal : worldNormal;\n mat3 lobeInvMat = mat3(1.0);\n #ifdef LIT_SPECULAR\n if (lobe >= 2) lobeInvMat = areaSpecularInvMat;\n #endif\n #ifdef LIT_CLEARCOAT\n if (lobe == 3) {\n lobeNormal = clearcoat_worldNormal;\n lobeInvMat = areaClearcoatInvMat;\n }\n #endif\n\n // only the enabled area light shapes are compiled\n float lobeValue = 0.0;\n #ifdef CLUSTER_AREALIGHT_RECT\n if (areaIsRect) {\n lobeValue = LTC_EvaluateRect(lobeNormal, viewDir, vPositionW, lobeInvMat, dLTCCoords);\n } else\n #endif\n #if defined(CLUSTER_AREALIGHT_DISK) || defined(CLUSTER_AREALIGHT_SPHERE)\n if (areaIsDisk || lobe >= 2) {\n // the specular of a sphere light uses the disk facing the reflection\n lobeValue = LTC_EvaluateDisk(lobeNormal, viewDir, vPositionW, lobeInvMat, dLTCCoords);\n if (lobe < 2) lobeValue = FixNan(lobeValue);\n } else\n #endif\n {\n #ifdef CLUSTER_AREALIGHT_SPHERE\n lobeValue = getSphereLightDiffuse(lobeNormal, viewDir, lightDirW, lightDirNormW);\n #endif\n }\n\n if (lobe == 0) diffuseAttenuation = lobeValue * 16.0;\n #ifdef LIT_DIFFUSE_TRANSMISSION\n if (lobe == 1) transmissionAttenuation = lobeValue * 16.0;\n #endif\n if (lobe == 2) areaLightSpecular = lobeValue;\n if (lobe == 3) areaLightSpecularCC = lobeValue;\n }\n\n #ifdef LIT_DIFFUSE_TRANSMISSION\n transmissionAttenuation *= falloffAttenuation;\n #endif\n\n } else\n\n #endif\n\n {\n // diffuse transmission, evaluated before the diffuse lighting multiplies into the\n // falloff\n #ifdef LIT_DIFFUSE_TRANSMISSION\n transmissionAttenuation = falloffAttenuation * getLightDiffuse(-worldNormal, viewDir, lightDirNormW);\n #endif\n\n falloffAttenuation *= getLightDiffuse(worldNormal, viewDir, lightDirNormW);\n }\n\n // spot light falloff\n if (light.isSpot) {\n ClusterLightSpotData spotData = decodeClusterLightSpot();\n float spotEffect = getSpotEffect(light.direction, spotData.innerConeAngleCos, spotData.outerConeAngleCos, lightDirNormW);\n falloffAttenuation *= spotEffect;\n\n #ifdef LIT_DIFFUSE_TRANSMISSION\n transmissionAttenuation *= spotEffect;\n #endif\n }\n\n #if defined(CLUSTER_COOKIES) || defined(CLUSTER_SHADOWS)\n\n // with diffuse transmission, the light can also reach the surface from behind\n #ifdef LIT_DIFFUSE_TRANSMISSION\n if (max(falloffAttenuation, transmissionAttenuation) > 0.00001) {\n #else\n if (falloffAttenuation > 0.00001) {\n #endif\n\n // shadow / cookie\n if (light.shadowIntensity > 0.0 || light.cookieIntensity > 0.0) {\n\n vec3 omniAtlasViewport = vec3(0.0);\n\n // shared shadow / cookie data depends on light type\n if (light.isSpot) {\n lightProjectionMatrix = decodeClusterLightProjectionMatrixData(light.lightIndex);\n } else {\n omniAtlasViewport = decodeClusterLightOmniAtlasViewport(light.lightIndex);\n }\n\n float shadowTextureResolution = shadowAtlasParams.x;\n float shadowEdgePixels = shadowAtlasParams.y;\n\n #ifdef CLUSTER_COOKIES\n\n // cookie\n if (light.cookieIntensity > 0.0) {\n vec4 cookieChannelMask = decodeClusterLightCookieData();\n\n if (light.isSpot) {\n cookieAttenuation = getCookie2DClustered(TEXTURE_PASS(cookieAtlasTexture), lightProjectionMatrix, vPositionW, light.cookieIntensity, cookieChannelMask);\n } else {\n cookieAttenuation = getCookieCubeClustered(TEXTURE_PASS(cookieAtlasTexture), lightDirW, light.cookieIntensity, cookieChannelMask, shadowTextureResolution, shadowEdgePixels, omniAtlasViewport);\n }\n }\n\n #endif\n\n #ifdef CLUSTER_SHADOWS\n\n // shadow\n if (light.shadowIntensity > 0.0) {\n ClusterLightShadowData shadowData = decodeClusterLightShadowData(light.biasesData);\n\n vec4 shadowParams = vec4(shadowTextureResolution, shadowData.shadowNormalBias, shadowData.shadowBias, 1.0 / light.range);\n\n // The normal offset moves the sample off the surface, towards the light. A\n // surface with diffuse transmission is also lit from behind, so the side\n // facing the light is used\n #ifdef LIT_DIFFUSE_TRANSMISSION\n vec3 shadowNormal = faceforward(geometricNormal, lightDirNormW, geometricNormal);\n #else\n vec3 shadowNormal = geometricNormal;\n #endif\n\n if (light.isSpot) {\n\n // spot shadow\n vec3 shadowCoord = getShadowCoordPerspZbufferNormalOffset(lightProjectionMatrix, shadowParams, shadowNormal);\n\n #if defined(CLUSTER_SHADOW_TYPE_PCF1)\n float shadow = getShadowSpotClusteredPCF1(SHADOWMAP_PASS(shadowAtlasTexture), shadowCoord, shadowParams);\n #elif defined(CLUSTER_SHADOW_TYPE_PCF3)\n float shadow = getShadowSpotClusteredPCF3(SHADOWMAP_PASS(shadowAtlasTexture), shadowCoord, shadowParams);\n #elif defined(CLUSTER_SHADOW_TYPE_PCF5)\n float shadow = getShadowSpotClusteredPCF5(SHADOWMAP_PASS(shadowAtlasTexture), shadowCoord, shadowParams);\n #elif defined(CLUSTER_SHADOW_TYPE_PCSS)\n float shadow = getShadowSpotClusteredPCSS(SHADOWMAP_PASS(shadowAtlasTexture), shadowCoord, shadowParams);\n #endif\n falloffAttenuation *= mix(1.0, shadow, light.shadowIntensity);\n\n #ifdef LIT_DIFFUSE_TRANSMISSION\n transmissionAttenuation *= mix(1.0, shadow, light.shadowIntensity);\n #endif\n\n } else {\n\n // omni shadow\n vec3 dir = normalOffsetPointShadow(shadowParams, light.position, lightDirW, lightDirNormW, shadowNormal); // normalBias adjusted for distance\n\n #if defined(CLUSTER_SHADOW_TYPE_PCF1)\n float shadow = getShadowOmniClusteredPCF1(SHADOWMAP_PASS(shadowAtlasTexture), shadowParams, omniAtlasViewport, shadowEdgePixels, dir);\n #elif defined(CLUSTER_SHADOW_TYPE_PCF3)\n float shadow = getShadowOmniClusteredPCF3(SHADOWMAP_PASS(shadowAtlasTexture), shadowParams, omniAtlasViewport, shadowEdgePixels, dir);\n #elif defined(CLUSTER_SHADOW_TYPE_PCF5)\n float shadow = getShadowOmniClusteredPCF5(SHADOWMAP_PASS(shadowAtlasTexture), shadowParams, omniAtlasViewport, shadowEdgePixels, dir);\n #endif\n falloffAttenuation *= mix(1.0, shadow, light.shadowIntensity);\n\n #ifdef LIT_DIFFUSE_TRANSMISSION\n transmissionAttenuation *= mix(1.0, shadow, light.shadowIntensity);\n #endif\n }\n }\n\n #endif\n }\n }\n\n #endif\n\n // diffuse / specular / clearcoat\n #ifdef CLUSTER_AREALIGHTS\n\n if (light.shape != {LIGHTSHAPE_PUNCTUAL}) { // area light\n\n // area light diffuse\n {\n vec3 areaDiffuse = (diffuseAttenuation * falloffAttenuation) * light.color * cookieAttenuation;\n\n #if defined(LIT_SPECULAR)\n areaDiffuse = mix(areaDiffuse, vec3(0), dLTCSpecFres);\n #endif\n\n // area light diffuse - it does not mix diffuse lighting into specular attenuation\n dDiffuseLight += areaDiffuse;\n\n #ifdef LIT_DIFFUSE_TRANSMISSION\n vec3 areaTransmission = transmissionAttenuation * light.color * cookieAttenuation;\n\n #if defined(LIT_SPECULAR)\n areaTransmission = mix(areaTransmission, vec3(0), dLTCSpecFres);\n #endif\n\n dDiffuseTransmissionLight += areaTransmission;\n #endif\n }\n\n // specular and clear coat are material settings and get included by a define based on the material\n #ifdef LIT_SPECULAR\n\n // area light specular, evaluated together with the diffuse term\n dSpecularLight += dLTCSpecFres * areaLightSpecular * falloffAttenuation * light.color * cookieAttenuation;\n\n #ifdef LIT_CLEARCOAT\n\n // area light specular clear coat\n ccSpecularLight += ccLTCSpecFres * areaLightSpecularCC * falloffAttenuation * light.color * cookieAttenuation;\n\n #endif\n\n #endif\n\n } else\n\n #endif\n\n { // punctual light\n\n // punctual light diffuse\n {\n vec3 punctualDiffuse = falloffAttenuation * light.color * cookieAttenuation;\n\n #if defined(CLUSTER_AREALIGHTS)\n #if defined(LIT_SPECULAR)\n punctualDiffuse = mix(punctualDiffuse, vec3(0), specularity);\n #endif\n #endif\n\n dDiffuseLight += punctualDiffuse;\n\n #ifdef LIT_DIFFUSE_TRANSMISSION\n vec3 punctualTransmission = transmissionAttenuation * light.color * cookieAttenuation;\n\n #if defined(CLUSTER_AREALIGHTS)\n #if defined(LIT_SPECULAR)\n punctualTransmission = mix(punctualTransmission, vec3(0), specularity);\n #endif\n #endif\n\n dDiffuseTransmissionLight += punctualTransmission;\n #endif\n }\n \n // specular and clear coat are material settings and get included by a define based on the material\n #ifdef LIT_SPECULAR\n\n vec3 halfDir = normalize(-lightDirNormW + viewDir);\n \n // specular\n #ifdef LIT_SPECULAR_FRESNEL\n dSpecularLight += \n getLightSpecular(halfDir, reflectionDir, worldNormal, viewDir, lightDirNormW, gloss, tbn) * falloffAttenuation * light.color * cookieAttenuation * \n getFresnel(\n dot(viewDir, halfDir), \n gloss, \n specularity\n #if defined(LIT_IRIDESCENCE)\n , iridescenceFresnel,\n iridescence_intensity\n #endif\n );\n #else\n dSpecularLight += getLightSpecular(halfDir, reflectionDir, worldNormal, viewDir, lightDirNormW, gloss, tbn) * falloffAttenuation * light.color * cookieAttenuation * specularity;\n #endif\n\n #ifdef LIT_CLEARCOAT\n #ifdef LIT_SPECULAR_FRESNEL\n ccSpecularLight += getLightSpecular(halfDir, clearcoatReflectionDir, clearcoat_worldNormal, viewDir, lightDirNormW, clearcoat_gloss, tbn) * falloffAttenuation * light.color * cookieAttenuation * getFresnelCC(dot(viewDir, halfDir));\n #else\n ccSpecularLight += getLightSpecular(halfDir, clearcoatReflectionDir, clearcoat_worldNormal, viewDir, lightDirNormW, clearcoat_gloss, tbn) * falloffAttenuation * light.color * cookieAttenuation; \n #endif\n #endif\n\n #ifdef LIT_SHEEN\n sSpecularLight += getLightSpecularSheen(halfDir, worldNormal, viewDir, lightDirNormW, sheen_gloss) * falloffAttenuation * light.color * cookieAttenuation;\n #endif\n\n #endif\n }\n }\n\n // Write to global attenuation values (for lightmapper)\n dAtten = falloffAttenuation;\n dLightDirNormW = lightDirNormW;\n}\n\nvoid evaluateClusterLight(\n int lightIndex, \n vec3 worldNormal, \n vec3 viewDir, \n vec3 reflectionDir, \n#if defined(LIT_CLEARCOAT)\n vec3 clearcoatReflectionDir,\n#endif\n float gloss, \n vec3 specularity, \n vec3 geometricNormal, \n mat3 tbn, \n#if defined(LIT_IRIDESCENCE)\n vec3 iridescenceFresnel,\n#endif\n vec3 clearcoat_worldNormal,\n float clearcoat_gloss,\n float sheen_gloss,\n float iridescence_intensity\n) {\n\n // decode core light data from textures\n ClusterLightData clusterLightData = decodeClusterLightCore(lightIndex);\n\n // evaluate light if it uses accepted light mask\n #ifdef CLUSTER_MESH_DYNAMIC_LIGHTS\n bool acceptLightMask = clusterLightData.isDynamic;\n #else\n bool acceptLightMask = clusterLightData.isLightmapped;\n #endif\n\n if (acceptLightMask)\n evaluateLight(\n clusterLightData, \n worldNormal, \n viewDir, \n reflectionDir, \n#if defined(LIT_CLEARCOAT)\n clearcoatReflectionDir, \n#endif\n gloss, \n specularity, \n geometricNormal, \n tbn, \n#if defined(LIT_IRIDESCENCE)\n iridescenceFresnel,\n#endif\n clearcoat_worldNormal,\n clearcoat_gloss,\n sheen_gloss,\n iridescence_intensity\n );\n}\n\nvoid addClusteredLights(\n vec3 worldNormal, \n vec3 viewDir, \n vec3 reflectionDir, \n#if defined(LIT_CLEARCOAT)\n vec3 clearcoatReflectionDir,\n#endif\n float gloss, \n vec3 specularity, \n vec3 geometricNormal, \n mat3 tbn, \n#if defined(LIT_IRIDESCENCE)\n vec3 iridescenceFresnel,\n#endif\n vec3 clearcoat_worldNormal,\n float clearcoat_gloss,\n float sheen_gloss,\n float iridescence_intensity\n) {\n\n // skip if no lights (index 0 is reserved for 'no light')\n if (numClusteredLights <= 1)\n return;\n\n // world space position to 3d integer cell coordinates in the cluster structure\n ivec3 cellCoords = ivec3(floor((vPositionW - clusterBoundsMin) * clusterCellsCountByBoundsSize));\n\n // no lighting when cell coordinate is out of range\n if (!(any(lessThan(cellCoords, ivec3(0))) || any(greaterThanEqual(cellCoords, clusterCellsMax)))) {\n\n // cell index (mapping from 3d cell coordinates to linear memory)\n int cellIndex = cellCoords.x * clusterCellsDot.x + cellCoords.y * clusterCellsDot.y + cellCoords.z * clusterCellsDot.z;\n\n // convert cell index to uv coordinates\n int clusterV = cellIndex / clusterTextureWidth;\n int clusterU = cellIndex - clusterV * clusterTextureWidth;\n\n // loop over maximum number of light cells\n for (int lightCellIndex = 0; lightCellIndex < clusterMaxCells; lightCellIndex++) {\n\n // using a single channel texture with data in red channel\n uint lightIndex = texelFetch(clusterWorldTexture, ivec2(clusterU + lightCellIndex, clusterV), 0).x;\n\n if (lightIndex == 0u)\n break;\n\n evaluateClusterLight(\n int(lightIndex), \n worldNormal, \n viewDir, \n reflectionDir,\n#if defined(LIT_CLEARCOAT)\n clearcoatReflectionDir,\n#endif\n gloss, \n specularity, \n geometricNormal, \n tbn, \n#if defined(LIT_IRIDESCENCE)\n iridescenceFresnel,\n#endif\n clearcoat_worldNormal,\n clearcoat_gloss,\n sheen_gloss,\n iridescence_intensity\n ); \n }\n }\n}\n";
2
2
  export default _default;
@@ -143,6 +143,10 @@ void evaluateLight(
143
143
  vec3 cookieAttenuation = vec3(1.0);
144
144
  float diffuseAttenuation = 1.0;
145
145
  float falloffAttenuation = 1.0;
146
+ #ifdef CLUSTER_AREALIGHTS
147
+ float areaLightSpecular = 0.0;
148
+ float areaLightSpecularCC = 0.0;
149
+ #endif
146
150
  #ifdef LIT_DIFFUSE_TRANSMISSION
147
151
  float transmissionAttenuation = 0.0;
148
152
  #endif
@@ -151,12 +155,13 @@ void evaluateLight(
151
155
  #ifdef CLUSTER_AREALIGHTS
152
156
  if (light.shape != {LIGHTSHAPE_PUNCTUAL}) {
153
157
  ClusterLightAreaData areaData = decodeClusterLightAreaData(light.lightIndex);
154
- if (light.shape == {LIGHTSHAPE_RECT}) {
155
- calcRectLightValues(light.position, areaData.halfWidth, areaData.halfHeight);
156
- } else if (light.shape == {LIGHTSHAPE_DISK}) {
157
- calcDiskLightValues(light.position, areaData.halfWidth, areaData.halfHeight);
158
- } else {
158
+ #ifdef CLUSTER_AREALIGHT_SPHERE
159
+ if (light.shape == {LIGHTSHAPE_SPHERE}) {
159
160
  calcSphereLightValues(light.position, areaData.halfWidth, areaData.halfHeight);
161
+ } else
162
+ #endif
163
+ {
164
+ calcRectLightValues(light.position, areaData.halfWidth, areaData.halfHeight);
160
165
  }
161
166
  falloffAttenuation = getFalloffWindow(light.range, lightDirW);
162
167
  } else
@@ -170,21 +175,58 @@ void evaluateLight(
170
175
  if (falloffAttenuation > 0.00001) {
171
176
  #ifdef CLUSTER_AREALIGHTS
172
177
  if (light.shape != {LIGHTSHAPE_PUNCTUAL}) {
173
- if (light.shape == {LIGHTSHAPE_RECT}) {
174
- diffuseAttenuation = getRectLightDiffuse(worldNormal, viewDir, lightDirW, lightDirNormW) * 16.0;
175
- #ifdef LIT_DIFFUSE_TRANSMISSION
176
- transmissionAttenuation = getRectLightDiffuse(-worldNormal, viewDir, lightDirW, lightDirNormW) * 16.0;
178
+ bool areaIsRect = light.shape == {LIGHTSHAPE_RECT};
179
+ bool areaIsDisk = light.shape == {LIGHTSHAPE_DISK};
180
+ #ifdef LIT_SPECULAR
181
+ mat3 areaSpecularInvMat = getLTCLightInvMat(dLTCUV);
182
+ #endif
183
+ #ifdef LIT_CLEARCOAT
184
+ mat3 areaClearcoatInvMat = getLTCLightInvMat(ccLTCUV);
185
+ #endif
186
+ for (int lobe = 0; lobe < 4; lobe++) {
187
+ #ifndef LIT_DIFFUSE_TRANSMISSION
188
+ if (lobe == 1) continue;
177
189
  #endif
178
- } else if (light.shape == {LIGHTSHAPE_DISK}) {
179
- diffuseAttenuation = getDiskLightDiffuse(worldNormal, viewDir, lightDirW, lightDirNormW) * 16.0;
180
- #ifdef LIT_DIFFUSE_TRANSMISSION
181
- transmissionAttenuation = getDiskLightDiffuse(-worldNormal, viewDir, lightDirW, lightDirNormW) * 16.0;
190
+ #ifndef LIT_SPECULAR
191
+ if (lobe == 2) break;
192
+ #endif
193
+ #ifndef LIT_CLEARCOAT
194
+ if (lobe == 3) break;
182
195
  #endif
183
- } else {
184
- diffuseAttenuation = getSphereLightDiffuse(worldNormal, viewDir, lightDirW, lightDirNormW) * 16.0;
196
+ vec3 lobeNormal = lobe == 1 ? -worldNormal : worldNormal;
197
+ mat3 lobeInvMat = mat3(1.0);
198
+ #ifdef LIT_SPECULAR
199
+ if (lobe >= 2) lobeInvMat = areaSpecularInvMat;
200
+ #endif
201
+ #ifdef LIT_CLEARCOAT
202
+ if (lobe == 3) {
203
+ lobeNormal = clearcoat_worldNormal;
204
+ lobeInvMat = areaClearcoatInvMat;
205
+ }
206
+ #endif
207
+ float lobeValue = 0.0;
208
+ #ifdef CLUSTER_AREALIGHT_RECT
209
+ if (areaIsRect) {
210
+ lobeValue = LTC_EvaluateRect(lobeNormal, viewDir, vPositionW, lobeInvMat, dLTCCoords);
211
+ } else
212
+ #endif
213
+ #if defined(CLUSTER_AREALIGHT_DISK) || defined(CLUSTER_AREALIGHT_SPHERE)
214
+ if (areaIsDisk || lobe >= 2) {
215
+ lobeValue = LTC_EvaluateDisk(lobeNormal, viewDir, vPositionW, lobeInvMat, dLTCCoords);
216
+ if (lobe < 2) lobeValue = FixNan(lobeValue);
217
+ } else
218
+ #endif
219
+ {
220
+ #ifdef CLUSTER_AREALIGHT_SPHERE
221
+ lobeValue = getSphereLightDiffuse(lobeNormal, viewDir, lightDirW, lightDirNormW);
222
+ #endif
223
+ }
224
+ if (lobe == 0) diffuseAttenuation = lobeValue * 16.0;
185
225
  #ifdef LIT_DIFFUSE_TRANSMISSION
186
- transmissionAttenuation = getSphereLightDiffuse(-worldNormal, viewDir, lightDirW, lightDirNormW) * 16.0;
226
+ if (lobe == 1) transmissionAttenuation = lobeValue * 16.0;
187
227
  #endif
228
+ if (lobe == 2) areaLightSpecular = lobeValue;
229
+ if (lobe == 3) areaLightSpecularCC = lobeValue;
188
230
  }
189
231
  #ifdef LIT_DIFFUSE_TRANSMISSION
190
232
  transmissionAttenuation *= falloffAttenuation;
@@ -290,24 +332,8 @@ void evaluateLight(
290
332
  #endif
291
333
  }
292
334
  #ifdef LIT_SPECULAR
293
- float areaLightSpecular;
294
- if (light.shape == {LIGHTSHAPE_RECT}) {
295
- areaLightSpecular = getRectLightSpecular(worldNormal, viewDir);
296
- } else if (light.shape == {LIGHTSHAPE_DISK}) {
297
- areaLightSpecular = getDiskLightSpecular(worldNormal, viewDir);
298
- } else {
299
- areaLightSpecular = getSphereLightSpecular(worldNormal, viewDir);
300
- }
301
335
  dSpecularLight += dLTCSpecFres * areaLightSpecular * falloffAttenuation * light.color * cookieAttenuation;
302
336
  #ifdef LIT_CLEARCOAT
303
- float areaLightSpecularCC;
304
- if (light.shape == {LIGHTSHAPE_RECT}) {
305
- areaLightSpecularCC = getRectLightSpecular(clearcoat_worldNormal, viewDir);
306
- } else if (light.shape == {LIGHTSHAPE_DISK}) {
307
- areaLightSpecularCC = getDiskLightSpecular(clearcoat_worldNormal, viewDir);
308
- } else {
309
- areaLightSpecularCC = getSphereLightSpecular(clearcoat_worldNormal, viewDir);
310
- }
311
337
  ccSpecularLight += ccLTCSpecFres * areaLightSpecularCC * falloffAttenuation * light.color * cookieAttenuation;
312
338
  #endif
313
339
  #endif
@@ -1,2 +1,2 @@
1
- declare const _default: "\n#if defined(LIGHT{i})\n\nvoid evaluateLight{i}(\n #if defined(LIT_IRIDESCENCE)\n vec3 iridescenceFresnel\n #endif\n) {\n\n // light color\n vec3 lightColor = light{i}_color;\n\n #if LIGHT{i}TYPE == DIRECTIONAL && !defined(LIT_SHADOW_CATCHER)\n // early return if the light color is black (used by shadow catcher - this way this light is very cheap)\n if (all(equal(lightColor, vec3(0.0)))) {\n return;\n }\n #endif\n\n #if LIGHT{i}TYPE == DIRECTIONAL // directional light\n\n dLightDirNormW = light{i}_direction;\n dAtten = 1.0;\n\n #else // omni or spot light\n \n vec3 lightDirW = evalOmniLight(light{i}_position);\n dLightDirNormW = normalize(lightDirW);\n\n // cookie attenuation\n #if defined(LIGHT{i}COOKIE)\n\n #if LIGHT{i}TYPE == SPOT\n #ifdef LIGHT{i}COOKIE_FALLOFF\n #ifdef LIGHT{i}COOKIE_TRANSFORM\n vec3 cookieAttenuation = getCookie2DXform(light{i}_cookie, light{i}_shadowMatrix, light{i}_cookieIntensity, light{i}_cookieMatrix, light{i}_cookieOffset).{LIGHT{i}COOKIE_CHANNEL};\n #else\n vec3 cookieAttenuation = getCookie2D(light{i}_cookie, light{i}_shadowMatrix, light{i}_cookieIntensity).{LIGHT{i}COOKIE_CHANNEL};\n #endif\n #else\n #ifdef LIGHT{i}COOKIE_TRANSFORM\n vec3 cookieAttenuation = getCookie2DClipXform(light{i}_cookie, light{i}_shadowMatrix, light{i}_cookieIntensity, light{i}_cookieMatrix, light{i}_cookieOffset).{LIGHT{i}COOKIE_CHANNEL};\n #else\n vec3 cookieAttenuation = getCookie2DClip(light{i}_cookie, light{i}_shadowMatrix, light{i}_cookieIntensity).{LIGHT{i}COOKIE_CHANNEL};\n #endif\n #endif\n #endif\n\n #if LIGHT{i}TYPE == OMNI\n vec3 cookieAttenuation = getCookieCube(light{i}_cookie, light{i}_shadowMatrix, light{i}_cookieIntensity).{LIGHT{i}COOKIE_CHANNEL};\n #endif\n\n // multiply light color by the cookie attenuation\n lightColor *= cookieAttenuation;\n\n #endif\n\n // distance falloff\n #if LIGHT{i}SHAPE == PUNCTUAL\n #if LIGHT{i}FALLOFF == LINEAR\n dAtten = getFalloffLinear(light{i}_radius, lightDirW);\n #else\n dAtten = getFalloffInvSquared(light{i}_radius, lightDirW);\n #endif\n #else\n // non punctual lights only gets the range window here\n dAtten = getFalloffWindow(light{i}_radius, lightDirW);\n #endif\n\n // spot light angle falloff\n #if LIGHT{i}TYPE == SPOT\n #if !defined(LIGHT{i}COOKIE) || defined(LIGHT{i}COOKIE_FALLOFF)\n dAtten *= getSpotEffect(light{i}_direction, light{i}_innerConeAngle, light{i}_outerConeAngle, dLightDirNormW);\n #endif\n #endif\n #endif\n\n if (dAtten < 0.00001) {\n return;\n }\n\n // evaluate area light values\n #if LIGHT{i}SHAPE != PUNCTUAL\n #if LIGHT{i}SHAPE == RECT\n calcRectLightValues(light{i}_position, light{i}_halfWidth, light{i}_halfHeight);\n #elif LIGHT{i}SHAPE == DISK\n calcDiskLightValues(light{i}_position, light{i}_halfWidth, light{i}_halfHeight);\n #elif LIGHT{i}SHAPE == SPHERE\n calcSphereLightValues(light{i}_position, light{i}_halfWidth, light{i}_halfHeight);\n #endif\n #endif\n\n // diffuse transmission - the light arriving at the back of the surface, which is the diffuse\n // lighting of the flipped normal. It is evaluated before the diffuse lighting of a punctual\n // light multiplies into its attenuation\n #ifdef LIT_DIFFUSE_TRANSMISSION\n #if LIGHT{i}SHAPE == PUNCTUAL\n float attenTransmission = dAtten * getLightDiffuse(-litArgs_worldNormal, vec3(0.0), dLightDirNormW);\n #elif LIGHT{i}TYPE == DIRECTIONAL\n float attenTransmission = dAtten * getLightDiffuse(-litArgs_worldNormal, dViewDirW, dLightDirNormW);\n #elif LIGHT{i}SHAPE == RECT\n float attenTransmission = dAtten * getRectLightDiffuse(-litArgs_worldNormal, dViewDirW, lightDirW, dLightDirNormW) * 16.0;\n #elif LIGHT{i}SHAPE == DISK\n float attenTransmission = dAtten * getDiskLightDiffuse(-litArgs_worldNormal, dViewDirW, lightDirW, dLightDirNormW) * 16.0;\n #elif LIGHT{i}SHAPE == SPHERE\n float attenTransmission = dAtten * getSphereLightDiffuse(-litArgs_worldNormal, dViewDirW, lightDirW, dLightDirNormW) * 16.0;\n #endif\n #endif\n\n // diffuse lighting - LTC lights do not mix diffuse lighting into attenuation that affects specular\n #if LIGHT{i}SHAPE != PUNCTUAL\n\n // attenDiffuse - separate diffuse attenuation for non-punctual light sources\n\n #if LIGHT{i}TYPE == DIRECTIONAL\n // NB: A better approximation perhaps using wrap lighting could be implemented here\n float attenDiffuse = getLightDiffuse(litArgs_worldNormal, dViewDirW, dLightDirNormW);\n #else\n // 16.0 is a constant that is in getFalloffInvSquared()\n #if LIGHT{i}SHAPE == RECT\n float attenDiffuse = getRectLightDiffuse(litArgs_worldNormal, dViewDirW, lightDirW, dLightDirNormW) * 16.0;\n #elif LIGHT{i}SHAPE == DISK\n float attenDiffuse = getDiskLightDiffuse(litArgs_worldNormal, dViewDirW, lightDirW, dLightDirNormW) * 16.0;\n #elif LIGHT{i}SHAPE == SPHERE\n float attenDiffuse = getSphereLightDiffuse(litArgs_worldNormal, dViewDirW, lightDirW, dLightDirNormW) * 16.0;\n #endif\n #endif\n #else\n // one parameter is unused for punctual lights\n dAtten *= getLightDiffuse(litArgs_worldNormal, vec3(0.0), dLightDirNormW);\n #endif\n\n // apply the shadow attenuation\n #ifdef LIGHT{i}CASTSHADOW\n\n #if LIGHT{i}TYPE == DIRECTIONAL\n float shadow = getShadow{i}(vec3(0.0));\n #else\n float shadow = getShadow{i}(lightDirW);\n #endif\n\n #ifdef LIGHT{i}_SHADOW_CASCADE_BLEND\n // Fade to fully lit at the shadow distance without sampling another cascade.\n float shadowDistance = light{i}_shadowCascadeDistances.w;\n float shadowFade = smoothstep(light{i}_shadowCascadeBlend * shadowDistance, shadowDistance, 1.0 / gl_FragCoord.w);\n shadow = mix(shadow, 1.0, shadowFade);\n #endif\n\n // Apply shadow intensity to the shadow value\n shadow = mix(1.0, shadow, light{i}_shadowIntensity);\n\n dAtten *= shadow;\n\n #ifdef LIT_DIFFUSE_TRANSMISSION\n attenTransmission *= shadow;\n #endif\n\n #if defined(LIT_SHADOW_CATCHER) && LIGHT{i}TYPE == DIRECTIONAL\n // accumulate shadows for directional lights\n dShadowCatcher *= shadow;\n #endif \n\n #endif\n\n // Relief self shadowing, which is a property of the surface rather than of the light, so it\n // applies whether or not the light casts a shadow map. Directional lights only for now: they are\n // never clustered, so a pixel sees at most a handful of them and the cost stays bounded.\n #if defined(STD_PARALLAX_SELF_SHADOW) && LIGHT{i}TYPE == DIRECTIONAL\n dAtten *= getParallaxSelfShadow(dLightDirNormW);\n #endif\n\n #if LIGHT{i}SHAPE != PUNCTUAL\n // area light - they do not mix diffuse lighting into specular attenuation\n #ifdef LIT_SPECULAR\n dDiffuseLight += ((attenDiffuse * dAtten) * lightColor) * (1.0 - dLTCSpecFres);\n #ifdef LIT_DIFFUSE_TRANSMISSION\n dDiffuseTransmissionLight += (attenTransmission * lightColor) * (1.0 - dLTCSpecFres);\n #endif\n #else\n dDiffuseLight += (attenDiffuse * dAtten) * lightColor;\n #ifdef LIT_DIFFUSE_TRANSMISSION\n dDiffuseTransmissionLight += attenTransmission * lightColor;\n #endif\n #endif \n #else\n // punctual light\n #if defined(AREA_LIGHTS) && defined(LIT_SPECULAR)\n dDiffuseLight += (dAtten * lightColor) * (1.0 - litArgs_specularity);\n #ifdef LIT_DIFFUSE_TRANSMISSION\n dDiffuseTransmissionLight += (attenTransmission * lightColor) * (1.0 - litArgs_specularity);\n #endif\n #else\n dDiffuseLight += dAtten * lightColor;\n #ifdef LIT_DIFFUSE_TRANSMISSION\n dDiffuseTransmissionLight += attenTransmission * lightColor;\n #endif\n #endif\n #endif\n\n // specular lighting\n #ifdef LIGHT{i}AFFECT_SPECULARITY\n\n #if LIGHT{i}SHAPE != PUNCTUAL // area light\n\n #ifdef LIT_CLEARCOAT\n #if LIGHT{i}SHAPE == RECT\n ccSpecularLight += ccLTCSpecFres * getRectLightSpecular(litArgs_clearcoat_worldNormal, dViewDirW) * dAtten * lightColor;\n #elif LIGHT{i}SHAPE == DISK\n ccSpecularLight += ccLTCSpecFres * getDiskLightSpecular(litArgs_clearcoat_worldNormal, dViewDirW) * dAtten * lightColor;\n #elif LIGHT{i}SHAPE == SPHERE\n ccSpecularLight += ccLTCSpecFres * getSphereLightSpecular(litArgs_clearcoat_worldNormal, dViewDirW) * dAtten * lightColor;\n #endif\n #endif\n\n #ifdef LIT_SPECULAR\n #if LIGHT{i}SHAPE == RECT\n dSpecularLight += dLTCSpecFres * getRectLightSpecular(litArgs_worldNormal, dViewDirW) * dAtten * lightColor;\n #elif LIGHT{i}SHAPE == DISK\n dSpecularLight += dLTCSpecFres * getDiskLightSpecular(litArgs_worldNormal, dViewDirW) * dAtten * lightColor;\n #elif LIGHT{i}SHAPE == SPHERE\n dSpecularLight += dLTCSpecFres * getSphereLightSpecular(litArgs_worldNormal, dViewDirW) * dAtten * lightColor;\n #endif\n #endif\n\n #else // punctual light\n\n // is fresnel needed\n #if LIGHT{i}TYPE == DIRECTIONAL && LIT_FRESNEL_MODEL != NONE\n #define LIGHT{i}FRESNEL\n #endif\n\n #ifdef LIT_SPECULAR\n vec3 halfDirW = normalize(-dLightDirNormW + dViewDirW);\n #endif\n\n // if LTC lights are present, specular must be accumulated with specularity (specularity is pre multiplied by punctual light fresnel)\n #ifdef LIT_CLEARCOAT\n vec3 lightspecularCC = getLightSpecular(halfDirW, ccReflDirW, litArgs_clearcoat_worldNormal, dViewDirW, dLightDirNormW, litArgs_clearcoat_gloss, dTBN) * dAtten * lightColor;\n #ifdef LIGHT{i}FRESNEL\n lightspecularCC *= getFresnelCC(dot(dViewDirW, halfDirW));\n #endif\n ccSpecularLight += lightspecularCC;\n #endif\n\n #ifdef LIT_SHEEN\n sSpecularLight += getLightSpecularSheen(halfDirW, litArgs_worldNormal, dViewDirW, dLightDirNormW, litArgs_sheen_gloss) * dAtten * lightColor;\n #endif\n\n #ifdef LIT_SPECULAR\n\n vec3 lightSpecular = getLightSpecular(halfDirW, dReflDirW, litArgs_worldNormal, dViewDirW, dLightDirNormW, litArgs_gloss, dTBN) * dAtten * lightColor;\n #ifdef LIGHT{i}FRESNEL\n\n #if defined(LIT_IRIDESCENCE)\n lightSpecular *= getFresnel(dot(dViewDirW, halfDirW), litArgs_gloss, litArgs_specularity, iridescenceFresnel, litArgs_iridescence_intensity);\n #else\n lightSpecular *= getFresnel(dot(dViewDirW, halfDirW), litArgs_gloss, litArgs_specularity);\n #endif\n\n #else\n lightSpecular *= litArgs_specularity;\n #endif\n \n dSpecularLight += lightSpecular;\n #endif\n #endif\n #endif\n}\n#endif\n";
1
+ declare const _default: "\n#if defined(LIGHT{i})\n\nvoid evaluateLight{i}(\n #if defined(LIT_IRIDESCENCE)\n vec3 iridescenceFresnel\n #endif\n) {\n\n // light color\n vec3 lightColor = light{i}_color;\n\n #if LIGHT{i}TYPE == DIRECTIONAL && !defined(LIT_SHADOW_CATCHER)\n // early return if the light color is black (used by shadow catcher - this way this light is very cheap)\n if (all(equal(lightColor, vec3(0.0)))) {\n return;\n }\n #endif\n\n #if LIGHT{i}TYPE == DIRECTIONAL // directional light\n\n dLightDirNormW = light{i}_direction;\n dAtten = 1.0;\n\n #else // omni or spot light\n \n vec3 lightDirW = evalOmniLight(light{i}_position);\n dLightDirNormW = normalize(lightDirW);\n\n // cookie attenuation\n #if defined(LIGHT{i}COOKIE)\n\n #if LIGHT{i}TYPE == SPOT\n #ifdef LIGHT{i}COOKIE_FALLOFF\n #ifdef LIGHT{i}COOKIE_TRANSFORM\n vec3 cookieAttenuation = getCookie2DXform(light{i}_cookie, light{i}_shadowMatrix, light{i}_cookieIntensity, light{i}_cookieMatrix, light{i}_cookieOffset).{LIGHT{i}COOKIE_CHANNEL};\n #else\n vec3 cookieAttenuation = getCookie2D(light{i}_cookie, light{i}_shadowMatrix, light{i}_cookieIntensity).{LIGHT{i}COOKIE_CHANNEL};\n #endif\n #else\n #ifdef LIGHT{i}COOKIE_TRANSFORM\n vec3 cookieAttenuation = getCookie2DClipXform(light{i}_cookie, light{i}_shadowMatrix, light{i}_cookieIntensity, light{i}_cookieMatrix, light{i}_cookieOffset).{LIGHT{i}COOKIE_CHANNEL};\n #else\n vec3 cookieAttenuation = getCookie2DClip(light{i}_cookie, light{i}_shadowMatrix, light{i}_cookieIntensity).{LIGHT{i}COOKIE_CHANNEL};\n #endif\n #endif\n #endif\n\n #if LIGHT{i}TYPE == OMNI\n vec3 cookieAttenuation = getCookieCube(light{i}_cookie, light{i}_shadowMatrix, light{i}_cookieIntensity).{LIGHT{i}COOKIE_CHANNEL};\n #endif\n\n // multiply light color by the cookie attenuation\n lightColor *= cookieAttenuation;\n\n #endif\n\n // distance falloff\n #if LIGHT{i}SHAPE == PUNCTUAL\n #if LIGHT{i}FALLOFF == LINEAR\n dAtten = getFalloffLinear(light{i}_radius, lightDirW);\n #else\n dAtten = getFalloffInvSquared(light{i}_radius, lightDirW);\n #endif\n #else\n // non punctual lights only gets the range window here\n dAtten = getFalloffWindow(light{i}_radius, lightDirW);\n #endif\n\n // spot light angle falloff\n #if LIGHT{i}TYPE == SPOT\n #if !defined(LIGHT{i}COOKIE) || defined(LIGHT{i}COOKIE_FALLOFF)\n dAtten *= getSpotEffect(light{i}_direction, light{i}_innerConeAngle, light{i}_outerConeAngle, dLightDirNormW);\n #endif\n #endif\n #endif\n\n if (dAtten < 0.00001) {\n return;\n }\n\n // evaluate area light values\n #if LIGHT{i}SHAPE != PUNCTUAL\n #if LIGHT{i}SHAPE == RECT\n calcRectLightValues(light{i}_position, light{i}_halfWidth, light{i}_halfHeight);\n #elif LIGHT{i}SHAPE == DISK\n calcDiskLightValues(light{i}_position, light{i}_halfWidth, light{i}_halfHeight);\n #elif LIGHT{i}SHAPE == SPHERE\n calcSphereLightValues(light{i}_position, light{i}_halfWidth, light{i}_halfHeight);\n #endif\n #endif\n\n // diffuse transmission - the light arriving at the back of the surface, which is the diffuse\n // lighting of the flipped normal. It is evaluated before the diffuse lighting of a punctual\n // light multiplies into its attenuation\n #ifdef LIT_DIFFUSE_TRANSMISSION\n #if LIGHT{i}SHAPE == PUNCTUAL\n float attenTransmission = dAtten * getLightDiffuse(-litArgs_worldNormal, vec3(0.0), dLightDirNormW);\n #elif LIGHT{i}TYPE == DIRECTIONAL\n float attenTransmission = dAtten * getLightDiffuse(-litArgs_worldNormal, dViewDirW, dLightDirNormW);\n #elif LIGHT{i}SHAPE == RECT\n float attenTransmission = dAtten * getRectLightDiffuse(-litArgs_worldNormal, dViewDirW, lightDirW, dLightDirNormW) * 16.0;\n #elif LIGHT{i}SHAPE == DISK\n float attenTransmission = dAtten * getDiskLightDiffuse(-litArgs_worldNormal, dViewDirW, lightDirW, dLightDirNormW) * 16.0;\n #elif LIGHT{i}SHAPE == SPHERE\n float attenTransmission = dAtten * getSphereLightDiffuse(-litArgs_worldNormal, dViewDirW, lightDirW, dLightDirNormW) * 16.0;\n #endif\n #endif\n\n // diffuse lighting - LTC lights do not mix diffuse lighting into attenuation that affects specular\n #if LIGHT{i}SHAPE != PUNCTUAL\n\n // attenDiffuse - separate diffuse attenuation for non-punctual light sources\n\n #if LIGHT{i}TYPE == DIRECTIONAL\n // NB: A better approximation perhaps using wrap lighting could be implemented here\n float attenDiffuse = getLightDiffuse(litArgs_worldNormal, dViewDirW, dLightDirNormW);\n #else\n // 16.0 is a constant that is in getFalloffInvSquared()\n #if LIGHT{i}SHAPE == RECT\n float attenDiffuse = getRectLightDiffuse(litArgs_worldNormal, dViewDirW, lightDirW, dLightDirNormW) * 16.0;\n #elif LIGHT{i}SHAPE == DISK\n float attenDiffuse = getDiskLightDiffuse(litArgs_worldNormal, dViewDirW, lightDirW, dLightDirNormW) * 16.0;\n #elif LIGHT{i}SHAPE == SPHERE\n float attenDiffuse = getSphereLightDiffuse(litArgs_worldNormal, dViewDirW, lightDirW, dLightDirNormW) * 16.0;\n #endif\n #endif\n #else\n // one parameter is unused for punctual lights\n dAtten *= getLightDiffuse(litArgs_worldNormal, vec3(0.0), dLightDirNormW);\n #endif\n\n // apply the shadow attenuation\n #ifdef LIGHT{i}CASTSHADOW\n\n #if LIGHT{i}TYPE == DIRECTIONAL\n float shadow = getShadow{i}(vec3(0.0));\n #else\n float shadow = getShadow{i}(lightDirW);\n #endif\n\n #ifdef LIGHT{i}_SHADOW_CASCADE_BLEND\n // Fade to fully lit at the shadow distance without sampling another cascade.\n float shadowDistance = light{i}_shadowCascadeDistances.w;\n float shadowFade = smoothstep(light{i}_shadowCascadeBlend * shadowDistance, shadowDistance, 1.0 / gl_FragCoord.w);\n shadow = mix(shadow, 1.0, shadowFade);\n #endif\n\n // Apply shadow intensity to the shadow value\n shadow = mix(1.0, shadow, light{i}_shadowIntensity);\n\n dAtten *= shadow;\n\n #ifdef LIT_DIFFUSE_TRANSMISSION\n attenTransmission *= shadow;\n #endif\n\n #if defined(LIT_SHADOW_CATCHER) && LIGHT{i}TYPE == DIRECTIONAL\n // accumulate shadows for directional lights\n dShadowCatcher *= shadow;\n #endif \n\n #endif\n\n // Relief self shadowing, which is a property of the surface rather than of the light, so it\n // applies whether or not the light casts a shadow map. Directional lights only for now: they are\n // never clustered, so a pixel sees at most a handful of them and the cost stays bounded.\n #if defined(STD_PARALLAX_SELF_SHADOW) && LIGHT{i}TYPE == DIRECTIONAL\n dAtten *= getParallaxSelfShadow(dLightDirNormW);\n #endif\n\n #if LIGHT{i}SHAPE != PUNCTUAL\n // area light - they do not mix diffuse lighting into specular attenuation\n #ifdef LIT_SPECULAR\n dDiffuseLight += ((attenDiffuse * dAtten) * lightColor) * (1.0 - dLTCSpecFres);\n #ifdef LIT_DIFFUSE_TRANSMISSION\n dDiffuseTransmissionLight += (attenTransmission * lightColor) * (1.0 - dLTCSpecFres);\n #endif\n #else\n dDiffuseLight += (attenDiffuse * dAtten) * lightColor;\n #ifdef LIT_DIFFUSE_TRANSMISSION\n dDiffuseTransmissionLight += attenTransmission * lightColor;\n #endif\n #endif \n #else\n // punctual light\n #if defined(AREA_LIGHTS) && defined(LIT_SPECULAR)\n dDiffuseLight += (dAtten * lightColor) * (1.0 - litArgs_specularity);\n #ifdef LIT_DIFFUSE_TRANSMISSION\n dDiffuseTransmissionLight += (attenTransmission * lightColor) * (1.0 - litArgs_specularity);\n #endif\n #else\n dDiffuseLight += dAtten * lightColor;\n #ifdef LIT_DIFFUSE_TRANSMISSION\n dDiffuseTransmissionLight += attenTransmission * lightColor;\n #endif\n #endif\n #endif\n\n // specular lighting\n #ifdef LIGHT{i}AFFECT_SPECULARITY\n\n #if LIGHT{i}SHAPE != PUNCTUAL // area light\n\n #ifdef LIT_CLEARCOAT\n // with the LTC matrix of the clear coat gloss (ccLTCUV)\n #if LIGHT{i}SHAPE == RECT\n ccSpecularLight += ccLTCSpecFres * getRectLightSpecularCC(litArgs_clearcoat_worldNormal, dViewDirW) * dAtten * lightColor;\n #elif LIGHT{i}SHAPE == DISK\n ccSpecularLight += ccLTCSpecFres * getDiskLightSpecularCC(litArgs_clearcoat_worldNormal, dViewDirW) * dAtten * lightColor;\n #elif LIGHT{i}SHAPE == SPHERE\n ccSpecularLight += ccLTCSpecFres * getSphereLightSpecularCC(litArgs_clearcoat_worldNormal, dViewDirW) * dAtten * lightColor;\n #endif\n #endif\n\n #ifdef LIT_SPECULAR\n #if LIGHT{i}SHAPE == RECT\n dSpecularLight += dLTCSpecFres * getRectLightSpecular(litArgs_worldNormal, dViewDirW) * dAtten * lightColor;\n #elif LIGHT{i}SHAPE == DISK\n dSpecularLight += dLTCSpecFres * getDiskLightSpecular(litArgs_worldNormal, dViewDirW) * dAtten * lightColor;\n #elif LIGHT{i}SHAPE == SPHERE\n dSpecularLight += dLTCSpecFres * getSphereLightSpecular(litArgs_worldNormal, dViewDirW) * dAtten * lightColor;\n #endif\n #endif\n\n #else // punctual light\n\n // is fresnel needed\n #if LIGHT{i}TYPE == DIRECTIONAL && LIT_FRESNEL_MODEL != NONE\n #define LIGHT{i}FRESNEL\n #endif\n\n #ifdef LIT_SPECULAR\n vec3 halfDirW = normalize(-dLightDirNormW + dViewDirW);\n #endif\n\n // if LTC lights are present, specular must be accumulated with specularity (specularity is pre multiplied by punctual light fresnel)\n #ifdef LIT_CLEARCOAT\n vec3 lightspecularCC = getLightSpecular(halfDirW, ccReflDirW, litArgs_clearcoat_worldNormal, dViewDirW, dLightDirNormW, litArgs_clearcoat_gloss, dTBN) * dAtten * lightColor;\n #ifdef LIGHT{i}FRESNEL\n lightspecularCC *= getFresnelCC(dot(dViewDirW, halfDirW));\n #endif\n ccSpecularLight += lightspecularCC;\n #endif\n\n #ifdef LIT_SHEEN\n sSpecularLight += getLightSpecularSheen(halfDirW, litArgs_worldNormal, dViewDirW, dLightDirNormW, litArgs_sheen_gloss) * dAtten * lightColor;\n #endif\n\n #ifdef LIT_SPECULAR\n\n vec3 lightSpecular = getLightSpecular(halfDirW, dReflDirW, litArgs_worldNormal, dViewDirW, dLightDirNormW, litArgs_gloss, dTBN) * dAtten * lightColor;\n #ifdef LIGHT{i}FRESNEL\n\n #if defined(LIT_IRIDESCENCE)\n lightSpecular *= getFresnel(dot(dViewDirW, halfDirW), litArgs_gloss, litArgs_specularity, iridescenceFresnel, litArgs_iridescence_intensity);\n #else\n lightSpecular *= getFresnel(dot(dViewDirW, halfDirW), litArgs_gloss, litArgs_specularity);\n #endif\n\n #else\n lightSpecular *= litArgs_specularity;\n #endif\n \n dSpecularLight += lightSpecular;\n #endif\n #endif\n #endif\n}\n#endif\n";
2
2
  export default _default;
@@ -146,11 +146,11 @@ void evaluateLight{i}(
146
146
  #if LIGHT{i}SHAPE != PUNCTUAL
147
147
  #ifdef LIT_CLEARCOAT
148
148
  #if LIGHT{i}SHAPE == RECT
149
- ccSpecularLight += ccLTCSpecFres * getRectLightSpecular(litArgs_clearcoat_worldNormal, dViewDirW) * dAtten * lightColor;
149
+ ccSpecularLight += ccLTCSpecFres * getRectLightSpecularCC(litArgs_clearcoat_worldNormal, dViewDirW) * dAtten * lightColor;
150
150
  #elif LIGHT{i}SHAPE == DISK
151
- ccSpecularLight += ccLTCSpecFres * getDiskLightSpecular(litArgs_clearcoat_worldNormal, dViewDirW) * dAtten * lightColor;
151
+ ccSpecularLight += ccLTCSpecFres * getDiskLightSpecularCC(litArgs_clearcoat_worldNormal, dViewDirW) * dAtten * lightColor;
152
152
  #elif LIGHT{i}SHAPE == SPHERE
153
- ccSpecularLight += ccLTCSpecFres * getSphereLightSpecular(litArgs_clearcoat_worldNormal, dViewDirW) * dAtten * lightColor;
153
+ ccSpecularLight += ccLTCSpecFres * getSphereLightSpecularCC(litArgs_clearcoat_worldNormal, dViewDirW) * dAtten * lightColor;
154
154
  #endif
155
155
  #endif
156
156
  #ifdef LIT_SPECULAR