@driftengine/core 4.8.2 → 4.8.3
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.json +56 -0
- package/README.md +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.js +6 -0
- package/dist/render/backend/materialChanges.d.ts +7 -2
- package/dist/render/backend/materialChanges.js +8 -3
- package/dist/render/backend/webgl2/renderer.d.ts +23 -0
- package/dist/render/backend/webgl2/renderer.js +58 -17
- package/dist/render/backend/webgpu/causticsPass.js +2 -1
- package/dist/render/backend/webgpu/depthPass.d.ts +13 -3
- package/dist/render/backend/webgpu/depthPass.js +4 -3
- package/dist/render/backend/webgpu/filmPass.js +2 -1
- package/dist/render/backend/webgpu/flatPass.d.ts +5 -0
- package/dist/render/backend/webgpu/flatPass.js +2 -1
- package/dist/render/backend/webgpu/linePass.js +2 -1
- package/dist/render/backend/webgpu/panelPass.js +2 -1
- package/dist/render/backend/webgpu/particlePass.js +2 -1
- package/dist/render/backend/webgpu/plumePass.js +2 -1
- package/dist/render/backend/webgpu/postPass.d.ts +12 -2
- package/dist/render/backend/webgpu/postPass.js +19 -5
- package/dist/render/backend/webgpu/renderer.d.ts +10 -1
- package/dist/render/backend/webgpu/renderer.js +38 -12
- package/dist/render/backend/webgpu/sdfTextPass.js +2 -1
- package/dist/render/backend/webgpu/textPass.js +2 -1
- package/dist/render/backend/webgpu/waterPass.js +2 -1
- package/dist/render/backend/webgpu/windStreakPass.js +2 -1
- package/dist/render/boltBatch.js +2 -1
- package/dist/render/causticsRenderer.js +2 -1
- package/dist/render/cutoutCaster.d.ts +3 -1
- package/dist/render/cutoutCaster.js +4 -0
- package/dist/render/decalPass.js +2 -1
- package/dist/render/globalMediumPass.js +3 -2
- package/dist/render/lineBatch.js +5 -1
- package/dist/render/oitPass.js +2 -1
- package/dist/render/particleBatch.js +3 -2
- package/dist/render/particlePool.d.ts +6 -2
- package/dist/render/plumeRenderer.js +3 -2
- package/dist/render/renderQuality.d.ts +9 -2
- package/dist/render/sceneCoverage.d.ts +54 -0
- package/dist/render/sceneCoverage.js +72 -0
- package/dist/render/sdfTextRenderer.js +2 -1
- package/dist/render/shaders/depth.js +3 -2
- package/dist/render/shaders/flat/index.js +6 -1
- package/dist/render/shaders/generated/depth.wgsl.d.ts +32 -12
- package/dist/render/shaders/generated/depth.wgsl.js +32 -12
- package/dist/render/shaders/generated/flat.wgsl.d.ts +25 -0
- package/dist/render/shaders/generated/flat.wgsl.js +47 -22
- package/dist/render/shaders/generated/line.wgsl.d.ts +1 -1
- package/dist/render/shaders/generated/line.wgsl.js +1 -1
- package/dist/render/shaders/generated/particle.wgsl.d.ts +3 -3
- package/dist/render/shaders/generated/particle.wgsl.js +3 -3
- package/dist/render/shaders/generated/rush.wgsl.d.ts +1 -1
- package/dist/render/shaders/generated/rush.wgsl.js +1 -1
- package/dist/render/shaders/generated/sdfText.wgsl.d.ts +1 -1
- package/dist/render/shaders/generated/sdfText.wgsl.js +1 -1
- package/dist/render/shaders/generated/temporalResolve.wgsl.d.ts +1 -1
- package/dist/render/shaders/generated/temporalResolve.wgsl.js +1 -1
- package/dist/render/shaders/line.d.ts +1 -1
- package/dist/render/shaders/outputTransform.d.ts +14 -1
- package/dist/render/shaders/outputTransform.js +23 -1
- package/dist/render/shaders/particle.d.ts +3 -3
- package/dist/render/shaders/recon/resolve.wgsl.js +15 -1
- package/dist/render/shaders/rush.js +13 -2
- package/dist/render/shaders/sdfText.d.ts +1 -1
- package/dist/render/shaders/temporalResolve.js +12 -5
- package/dist/render/ssrPass.js +2 -1
- package/dist/render/surfaceTexture.d.ts +11 -0
- package/dist/render/textRenderer.js +2 -1
- package/dist/render/vertexDefaults.js +1 -0
- package/dist/render/waterRenderer.js +2 -1
- package/dist/render/windStreakRenderer.js +2 -1
- package/package.json +6 -6
- package/src/index.ts +13 -0
- package/src/render/backend/materialChanges.ts +8 -3
- package/src/render/backend/webgl2/renderer.ts +57 -16
- package/src/render/backend/webgpu/causticsPass.ts +2 -1
- package/src/render/backend/webgpu/depthPass.ts +4 -3
- package/src/render/backend/webgpu/filmPass.ts +2 -1
- package/src/render/backend/webgpu/flatPass.ts +2 -1
- package/src/render/backend/webgpu/linePass.ts +2 -1
- package/src/render/backend/webgpu/panelPass.ts +2 -1
- package/src/render/backend/webgpu/particlePass.ts +2 -1
- package/src/render/backend/webgpu/plumePass.ts +2 -1
- package/src/render/backend/webgpu/postPass.ts +20 -5
- package/src/render/backend/webgpu/renderer.ts +40 -11
- package/src/render/backend/webgpu/sdfTextPass.ts +2 -1
- package/src/render/backend/webgpu/textPass.ts +2 -1
- package/src/render/backend/webgpu/waterPass.ts +2 -1
- package/src/render/backend/webgpu/windStreakPass.ts +2 -1
- package/src/render/boltBatch.ts +2 -1
- package/src/render/causticsRenderer.ts +2 -1
- package/src/render/cutoutCaster.ts +15 -2
- package/src/render/decalPass.ts +2 -1
- package/src/render/globalMediumPass.ts +3 -2
- package/src/render/lineBatch.ts +3 -1
- package/src/render/oitPass.ts +2 -1
- package/src/render/particleBatch.ts +3 -2
- package/src/render/particlePool.ts +6 -2
- package/src/render/plumeRenderer.ts +3 -2
- package/src/render/renderQuality.ts +9 -2
- package/src/render/sceneCoverage.ts +78 -0
- package/src/render/sdfTextRenderer.ts +2 -1
- package/src/render/shaders/depth.ts +3 -2
- package/src/render/shaders/flat/index.ts +6 -1
- package/src/render/shaders/generated/depth.wgsl.ts +32 -12
- package/src/render/shaders/generated/flat.wgsl.ts +47 -22
- package/src/render/shaders/generated/line.wgsl.ts +1 -1
- package/src/render/shaders/generated/particle.wgsl.ts +3 -3
- package/src/render/shaders/generated/rush.wgsl.ts +1 -1
- package/src/render/shaders/generated/sdfText.wgsl.ts +1 -1
- package/src/render/shaders/generated/temporalResolve.wgsl.ts +1 -1
- package/src/render/shaders/outputTransform.ts +24 -1
- package/src/render/shaders/recon/resolve.wgsl.ts +15 -1
- package/src/render/shaders/rush.ts +13 -2
- package/src/render/shaders/temporalResolve.ts +12 -5
- package/src/render/ssrPass.ts +2 -1
- package/src/render/surfaceTexture.ts +11 -0
- package/src/render/textRenderer.ts +2 -1
- package/src/render/vertexDefaults.ts +1 -0
- package/src/render/waterRenderer.ts +2 -1
- package/src/render/windStreakRenderer.ts +2 -1
|
@@ -9,6 +9,7 @@ import { SSRTRACE_BINDINGS as SSR_BINDINGS, SSR_TRACE_FRAG_WGSL, } from '../../s
|
|
|
9
9
|
import { SSRRESOLVE_BINDINGS as SSR_RESOLVE_BINDINGS, SSR_RESOLVE_FRAG_WGSL, } from '../../shaders/generated/ssrResolve.wgsl.js';
|
|
10
10
|
import { GLOBALMEDIUM_BINDINGS as MEDIUM_BINDINGS, MEDIUM_FRAG_WGSL, MEDIUM_UPSAMPLE_FRAG_WGSL, } from '../../shaders/generated/globalMedium.wgsl.js';
|
|
11
11
|
import { shaderModule } from './shaderModules.js';
|
|
12
|
+
import { SCENE_ALPHA_KEEPS, SCENE_ALPHA_TRANSMITS } from '../../sceneCoverage.js';
|
|
12
13
|
/**
|
|
13
14
|
* The composite: everything that happens to the frame after the world is drawn.
|
|
14
15
|
*
|
|
@@ -197,7 +198,7 @@ export function createPostStageLayout(device, uniformBinding, uniformSize, textu
|
|
|
197
198
|
*/
|
|
198
199
|
export const OIT_COMPOSITE_BLEND = {
|
|
199
200
|
color: { srcFactor: 'one-minus-src-alpha', dstFactor: 'src-alpha', operation: 'add' },
|
|
200
|
-
alpha:
|
|
201
|
+
alpha: SCENE_ALPHA_TRANSMITS,
|
|
201
202
|
};
|
|
202
203
|
/**
|
|
203
204
|
* What a drawn decal does to the pixel under it: `dst * src`.
|
|
@@ -215,16 +216,18 @@ export const OIT_COMPOSITE_BLEND = {
|
|
|
215
216
|
* exactly the integral — and the composite therefore never samples the colour it is modifying,
|
|
216
217
|
* which is the read both backends are entitled to call undefined.
|
|
217
218
|
*
|
|
218
|
-
* Alpha is
|
|
219
|
-
*
|
|
219
|
+
* Alpha is the transmittance again, `share * a`: the scene's alpha is how much of the pixel is
|
|
220
|
+
* still the opaque surface, which is what the composite lets ambient occlusion darken, and a
|
|
221
|
+
* medium thick enough to hide a corner hides its occlusion too (`sceneCoverage.ts`). It reaches
|
|
222
|
+
* no canvas: both are opaque, and the composite writes 1.
|
|
220
223
|
*/
|
|
221
224
|
export const MEDIUM_TRANSMIT_BLEND = {
|
|
222
225
|
color: { srcFactor: 'one', dstFactor: 'src-alpha', operation: 'add' },
|
|
223
|
-
alpha:
|
|
226
|
+
alpha: SCENE_ALPHA_TRANSMITS,
|
|
224
227
|
};
|
|
225
228
|
export const DECAL_MULTIPLY_BLEND = {
|
|
226
229
|
color: { srcFactor: 'zero', dstFactor: 'src', operation: 'add' },
|
|
227
|
-
alpha:
|
|
230
|
+
alpha: SCENE_ALPHA_KEEPS,
|
|
228
231
|
};
|
|
229
232
|
/**
|
|
230
233
|
* `over`, against colour a pass has already multiplied by its own coverage.
|
|
@@ -238,6 +241,17 @@ export const PREMULTIPLIED_OVER_BLEND = {
|
|
|
238
241
|
color: { srcFactor: 'one', dstFactor: 'one-minus-src-alpha', operation: 'add' },
|
|
239
242
|
alpha: { srcFactor: 'one', dstFactor: 'one-minus-src-alpha', operation: 'add' },
|
|
240
243
|
};
|
|
244
|
+
/**
|
|
245
|
+
* The same `over` for the resolve that lands a reflection on the scene, keeping the scene's alpha.
|
|
246
|
+
*
|
|
247
|
+
* A reflection is a layer of the surface it is seen in, so the surface's share of the pixel is
|
|
248
|
+
* unchanged (`sceneCoverage.ts`). The trace keeps `PREMULTIPLIED_OVER_BLEND`, because its target is
|
|
249
|
+
* the reflection's own and its alpha is the coverage this resolve reads.
|
|
250
|
+
*/
|
|
251
|
+
export const REFLECTION_OVER_BLEND = {
|
|
252
|
+
color: { srcFactor: 'one', dstFactor: 'one-minus-src-alpha', operation: 'add' },
|
|
253
|
+
alpha: SCENE_ALPHA_KEEPS,
|
|
254
|
+
};
|
|
241
255
|
export const ADDITIVE_BLEND = {
|
|
242
256
|
color: { srcFactor: 'one', dstFactor: 'one', operation: 'add' },
|
|
243
257
|
alpha: { srcFactor: 'one', dstFactor: 'one', operation: 'add' },
|
|
@@ -1113,6 +1113,13 @@ export declare class WebGPURenderer implements RendererApi {
|
|
|
1113
1113
|
private blankAlbedoBindGroup;
|
|
1114
1114
|
/** Repeats across the mesh's own UV range. Per draw, because `uUvScale` is a vertex uniform. */
|
|
1115
1115
|
private readonly uvScale;
|
|
1116
|
+
/** Where the texture starts, after the scale. Per draw, beside `uvScale`. */
|
|
1117
|
+
private readonly uvOffset;
|
|
1118
|
+
/**
|
|
1119
|
+
* How the surfaces drawn next meet the medium: what `setSurfaceFog` set, and `FOG_RECEDE` from
|
|
1120
|
+
* each `bindMeshPass`. A translucent draw's own fog is measured against this and puts it back.
|
|
1121
|
+
*/
|
|
1122
|
+
private surfaceFog;
|
|
1116
1123
|
/** Whether the bound material is seen from both faces. See `SurfaceMaterial.doubleSided`. */
|
|
1117
1124
|
private materialDoubleSided;
|
|
1118
1125
|
/** Whether the bound material has a map, for the warning a mesh with no coordinates earns. */
|
|
@@ -1198,6 +1205,8 @@ export declare class WebGPURenderer implements RendererApi {
|
|
|
1198
1205
|
/** How bright the environment the surfaces drawn next reflect is. See `renderer.ts`. */
|
|
1199
1206
|
setEnvironmentGain(gain: number): void;
|
|
1200
1207
|
setSurfaceGrain(amount: number): void;
|
|
1208
|
+
/** Whether the surfaces drawn next meet the medium. See `renderer.ts`. */
|
|
1209
|
+
setSurfaceFog(enabled: boolean): void;
|
|
1201
1210
|
/**
|
|
1202
1211
|
* How much of the surfaces drawn next survives a screen-door dither. See `renderer.ts`.
|
|
1203
1212
|
*
|
|
@@ -1816,7 +1825,7 @@ export declare class WebGPURenderer implements RendererApi {
|
|
|
1816
1825
|
*/
|
|
1817
1826
|
/** The cutout bind group for `texture` in the open layer, built the first time it casts there. */
|
|
1818
1827
|
private cutoutGroup;
|
|
1819
|
-
/** The cutoff and UV scale into a cutout draw's slot, at
|
|
1828
|
+
/** The cutoff and UV scale and offset into a cutout draw's slot, at its own variant's offsets. */
|
|
1820
1829
|
private writeCutout;
|
|
1821
1830
|
private shadowGroup;
|
|
1822
1831
|
/**
|
|
@@ -61,7 +61,7 @@ import { ExposurePass } from './exposurePass.js';
|
|
|
61
61
|
import { AO_STORE } from '../../shaders/ambientOcclusion.js';
|
|
62
62
|
import { clampAutoExposure } from '../../shaders/exposure.js';
|
|
63
63
|
import { clampLocalExposure } from '../../shaders/localExposure.js';
|
|
64
|
-
import { createDepthResolveLayout, createPostStageLayout, depthResolvePipeline, postPipeline, postPipelineTargets, sceneColorFormat, AO_BLUR_FIELDS, AO_BLUR_FRAG_WGSL, AO_BLUR_SIZE, AO_BLUR_TEXTURES, AO_BLUR_UNIFORMS, AO_FRAG_FIELDS, AO_FRAG_SIZE, AO_FRAG_WGSL, AO_TEXTURES, AO_UNIFORMS, ADDITIVE_BLEND, BLOOM_DOWNSAMPLE_FIELDS, BLOOM_DOWNSAMPLE_FRAG_WGSL, BLOOM_PREFILTER_FIELDS, BLOOM_PREFILTER_FRAG_WGSL, BLOOM_STAGE_SIZE, BLOOM_TEXTURES, BLOOM_UNIFORMS, BLOOM_UPSAMPLE_FIELDS, BLOOM_UPSAMPLE_FRAG_WGSL, RESOLVED_DEPTH_FORMAT, RUSH_FRAG_FIELDS, RUSH_FRAG_SIZE, RUSH_FRAG_WGSL, RUSH_TEXTURES, RUSH_UNIFORMS, TAA_FRAG_FIELDS, TAA_FRAG_SIZE, TAA_TEXTURES, TAA_UNIFORMS, TEMPORAL_RESOLVE_FRAG_WGSL, MEDIUM_FRAG_FIELDS, MEDIUM_FRAG_SIZE, MEDIUM_FRAG_WGSL, MEDIUM_TEXTURES, MEDIUM_TRANSMIT_BLEND, MEDIUM_UNIFORMS, MEDIUM_UPSAMPLE_FIELDS, MEDIUM_UPSAMPLE_FRAG_WGSL, MEDIUM_UPSAMPLE_SIZE, MEDIUM_UPSAMPLE_TEXTURES, MEDIUM_UPSAMPLE_UNIFORMS, OIT_COMPOSITE_BLEND, OIT_RESOLVE_FRAG_WGSL, OIT_TEXTURES, DECAL_FRAG_FIELDS, DECAL_FRAG_SIZE, DECAL_MULTIPLY_BLEND, DECAL_PROJECT_FRAG_WGSL, DECAL_TEXTURES, DECAL_UNIFORMS, PREMULTIPLIED_OVER_BLEND, SSR_FRAG_FIELDS, SSR_FRAG_SIZE, SSR_RESOLVE_FRAG_WGSL, SSR_RESOLVE_TEXTURES, SSR_TEXTURES, SSR_TRACE_FRAG_WGSL, SSR_UNIFORMS, createOitResolveLayout, createSsrResolveLayout, } from './postPass.js';
|
|
64
|
+
import { createDepthResolveLayout, createPostStageLayout, depthResolvePipeline, postPipeline, postPipelineTargets, sceneColorFormat, AO_BLUR_FIELDS, AO_BLUR_FRAG_WGSL, AO_BLUR_SIZE, AO_BLUR_TEXTURES, AO_BLUR_UNIFORMS, AO_FRAG_FIELDS, AO_FRAG_SIZE, AO_FRAG_WGSL, AO_TEXTURES, AO_UNIFORMS, ADDITIVE_BLEND, BLOOM_DOWNSAMPLE_FIELDS, BLOOM_DOWNSAMPLE_FRAG_WGSL, BLOOM_PREFILTER_FIELDS, BLOOM_PREFILTER_FRAG_WGSL, BLOOM_STAGE_SIZE, BLOOM_TEXTURES, BLOOM_UNIFORMS, BLOOM_UPSAMPLE_FIELDS, BLOOM_UPSAMPLE_FRAG_WGSL, RESOLVED_DEPTH_FORMAT, RUSH_FRAG_FIELDS, RUSH_FRAG_SIZE, RUSH_FRAG_WGSL, RUSH_TEXTURES, RUSH_UNIFORMS, TAA_FRAG_FIELDS, TAA_FRAG_SIZE, TAA_TEXTURES, TAA_UNIFORMS, TEMPORAL_RESOLVE_FRAG_WGSL, MEDIUM_FRAG_FIELDS, MEDIUM_FRAG_SIZE, MEDIUM_FRAG_WGSL, MEDIUM_TEXTURES, MEDIUM_TRANSMIT_BLEND, MEDIUM_UNIFORMS, MEDIUM_UPSAMPLE_FIELDS, MEDIUM_UPSAMPLE_FRAG_WGSL, MEDIUM_UPSAMPLE_SIZE, MEDIUM_UPSAMPLE_TEXTURES, MEDIUM_UPSAMPLE_UNIFORMS, OIT_COMPOSITE_BLEND, OIT_RESOLVE_FRAG_WGSL, OIT_TEXTURES, DECAL_FRAG_FIELDS, DECAL_FRAG_SIZE, DECAL_MULTIPLY_BLEND, DECAL_PROJECT_FRAG_WGSL, DECAL_TEXTURES, DECAL_UNIFORMS, PREMULTIPLIED_OVER_BLEND, REFLECTION_OVER_BLEND, SSR_FRAG_FIELDS, SSR_FRAG_SIZE, SSR_RESOLVE_FRAG_WGSL, SSR_RESOLVE_TEXTURES, SSR_TEXTURES, SSR_TRACE_FRAG_WGSL, SSR_UNIFORMS, createOitResolveLayout, createSsrResolveLayout, } from './postPass.js';
|
|
65
65
|
import { GRADE_PLACEHOLDER_SIZE, identityGradeLut, validateGradeLut, } from '../../colourGrade.js';
|
|
66
66
|
import { FILM_LOOK_WITHOUT_COMPOSITE, clampGrain, clampVignette, grainSeed, } from '../../shaders/filmLook.js';
|
|
67
67
|
import { createProbeMipBindGroupLayout, createProbePrefilterBindGroupLayout, PREFILTER_UNIFORM_OFFSETS, probeLevels, probeMipPipeline, probePrefilterPipeline, PROBE_FACES, PROBE_FACE_BASIS, PROBE_FAR_M, PROBE_FOV_DEG, PROBE_MIP_UNIFORM_SIZE, PROBE_NEAR_M, } from './probePass.js';
|
|
@@ -100,7 +100,7 @@ import { LIGHT_VOLUME_VERT_FIELDS, LIGHT_VOLUME_VERT_SIZE, createLightVolumeBind
|
|
|
100
100
|
import { aimReflection, createGpuReflection } from './reflectionPass.js';
|
|
101
101
|
import { FrameBudget } from '../budget.js';
|
|
102
102
|
import { MaterialChanges, ownsMaterial } from '../materialChanges.js';
|
|
103
|
-
import { FOG_RECEDE, fogModeOf } from '../drawFog.js';
|
|
103
|
+
import { FOG_OFF, FOG_RECEDE, fogModeOf } from '../drawFog.js';
|
|
104
104
|
import { DYNAMIC_ALIGNMENT as DYNAMIC_UNIFORM_ALIGNMENT, UniformRing } from './uniformRing.js';
|
|
105
105
|
import { DecalQueue, MAX_DRAWN_DECALS } from '../../decalQueue.js';
|
|
106
106
|
import { DistanceFieldScene } from '../../gi/fieldScene.js';
|
|
@@ -2131,6 +2131,13 @@ export class WebGPURenderer {
|
|
|
2131
2131
|
blankAlbedoBindGroup;
|
|
2132
2132
|
/** Repeats across the mesh's own UV range. Per draw, because `uUvScale` is a vertex uniform. */
|
|
2133
2133
|
uvScale = new Float32Array([1, 1]);
|
|
2134
|
+
/** Where the texture starts, after the scale. Per draw, beside `uvScale`. */
|
|
2135
|
+
uvOffset = new Float32Array([0, 0]);
|
|
2136
|
+
/**
|
|
2137
|
+
* How the surfaces drawn next meet the medium: what `setSurfaceFog` set, and `FOG_RECEDE` from
|
|
2138
|
+
* each `bindMeshPass`. A translucent draw's own fog is measured against this and puts it back.
|
|
2139
|
+
*/
|
|
2140
|
+
surfaceFog = FOG_RECEDE;
|
|
2134
2141
|
/** Whether the bound material is seen from both faces. See `SurfaceMaterial.doubleSided`. */
|
|
2135
2142
|
materialDoubleSided = false;
|
|
2136
2143
|
/** Whether the bound material has a map, for the warning a mesh with no coordinates earns. */
|
|
@@ -2260,6 +2267,8 @@ export class WebGPURenderer {
|
|
|
2260
2267
|
const albedo = material?.albedo ?? null;
|
|
2261
2268
|
this.uvScale[0] = material?.uScale ?? 1;
|
|
2262
2269
|
this.uvScale[1] = material?.vScale ?? 1;
|
|
2270
|
+
this.uvOffset[0] = material?.uOffset ?? 0;
|
|
2271
|
+
this.uvOffset[1] = material?.vOffset ?? 0;
|
|
2263
2272
|
this.materialDoubleSided = material?.doubleSided === true;
|
|
2264
2273
|
this.materialHasMaps = materialHasMaps(material);
|
|
2265
2274
|
const normal = (material?.normal ?? null);
|
|
@@ -2408,6 +2417,13 @@ export class WebGPURenderer {
|
|
|
2408
2417
|
f[this.materialField('uGrain')] = Math.min(1, Math.max(0, amount));
|
|
2409
2418
|
});
|
|
2410
2419
|
}
|
|
2420
|
+
/** Whether the surfaces drawn next meet the medium. See `renderer.ts`. */
|
|
2421
|
+
setSurfaceFog(enabled) {
|
|
2422
|
+
this.surfaceFog = enabled ? FOG_RECEDE : FOG_OFF;
|
|
2423
|
+
this.material((_f, i) => {
|
|
2424
|
+
i[this.materialField('uFogEnabled')] = this.surfaceFog;
|
|
2425
|
+
});
|
|
2426
|
+
}
|
|
2411
2427
|
/**
|
|
2412
2428
|
* How much of the surfaces drawn next survives a screen-door dither. See `renderer.ts`.
|
|
2413
2429
|
*
|
|
@@ -4363,10 +4379,12 @@ export class WebGPURenderer {
|
|
|
4363
4379
|
groups.set(texture, group);
|
|
4364
4380
|
return group;
|
|
4365
4381
|
}
|
|
4366
|
-
/** The cutoff and UV scale into a cutout draw's slot, at
|
|
4382
|
+
/** The cutoff and UV scale and offset into a cutout draw's slot, at its own variant's offsets. */
|
|
4367
4383
|
writeCutout(slot, cutout, fields) {
|
|
4368
4384
|
this.shadowDraws.writeFloat(slot, fields.uUvScale.offset, cutout.u);
|
|
4369
4385
|
this.shadowDraws.writeFloat(slot, fields.uUvScale.offset + 4, cutout.v);
|
|
4386
|
+
this.shadowDraws.writeFloat(slot, fields.uUvOffset.offset, cutout.uOffset);
|
|
4387
|
+
this.shadowDraws.writeFloat(slot, fields.uUvOffset.offset + 4, cutout.vOffset);
|
|
4370
4388
|
this.shadowDraws.writeFloat(slot, fields.uAlphaCutout.offset, cutout.cutoff);
|
|
4371
4389
|
}
|
|
4372
4390
|
shadowGroup() {
|
|
@@ -6248,7 +6266,8 @@ export class WebGPURenderer {
|
|
|
6248
6266
|
* these per draw through the material block, the same shape `uOpacity` already uses.
|
|
6249
6267
|
*/
|
|
6250
6268
|
i[at('uLightingEnabled')] = 1;
|
|
6251
|
-
i[at('uFogEnabled')] =
|
|
6269
|
+
i[at('uFogEnabled')] = FOG_RECEDE;
|
|
6270
|
+
this.surfaceFog = FOG_RECEDE;
|
|
6252
6271
|
i[at('uAlbedoEnabled')] = 0;
|
|
6253
6272
|
this.materialHasMaps = false;
|
|
6254
6273
|
/*
|
|
@@ -6666,6 +6685,8 @@ export class WebGPURenderer {
|
|
|
6666
6685
|
this.bindGroup = this.blankAlbedoBindGroup;
|
|
6667
6686
|
this.uvScale[0] = 1;
|
|
6668
6687
|
this.uvScale[1] = 1;
|
|
6688
|
+
this.uvOffset[0] = 0;
|
|
6689
|
+
this.uvOffset[1] = 0;
|
|
6669
6690
|
}
|
|
6670
6691
|
/**
|
|
6671
6692
|
* The slot the next draw's material lives in, taking a new one when something dirtied it.
|
|
@@ -6744,7 +6765,10 @@ export class WebGPURenderer {
|
|
|
6744
6765
|
if (this.tracksMotionNow())
|
|
6745
6766
|
this.recordDrawMotion(mesh, model, previousModel);
|
|
6746
6767
|
noteMapsWithoutUvs(mesh, mesh.hasUvs, this.materialHasMaps);
|
|
6747
|
-
|
|
6768
|
+
/* An opaque draw meets the medium as the dial says: `submitMesh` reads an absent `fog` as
|
|
6769
|
+
true, which is a translucent draw's default and would put every opaque draw back in the fog
|
|
6770
|
+
`setSurfaceFog(false)` took it out of. */
|
|
6771
|
+
this.submitMesh(mesh, model, tint, 1, false, { depthLayer, fog: this.surfaceFog !== FOG_OFF });
|
|
6748
6772
|
}
|
|
6749
6773
|
/**
|
|
6750
6774
|
* Draw a mesh that lets the world through, at `opacity`.
|
|
@@ -6894,6 +6918,7 @@ export class WebGPURenderer {
|
|
|
6894
6918
|
/* The material's repeats, not a constant: geometry authored in metres is textured at
|
|
6895
6919
|
whatever density the material asks for without rebuilding it. */
|
|
6896
6920
|
this.perDraw.writeFloats(slot, FLAT_VERT_FIELDS.uUvScale.offset, this.uvScale);
|
|
6921
|
+
this.perDraw.writeFloats(slot, FLAT_VERT_FIELDS.uUvOffset.offset, this.uvOffset);
|
|
6897
6922
|
this.perDraw.writeFloats(slot, FLAT_VERT_FIELDS.uLightViewProj.offset, this.lightViewProj);
|
|
6898
6923
|
/*
|
|
6899
6924
|
* Whether this mesh's tangent frame is real. WebGPU has no disabled attribute, so location 10
|
|
@@ -6926,7 +6951,7 @@ export class WebGPURenderer {
|
|
|
6926
6951
|
/* Added light fades in the medium rather than receding into it. See `drawFog.ts`. */
|
|
6927
6952
|
const fog = fogModeOf(options.fog ?? true, blend && options.additive === true);
|
|
6928
6953
|
const toneMapped = options.toneMapped ?? true;
|
|
6929
|
-
const unlitOrUnfogged = !lit || fog !==
|
|
6954
|
+
const unlitOrUnfogged = !lit || fog !== this.surfaceFog;
|
|
6930
6955
|
if (unlitOrUnfogged) {
|
|
6931
6956
|
if (!lit)
|
|
6932
6957
|
this.perFrameInts[this.materialField('uLightingEnabled')] = 0;
|
|
@@ -6949,7 +6974,7 @@ export class WebGPURenderer {
|
|
|
6949
6974
|
const refracting = this.bindSeeThrough(options);
|
|
6950
6975
|
/* Taken for itself when any of those differs from the pass, and put back below: the rule both
|
|
6951
6976
|
backends count material changes by. See `materialChanges.ts`. */
|
|
6952
|
-
const own = ownsMaterial({ opacity, lit, fog, toneMapped, refracting });
|
|
6977
|
+
const own = ownsMaterial({ opacity, lit, fog, toneMapped, refracting }, this.surfaceFog);
|
|
6953
6978
|
if (own)
|
|
6954
6979
|
this.materials.dirty();
|
|
6955
6980
|
const base = mesh.key ?? 'flat:s0:u0';
|
|
@@ -7079,7 +7104,7 @@ export class WebGPURenderer {
|
|
|
7079
7104
|
if (unlitOrUnfogged) {
|
|
7080
7105
|
if (!lit)
|
|
7081
7106
|
this.perFrameInts[this.materialField('uLightingEnabled')] = 1;
|
|
7082
|
-
this.perFrameInts[this.materialField('uFogEnabled')] =
|
|
7107
|
+
this.perFrameInts[this.materialField('uFogEnabled')] = this.surfaceFog;
|
|
7083
7108
|
}
|
|
7084
7109
|
/* Back to off for every other draw in the frame, exactly as `uOpacity` is: this scratch is
|
|
7085
7110
|
shared, so a strength left set is worn by everything drawn after it. */
|
|
@@ -7284,6 +7309,7 @@ export class WebGPURenderer {
|
|
|
7284
7309
|
const lands = blend && options.reconstructed !== true && this.drawsLate();
|
|
7285
7310
|
this.perDraw.writeFloats(slot, FLAT_VERT_FIELDS.uViewProj.offset, lands ? this.correctedViewProj : this.viewProj);
|
|
7286
7311
|
this.perDraw.writeFloats(slot, FLAT_VERT_FIELDS.uUvScale.offset, this.uvScale);
|
|
7312
|
+
this.perDraw.writeFloats(slot, FLAT_VERT_FIELDS.uUvOffset.offset, this.uvOffset);
|
|
7287
7313
|
this.perDraw.writeFloats(slot, FLAT_VERT_FIELDS.uLightViewProj.offset, this.lightViewProj);
|
|
7288
7314
|
this.perDraw.writeInt(slot, FLAT_VERT_FIELDS.uHasTangents.offset, mesh.hasTangents ? 1 : 0);
|
|
7289
7315
|
const dimmed = opacity < 1;
|
|
@@ -7293,7 +7319,7 @@ export class WebGPURenderer {
|
|
|
7293
7319
|
const lit = options.lit ?? true;
|
|
7294
7320
|
const fog = fogModeOf(options.fog ?? true, blend && options.additive === true);
|
|
7295
7321
|
const toneMapped = options.toneMapped ?? true;
|
|
7296
|
-
const unlitOrUnfogged = !lit || fog !==
|
|
7322
|
+
const unlitOrUnfogged = !lit || fog !== this.surfaceFog;
|
|
7297
7323
|
if (unlitOrUnfogged) {
|
|
7298
7324
|
if (!lit)
|
|
7299
7325
|
this.perFrameInts[this.materialField('uLightingEnabled')] = 0;
|
|
@@ -7306,7 +7332,7 @@ export class WebGPURenderer {
|
|
|
7306
7332
|
const layer = Math.min(Math.max(Math.round(options.depthLayer ?? 0), 0), MAX_DEPTH_LAYER);
|
|
7307
7333
|
/* A blended batch shows what is behind it — refraction or glass — as a single draw does. */
|
|
7308
7334
|
const refracting = blend && this.bindSeeThrough(options);
|
|
7309
|
-
const own = ownsMaterial({ opacity, lit, fog, toneMapped, refracting });
|
|
7335
|
+
const own = ownsMaterial({ opacity, lit, fog, toneMapped, refracting }, this.surfaceFog);
|
|
7310
7336
|
if (own)
|
|
7311
7337
|
this.materials.dirty();
|
|
7312
7338
|
const base = mesh.key ?? 'flat:s0:u0';
|
|
@@ -7375,7 +7401,7 @@ export class WebGPURenderer {
|
|
|
7375
7401
|
}
|
|
7376
7402
|
if (unlitOrUnfogged) {
|
|
7377
7403
|
this.perFrameInts[this.materialField('uLightingEnabled')] = 1;
|
|
7378
|
-
this.perFrameInts[this.materialField('uFogEnabled')] =
|
|
7404
|
+
this.perFrameInts[this.materialField('uFogEnabled')] = this.surfaceFog;
|
|
7379
7405
|
}
|
|
7380
7406
|
if (!toneMapped) {
|
|
7381
7407
|
this.perFrameInts[this.materialField('uOutputTransform')] = this.gradeCode();
|
|
@@ -9583,7 +9609,7 @@ export class WebGPURenderer {
|
|
|
9583
9609
|
label: 'post.ssr.resolve',
|
|
9584
9610
|
colorAttachments: [{ view: scene, loadOp: 'load', storeOp: 'store' }],
|
|
9585
9611
|
});
|
|
9586
|
-
resolve.setPipeline(postPipeline(this.pipelines, device, this.ssrResolveLayout, 'post.ssrResolve', SSR_RESOLVE_FRAG_WGSL, this.pipelines.format,
|
|
9612
|
+
resolve.setPipeline(postPipeline(this.pipelines, device, this.ssrResolveLayout, 'post.ssrResolve', SSR_RESOLVE_FRAG_WGSL, this.pipelines.format, REFLECTION_OVER_BLEND));
|
|
9587
9613
|
resolve.setBindGroup(0, resolveGroup);
|
|
9588
9614
|
resolve.draw(3);
|
|
9589
9615
|
resolve.end();
|
|
@@ -3,6 +3,7 @@ import { SDFTEXT_BINDINGS, SDF_TEXT_FRAG_WGSL, SDF_TEXT_VERT_WGSL, } from '../..
|
|
|
3
3
|
import { DEPTH_FORMAT } from './flatPass.js';
|
|
4
4
|
import { SdfTextLayout } from '../../sdfTextLayout.js';
|
|
5
5
|
import { shaderModule } from './shaderModules.js';
|
|
6
|
+
import { SCENE_ALPHA_COVERS } from '../../sceneCoverage.js';
|
|
6
7
|
/**
|
|
7
8
|
* The SDF text pass: a string as flat, textured quads, drawn into the scene.
|
|
8
9
|
*
|
|
@@ -183,7 +184,7 @@ export function sdfTextPipeline(cache, device, layout) {
|
|
|
183
184
|
format: cache.format,
|
|
184
185
|
blend: {
|
|
185
186
|
color: { srcFactor: 'src-alpha', dstFactor: 'one-minus-src-alpha', operation: 'add' },
|
|
186
|
-
alpha:
|
|
187
|
+
alpha: SCENE_ALPHA_COVERS,
|
|
187
188
|
},
|
|
188
189
|
},
|
|
189
190
|
],
|
|
@@ -3,6 +3,7 @@ import { TEXT_BINDINGS, TEXT_FRAG_WGSL, TEXT_VERT_WGSL, } from '../../shaders/ge
|
|
|
3
3
|
import { MAX_CELLS, TEXT_CUBE, TextLayout } from '../../textLayout.js';
|
|
4
4
|
import { DEPTH_FORMAT } from './flatPass.js';
|
|
5
5
|
import { shaderModule } from './shaderModules.js';
|
|
6
|
+
import { SCENE_ALPHA_COVERS } from '../../sceneCoverage.js';
|
|
6
7
|
/**
|
|
7
8
|
* The text pass: a string as instanced glyph cubes, drawn over the scene.
|
|
8
9
|
*
|
|
@@ -174,7 +175,7 @@ export function textPipeline(cache, device, layout) {
|
|
|
174
175
|
format: cache.format,
|
|
175
176
|
blend: {
|
|
176
177
|
color: { srcFactor: 'src-alpha', dstFactor: 'one-minus-src-alpha', operation: 'add' },
|
|
177
|
-
alpha:
|
|
178
|
+
alpha: SCENE_ALPHA_COVERS,
|
|
178
179
|
},
|
|
179
180
|
},
|
|
180
181
|
],
|
|
@@ -3,6 +3,7 @@ import { WATER_BINDINGS, WATER_FRAG_WGSL, WATER_VERT_WGSL, } from '../../shaders
|
|
|
3
3
|
import { PATCH_RESOLUTION, buildUnitSheet, buildWaterGrid } from '../../waterGrid.js';
|
|
4
4
|
import { DEPTH_FORMAT } from './flatPass.js';
|
|
5
5
|
import { shaderModule } from './shaderModules.js';
|
|
6
|
+
import { SCENE_ALPHA_COVERS } from '../../sceneCoverage.js';
|
|
6
7
|
/**
|
|
7
8
|
* The water pass: two sheets and a patch, displaced by a wave field in the vertex stage.
|
|
8
9
|
*
|
|
@@ -148,7 +149,7 @@ export function waterPipeline(cache, device, layout) {
|
|
|
148
149
|
format: cache.format,
|
|
149
150
|
blend: {
|
|
150
151
|
color: { srcFactor: 'src-alpha', dstFactor: 'one-minus-src-alpha', operation: 'add' },
|
|
151
|
-
alpha:
|
|
152
|
+
alpha: SCENE_ALPHA_COVERS,
|
|
152
153
|
},
|
|
153
154
|
},
|
|
154
155
|
],
|
|
@@ -3,6 +3,7 @@ import { WINDSTREAKS_BINDINGS, WIND_STREAK_FRAG_WGSL, WIND_STREAK_VERT_WGSL, } f
|
|
|
3
3
|
import { buildWindStreakLattice } from '../../windStreakDraw.js';
|
|
4
4
|
import { DEPTH_FORMAT } from './flatPass.js';
|
|
5
5
|
import { shaderModule } from './shaderModules.js';
|
|
6
|
+
import { SCENE_ALPHA_COVERS } from '../../sceneCoverage.js';
|
|
6
7
|
/**
|
|
7
8
|
* The wind-streak pass: a lattice of debris that follows the camera and wraps.
|
|
8
9
|
*
|
|
@@ -141,7 +142,7 @@ export function windStreakPipeline(cache, device, layout) {
|
|
|
141
142
|
dstFactor: 'one-minus-src-alpha',
|
|
142
143
|
operation: 'add',
|
|
143
144
|
},
|
|
144
|
-
alpha:
|
|
145
|
+
alpha: SCENE_ALPHA_COVERS,
|
|
145
146
|
},
|
|
146
147
|
},
|
|
147
148
|
],
|
package/dist/render/boltBatch.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { compileProgram, uniformLocations } from './shader.js';
|
|
2
2
|
import { BOLT_FRAG, BOLT_VERT } from './shaders/bolt.js';
|
|
3
3
|
import { buildSegmentQuads, expandBoltSegments } from './segmentQuads.js';
|
|
4
|
+
import { blendKeeping } from './sceneCoverage.js';
|
|
4
5
|
/**
|
|
5
6
|
* One draw call for a pool of electrical arcs.
|
|
6
7
|
*
|
|
@@ -80,7 +81,7 @@ export class BoltBatch {
|
|
|
80
81
|
if (segmentCount === 0)
|
|
81
82
|
return;
|
|
82
83
|
gl.enable(gl.BLEND);
|
|
83
|
-
|
|
84
|
+
blendKeeping(gl, gl.SRC_ALPHA, gl.ONE);
|
|
84
85
|
gl.depthMask(false);
|
|
85
86
|
gl.disable(gl.CULL_FACE);
|
|
86
87
|
gl.bindVertexArray(this.vao);
|
|
@@ -3,6 +3,7 @@ import { CAUSTICS_FRAG, CAUSTICS_VERT } from './shaders/caustics.js';
|
|
|
3
3
|
import { bindAtmosphere } from './atmosphere.js';
|
|
4
4
|
import { buildSheets } from './surfaceSheet.js';
|
|
5
5
|
import { seaStateForWind } from './seaState.js';
|
|
6
|
+
import { blendKeeping } from './sceneCoverage.js';
|
|
6
7
|
/**
|
|
7
8
|
* How far from the water the effect has died out.
|
|
8
9
|
*
|
|
@@ -97,7 +98,7 @@ export class CausticsRenderer {
|
|
|
97
98
|
gl.uniform1f(u['uWaveGain'] ?? null, sea.steepness);
|
|
98
99
|
bindAtmosphere(gl, u, atmosphere, camera.position[1] ?? 0, underwaterEnabled);
|
|
99
100
|
gl.enable(gl.BLEND);
|
|
100
|
-
|
|
101
|
+
blendKeeping(gl, gl.ONE, gl.ONE);
|
|
101
102
|
/*
|
|
102
103
|
* No depth write, but depth *testing* stays on: the sheet sits a couple of
|
|
103
104
|
* centimetres under the surface it lights, so geometry in front of it — the
|
|
@@ -12,12 +12,14 @@
|
|
|
12
12
|
* reads it and binds before it asks again.
|
|
13
13
|
*/
|
|
14
14
|
import type { SurfaceMaterial } from './surfaceTexture.ts';
|
|
15
|
-
/** A cutout caster's texture, its cutoff and its UV scale. */
|
|
15
|
+
/** A cutout caster's texture, its cutoff and its UV scale and offset. */
|
|
16
16
|
export interface CutoutCaster<Texture> {
|
|
17
17
|
readonly albedo: Texture;
|
|
18
18
|
readonly cutoff: number;
|
|
19
19
|
readonly u: number;
|
|
20
20
|
readonly v: number;
|
|
21
|
+
readonly uOffset: number;
|
|
22
|
+
readonly vOffset: number;
|
|
21
23
|
}
|
|
22
24
|
/** The cutout `material` casts, or null for an ordinary caster. */
|
|
23
25
|
export declare function cutoutOf<Texture>(material: SurfaceMaterial<Texture> | null | undefined): CutoutCaster<Texture> | null;
|
|
@@ -3,6 +3,8 @@ const answer = {
|
|
|
3
3
|
cutoff: 0,
|
|
4
4
|
u: 1,
|
|
5
5
|
v: 1,
|
|
6
|
+
uOffset: 0,
|
|
7
|
+
vOffset: 0,
|
|
6
8
|
};
|
|
7
9
|
/** The cutout `material` casts, or null for an ordinary caster. */
|
|
8
10
|
export function cutoutOf(material) {
|
|
@@ -16,5 +18,7 @@ export function cutoutOf(material) {
|
|
|
16
18
|
answer.cutoff = cutoff;
|
|
17
19
|
answer.u = material.uScale ?? 1;
|
|
18
20
|
answer.v = material.vScale ?? 1;
|
|
21
|
+
answer.uOffset = material.uOffset ?? 0;
|
|
22
|
+
answer.vOffset = material.vOffset ?? 0;
|
|
19
23
|
return answer;
|
|
20
24
|
}
|
package/dist/render/decalPass.js
CHANGED
|
@@ -4,6 +4,7 @@ import { DEPTH_01_TO_CLIP } from './lightVolumeDraw.js';
|
|
|
4
4
|
import { compileProgram, uniformLocations } from './shader.js';
|
|
5
5
|
import { DECAL_PROJECT_FRAG } from './shaders/decalProject.js';
|
|
6
6
|
import { FULLSCREEN_VERT } from './shaders/fullscreen.js';
|
|
7
|
+
import { blendKeeping } from './sceneCoverage.js';
|
|
7
8
|
/**
|
|
8
9
|
* Drawn decals on WebGL2: one scissored triangle per mark, multiplied into the finished scene.
|
|
9
10
|
*
|
|
@@ -54,7 +55,7 @@ export class DecalPass {
|
|
|
54
55
|
gl.depthMask(false);
|
|
55
56
|
gl.disable(gl.CULL_FACE);
|
|
56
57
|
gl.enable(gl.BLEND);
|
|
57
|
-
|
|
58
|
+
blendKeeping(gl, gl.ZERO, gl.SRC_COLOR);
|
|
58
59
|
gl.enable(gl.SCISSOR_TEST);
|
|
59
60
|
/* Unit 0, chosen rather than inherited: `bindTexture` binds to whichever unit is active, and
|
|
60
61
|
the renderer leaves that wherever its last pass finished. `OitPass` records what leaving it
|
|
@@ -4,6 +4,7 @@ import { DEPTH_01_TO_CLIP } from './lightVolumeDraw.js';
|
|
|
4
4
|
import { compileProgram, uniformLocations } from './shader.js';
|
|
5
5
|
import { FULLSCREEN_VERT } from './shaders/fullscreen.js';
|
|
6
6
|
import { MEDIUM_FRAG, MEDIUM_UPSAMPLE_FRAG } from './shaders/globalMedium.js';
|
|
7
|
+
import { blendCovering, blendTransmitting } from './sceneCoverage.js';
|
|
7
8
|
/**
|
|
8
9
|
* The global participating medium on WebGL2: a half-res march, then a depth-aware composite.
|
|
9
10
|
*
|
|
@@ -200,7 +201,7 @@ export class GlobalMediumPass {
|
|
|
200
201
|
gl.depthMask(false);
|
|
201
202
|
gl.disable(gl.CULL_FACE);
|
|
202
203
|
gl.enable(gl.BLEND);
|
|
203
|
-
|
|
204
|
+
blendTransmitting(gl, gl.ONE, gl.SRC_ALPHA);
|
|
204
205
|
gl.useProgram(this.upsample);
|
|
205
206
|
gl.bindVertexArray(this.vao);
|
|
206
207
|
gl.activeTexture(gl.TEXTURE0);
|
|
@@ -220,7 +221,7 @@ export class GlobalMediumPass {
|
|
|
220
221
|
gl.activeTexture(gl.TEXTURE0);
|
|
221
222
|
gl.bindTexture(gl.TEXTURE_2D, null);
|
|
222
223
|
/* Back to the blend every other pass in this renderer assumes it inherits. */
|
|
223
|
-
|
|
224
|
+
blendCovering(gl, gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA);
|
|
224
225
|
gl.disable(gl.BLEND);
|
|
225
226
|
gl.depthMask(true);
|
|
226
227
|
gl.enable(gl.DEPTH_TEST);
|
package/dist/render/lineBatch.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { compileProgram, uniformLocations } from './shader.js';
|
|
2
2
|
import { LINE_FRAG, LINE_VERT } from './shaders/line.js';
|
|
3
3
|
import { buildSegmentQuads, expandLineSegments } from './segmentQuads.js';
|
|
4
|
+
import { blendCovering, blendKeeping } from './sceneCoverage.js';
|
|
4
5
|
/**
|
|
5
6
|
* One draw call for a polyline with a real width — an audio waveform, a trail, a plotted
|
|
6
7
|
* curve, anything that is a stroke rather than a surface.
|
|
@@ -96,7 +97,10 @@ export class LineBatch {
|
|
|
96
97
|
if (segmentCount === 0)
|
|
97
98
|
return;
|
|
98
99
|
gl.enable(gl.BLEND);
|
|
99
|
-
|
|
100
|
+
if (additive)
|
|
101
|
+
blendKeeping(gl, gl.SRC_ALPHA, gl.ONE);
|
|
102
|
+
else
|
|
103
|
+
blendCovering(gl, gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA);
|
|
100
104
|
gl.depthMask(false);
|
|
101
105
|
// The quad billboards toward the view direction the same way a bolt's does — see
|
|
102
106
|
// `line.ts`'s own `cross(dir, view)` — so its winding can flip with the camera exactly
|
package/dist/render/oitPass.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { compileProgram, uniformLocations } from './shader.js';
|
|
2
2
|
import { FULLSCREEN_VERT } from './shaders/fullscreen.js';
|
|
3
3
|
import { OIT_RESOLVE_FRAG } from './shaders/oitResolve.js';
|
|
4
|
+
import { blendTransmitting } from './sceneCoverage.js';
|
|
4
5
|
/**
|
|
5
6
|
* Order-independent transparency on WebGL2: two targets, two passes over the same geometry, and a
|
|
6
7
|
* resolve.
|
|
@@ -163,7 +164,7 @@ export class OitPass {
|
|
|
163
164
|
gl.depthMask(false);
|
|
164
165
|
gl.disable(gl.CULL_FACE);
|
|
165
166
|
gl.enable(gl.BLEND);
|
|
166
|
-
|
|
167
|
+
blendTransmitting(gl, gl.ONE_MINUS_SRC_ALPHA, gl.SRC_ALPHA);
|
|
167
168
|
gl.useProgram(this.program);
|
|
168
169
|
gl.activeTexture(gl.TEXTURE0);
|
|
169
170
|
gl.bindTexture(gl.TEXTURE_2D, this.accum);
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { compileProgram, uniformLocations } from './shader.js';
|
|
2
2
|
import { particleShaders } from './particleMaterial.js';
|
|
3
3
|
import { PARTICLE_BLADES, PARTICLE_INDICES, PARTICLE_VERTS } from './shaders/particle.js';
|
|
4
|
+
import { blendCovering, blendKeeping } from './sceneCoverage.js';
|
|
4
5
|
/** Corners of one blade, as a triangle-strip-friendly quad. */
|
|
5
6
|
const CORNERS = [
|
|
6
7
|
[-1, -1],
|
|
@@ -138,9 +139,9 @@ export class ParticleBatch {
|
|
|
138
139
|
return;
|
|
139
140
|
gl.enable(gl.BLEND);
|
|
140
141
|
if (this.blend === 'additive')
|
|
141
|
-
|
|
142
|
+
blendKeeping(gl, gl.SRC_ALPHA, gl.ONE);
|
|
142
143
|
else
|
|
143
|
-
|
|
144
|
+
blendCovering(gl, gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA);
|
|
144
145
|
gl.depthMask(false);
|
|
145
146
|
gl.disable(gl.CULL_FACE);
|
|
146
147
|
gl.bindVertexArray(this.vao);
|
|
@@ -19,7 +19,11 @@ import type { Vec3 } from '../math/color.ts';
|
|
|
19
19
|
export interface ParticleInstances {
|
|
20
20
|
/** World position, three floats each. */
|
|
21
21
|
positions: Float32Array;
|
|
22
|
-
/**
|
|
22
|
+
/**
|
|
23
|
+
* Metres from the centre to an edge, a half-width: the quad spans twice this. Then radians of
|
|
24
|
+
* roll about the particle's own axis. This said metres across until 4.8.3, and a caller who took
|
|
25
|
+
* it at its word drew every particle twice as wide as meant.
|
|
26
|
+
*/
|
|
23
27
|
sizes: Float32Array;
|
|
24
28
|
spins: Float32Array;
|
|
25
29
|
/** Linear colour, three floats each. */
|
|
@@ -54,7 +58,7 @@ export interface ParticlePoolOptions {
|
|
|
54
58
|
capacity: number;
|
|
55
59
|
/** Seconds a particle lives. */
|
|
56
60
|
lifeSec: number;
|
|
57
|
-
/** Size at birth and at death,
|
|
61
|
+
/** Size at birth and at death: metres from the centre to an edge, so the quad is twice as wide. */
|
|
58
62
|
sizeStart: number;
|
|
59
63
|
sizeEnd: number;
|
|
60
64
|
/** Colour at birth and at death. */
|
|
@@ -3,6 +3,7 @@ import { compileProgram, uniformLocations } from './shader.js';
|
|
|
3
3
|
import { bindAtmosphere } from './atmosphere.js';
|
|
4
4
|
import { plumeShaders } from './plumeMaterial.js';
|
|
5
5
|
import { buildPlumeGeometry } from './plumeGeometry.js';
|
|
6
|
+
import { blendCovering, blendKeeping } from './sceneCoverage.js';
|
|
6
7
|
/* The shared count, so both backends step a plume's vertices the same way. */
|
|
7
8
|
const VERTS_PER_PLUME = PLUME_VERTS;
|
|
8
9
|
export class PlumeRenderer {
|
|
@@ -123,9 +124,9 @@ export class PlumeRenderer {
|
|
|
123
124
|
bindAtmosphere(gl, u, atmosphere, atmosphereCameraY, underwaterEnabled);
|
|
124
125
|
gl.enable(gl.BLEND);
|
|
125
126
|
if (this.blend === 'additive')
|
|
126
|
-
|
|
127
|
+
blendKeeping(gl, gl.SRC_ALPHA, gl.ONE);
|
|
127
128
|
else
|
|
128
|
-
|
|
129
|
+
blendCovering(gl, gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA);
|
|
129
130
|
gl.depthMask(false);
|
|
130
131
|
gl.disable(gl.CULL_FACE);
|
|
131
132
|
gl.bindVertexArray(this.vao);
|
|
@@ -14,8 +14,14 @@
|
|
|
14
14
|
* `aces` adds filmic tone mapping before that, rolling highlights off instead of
|
|
15
15
|
* clipping them flat. Together they are what three.js does by default, which is why a
|
|
16
16
|
* world ported from it looks wrong until this exists.
|
|
17
|
+
*
|
|
18
|
+
* `shoulder` keeps sRGB's midtones exactly and eases only the highlights: past 0.8 the brightest
|
|
19
|
+
* channel rolls toward 1 and the other two are scaled with it, so an overbright red spark stays
|
|
20
|
+
* red where `srgb` clips each channel and turns its core pink and wide. It is the curve a ported
|
|
21
|
+
* game's own resolve applied — for a port that has to match one — and it has none of ACES's toe or
|
|
22
|
+
* desaturation. See `HIGHLIGHT_SHOULDER_GLSL` for the arithmetic.
|
|
17
23
|
*/
|
|
18
|
-
export type OutputTransform = 'none' | 'srgb' | 'aces';
|
|
24
|
+
export type OutputTransform = 'none' | 'srgb' | 'aces' | 'shoulder';
|
|
19
25
|
/**
|
|
20
26
|
* The three settings whose accepted values are a closed set, written as one.
|
|
21
27
|
*
|
|
@@ -60,7 +66,8 @@ export interface RenderQuality {
|
|
|
60
66
|
* chooses is a look rather than a standard — three.js uses 1/0.6 and says so. This
|
|
61
67
|
* engine holds no opinion and takes the number from whoever is authoring the world, so
|
|
62
68
|
* that matching another renderer, or simply preferring a different grade, is a setting
|
|
63
|
-
* rather than a patch.
|
|
69
|
+
* rather than a patch. Scales into `aces` and into `shoulder`, the two curves; ignored by
|
|
70
|
+
* `none` and `srgb`, which have none.
|
|
64
71
|
*/
|
|
65
72
|
readonly outputExposure: number;
|
|
66
73
|
/**
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* What a blended draw into the scene does to its alpha, which holds how much of each pixel is still
|
|
3
|
+
* the opaque surface — the share ambient occlusion is allowed to darken.
|
|
4
|
+
*
|
|
5
|
+
* **Occlusion is applied in the composite, from the depth the opaque world left**, after every
|
|
6
|
+
* translucent draw has landed, and a particle or a pane writes no depth: so a puff of smoke in front
|
|
7
|
+
* of a wall was darkened by the corner behind it. Reported from a game as walls and two figures
|
|
8
|
+
* standing behind a cloud showing through it as outlines, and nothing at all with occlusion off.
|
|
9
|
+
*
|
|
10
|
+
* So the scene's alpha is a running share. The frame clears it to 1 and an opaque draw writes 1,
|
|
11
|
+
* which is the whole of the surface; then each draw blended in front of it says which of three
|
|
12
|
+
* things it is, and the composite darkens `share` of the pixel and leaves the rest:
|
|
13
|
+
*
|
|
14
|
+
* - **It covers**: glass, smoke, water, a line, text in the world, a panel. The share falls by what
|
|
15
|
+
* the draw lets through, `share * (1 - a)`.
|
|
16
|
+
* - **It transmits**: the medium, the order-independent composite. Its alpha already *is* what gets
|
|
17
|
+
* through, `share * a`.
|
|
18
|
+
* - **It keeps**: light added to the surface or a layer of the surface itself — caustics, a light
|
|
19
|
+
* volume, a bolt, a wet film, a decal, a reflection. The share is unchanged.
|
|
20
|
+
*
|
|
21
|
+
* **What it gives up** is added light in front of an occluded corner: a glow or a spark adds to the
|
|
22
|
+
* pixel and keeps the share, so the composite darkens what it added along with the surface — which
|
|
23
|
+
* is how every blended draw behaved before this, and one multiply cannot tell the two apart. **What
|
|
24
|
+
* would make it wrong** is an opaque pass writing an alpha below 1: its surface would then take less
|
|
25
|
+
* occlusion than it should, which is the safe direction, and is the lit shader's own case at a
|
|
26
|
+
* cutout's edge or under a per-vertex alpha lane.
|
|
27
|
+
*
|
|
28
|
+
* **It survives the temporal passes.** A temporal resolve passes the frame's alpha through, since
|
|
29
|
+
* it is a fact about this frame and takes no history; a reconstruction reads it from the scene it
|
|
30
|
+
* rebuilt into the picture it shows, and the blended draws that land after it cover that picture.
|
|
31
|
+
*
|
|
32
|
+
* **One pipeline does not take part**: the GPU-driven pass blends its own surfaces into a target
|
|
33
|
+
* whose alpha is the mask its blit reads to tell a drawn pixel from an undrawn one, and that blit
|
|
34
|
+
* writes the share back to 1. Its panes still take the occlusion of what they cover. What would
|
|
35
|
+
* close it is a channel for that mask other than alpha.
|
|
36
|
+
*
|
|
37
|
+
* A contributed pass drawing something blended into the scene states the same thing for its own
|
|
38
|
+
* pipeline with `SCENE_ALPHA_COVERS`, `SCENE_ALPHA_TRANSMITS` or `SCENE_ALPHA_KEEPS`.
|
|
39
|
+
*/
|
|
40
|
+
/** A blended draw that covers the surface by its alpha: `share * (1 - a)`. */
|
|
41
|
+
export declare const SCENE_ALPHA_COVERS: GPUBlendComponent;
|
|
42
|
+
/** A draw whose alpha is what gets through it, a transmittance or a revealage: `share * a`. */
|
|
43
|
+
export declare const SCENE_ALPHA_TRANSMITS: GPUBlendComponent;
|
|
44
|
+
/** Light added to the surface, or a layer of it: the share unchanged. */
|
|
45
|
+
export declare const SCENE_ALPHA_KEEPS: GPUBlendComponent;
|
|
46
|
+
/**
|
|
47
|
+
* The same three on WebGL2, as the alpha half of `blendFuncSeparate` beside the colour half a
|
|
48
|
+
* caller already had. `src` and `dst` are the colour factors.
|
|
49
|
+
*/
|
|
50
|
+
export declare function blendCovering(gl: WebGL2RenderingContext, src: number, dst: number): void;
|
|
51
|
+
/** `SCENE_ALPHA_TRANSMITS` on WebGL2, beside the colour factors `src` and `dst`. */
|
|
52
|
+
export declare function blendTransmitting(gl: WebGL2RenderingContext, src: number, dst: number): void;
|
|
53
|
+
/** `SCENE_ALPHA_KEEPS` on WebGL2, beside the colour factors `src` and `dst`. */
|
|
54
|
+
export declare function blendKeeping(gl: WebGL2RenderingContext, src: number, dst: number): void;
|