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.
- package/build/playcanvas/src/core/core.js +2 -2
- package/build/playcanvas/src/framework/lightmapper/lightmapper.js +3 -0
- package/build/playcanvas/src/scene/lighting/lighting-params.d.ts +58 -0
- package/build/playcanvas/src/scene/lighting/lighting-params.js +35 -1
- package/build/playcanvas/src/scene/lighting/lights-buffer.d.ts +7 -1
- package/build/playcanvas/src/scene/lighting/lights-buffer.js +6 -4
- package/build/playcanvas/src/scene/lighting/world-clusters.js +2 -2
- package/build/playcanvas/src/scene/materials/lit-material-options-builder.js +4 -1
- package/build/playcanvas/src/scene/materials/standard-material-options-builder.js +5 -2
- package/build/playcanvas/src/scene/shader-lib/glsl/chunks/lit/frag/clusteredLight.d.ts +1 -1
- package/build/playcanvas/src/scene/shader-lib/glsl/chunks/lit/frag/clusteredLight.js +58 -32
- package/build/playcanvas/src/scene/shader-lib/glsl/chunks/lit/frag/lighting/lightFunctionLight.d.ts +1 -1
- package/build/playcanvas/src/scene/shader-lib/glsl/chunks/lit/frag/lighting/lightFunctionLight.js +3 -3
- package/build/playcanvas/src/scene/shader-lib/glsl/chunks/lit/frag/ltc.d.ts +1 -1
- package/build/playcanvas/src/scene/shader-lib/glsl/chunks/lit/frag/ltc.js +13 -2
- package/build/playcanvas/src/scene/shader-lib/glsl/chunks/lit/frag/pass-forward/litForwardBackend.d.ts +1 -1
- package/build/playcanvas/src/scene/shader-lib/glsl/chunks/lit/frag/pass-forward/litForwardBackend.js +6 -4
- package/build/playcanvas/src/scene/shader-lib/programs/lit-shader-options.d.ts +8 -0
- package/build/playcanvas/src/scene/shader-lib/programs/lit-shader-options.js +11 -1
- package/build/playcanvas/src/scene/shader-lib/programs/lit-shader.js +10 -1
- package/build/playcanvas/src/scene/shader-lib/wgsl/chunks/lit/frag/clusteredLight.d.ts +1 -1
- package/build/playcanvas/src/scene/shader-lib/wgsl/chunks/lit/frag/clusteredLight.js +59 -32
- package/build/playcanvas/src/scene/shader-lib/wgsl/chunks/lit/frag/lighting/lightFunctionLight.d.ts +1 -1
- package/build/playcanvas/src/scene/shader-lib/wgsl/chunks/lit/frag/lighting/lightFunctionLight.js +3 -3
- package/build/playcanvas/src/scene/shader-lib/wgsl/chunks/lit/frag/ltc.d.ts +1 -1
- package/build/playcanvas/src/scene/shader-lib/wgsl/chunks/lit/frag/ltc.js +13 -2
- package/build/playcanvas/src/scene/shader-lib/wgsl/chunks/lit/frag/pass-forward/litForwardBackend.d.ts +1 -1
- package/build/playcanvas/src/scene/shader-lib/wgsl/chunks/lit/frag/pass-forward/litForwardBackend.js +6 -4
- package/build/playcanvas.d.ts +73 -1
- package/build/playcanvas.dbg/src/core/core.js +2 -2
- package/build/playcanvas.dbg/src/framework/lightmapper/lightmapper.js +3 -0
- package/build/playcanvas.dbg/src/scene/lighting/lighting-params.js +83 -1
- package/build/playcanvas.dbg/src/scene/lighting/lights-buffer.js +12 -4
- package/build/playcanvas.dbg/src/scene/lighting/world-clusters.js +2 -2
- package/build/playcanvas.dbg/src/scene/materials/lit-material-options-builder.js +4 -1
- package/build/playcanvas.dbg/src/scene/materials/standard-material-options-builder.js +5 -2
- package/build/playcanvas.dbg/src/scene/shader-lib/glsl/chunks/lit/frag/clusteredLight.js +75 -39
- package/build/playcanvas.dbg/src/scene/shader-lib/glsl/chunks/lit/frag/lighting/lightFunctionLight.js +4 -3
- package/build/playcanvas.dbg/src/scene/shader-lib/glsl/chunks/lit/frag/ltc.js +17 -2
- package/build/playcanvas.dbg/src/scene/shader-lib/glsl/chunks/lit/frag/pass-forward/litForwardBackend.js +10 -6
- package/build/playcanvas.dbg/src/scene/shader-lib/programs/lit-shader-options.js +18 -1
- package/build/playcanvas.dbg/src/scene/shader-lib/programs/lit-shader.js +10 -1
- package/build/playcanvas.dbg/src/scene/shader-lib/wgsl/chunks/lit/frag/clusteredLight.js +76 -39
- package/build/playcanvas.dbg/src/scene/shader-lib/wgsl/chunks/lit/frag/lighting/lightFunctionLight.js +4 -3
- package/build/playcanvas.dbg/src/scene/shader-lib/wgsl/chunks/lit/frag/ltc.js +17 -2
- package/build/playcanvas.dbg/src/scene/shader-lib/wgsl/chunks/lit/frag/pass-forward/litForwardBackend.js +10 -6
- package/build/playcanvas.dbg.js +339 -113
- package/build/playcanvas.dbg.mjs +339 -113
- package/build/playcanvas.js +226 -95
- package/build/playcanvas.min.js +254 -175
- package/build/playcanvas.min.mjs +256 -177
- package/build/playcanvas.mjs +226 -95
- package/build/playcanvas.prf/src/core/core.js +2 -2
- package/build/playcanvas.prf/src/framework/lightmapper/lightmapper.js +3 -0
- package/build/playcanvas.prf/src/scene/lighting/lighting-params.js +35 -1
- package/build/playcanvas.prf/src/scene/lighting/lights-buffer.js +6 -4
- package/build/playcanvas.prf/src/scene/lighting/world-clusters.js +2 -2
- package/build/playcanvas.prf/src/scene/materials/lit-material-options-builder.js +4 -1
- package/build/playcanvas.prf/src/scene/materials/standard-material-options-builder.js +5 -2
- package/build/playcanvas.prf/src/scene/shader-lib/glsl/chunks/lit/frag/clusteredLight.js +58 -32
- package/build/playcanvas.prf/src/scene/shader-lib/glsl/chunks/lit/frag/lighting/lightFunctionLight.js +3 -3
- package/build/playcanvas.prf/src/scene/shader-lib/glsl/chunks/lit/frag/ltc.js +13 -2
- package/build/playcanvas.prf/src/scene/shader-lib/glsl/chunks/lit/frag/pass-forward/litForwardBackend.js +6 -4
- package/build/playcanvas.prf/src/scene/shader-lib/programs/lit-shader-options.js +11 -1
- package/build/playcanvas.prf/src/scene/shader-lib/programs/lit-shader.js +10 -1
- package/build/playcanvas.prf/src/scene/shader-lib/wgsl/chunks/lit/frag/clusteredLight.js +59 -32
- package/build/playcanvas.prf/src/scene/shader-lib/wgsl/chunks/lit/frag/lighting/lightFunctionLight.js +3 -3
- package/build/playcanvas.prf/src/scene/shader-lib/wgsl/chunks/lit/frag/ltc.js +13 -2
- package/build/playcanvas.prf/src/scene/shader-lib/wgsl/chunks/lit/frag/pass-forward/litForwardBackend.js +6 -4
- package/build/playcanvas.prf.js +226 -95
- package/build/playcanvas.prf.mjs +226 -95
- package/package.json +1 -1
|
@@ -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
|
-
|
|
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
|
-
|
|
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.
|
|
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.
|
|
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
|
-
|
|
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.
|
|
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.
|
|
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
|
-
|
|
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
|
-
|
|
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 =
|
|
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
|
-
|
|
155
|
-
|
|
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
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
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
|
-
|
|
179
|
-
|
|
180
|
-
#
|
|
181
|
-
|
|
190
|
+
#ifndef LIT_SPECULAR
|
|
191
|
+
if (lobe == 2) break;
|
|
192
|
+
#endif
|
|
193
|
+
#ifndef LIT_CLEARCOAT
|
|
194
|
+
if (lobe == 3) break;
|
|
182
195
|
#endif
|
|
183
|
-
|
|
184
|
-
|
|
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
|
-
|
|
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
|
package/build/playcanvas/src/scene/shader-lib/glsl/chunks/lit/frag/lighting/lightFunctionLight.d.ts
CHANGED
|
@@ -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;
|
package/build/playcanvas/src/scene/shader-lib/glsl/chunks/lit/frag/lighting/lightFunctionLight.js
CHANGED
|
@@ -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 *
|
|
149
|
+
ccSpecularLight += ccLTCSpecFres * getRectLightSpecularCC(litArgs_clearcoat_worldNormal, dViewDirW) * dAtten * lightColor;
|
|
150
150
|
#elif LIGHT{i}SHAPE == DISK
|
|
151
|
-
ccSpecularLight += ccLTCSpecFres *
|
|
151
|
+
ccSpecularLight += ccLTCSpecFres * getDiskLightSpecularCC(litArgs_clearcoat_worldNormal, dViewDirW) * dAtten * lightColor;
|
|
152
152
|
#elif LIGHT{i}SHAPE == SPHERE
|
|
153
|
-
ccSpecularLight += ccLTCSpecFres *
|
|
153
|
+
ccSpecularLight += ccLTCSpecFres * getSphereLightSpecularCC(litArgs_clearcoat_worldNormal, dViewDirW) * dAtten * lightColor;
|
|
154
154
|
#endif
|
|
155
155
|
#endif
|
|
156
156
|
#ifdef LIT_SPECULAR
|