babylon.quarks 0.27.0 → 0.29.0
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/CHANGELOG.md +585 -0
- package/README.md +3 -1
- package/dist/babylon.quarks.cjs +1 -1
- package/dist/babylon.quarks.cjs.map +1 -1
- package/dist/babylon.quarks.esm.js +1 -1
- package/dist/babylon.quarks.esm.js.map +1 -1
- package/dist/babylon.quarks.umd.min.js +1 -1
- package/dist/babylon.quarks.umd.min.js.map +1 -1
- package/dist/types/index.d.cts +4811 -974
- package/dist/types/index.d.ts +4811 -974
- package/package.json +7 -2
- package/src/BatchedRenderer.ts +106 -79
- package/src/EffectPool.ts +96 -16
- package/src/MeshSurfaceEmitter.ts +24 -13
- package/src/ParticleEmitter.ts +119 -26
- package/src/ParticleSystem.ts +305 -55
- package/src/QuarksLoader.ts +298 -171
- package/src/QuarksPrefab.ts +301 -94
- package/src/QuarksSerializer.ts +44 -16
- package/src/QuarksUtil.ts +88 -11
- package/src/SpriteBatch.ts +24 -6
- package/src/TrailBatch.ts +20 -7
- package/src/VFXBatch.ts +182 -528
- package/src/batchKey.ts +6 -2
- package/src/batchSettings.ts +548 -0
- package/src/effectDocumentJSON.ts +261 -0
- package/src/effectResources.ts +21 -24
- package/src/envAtlas.ts +8 -2
- package/src/geometryUtil.ts +1 -1
- package/src/index.ts +41 -4
- package/src/nodeId.ts +25 -2
- package/src/shaderProgram.ts +213 -16
- package/src/shaderVariants.ts +124 -7
- package/src/shaders/chunks/blend.ts +39 -0
- package/src/shaders/chunks/fog.ts +8 -3
- package/src/shaders/chunks/imageProcessing.ts +143 -0
- package/src/shaders/chunks/soft.ts +6 -5
- package/src/shaders/chunks/surface.ts +2 -2
- package/src/shaders/particle_frag.glsl.ts +5 -1
- package/src/shaders/particle_frag.wgsl.ts +4 -0
- package/src/shaders/particle_physics_frag.glsl.ts +7 -2
- package/src/shaders/particle_physics_frag.wgsl.ts +4 -0
- package/src/shaders/trail_frag.glsl.ts +6 -1
- package/src/shaders/trail_frag.wgsl.ts +4 -0
- package/src/system/EmissionController.ts +23 -20
- package/src/system/EventDispatcher.ts +26 -10
- package/src/system/ParticleSpawner.ts +96 -19
- package/src/system/geometry.ts +22 -29
- package/src/system/integrate.ts +61 -4
- package/src/system/materialResolve.ts +56 -7
- package/src/system/normalize.ts +34 -2
- package/src/system/serialization.ts +54 -33
- package/src/system/settingsSchema.ts +57 -17
- package/src/system/types.ts +150 -42
- package/src/trailSettings.ts +9 -6
package/src/shaderProgram.ts
CHANGED
|
@@ -1,5 +1,11 @@
|
|
|
1
1
|
import {Constants} from '@babylonjs/core/Engines/constants';
|
|
2
|
+
import type {IImageProcessingConfigurationDefines} from '@babylonjs/core/Materials/imageProcessingConfiguration.defines';
|
|
3
|
+
import {
|
|
4
|
+
PrepareSamplersForImageProcessing,
|
|
5
|
+
PrepareUniformsForImageProcessing,
|
|
6
|
+
} from '@babylonjs/core/Materials/imageProcessingConfiguration.functions';
|
|
2
7
|
import {hasSizeLimits, takesPivotAndSizeLimits, tilesSheet} from './batchKey';
|
|
8
|
+
import {readsUV1, RenderMode, StoredBatchSettings} from './batchSettings';
|
|
3
9
|
import local_particle_physics_vert from './shaders/local_particle_physics_vert.glsl';
|
|
4
10
|
import local_particle_physics_vert_wgsl from './shaders/local_particle_physics_vert.wgsl';
|
|
5
11
|
import particle_frag from './shaders/particle_frag.glsl';
|
|
@@ -15,7 +21,6 @@ import trail_frag from './shaders/trail_frag.glsl';
|
|
|
15
21
|
import trail_frag_wgsl from './shaders/trail_frag.wgsl';
|
|
16
22
|
import trail_vert from './shaders/trail_vert.glsl';
|
|
17
23
|
import trail_vert_wgsl from './shaders/trail_vert.wgsl';
|
|
18
|
-
import {readsUV1, RenderMode, StoredBatchSettings} from './VFXBatch';
|
|
19
24
|
|
|
20
25
|
/**
|
|
21
26
|
* The part of a batch's settings that decides its shader: the render mode, which textures and
|
|
@@ -35,6 +40,7 @@ export type ShaderSettings = Pick<
|
|
|
35
40
|
| 'materialTransparent'
|
|
36
41
|
| 'materialBlendMode'
|
|
37
42
|
| 'applyFog'
|
|
43
|
+
| 'applyImageProcessing'
|
|
38
44
|
| 'pivot'
|
|
39
45
|
| 'minParticleSize'
|
|
40
46
|
| 'maxParticleSize'
|
|
@@ -66,8 +72,41 @@ export interface ShaderContext {
|
|
|
66
72
|
envAtlasReady: boolean;
|
|
67
73
|
/** Whether the mesh batch has a second UV set to read for {@link UVChannel} 1. */
|
|
68
74
|
meshUV2: boolean;
|
|
75
|
+
/**
|
|
76
|
+
* The scene's image processing as `ImageProcessingConfiguration.prepareDefines` writes it;
|
|
77
|
+
* null or left out for none. Compiled in only when the settings apply it, see
|
|
78
|
+
* {@link ParticleSystem.applyImageProcessing}.
|
|
79
|
+
*/
|
|
80
|
+
imageProcessing?: ShaderImageProcessing | null;
|
|
69
81
|
}
|
|
70
82
|
|
|
83
|
+
/**
|
|
84
|
+
* The defines an `ImageProcessingConfiguration` prepares for a material
|
|
85
|
+
* (`ImageProcessingConfigurationDefines`): which of its effects are on, and whether a post
|
|
86
|
+
* process applies it instead.
|
|
87
|
+
*/
|
|
88
|
+
export type ShaderImageProcessing = Readonly<IImageProcessingConfigurationDefines>;
|
|
89
|
+
|
|
90
|
+
/** The keys of {@link ShaderImageProcessing}, in the order Babylon writes them. @internal */
|
|
91
|
+
export const IMAGE_PROCESSING_DEFINES: readonly (keyof IImageProcessingConfigurationDefines)[] = [
|
|
92
|
+
'IMAGEPROCESSING',
|
|
93
|
+
'WHITEBALANCE',
|
|
94
|
+
'VIGNETTE',
|
|
95
|
+
'VIGNETTEBLENDMODEMULTIPLY',
|
|
96
|
+
'VIGNETTEBLENDMODEOPAQUE',
|
|
97
|
+
'TONEMAPPING',
|
|
98
|
+
'CONTRAST',
|
|
99
|
+
'COLORCURVES',
|
|
100
|
+
'COLORGRADING',
|
|
101
|
+
'COLORGRADING3D',
|
|
102
|
+
'SAMPLER3DGREENDEPTH',
|
|
103
|
+
'SAMPLER3DBGRMAP',
|
|
104
|
+
'DITHER',
|
|
105
|
+
'IMAGEPROCESSINGPOSTPROCESS',
|
|
106
|
+
'EXPOSURE',
|
|
107
|
+
'SKIPFINALCOLORCLAMP',
|
|
108
|
+
];
|
|
109
|
+
|
|
71
110
|
/**
|
|
72
111
|
* One compiled shader of the library: the sources in both languages and the defines, attributes,
|
|
73
112
|
* uniforms and samplers the material is built with. Built by {@link shaderProgram} from a
|
|
@@ -111,12 +150,94 @@ export function samplesMap(settings: {renderMode: RenderMode; texture: unknown})
|
|
|
111
150
|
return settings.texture !== null || settings.renderMode === RenderMode.Mesh;
|
|
112
151
|
}
|
|
113
152
|
|
|
114
|
-
/**
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
153
|
+
/**
|
|
154
|
+
* How a particle's colour has to leave the fragment shader for its blend mode, so that its alpha
|
|
155
|
+
* means coverage: alpha 0 leaves the frame as it was, alpha 1 draws the colour in full, and the
|
|
156
|
+
* fades (soft particles, erosion, a rim fade, alpha over life) only lower alpha. Unity's particle
|
|
157
|
+
* shaders do the same for their Alpha, Premultiply, Additive and Multiply modes. Alpha has no
|
|
158
|
+
* such meaning for an opaque material, nor under `ALPHA_INTERPOLATE` (constant blend colour),
|
|
159
|
+
* `ALPHA_REPLACE_COLOR` (replaces the frame) and `ALPHA_DUAL_SRC0_ADD_SRC1xDST` (reads a second
|
|
160
|
+
* shader output the particle shaders do not write); these leave the colour as it is.
|
|
161
|
+
*
|
|
162
|
+
* - `'straight'`: the blend weighs the colour by its alpha itself (Babylon's `ALPHA_COMBINE`,
|
|
163
|
+
* `ALPHA_ADD`, `ALPHA_LAYER_ACCUMULATE`, …) or does not blend; the colour leaves as it is.
|
|
164
|
+
* - `'premultiply'`: the blend takes the colour at full weight, or the frame by one minus the
|
|
165
|
+
* colour (`ALPHA_PREMULTIPLIED`, `ALPHA_ONEONE`, `ALPHA_SCREENMODE`, `ALPHA_SUBTRACT`,
|
|
166
|
+
* `ALPHA_MAXIMIZED`, `ALPHA_MAX`, …), so the shader multiplies it by its alpha: black, which
|
|
167
|
+
* these modes leave the frame alone with, at alpha 0. Textures stay straight alpha, as in
|
|
168
|
+
* Unity's Premultiply mode. The fade is linear in alpha except under `ALPHA_MAXIMIZED`, whose
|
|
169
|
+
* blend weighs the colour by alpha once more, and `ALPHA_MAX`.
|
|
170
|
+
* - `'multiply'`: the blend multiplies the frame by the colour (`ALPHA_MULTIPLY`, `ALPHA_MIN`), so
|
|
171
|
+
* the shader moves it toward white, which leaves the frame alone, as alpha falls. `ALPHA_MIN`
|
|
172
|
+
* clamps a frame brighter than 1 (an HDR target) to 1 where a particle draws, even at alpha 0.
|
|
173
|
+
*/
|
|
174
|
+
export type BlendOutput = 'straight' | 'premultiply' | 'multiply';
|
|
175
|
+
|
|
176
|
+
/**
|
|
177
|
+
* What the scene's fog turns a particle's colour into with distance: the fog colour where the
|
|
178
|
+
* particle covers the frame (`'color'`), black where it adds to it (`'black'`, or the fog would
|
|
179
|
+
* add itself to the whole screen), white where it multiplies it (`'white'`). Unity fogs its
|
|
180
|
+
* additive and multiply particles toward black and white for the same reason.
|
|
181
|
+
*/
|
|
182
|
+
export type FogTarget = 'color' | 'black' | 'white';
|
|
183
|
+
|
|
184
|
+
/** The {@link BlendOutput} and {@link FogTarget} of a blend mode. */
|
|
185
|
+
export interface BlendCategory {
|
|
186
|
+
/** How the colour leaves the shader. */
|
|
187
|
+
output: BlendOutput;
|
|
188
|
+
/** Where the fog takes the colour. */
|
|
189
|
+
fogTarget: FogTarget;
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
const PREMULTIPLY_MODES = new Set<number>([
|
|
193
|
+
Constants.ALPHA_SUBTRACT,
|
|
194
|
+
Constants.ALPHA_MAXIMIZED,
|
|
195
|
+
Constants.ALPHA_ONEONE,
|
|
196
|
+
Constants.ALPHA_PREMULTIPLIED,
|
|
197
|
+
Constants.ALPHA_PREMULTIPLIED_PORTERDUFF,
|
|
198
|
+
Constants.ALPHA_SCREENMODE,
|
|
199
|
+
Constants.ALPHA_ONEONE_ONEONE,
|
|
200
|
+
Constants.ALPHA_ALPHATOCOLOR,
|
|
201
|
+
Constants.ALPHA_REVERSEONEMINUS,
|
|
202
|
+
Constants.ALPHA_SRC_DSTONEMINUSSRCALPHA,
|
|
203
|
+
Constants.ALPHA_ONEONE_ONEZERO,
|
|
204
|
+
Constants.ALPHA_EXCLUSION,
|
|
205
|
+
Constants.ALPHA_MAX,
|
|
206
|
+
]);
|
|
207
|
+
const MULTIPLY_MODES = new Set<number>([Constants.ALPHA_MULTIPLY, Constants.ALPHA_MIN]);
|
|
208
|
+
/** The modes whose fog is the fog colour: those that lay the colour over the frame or replace it. */
|
|
209
|
+
const OVER_MODES = new Set<number>([
|
|
210
|
+
Constants.ALPHA_COMBINE,
|
|
211
|
+
Constants.ALPHA_PREMULTIPLIED,
|
|
212
|
+
Constants.ALPHA_PREMULTIPLIED_PORTERDUFF,
|
|
213
|
+
Constants.ALPHA_INTERPOLATE,
|
|
214
|
+
Constants.ALPHA_SRC_DSTONEMINUSSRCALPHA,
|
|
215
|
+
Constants.ALPHA_LAYER_ACCUMULATE,
|
|
216
|
+
Constants.ALPHA_REPLACE_COLOR,
|
|
217
|
+
]);
|
|
218
|
+
|
|
219
|
+
/**
|
|
220
|
+
* The {@link BlendCategory} of a material: an opaque one (`materialTransparent` false, or
|
|
221
|
+
* `ALPHA_DISABLE`) is straight and fogs toward the fog colour; a transparent one by its Babylon
|
|
222
|
+
* alpha mode. A mode this table does not know is straight and fogs toward the fog colour.
|
|
223
|
+
*/
|
|
224
|
+
export function blendCategory(settings: {materialTransparent: boolean; materialBlendMode: number}): BlendCategory {
|
|
225
|
+
const mode = settings.materialBlendMode;
|
|
226
|
+
if (!settings.materialTransparent || mode === Constants.ALPHA_DISABLE) {
|
|
227
|
+
return {output: 'straight', fogTarget: 'color'};
|
|
228
|
+
}
|
|
229
|
+
const output: BlendOutput = PREMULTIPLY_MODES.has(mode)
|
|
230
|
+
? 'premultiply'
|
|
231
|
+
: MULTIPLY_MODES.has(mode)
|
|
232
|
+
? 'multiply'
|
|
233
|
+
: 'straight';
|
|
234
|
+
const fogTarget: FogTarget =
|
|
235
|
+
output === 'multiply'
|
|
236
|
+
? 'white'
|
|
237
|
+
: OVER_MODES.has(mode) || (output === 'straight' && mode !== Constants.ALPHA_ADD)
|
|
238
|
+
? 'color'
|
|
239
|
+
: 'black';
|
|
240
|
+
return {output, fogTarget};
|
|
120
241
|
}
|
|
121
242
|
|
|
122
243
|
/** The shader program a batch with `settings` compiles, given what it knows about its textures. */
|
|
@@ -146,7 +267,7 @@ function spriteShaderProgram(settings: ShaderSettings, context: ShaderContext):
|
|
|
146
267
|
fragment = {glsl: particle_frag, wgsl: particle_frag_wgsl};
|
|
147
268
|
}
|
|
148
269
|
|
|
149
|
-
const defines =
|
|
270
|
+
const defines = blendDefines(settings);
|
|
150
271
|
const attributes = [...SPRITE_ATTRIBUTES];
|
|
151
272
|
const uniforms = ['world', 'view', 'projection', ...FOG_UNIFORMS];
|
|
152
273
|
const samplers: string[] = [];
|
|
@@ -210,6 +331,7 @@ function spriteShaderProgram(settings: ShaderSettings, context: ShaderContext):
|
|
|
210
331
|
uniforms.push('eyePosition');
|
|
211
332
|
}
|
|
212
333
|
surfaceDefines(settings, defines, uniforms, samplers);
|
|
334
|
+
imageProcessingDefines(settings, context, defines, uniforms, samplers);
|
|
213
335
|
if (isMesh) {
|
|
214
336
|
attributes.push('normal');
|
|
215
337
|
if (readsUV1(settings) && context.meshUV2) {
|
|
@@ -225,7 +347,7 @@ function spriteShaderProgram(settings: ShaderSettings, context: ShaderContext):
|
|
|
225
347
|
|
|
226
348
|
/** The program of a trail batch. */
|
|
227
349
|
function trailShaderProgram(settings: ShaderSettings, context: ShaderContext): ShaderProgram {
|
|
228
|
-
const defines =
|
|
350
|
+
const defines = blendDefines(settings);
|
|
229
351
|
const attributes = [...TRAIL_ATTRIBUTES];
|
|
230
352
|
const uniforms = ['view', 'projection', ...FOG_UNIFORMS];
|
|
231
353
|
const samplers: string[] = [];
|
|
@@ -235,6 +357,7 @@ function trailShaderProgram(settings: ShaderSettings, context: ShaderContext): S
|
|
|
235
357
|
}
|
|
236
358
|
surfaceDefines(settings, defines, uniforms, samplers);
|
|
237
359
|
softDefines(settings, context, defines, uniforms, samplers);
|
|
360
|
+
imageProcessingDefines(settings, context, defines, uniforms, samplers);
|
|
238
361
|
if (readsUV1(settings)) {
|
|
239
362
|
// A trail's channel 1 arrives as its own vertex buffer.
|
|
240
363
|
attributes.push('uv2');
|
|
@@ -251,15 +374,89 @@ function trailShaderProgram(settings: ShaderSettings, context: ShaderContext): S
|
|
|
251
374
|
}
|
|
252
375
|
|
|
253
376
|
/**
|
|
254
|
-
* The
|
|
255
|
-
*
|
|
256
|
-
*
|
|
377
|
+
* The blend defines: `BLEND_PREMULTIPLY` or `BLEND_MULTIPLY` for how the colour leaves the shader,
|
|
378
|
+
* and with `FOG` (when the settings apply the scene's fog) `FOG_ADDITIVE` or `FOG_MULTIPLY` for a
|
|
379
|
+
* fog toward black or white. See {@link blendCategory}.
|
|
257
380
|
*/
|
|
258
|
-
function
|
|
259
|
-
|
|
260
|
-
|
|
381
|
+
function blendDefines(settings: ShaderSettings): string[] {
|
|
382
|
+
const {output, fogTarget} = blendCategory(settings);
|
|
383
|
+
const defines: string[] = [];
|
|
384
|
+
if (output === 'premultiply') {
|
|
385
|
+
defines.push('BLEND_PREMULTIPLY');
|
|
386
|
+
} else if (output === 'multiply') {
|
|
387
|
+
defines.push('BLEND_MULTIPLY');
|
|
261
388
|
}
|
|
262
|
-
|
|
389
|
+
if (settings.applyFog) {
|
|
390
|
+
defines.push('FOG');
|
|
391
|
+
if (fogTarget === 'black') {
|
|
392
|
+
defines.push('FOG_ADDITIVE');
|
|
393
|
+
} else if (fogTarget === 'white') {
|
|
394
|
+
defines.push('FOG_MULTIPLY');
|
|
395
|
+
}
|
|
396
|
+
}
|
|
397
|
+
return defines;
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
/**
|
|
401
|
+
* Whether a batch with `settings` compiles the scene's image processing `ip` in: when the settings
|
|
402
|
+
* apply it and it does something, either in the material (`IMAGEPROCESSING`) or through a post
|
|
403
|
+
* process the colour is left in linear space for (`IMAGEPROCESSINGPOSTPROCESS`). A material that
|
|
404
|
+
* multiplies the frame takes no in-material processing: its colour is a factor, and tone mapping
|
|
405
|
+
* white would darken the frame where the particle is fully faded. See `chunks/imageProcessing.ts`.
|
|
406
|
+
*/
|
|
407
|
+
export function compilesImageProcessing(
|
|
408
|
+
settings: Pick<ShaderSettings, 'applyImageProcessing' | 'materialTransparent' | 'materialBlendMode'>,
|
|
409
|
+
ip: ShaderImageProcessing | null | undefined
|
|
410
|
+
): boolean {
|
|
411
|
+
if (settings.applyImageProcessing === false || !ip) {
|
|
412
|
+
return false;
|
|
413
|
+
}
|
|
414
|
+
if (ip.IMAGEPROCESSINGPOSTPROCESS) {
|
|
415
|
+
return true;
|
|
416
|
+
}
|
|
417
|
+
return ip.IMAGEPROCESSING && blendCategory(settings).output !== 'multiply';
|
|
418
|
+
}
|
|
419
|
+
|
|
420
|
+
/**
|
|
421
|
+
* The defines, uniforms and samplers the scene's image processing `ip` adds to a material with
|
|
422
|
+
* `settings`: nothing unless {@link compilesImageProcessing}, otherwise its defines as Babylon
|
|
423
|
+
* writes them (a number always, a flag when set) and the uniforms and samplers its effects read.
|
|
424
|
+
* A batch swaps these in place on its material when the configuration changes what compiles.
|
|
425
|
+
* @internal
|
|
426
|
+
*/
|
|
427
|
+
export function imageProcessingParts(
|
|
428
|
+
settings: Pick<ShaderSettings, 'applyImageProcessing' | 'materialTransparent' | 'materialBlendMode'>,
|
|
429
|
+
ip: ShaderImageProcessing | null | undefined
|
|
430
|
+
): {defines: string[]; uniforms: string[]; samplers: string[]} {
|
|
431
|
+
const parts = {defines: [] as string[], uniforms: [] as string[], samplers: [] as string[]};
|
|
432
|
+
if (!ip || !compilesImageProcessing(settings, ip)) {
|
|
433
|
+
return parts;
|
|
434
|
+
}
|
|
435
|
+
for (const key of IMAGE_PROCESSING_DEFINES) {
|
|
436
|
+
const value = ip[key];
|
|
437
|
+
if (typeof value === 'number') {
|
|
438
|
+
parts.defines.push(`${key} ${value}`);
|
|
439
|
+
} else if (value) {
|
|
440
|
+
parts.defines.push(key);
|
|
441
|
+
}
|
|
442
|
+
}
|
|
443
|
+
PrepareUniformsForImageProcessing(parts.uniforms, ip);
|
|
444
|
+
PrepareSamplersForImageProcessing(parts.samplers, ip);
|
|
445
|
+
return parts;
|
|
446
|
+
}
|
|
447
|
+
|
|
448
|
+
/** Appends {@link imageProcessingParts} to a program's lists. */
|
|
449
|
+
function imageProcessingDefines(
|
|
450
|
+
settings: ShaderSettings,
|
|
451
|
+
context: ShaderContext,
|
|
452
|
+
defines: string[],
|
|
453
|
+
uniforms: string[],
|
|
454
|
+
samplers: string[]
|
|
455
|
+
): void {
|
|
456
|
+
const parts = imageProcessingParts(settings, context.imageProcessing);
|
|
457
|
+
defines.push(...parts.defines);
|
|
458
|
+
uniforms.push(...parts.uniforms);
|
|
459
|
+
samplers.push(...parts.samplers);
|
|
263
460
|
}
|
|
264
461
|
|
|
265
462
|
/** Soft particles, when on and a depth texture is there to read, and the alpha test. */
|
package/src/shaderVariants.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import {Constants} from '@babylonjs/core/Engines/constants';
|
|
2
2
|
import type {BaseTexture} from '@babylonjs/core/Materials/Textures/baseTexture';
|
|
3
3
|
import {Vector3} from 'quarks.core';
|
|
4
|
-
import {ShaderContext, shaderProgram, ShaderProgram, ShaderSettings} from './shaderProgram';
|
|
4
|
+
import {ShaderContext, ShaderImageProcessing, shaderProgram, ShaderProgram, ShaderSettings} from './shaderProgram';
|
|
5
5
|
import {
|
|
6
6
|
normalizeErosion,
|
|
7
7
|
normalizeGradientMap,
|
|
@@ -25,7 +25,10 @@ export interface ShaderVariant {
|
|
|
25
25
|
/** A texture reference for settings that only ask whether one is set; it is never sampled. */
|
|
26
26
|
const TEXTURE = {} as BaseTexture;
|
|
27
27
|
|
|
28
|
-
/**
|
|
28
|
+
/**
|
|
29
|
+
* The settings of a bare batch in `renderMode`: no texture, no effects, fog off, 1×1 sheet. Image
|
|
30
|
+
* processing is applied, and compiles in only with an image processing axis in the context.
|
|
31
|
+
*/
|
|
29
32
|
function bare(renderMode: RenderMode): ShaderSettings {
|
|
30
33
|
return {
|
|
31
34
|
renderMode,
|
|
@@ -39,6 +42,7 @@ function bare(renderMode: RenderMode): ShaderSettings {
|
|
|
39
42
|
materialTransparent: true,
|
|
40
43
|
materialBlendMode: Constants.ALPHA_COMBINE,
|
|
41
44
|
applyFog: false,
|
|
45
|
+
applyImageProcessing: true,
|
|
42
46
|
pivot: undefined,
|
|
43
47
|
minParticleSize: 0,
|
|
44
48
|
maxParticleSize: Infinity,
|
|
@@ -123,6 +127,21 @@ const FOG: Axis[] = [
|
|
|
123
127
|
{name: 'fog additive', settings: {applyFog: true, materialBlendMode: Constants.ALPHA_ADD}},
|
|
124
128
|
];
|
|
125
129
|
|
|
130
|
+
/** The blend resolves (see `blendCategory`), with and without the fog each one takes. */
|
|
131
|
+
const BLEND: Axis[] = [
|
|
132
|
+
{name: 'premultiplied', settings: {materialBlendMode: Constants.ALPHA_PREMULTIPLIED}},
|
|
133
|
+
{name: 'premultiplied fog', settings: {materialBlendMode: Constants.ALPHA_PREMULTIPLIED, applyFog: true}},
|
|
134
|
+
{name: 'one-one', settings: {materialBlendMode: Constants.ALPHA_ONEONE}},
|
|
135
|
+
{name: 'one-one fog', settings: {materialBlendMode: Constants.ALPHA_ONEONE, applyFog: true}},
|
|
136
|
+
{name: 'multiply', settings: {materialBlendMode: Constants.ALPHA_MULTIPLY}},
|
|
137
|
+
{name: 'multiply fog', settings: {materialBlendMode: Constants.ALPHA_MULTIPLY, applyFog: true}},
|
|
138
|
+
{
|
|
139
|
+
name: 'multiply fog alpha test',
|
|
140
|
+
settings: {materialBlendMode: Constants.ALPHA_MULTIPLY, applyFog: true, materialAlphaTest: 0.5},
|
|
141
|
+
},
|
|
142
|
+
{name: 'opaque', settings: {materialTransparent: false}},
|
|
143
|
+
];
|
|
144
|
+
|
|
126
145
|
/** Texture effects (`chunks/surface.ts`), alone and together, on both UV channels. */
|
|
127
146
|
const SURFACE: Axis[] = (() => {
|
|
128
147
|
const uv0 = normalizeMapUV({tiling: [2, 2]});
|
|
@@ -169,20 +188,105 @@ const SURFACE_TARGETS = [
|
|
|
169
188
|
'trail',
|
|
170
189
|
];
|
|
171
190
|
|
|
191
|
+
/** A scene configuration's defines with `on` set and the rest off, see {@link ShaderImageProcessing}. */
|
|
192
|
+
function imageProcessing(on: Partial<ShaderImageProcessing>): ShaderImageProcessing {
|
|
193
|
+
return {
|
|
194
|
+
IMAGEPROCESSING: false,
|
|
195
|
+
WHITEBALANCE: false,
|
|
196
|
+
VIGNETTE: false,
|
|
197
|
+
VIGNETTEBLENDMODEMULTIPLY: false,
|
|
198
|
+
VIGNETTEBLENDMODEOPAQUE: false,
|
|
199
|
+
TONEMAPPING: 0,
|
|
200
|
+
CONTRAST: false,
|
|
201
|
+
EXPOSURE: false,
|
|
202
|
+
COLORCURVES: false,
|
|
203
|
+
COLORGRADING: false,
|
|
204
|
+
COLORGRADING3D: false,
|
|
205
|
+
SAMPLER3DGREENDEPTH: false,
|
|
206
|
+
SAMPLER3DBGRMAP: false,
|
|
207
|
+
DITHER: false,
|
|
208
|
+
IMAGEPROCESSINGPOSTPROCESS: false,
|
|
209
|
+
SKIPFINALCOLORCLAMP: false,
|
|
210
|
+
...on,
|
|
211
|
+
};
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
/**
|
|
215
|
+
* The scene's image processing: applied by a post process, and in the material with each of its
|
|
216
|
+
* effects and tone mappers at least once.
|
|
217
|
+
*/
|
|
218
|
+
const IMAGE_PROCESSING: Axis[] = [
|
|
219
|
+
{name: 'post-processed', context: {imageProcessing: imageProcessing({IMAGEPROCESSINGPOSTPROCESS: true})}},
|
|
220
|
+
{name: 'tone mapped', context: {imageProcessing: imageProcessing({IMAGEPROCESSING: true, TONEMAPPING: 1})}},
|
|
221
|
+
{
|
|
222
|
+
name: 'aces exposure contrast',
|
|
223
|
+
context: {
|
|
224
|
+
imageProcessing: imageProcessing({IMAGEPROCESSING: true, TONEMAPPING: 2, EXPOSURE: true, CONTRAST: true}),
|
|
225
|
+
},
|
|
226
|
+
},
|
|
227
|
+
{
|
|
228
|
+
name: 'neutral vignette dither',
|
|
229
|
+
context: {
|
|
230
|
+
imageProcessing: imageProcessing({
|
|
231
|
+
IMAGEPROCESSING: true,
|
|
232
|
+
TONEMAPPING: 3,
|
|
233
|
+
VIGNETTE: true,
|
|
234
|
+
VIGNETTEBLENDMODEMULTIPLY: true,
|
|
235
|
+
DITHER: true,
|
|
236
|
+
}),
|
|
237
|
+
},
|
|
238
|
+
},
|
|
239
|
+
{
|
|
240
|
+
name: 'opaque vignette curves white balance grading',
|
|
241
|
+
context: {
|
|
242
|
+
imageProcessing: imageProcessing({
|
|
243
|
+
IMAGEPROCESSING: true,
|
|
244
|
+
VIGNETTE: true,
|
|
245
|
+
VIGNETTEBLENDMODEOPAQUE: true,
|
|
246
|
+
COLORCURVES: true,
|
|
247
|
+
WHITEBALANCE: true,
|
|
248
|
+
COLORGRADING: true,
|
|
249
|
+
SKIPFINALCOLORCLAMP: true,
|
|
250
|
+
}),
|
|
251
|
+
},
|
|
252
|
+
},
|
|
253
|
+
{
|
|
254
|
+
name: 'grading 3d green depth bgr',
|
|
255
|
+
context: {
|
|
256
|
+
imageProcessing: imageProcessing({
|
|
257
|
+
IMAGEPROCESSING: true,
|
|
258
|
+
COLORGRADING: true,
|
|
259
|
+
COLORGRADING3D: true,
|
|
260
|
+
SAMPLER3DGREENDEPTH: true,
|
|
261
|
+
SAMPLER3DBGRMAP: true,
|
|
262
|
+
}),
|
|
263
|
+
},
|
|
264
|
+
},
|
|
265
|
+
];
|
|
266
|
+
|
|
267
|
+
/** The blends image processing is tried with: over, premultiplied, additive and multiply. */
|
|
268
|
+
const IMAGE_PROCESSING_BLEND: Axis[] = [
|
|
269
|
+
NONE,
|
|
270
|
+
{name: 'premultiplied', settings: {materialBlendMode: Constants.ALPHA_PREMULTIPLIED}},
|
|
271
|
+
{name: 'additive fog', settings: {materialBlendMode: Constants.ALPHA_ADD, applyFog: true}},
|
|
272
|
+
{name: 'multiply', settings: {materialBlendMode: Constants.ALPHA_MULTIPLY}},
|
|
273
|
+
];
|
|
274
|
+
|
|
172
275
|
/** A mesh batch whose geometry has a second UV set. */
|
|
173
276
|
const MESH_UV2: Axis = {name: 'uv2', context: {meshUV2: true}};
|
|
174
277
|
|
|
175
278
|
/** Soft, alpha-tested and fogged at once, to try the effects with the rest of the fragment. */
|
|
176
279
|
const EVERYTHING_ELSE: Axis = {
|
|
177
|
-
name: 'soft packed alpha test fog
|
|
178
|
-
settings: {softParticles: true, materialAlphaTest: 0.5, applyFog: true, materialBlendMode: Constants.
|
|
280
|
+
name: 'soft packed alpha test fog one-one',
|
|
281
|
+
settings: {softParticles: true, materialAlphaTest: 0.5, applyFog: true, materialBlendMode: Constants.ALPHA_ONEONE},
|
|
179
282
|
context: {depth: {packed: true}},
|
|
180
283
|
};
|
|
181
284
|
|
|
182
285
|
/**
|
|
183
286
|
* Every shader program the library can compile, one per distinct set of defines: every render
|
|
184
|
-
* mode with every combination of sheet, map, soft particles, alpha test and fog,
|
|
185
|
-
*
|
|
287
|
+
* mode with every combination of sheet, map, soft particles, alpha test and fog, every blend
|
|
288
|
+
* resolve with and without fog and soft particles, the texture effects alone and together on
|
|
289
|
+
* each shader family, and the scene's image processing on every render mode. What a shader validator compiles, and what
|
|
186
290
|
* an application can warm the shader cache with before its first frame (Unity's
|
|
187
291
|
* ShaderVariantCollection). Built by {@link shaderProgram}, so a variant here is a material a
|
|
188
292
|
* batch would build.
|
|
@@ -192,7 +296,7 @@ export function shaderVariants(): ShaderVariant[] {
|
|
|
192
296
|
const out: ShaderVariant[] = [];
|
|
193
297
|
const add = (axes: Axis[]) => {
|
|
194
298
|
const settings = bare(RenderMode.BillBoard);
|
|
195
|
-
const context: ShaderContext = {depth: null, envAtlasReady: false, meshUV2: false};
|
|
299
|
+
const context: ShaderContext = {depth: null, envAtlasReady: false, meshUV2: false, imageProcessing: null};
|
|
196
300
|
for (const axis of axes) {
|
|
197
301
|
Object.assign(settings, axis.settings);
|
|
198
302
|
Object.assign(context, axis.context);
|
|
@@ -215,11 +319,24 @@ export function shaderVariants(): ShaderVariant[] {
|
|
|
215
319
|
for (const alphaTest of ALPHA_TEST)
|
|
216
320
|
for (const fog of FOG) add([target, quad, tile, map, soft, alphaTest, fog]);
|
|
217
321
|
|
|
322
|
+
for (const target of TARGETS)
|
|
323
|
+
for (const blend of BLEND)
|
|
324
|
+
for (const soft of SOFT.slice(0, 2)) for (const map of MAP) add([target, blend, soft, map]);
|
|
325
|
+
|
|
218
326
|
for (const target of TARGETS.filter((t) => SURFACE_TARGETS.includes(t.name)))
|
|
219
327
|
for (const uv2 of target.settings?.renderMode === RenderMode.Mesh ? [NONE, MESH_UV2] : [NONE])
|
|
220
328
|
for (const surface of SURFACE)
|
|
221
329
|
for (const map of MAP)
|
|
222
330
|
for (const tile of [NONE, TILE[2]])
|
|
223
331
|
for (const rest of [NONE, EVERYTHING_ELSE]) add([target, uv2, surface, map, tile, rest]);
|
|
332
|
+
|
|
333
|
+
for (const target of TARGETS)
|
|
334
|
+
for (const ip of IMAGE_PROCESSING)
|
|
335
|
+
for (const blend of IMAGE_PROCESSING_BLEND) for (const map of MAP) add([target, ip, blend, map]);
|
|
336
|
+
|
|
337
|
+
// With the rest of the fragment: soft, alpha-tested, fogged and with every texture effect.
|
|
338
|
+
const everySurface = SURFACE[SURFACE.length - 3];
|
|
339
|
+
for (const target of TARGETS.filter((t) => SURFACE_TARGETS.includes(t.name)))
|
|
340
|
+
for (const ip of IMAGE_PROCESSING) add([target, ip, everySurface, MAP[MAP.length - 1], EVERYTHING_ELSE]);
|
|
224
341
|
return out;
|
|
225
342
|
}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The blend resolve for the particle shaders, in both shader languages.
|
|
3
|
+
*
|
|
4
|
+
* Every fade in the shaders (soft particles, erosion, a rim fade, alpha over life) lowers alpha
|
|
5
|
+
* only, so that alpha means coverage. Blend modes that take the colour at full weight
|
|
6
|
+
* (`BLEND_PREMULTIPLY`: one-one, premultiplied, screen, subtract, …) would ignore it, so the
|
|
7
|
+
* resolve multiplies the colour by its alpha; modes that multiply the frame (`BLEND_MULTIPLY`)
|
|
8
|
+
* move it toward white instead. It runs last, after fog and the soft fade, with the scene's image
|
|
9
|
+
* processing (`imageProcessing.ts`) around it. See `blendCategory`.
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
import {
|
|
13
|
+
imageProcessingFragmentGLSL,
|
|
14
|
+
imageProcessingFragmentWGSL,
|
|
15
|
+
imageProcessingResolvedGLSL,
|
|
16
|
+
imageProcessingResolvedWGSL,
|
|
17
|
+
} from './imageProcessing';
|
|
18
|
+
|
|
19
|
+
/** Processes and resolves the vec4 lvalue `color` for the blend mode, GLSL. */
|
|
20
|
+
export function blendFragmentGLSL(color: string): string {
|
|
21
|
+
return /* glsl */ `${imageProcessingFragmentGLSL(color)}
|
|
22
|
+
#if defined(BLEND_PREMULTIPLY)
|
|
23
|
+
${color}.rgb *= ${color}.a;
|
|
24
|
+
#elif defined(BLEND_MULTIPLY)
|
|
25
|
+
${color}.rgb = mix(vec3(1.0), ${color}.rgb, ${color}.a);
|
|
26
|
+
#endif
|
|
27
|
+
${imageProcessingResolvedGLSL(color)}`;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/** Processes and resolves the `var` vec4 `color` for the blend mode, WGSL. */
|
|
31
|
+
export function blendFragmentWGSL(color: string): string {
|
|
32
|
+
return /* wgsl */ `${imageProcessingFragmentWGSL(color)}
|
|
33
|
+
#if defined(BLEND_PREMULTIPLY)
|
|
34
|
+
${color} = vec4f(${color}.rgb * ${color}.a, ${color}.a);
|
|
35
|
+
#elif defined(BLEND_MULTIPLY)
|
|
36
|
+
${color} = vec4f(mix(vec3f(1.0), ${color}.rgb, ${color}.a), ${color}.a);
|
|
37
|
+
#endif
|
|
38
|
+
${imageProcessingResolvedWGSL(color)}`;
|
|
39
|
+
}
|
|
@@ -11,7 +11,8 @@
|
|
|
11
11
|
*
|
|
12
12
|
* Additive particles cannot fade toward a fog colour (that would add fog to
|
|
13
13
|
* the whole screen), so under `FOG_ADDITIVE` they fade toward black instead,
|
|
14
|
-
*
|
|
14
|
+
* and multiplying particles (`FOG_MULTIPLY`) toward white, which leaves the
|
|
15
|
+
* frame as it is: what Unity does for additive and multiply materials.
|
|
15
16
|
*/
|
|
16
17
|
|
|
17
18
|
/** Vertex-side declaration, GLSL. */
|
|
@@ -62,8 +63,10 @@ export function fogFragmentGLSL(rgb: string): string {
|
|
|
62
63
|
#ifdef FOG
|
|
63
64
|
{
|
|
64
65
|
float fogFactor = quarksFogFactor();
|
|
65
|
-
#
|
|
66
|
+
#if defined(FOG_ADDITIVE)
|
|
66
67
|
${rgb} *= fogFactor;
|
|
68
|
+
#elif defined(FOG_MULTIPLY)
|
|
69
|
+
${rgb} = mix(vec3(1.0), ${rgb}, fogFactor);
|
|
67
70
|
#else
|
|
68
71
|
${rgb} = mix(vFogColor, ${rgb}, fogFactor);
|
|
69
72
|
#endif
|
|
@@ -110,8 +113,10 @@ fn quarksFog(rgb: vec3f, fogDistanceVector: vec3f) -> vec3f {
|
|
|
110
113
|
fogCoeff = exp(-fogDistance * fogDistance * fogDensity * fogDensity);
|
|
111
114
|
}
|
|
112
115
|
let fogFactor = clamp(fogCoeff, 0.0, 1.0);
|
|
113
|
-
#
|
|
116
|
+
#if defined(FOG_ADDITIVE)
|
|
114
117
|
return rgb * fogFactor;
|
|
118
|
+
#elif defined(FOG_MULTIPLY)
|
|
119
|
+
return mix(vec3f(1.0), rgb, fogFactor);
|
|
115
120
|
#else
|
|
116
121
|
return mix(uniforms.vFogColor, rgb, fogFactor);
|
|
117
122
|
#endif
|