@driftengine/core 4.8.6 → 4.8.7
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 +71 -0
- package/README.md +1 -1
- package/dist/index.d.ts +2 -2
- package/dist/index.js +1 -1
- package/dist/render/anisotropicLobe.d.ts +10 -0
- package/dist/render/anisotropicLobe.js +19 -0
- package/dist/render/areaLights.d.ts +35 -0
- package/dist/render/areaLights.js +35 -0
- package/dist/render/backend/api.d.ts +33 -3
- package/dist/render/backend/webgl2/renderer.d.ts +50 -14
- package/dist/render/backend/webgl2/renderer.js +141 -30
- package/dist/render/backend/webgl2/sceneCaptureTarget.d.ts +29 -0
- package/dist/render/backend/webgl2/sceneCaptureTarget.js +96 -0
- package/dist/render/backend/webgpu/particlePass.js +2 -1
- package/dist/render/backend/webgpu/renderer.d.ts +35 -1
- package/dist/render/backend/webgpu/renderer.js +155 -16
- package/dist/render/backend/webgpu/sceneCaptureTarget.d.ts +24 -0
- package/dist/render/backend/webgpu/sceneCaptureTarget.js +59 -0
- package/dist/render/backend/webgpu/shaders/clusterBin.wgsl.d.ts +1 -1
- package/dist/render/backend/webgpu/surfaceTexturePass.d.ts +4 -1
- package/dist/render/backend/webgpu/surfaceTexturePass.js +34 -8
- package/dist/render/backend/webgpu/waterPass.d.ts +1 -1
- package/dist/render/clusteredLights.d.ts +23 -5
- package/dist/render/clusteredLights.js +35 -8
- package/dist/render/driftLight/bake.d.ts +2 -0
- package/dist/render/driftLight/bake.js +1 -1
- package/dist/render/driftLight/falloff.d.ts +4 -2
- package/dist/render/driftLight/falloff.js +8 -2
- package/dist/render/gi/bounceLights.d.ts +3 -2
- package/dist/render/gi/bounceLights.js +8 -3
- package/dist/render/instanceCull.js +5 -0
- package/dist/render/instances.d.ts +19 -5
- package/dist/render/instances.js +9 -3
- package/dist/render/lightBudget.d.ts +12 -1
- package/dist/render/lightBudget.js +23 -3
- package/dist/render/lightmap.d.ts +23 -9
- package/dist/render/lightmap.js +40 -18
- package/dist/render/particlePool.d.ts +21 -0
- package/dist/render/particlePool.js +7 -0
- package/dist/render/pointLightSelection.d.ts +13 -0
- package/dist/render/pointLightSelection.js +3 -1
- package/dist/render/renderQuality.d.ts +8 -7
- package/dist/render/rendererHarness.js +3 -0
- package/dist/render/rgb9e5.d.ts +22 -0
- package/dist/render/rgb9e5.js +47 -0
- package/dist/render/sceneCapture.d.ts +29 -0
- package/dist/render/sceneCapture.js +36 -0
- package/dist/render/screenSpaceReflection.d.ts +27 -1
- package/dist/render/screenSpaceReflection.js +9 -0
- package/dist/render/shaders/bolt.d.ts +1 -1
- package/dist/render/shaders/bolt.js +7 -1
- package/dist/render/shaders/depth.js +11 -0
- package/dist/render/shaders/flat/anisotropic.d.ts +1 -1
- package/dist/render/shaders/flat/anisotropic.js +37 -3
- package/dist/render/shaders/flat/areaLight.d.ts +1 -1
- package/dist/render/shaders/flat/areaLight.js +46 -0
- package/dist/render/shaders/flat/glassTint.d.ts +3 -2
- package/dist/render/shaders/flat/glassTint.js +3 -1
- package/dist/render/shaders/flat/index.d.ts +5 -0
- package/dist/render/shaders/flat/index.js +16 -2
- package/dist/render/shaders/flat/lobes.d.ts +6 -0
- package/dist/render/shaders/flat/lobes.js +49 -43
- package/dist/render/shaders/flat/main.d.ts +1 -1
- package/dist/render/shaders/flat/main.js +57 -22
- package/dist/render/shaders/flat/models.d.ts +5 -2
- package/dist/render/shaders/flat/models.js +100 -16
- package/dist/render/shaders/flat/preamble.js +10 -3
- package/dist/render/shaders/generated/bolt.wgsl.d.ts +1 -1
- package/dist/render/shaders/generated/bolt.wgsl.js +1 -1
- package/dist/render/shaders/generated/depth.wgsl.d.ts +4 -4
- package/dist/render/shaders/generated/depth.wgsl.js +4 -4
- package/dist/render/shaders/generated/flat.wgsl.d.ts +16 -0
- package/dist/render/shaders/generated/flat.wgsl.js +54 -38
- package/dist/render/shaders/generated/ssrTrace.wgsl.d.ts +11 -6
- package/dist/render/shaders/generated/ssrTrace.wgsl.js +11 -6
- package/dist/render/shaders/generated/water.wgsl.d.ts +23 -16
- package/dist/render/shaders/generated/water.wgsl.js +23 -16
- package/dist/render/shaders/gi/probeBake.wgsl.js +9 -5
- package/dist/render/shaders/recon/motion.wgsl.d.ts +1 -1
- package/dist/render/shaders/recon/motion.wgsl.js +13 -2
- package/dist/render/shaders/ssrTrace.js +20 -1
- package/dist/render/shaders/water.d.ts +1 -1
- package/dist/render/shaders/water.js +12 -3
- package/dist/render/skinScatter.d.ts +3 -1
- package/dist/render/skinScatter.js +3 -1
- package/dist/render/ssrPass.js +1 -0
- package/dist/render/surfaceModel.d.ts +29 -8
- package/dist/render/surfaceModel.js +33 -9
- package/dist/render/surfaceTexture.d.ts +25 -1
- package/dist/render/surfaceTexture.js +27 -5
- package/package.json +6 -6
- package/src/index.ts +6 -2
- package/src/render/anisotropicLobe.ts +29 -0
- package/src/render/areaLights.ts +58 -0
- package/src/render/backend/api.ts +49 -2
- package/src/render/backend/webgl2/renderer.ts +192 -38
- package/src/render/backend/webgl2/sceneCaptureTarget.ts +130 -0
- package/src/render/backend/webgpu/particlePass.ts +2 -1
- package/src/render/backend/webgpu/renderer.ts +194 -19
- package/src/render/backend/webgpu/sceneCaptureTarget.ts +63 -0
- package/src/render/backend/webgpu/surfaceTexturePass.ts +44 -14
- package/src/render/clusteredLights.ts +41 -8
- package/src/render/driftLight/bake.ts +3 -1
- package/src/render/driftLight/falloff.ts +13 -2
- package/src/render/gi/bounceLights.ts +12 -3
- package/src/render/instanceCull.ts +4 -0
- package/src/render/instances.ts +26 -6
- package/src/render/lightBudget.ts +40 -2
- package/src/render/lightmap.ts +64 -23
- package/src/render/particlePool.ts +23 -0
- package/src/render/pointLightSelection.ts +20 -1
- package/src/render/renderQuality.ts +8 -7
- package/src/render/rendererHarness.ts +3 -0
- package/src/render/rgb9e5.ts +52 -0
- package/src/render/sceneCapture.ts +56 -0
- package/src/render/screenSpaceReflection.ts +33 -1
- package/src/render/shaders/bolt.ts +7 -1
- package/src/render/shaders/depth.ts +11 -0
- package/src/render/shaders/flat/anisotropic.ts +37 -3
- package/src/render/shaders/flat/areaLight.ts +46 -0
- package/src/render/shaders/flat/glassTint.ts +9 -2
- package/src/render/shaders/flat/index.ts +21 -2
- package/src/render/shaders/flat/lobes.ts +50 -43
- package/src/render/shaders/flat/main.ts +57 -22
- package/src/render/shaders/flat/models.ts +100 -16
- package/src/render/shaders/flat/preamble.ts +10 -3
- package/src/render/shaders/generated/bolt.wgsl.ts +1 -1
- package/src/render/shaders/generated/depth.wgsl.ts +4 -4
- package/src/render/shaders/generated/flat.wgsl.ts +54 -38
- package/src/render/shaders/generated/ssrTrace.wgsl.ts +11 -6
- package/src/render/shaders/generated/water.wgsl.ts +23 -16
- package/src/render/shaders/gi/probeBake.wgsl.ts +9 -5
- package/src/render/shaders/recon/motion.wgsl.ts +13 -2
- package/src/render/shaders/ssrTrace.ts +20 -1
- package/src/render/shaders/water.ts +12 -3
- package/src/render/skinScatter.ts +3 -1
- package/src/render/ssrPass.ts +1 -0
- package/src/render/surfaceModel.ts +47 -10
- package/src/render/surfaceTexture.ts +69 -9
|
@@ -41,7 +41,7 @@ import { createDriftLightUniforms, resolveDriftLight } from '../../driftLight/un
|
|
|
41
41
|
import { flatFrag, flatVert } from '../../shaders/flat/index.js';
|
|
42
42
|
import { skinEight } from '../../shaders/skinning.js';
|
|
43
43
|
import { MODEL_MAP_TEXTURE_UNIT, MORPH_DELTA_TEXTURE_UNIT, SKIN_PALETTE_TEXTURE_UNIT, } from '../../lightBudget.js';
|
|
44
|
-
import { MODEL_PARAM_FLOATS, eyeAxisInWorld, packModel } from '../../surfaceModel.js';
|
|
44
|
+
import { MODEL_PARAM_FLOATS, eyeAxisInWorld, packModel, skinProfileDistance, } from '../../surfaceModel.js';
|
|
45
45
|
import { lightmapRegionsBound, modelBound, skinHalfBound } from '../../shaders/flat/models.js';
|
|
46
46
|
import { lightmapTexels, warnRegionsWithoutPage, warnUnpagedLightmap, withLightmapUvs, } from '../../lightmap.js';
|
|
47
47
|
import { GlSkinScatter } from './skinScatter.js';
|
|
@@ -86,6 +86,8 @@ import { resolveRenderQuality } from '../../renderQuality.js';
|
|
|
86
86
|
import { drawingBufferSize } from '../../drawingBuffer.js';
|
|
87
87
|
import { Mesh, createMeshIncremental } from '../../mesh.js';
|
|
88
88
|
import { SurfaceTexture } from '../../surfaceTexture.js';
|
|
89
|
+
import { sceneCaptureTexels } from '../../sceneCapture.js';
|
|
90
|
+
import { GlSceneCaptureTarget } from './sceneCaptureTarget.js';
|
|
89
91
|
import { glCompressedFormats } from '../../glCompressed.js';
|
|
90
92
|
import { PlumeRenderer } from '../../plumeRenderer.js';
|
|
91
93
|
import { createScatterDeform, resolveScatterDeform } from '../../scatterDeform.js';
|
|
@@ -875,6 +877,15 @@ export class WebGL2Renderer {
|
|
|
875
877
|
warnedEnvironmentAmbient = false;
|
|
876
878
|
/** Whether a probe is being baked, so the passes that would recurse into one can tell. */
|
|
877
879
|
probePassActive = false;
|
|
880
|
+
/**
|
|
881
|
+
* Whether a scene capture is being drawn (`captureScene`): an off-screen pass like a bake, so the
|
|
882
|
+
* frame's own view, its refraction copy and skin's spread are left alone, but lit as the frame is.
|
|
883
|
+
*/
|
|
884
|
+
capturePassActive = false;
|
|
885
|
+
/** Each capture's framebuffer, beside the texture a material binds. See `createSceneCapture`. */
|
|
886
|
+
captureTargets = new WeakMap();
|
|
887
|
+
/** Said once, where a driver refuses to draw into a capture. */
|
|
888
|
+
warnedCapture = false;
|
|
878
889
|
/** Whether the bake under way lights its faces by the grid. `ProbeBakeOptions.bounce`. */
|
|
879
890
|
probeBounce = false;
|
|
880
891
|
/** The clip-control extension, or null where this context does not offer it. */
|
|
@@ -941,7 +952,8 @@ export class WebGL2Renderer {
|
|
|
941
952
|
* the camera's matrix unchanged and multiplies nothing.
|
|
942
953
|
*/
|
|
943
954
|
viewProjFor(camera) {
|
|
944
|
-
|
|
955
|
+
/* A capture's rows are flipped as a probe face's are, so it reads as an image does. */
|
|
956
|
+
const scene = this.probePassActive || this.capturePassActive
|
|
945
957
|
? cubeFaceProjection(this.cubeFaceViewProj, this.sceneMatrix(camera.viewProjection))
|
|
946
958
|
: this.sceneMatrix(camera.viewProjection);
|
|
947
959
|
/*
|
|
@@ -2545,6 +2557,76 @@ export class WebGL2Renderer {
|
|
|
2545
2557
|
createLightmap(page) {
|
|
2546
2558
|
return new SurfaceTexture(this.gl, lightmapTexels(page), { wrap: 'clamp', mipmap: false });
|
|
2547
2559
|
}
|
|
2560
|
+
/**
|
|
2561
|
+
* A texture the scene can be drawn into, for a material to show. See `RendererApi` and
|
|
2562
|
+
* `sceneCapture.ts`. Half floats where the frame keeps range, as a probe does.
|
|
2563
|
+
*/
|
|
2564
|
+
createSceneCapture(width, height) {
|
|
2565
|
+
const float = this.quality.hdrScene && this.gl.getExtension('EXT_color_buffer_float') !== null;
|
|
2566
|
+
const texture = new SurfaceTexture(this.gl, sceneCaptureTexels(width, height, float), {
|
|
2567
|
+
wrap: 'clamp',
|
|
2568
|
+
mipmap: false,
|
|
2569
|
+
});
|
|
2570
|
+
this.captureTargets.set(texture, new GlSceneCaptureTarget(this.gl, texture, width, height, this.sceneTarget?.sampleCount ?? 1, float));
|
|
2571
|
+
return texture;
|
|
2572
|
+
}
|
|
2573
|
+
/**
|
|
2574
|
+
* Draw the scene into a capture from `camera`: `draw` is handed the camera, its matrices updated
|
|
2575
|
+
* for the capture's shape, and submits the scene as it would to the screen. See `RendererApi`.
|
|
2576
|
+
*
|
|
2577
|
+
* **Radiance, as a probe bake stores it**: the output transform and exposure are held off for the
|
|
2578
|
+
* pass, so the frame grades a screen showing it once. No temporal jitter, which a capture has no
|
|
2579
|
+
* resolve to undo. Returns whether it drew: false for a texture that is not a capture, or one the
|
|
2580
|
+
* driver will not draw into.
|
|
2581
|
+
*/
|
|
2582
|
+
captureScene(capture, camera, clearColor, draw) {
|
|
2583
|
+
if (this.contextLost)
|
|
2584
|
+
return false;
|
|
2585
|
+
const target = this.captureTargets.get(capture);
|
|
2586
|
+
if (target === undefined)
|
|
2587
|
+
return false;
|
|
2588
|
+
if (!target.usable) {
|
|
2589
|
+
if (!this.warnedCapture) {
|
|
2590
|
+
this.warnedCapture = true;
|
|
2591
|
+
console.warn('Renderer: this driver will not draw into a scene capture, so it keeps its clear. ' +
|
|
2592
|
+
'Nothing else is affected.');
|
|
2593
|
+
}
|
|
2594
|
+
return false;
|
|
2595
|
+
}
|
|
2596
|
+
camera.updateMatrices(target.width / target.height);
|
|
2597
|
+
const { gl } = this;
|
|
2598
|
+
const jittering = this.temporalJittering;
|
|
2599
|
+
this.temporalJittering = false;
|
|
2600
|
+
this.capturePassActive = true;
|
|
2601
|
+
this.useFlatProgram();
|
|
2602
|
+
gl.uniform1i(this.flatUniforms['uOutputTransform'] ?? null, 0);
|
|
2603
|
+
gl.uniform1f(this.flatUniforms['uOutputExposure'] ?? null, 1);
|
|
2604
|
+
this.materials.dirty();
|
|
2605
|
+
target.begin(gl, clearColor, this.depthClear);
|
|
2606
|
+
/*
|
|
2607
|
+
* **Rows flipped, as a probe face's are.** A GL framebuffer's first row is its bottom and an
|
|
2608
|
+
* image's is its top, so a capture drawn upright would be shown upside down by every material
|
|
2609
|
+
* reading it as the image it is; `viewProjFor` negates clip y for the pass, and negating one axis
|
|
2610
|
+
* reverses every triangle's winding, which the front face follows. WebGPU's rows already run
|
|
2611
|
+
* the image's way.
|
|
2612
|
+
*/
|
|
2613
|
+
gl.frontFace(gl.CW);
|
|
2614
|
+
try {
|
|
2615
|
+
draw(camera);
|
|
2616
|
+
}
|
|
2617
|
+
finally {
|
|
2618
|
+
target.end(gl);
|
|
2619
|
+
gl.frontFace(gl.CCW);
|
|
2620
|
+
this.useFlatProgram();
|
|
2621
|
+
gl.uniform1i(this.flatUniforms['uOutputTransform'] ?? null, this.gradeCode());
|
|
2622
|
+
gl.uniform1f(this.flatUniforms['uOutputExposure'] ?? null, this.gradeExposure());
|
|
2623
|
+
this.materials.dirty();
|
|
2624
|
+
this.capturePassActive = false;
|
|
2625
|
+
this.temporalJittering = jittering;
|
|
2626
|
+
this.restoreViewport();
|
|
2627
|
+
}
|
|
2628
|
+
return true;
|
|
2629
|
+
}
|
|
2548
2630
|
/**
|
|
2549
2631
|
* Replace a surface texture's pixels, keeping the GPU object and its sampler state.
|
|
2550
2632
|
*
|
|
@@ -2566,6 +2648,8 @@ export class WebGL2Renderer {
|
|
|
2566
2648
|
if (this.contextLost)
|
|
2567
2649
|
return;
|
|
2568
2650
|
texture.dispose(this.gl);
|
|
2651
|
+
this.captureTargets.get(texture)?.dispose(this.gl);
|
|
2652
|
+
this.captureTargets.delete(texture);
|
|
2569
2653
|
}
|
|
2570
2654
|
/**
|
|
2571
2655
|
* Choose the surface texture the following `drawMesh` calls sample, or `null` for none.
|
|
@@ -2768,7 +2852,7 @@ export class WebGL2Renderer {
|
|
|
2768
2852
|
const model = material?.model;
|
|
2769
2853
|
warnUnpagedLightmap(model, material?.modelMap);
|
|
2770
2854
|
if (model?.kind === 'skin' && this.skinScatter !== null) {
|
|
2771
|
-
this.skinScatter.setProfile(model.profile, model
|
|
2855
|
+
this.skinScatter.setProfile(model.profile, skinProfileDistance(model, 0), skinProfileDistance(model, 1), skinProfileDistance(model, 2));
|
|
2772
2856
|
}
|
|
2773
2857
|
/*
|
|
2774
2858
|
* Written to every flat program, because material state persists across draws and a skinned
|
|
@@ -2892,7 +2976,10 @@ export class WebGL2Renderer {
|
|
|
2892
2976
|
if (modelMap !== null)
|
|
2893
2977
|
modelMap.bind(gl, MODEL_MAP_TEXTURE_UNIT);
|
|
2894
2978
|
gl.uniform1i(u['uModelMap'] ?? null, MODEL_MAP_TEXTURE_UNIT);
|
|
2895
|
-
packModel(material?.model ?? null, modelMap !== null, this.modelParams);
|
|
2979
|
+
packModel(material?.model ?? null, modelMap !== null, this.modelParams, material?.physicalSpecular === true);
|
|
2980
|
+
/* The first physical highlight asked for is when the lit programs start carrying one. */
|
|
2981
|
+
if (material?.physicalSpecular === true)
|
|
2982
|
+
this.askLit('PHYSICAL_SPECULAR');
|
|
2896
2983
|
gl.uniform4fv(u['uModelParams'] ?? null, this.modelParams);
|
|
2897
2984
|
/* The albedo's effects table, or the stand-in the shader reads as none. Before the early
|
|
2898
2985
|
return, so a material with no albedo still leaves a complete texture on the unit. */
|
|
@@ -2952,18 +3039,6 @@ export class WebGL2Renderer {
|
|
|
2952
3039
|
gl.activeTexture(gl.TEXTURE0 + SURFACE_TEXTURE_UNIT);
|
|
2953
3040
|
gl.bindTexture(gl.TEXTURE_2D_ARRAY, this.emptyTexture2DArray);
|
|
2954
3041
|
}
|
|
2955
|
-
/**
|
|
2956
|
-
* A pass-level scale over the grain the geometry itself declared, 0 to 1.
|
|
2957
|
-
*
|
|
2958
|
-
* Which surfaces are mineral is now `MeshData.grain`, stated per vertex through
|
|
2959
|
-
* `MeshBuilder.setGrain` — because it is a property of a material, and absent means none.
|
|
2960
|
-
* This stays as the override for the case that has no answer to state: an imported model
|
|
2961
|
-
* arrives with no grain information at all, and a caller drawing one decides for it.
|
|
2962
|
-
*
|
|
2963
|
-
* Pass state, like `setSurfaceTexture`, because a material covers many draws. The default
|
|
2964
|
-
* is 1 and `bindMeshPass` restores it, so it scales geometry that declared grain and
|
|
2965
|
-
* leaves geometry that did not exactly as it is.
|
|
2966
|
-
*/
|
|
2967
3042
|
/**
|
|
2968
3043
|
* The ambient the following draws take, as nine spherical-harmonic coefficients of incoming
|
|
2969
3044
|
* radiance — red, green and blue of each, twenty-seven numbers — or `null` for the frame's own:
|
|
@@ -2975,16 +3050,6 @@ export class WebGL2Renderer {
|
|
|
2975
3050
|
* only — a glossy surface still reflects the probes — and it is pass state like the material,
|
|
2976
3051
|
* which `bindMeshPass` clears, so a mirror or a probe bake drawn after does not inherit it.
|
|
2977
3052
|
*/
|
|
2978
|
-
/**
|
|
2979
|
-
* How much of the environment the following draws mirror, 0 to 1.
|
|
2980
|
-
*
|
|
2981
|
-
* Pass state beside the grain, and the counterpart to it: grain is a surface being uneven,
|
|
2982
|
-
* this is a surface being smooth enough to carry an image. Polished paint, glass, chrome
|
|
2983
|
-
* and still water want it; plaster and stone want none.
|
|
2984
|
-
*
|
|
2985
|
-
* Defaults to zero and bindMeshPass restores it, so a scene that never calls this looks
|
|
2986
|
-
* exactly as it did.
|
|
2987
|
-
*/
|
|
2988
3053
|
setAmbientSH(coefficients) {
|
|
2989
3054
|
if (this.contextLost)
|
|
2990
3055
|
return;
|
|
@@ -2996,6 +3061,16 @@ export class WebGL2Renderer {
|
|
|
2996
3061
|
}
|
|
2997
3062
|
this.useFlatProgram();
|
|
2998
3063
|
}
|
|
3064
|
+
/**
|
|
3065
|
+
* How much of the environment the following draws mirror, 0 to 1.
|
|
3066
|
+
*
|
|
3067
|
+
* Pass state beside the grain, and the counterpart to it: grain is a surface being uneven,
|
|
3068
|
+
* this is a surface being smooth enough to carry an image. Polished paint, glass, chrome
|
|
3069
|
+
* and still water want it; plaster and stone want none.
|
|
3070
|
+
*
|
|
3071
|
+
* Defaults to zero and bindMeshPass restores it, so a scene that never calls this looks
|
|
3072
|
+
* exactly as it did.
|
|
3073
|
+
*/
|
|
2999
3074
|
setSurfaceReflectivity(amount) {
|
|
3000
3075
|
if (this.contextLost)
|
|
3001
3076
|
return;
|
|
@@ -3043,6 +3118,18 @@ export class WebGL2Renderer {
|
|
|
3043
3118
|
console.warn('Renderer: setEnvironmentAmbient no longer does anything. A baked probe now lights a ' +
|
|
3044
3119
|
'surface through projected irradiance, and there is nothing left to tune.');
|
|
3045
3120
|
}
|
|
3121
|
+
/**
|
|
3122
|
+
* A pass-level scale over the grain the geometry itself declared, 0 to 1.
|
|
3123
|
+
*
|
|
3124
|
+
* Which surfaces are mineral is now `MeshData.grain`, stated per vertex through
|
|
3125
|
+
* `MeshBuilder.setGrain` — because it is a property of a material, and absent means none.
|
|
3126
|
+
* This stays as the override for the case that has no answer to state: an imported model
|
|
3127
|
+
* arrives with no grain information at all, and a caller drawing one decides for it.
|
|
3128
|
+
*
|
|
3129
|
+
* Pass state, like `setSurfaceTexture`, because a material covers many draws. The default
|
|
3130
|
+
* is 1 and `bindMeshPass` restores it, so it scales geometry that declared grain and
|
|
3131
|
+
* leaves geometry that did not exactly as it is.
|
|
3132
|
+
*/
|
|
3046
3133
|
setSurfaceGrain(amount) {
|
|
3047
3134
|
if (this.contextLost)
|
|
3048
3135
|
return;
|
|
@@ -3464,7 +3551,7 @@ export class WebGL2Renderer {
|
|
|
3464
3551
|
const glassy = resolveGlass(options.glass, this.glassScratch);
|
|
3465
3552
|
if (refraction <= 0 && !glassy)
|
|
3466
3553
|
return false;
|
|
3467
|
-
if (this.probePassActive || this.reflectionPassActive)
|
|
3554
|
+
if (this.probePassActive || this.reflectionPassActive || this.capturePassActive)
|
|
3468
3555
|
return false;
|
|
3469
3556
|
const snapshot = this.sceneTarget?.snapshotColor(false, glassy) ?? null;
|
|
3470
3557
|
if (snapshot === null)
|
|
@@ -3763,6 +3850,20 @@ export class WebGL2Renderer {
|
|
|
3763
3850
|
createParticles(capacity, options) {
|
|
3764
3851
|
return new ParticleBatch(this.gl, capacity, options);
|
|
3765
3852
|
}
|
|
3853
|
+
/**
|
|
3854
|
+
* Particles a caller's own compute wrote: WebGPU's alone, since this backend has no compute stage
|
|
3855
|
+
* to have written them (`computeSupported`). Said once, and nothing is drawn.
|
|
3856
|
+
*/
|
|
3857
|
+
drawDeviceParticles(_batch, _particles, _camera, _env, _timeSeconds) {
|
|
3858
|
+
if (this.warnedDeviceParticles)
|
|
3859
|
+
return;
|
|
3860
|
+
this.warnedDeviceParticles = true;
|
|
3861
|
+
console.warn('Renderer: drawDeviceParticles draws particles a compute shader wrote, and WebGL2 has no ' +
|
|
3862
|
+
'compute stage, so nothing is drawn. Check `computeSupported`, and draw a pool with ' +
|
|
3863
|
+
'`drawParticles` where it is false.');
|
|
3864
|
+
}
|
|
3865
|
+
/** Said once: see `drawDeviceParticles`. */
|
|
3866
|
+
warnedDeviceParticles = false;
|
|
3766
3867
|
/**
|
|
3767
3868
|
* Draw a live particle pool.
|
|
3768
3869
|
*
|
|
@@ -3820,7 +3921,10 @@ export class WebGL2Renderer {
|
|
|
3820
3921
|
const u = batch.uniforms;
|
|
3821
3922
|
const scene = this.sceneTarget;
|
|
3822
3923
|
let depth = null;
|
|
3823
|
-
if (batch.softDepth > 0 &&
|
|
3924
|
+
if (batch.softDepth > 0 &&
|
|
3925
|
+
!this.reflectionPassActive &&
|
|
3926
|
+
!this.probePassActive &&
|
|
3927
|
+
!this.capturePassActive) {
|
|
3824
3928
|
depth = scene?.snapshotDepth(true) ?? null;
|
|
3825
3929
|
if (depth === null && !this.warnedSpriteDepth) {
|
|
3826
3930
|
this.warnedSpriteDepth = true;
|
|
@@ -4692,6 +4796,7 @@ export class WebGL2Renderer {
|
|
|
4692
4796
|
lightFixtures: this.litOn.LIGHT_FIXTURES,
|
|
4693
4797
|
surfaceEffects: this.litOn.SURFACE_EFFECTS,
|
|
4694
4798
|
driftLight: this.litOn.DRIFT_LIGHT,
|
|
4799
|
+
physicalSpecular: this.litOn.PHYSICAL_SPECULAR,
|
|
4695
4800
|
maxLights: budget.maxLights,
|
|
4696
4801
|
maxAreaLights: budget.maxAreaLights,
|
|
4697
4802
|
});
|
|
@@ -6328,7 +6433,10 @@ export class WebGL2Renderer {
|
|
|
6328
6433
|
*/
|
|
6329
6434
|
if (this.framePresented)
|
|
6330
6435
|
this.temporalJittering = false;
|
|
6331
|
-
if (!this.reflectionPassActive &&
|
|
6436
|
+
if (!this.reflectionPassActive &&
|
|
6437
|
+
!this.probePassActive &&
|
|
6438
|
+
!this.capturePassActive &&
|
|
6439
|
+
!this.framePresented) {
|
|
6332
6440
|
this.frameViewProj = camera.viewProjection;
|
|
6333
6441
|
/* Copied rather than referenced: a caller moves its camera in place, and the decal pass
|
|
6334
6442
|
reads this at the end of the frame. */
|
|
@@ -6986,6 +7094,7 @@ export class WebGL2Renderer {
|
|
|
6986
7094
|
!this.skinScattered &&
|
|
6987
7095
|
!this.reflectionPassActive &&
|
|
6988
7096
|
!this.probePassActive &&
|
|
7097
|
+
!this.capturePassActive &&
|
|
6989
7098
|
!this.framePresented &&
|
|
6990
7099
|
skin.ready(scene));
|
|
6991
7100
|
}
|
|
@@ -7006,7 +7115,9 @@ export class WebGL2Renderer {
|
|
|
7006
7115
|
this.skinScattered = true;
|
|
7007
7116
|
if (!skin.pending)
|
|
7008
7117
|
return;
|
|
7009
|
-
if (this.reflectionPassActive || this.probePassActive ||
|
|
7118
|
+
if (this.reflectionPassActive || this.probePassActive || this.capturePassActive)
|
|
7119
|
+
return;
|
|
7120
|
+
if (projection === null)
|
|
7010
7121
|
return;
|
|
7011
7122
|
const depth = scene.snapshotDepth(true);
|
|
7012
7123
|
mat4.invert(this.skinInverseProjection, projection);
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import type { SurfaceTexture } from '../../surfaceTexture.ts';
|
|
2
|
+
/**
|
|
3
|
+
* A scene capture's framebuffers on WebGL2: the capture's own texture as the colour a pass resolves
|
|
4
|
+
* into, and a depth of its own, at the capture's size. See `sceneCapture.ts` for what a capture is.
|
|
5
|
+
*
|
|
6
|
+
* **At the frame's sample count, as WebGPU's is**, so a capture's edges are antialiased exactly as
|
|
7
|
+
* the frame's are: drawn into multisampled renderbuffers and blitted into the texture at the end,
|
|
8
|
+
* since a texture layer cannot be multisampled and sampled both. A count the driver refuses falls
|
|
9
|
+
* back to one, as the scene target's does, rather than drawing nothing.
|
|
10
|
+
*/
|
|
11
|
+
export declare class GlSceneCaptureTarget {
|
|
12
|
+
readonly width: number;
|
|
13
|
+
readonly height: number;
|
|
14
|
+
/** Where the capture resolves: its texture's layer, and a depth when it is drawn into directly. */
|
|
15
|
+
private readonly resolveFramebuffer;
|
|
16
|
+
private readonly resolveDepth;
|
|
17
|
+
/** Where it is drawn above one sample, or null. */
|
|
18
|
+
private multisampleFramebuffer;
|
|
19
|
+
private multisampleColor;
|
|
20
|
+
private multisampleDepth;
|
|
21
|
+
/** False where the driver refused the attachment, said once by the renderer. */
|
|
22
|
+
readonly usable: boolean;
|
|
23
|
+
constructor(gl: WebGL2RenderingContext, texture: SurfaceTexture, width: number, height: number, samples: number, float: boolean);
|
|
24
|
+
/** Bind where the pass draws, size the viewport and clear: colour to `clear`, depth to `depthClear`. */
|
|
25
|
+
begin(gl: WebGL2RenderingContext, clear: readonly [number, number, number], depthClear: number): void;
|
|
26
|
+
/** Resolve the multisampled colour into the texture, where there is any. */
|
|
27
|
+
end(gl: WebGL2RenderingContext): void;
|
|
28
|
+
dispose(gl: WebGL2RenderingContext): void;
|
|
29
|
+
}
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
import { glSceneDepthFormat } from '../../depthConvention.js';
|
|
2
|
+
/**
|
|
3
|
+
* A scene capture's framebuffers on WebGL2: the capture's own texture as the colour a pass resolves
|
|
4
|
+
* into, and a depth of its own, at the capture's size. See `sceneCapture.ts` for what a capture is.
|
|
5
|
+
*
|
|
6
|
+
* **At the frame's sample count, as WebGPU's is**, so a capture's edges are antialiased exactly as
|
|
7
|
+
* the frame's are: drawn into multisampled renderbuffers and blitted into the texture at the end,
|
|
8
|
+
* since a texture layer cannot be multisampled and sampled both. A count the driver refuses falls
|
|
9
|
+
* back to one, as the scene target's does, rather than drawing nothing.
|
|
10
|
+
*/
|
|
11
|
+
export class GlSceneCaptureTarget {
|
|
12
|
+
width;
|
|
13
|
+
height;
|
|
14
|
+
/** Where the capture resolves: its texture's layer, and a depth when it is drawn into directly. */
|
|
15
|
+
resolveFramebuffer;
|
|
16
|
+
resolveDepth;
|
|
17
|
+
/** Where it is drawn above one sample, or null. */
|
|
18
|
+
multisampleFramebuffer = null;
|
|
19
|
+
multisampleColor = null;
|
|
20
|
+
multisampleDepth = null;
|
|
21
|
+
/** False where the driver refused the attachment, said once by the renderer. */
|
|
22
|
+
usable;
|
|
23
|
+
constructor(gl, texture, width, height, samples, float) {
|
|
24
|
+
this.width = width;
|
|
25
|
+
this.height = height;
|
|
26
|
+
const depthFormat = glSceneDepthFormat(gl).internalFormat;
|
|
27
|
+
this.resolveFramebuffer = must(gl.createFramebuffer());
|
|
28
|
+
this.resolveDepth = must(gl.createRenderbuffer());
|
|
29
|
+
gl.bindRenderbuffer(gl.RENDERBUFFER, this.resolveDepth);
|
|
30
|
+
gl.renderbufferStorage(gl.RENDERBUFFER, depthFormat, width, height);
|
|
31
|
+
gl.bindFramebuffer(gl.FRAMEBUFFER, this.resolveFramebuffer);
|
|
32
|
+
texture.attachColor(gl);
|
|
33
|
+
gl.framebufferRenderbuffer(gl.FRAMEBUFFER, gl.DEPTH_ATTACHMENT, gl.RENDERBUFFER, this.resolveDepth);
|
|
34
|
+
this.usable = gl.checkFramebufferStatus(gl.FRAMEBUFFER) === gl.FRAMEBUFFER_COMPLETE;
|
|
35
|
+
const maximum = gl.getParameter(gl.MAX_SAMPLES);
|
|
36
|
+
const count = Math.max(1, Math.min(Math.round(samples), Number.isFinite(maximum) ? maximum : 1));
|
|
37
|
+
if (this.usable && count > 1) {
|
|
38
|
+
const color = must(gl.createRenderbuffer());
|
|
39
|
+
const depth = must(gl.createRenderbuffer());
|
|
40
|
+
gl.bindRenderbuffer(gl.RENDERBUFFER, color);
|
|
41
|
+
gl.renderbufferStorageMultisample(gl.RENDERBUFFER, count, float ? gl.RGBA16F : gl.RGBA8, width, height);
|
|
42
|
+
gl.bindRenderbuffer(gl.RENDERBUFFER, depth);
|
|
43
|
+
gl.renderbufferStorageMultisample(gl.RENDERBUFFER, count, depthFormat, width, height);
|
|
44
|
+
const framebuffer = must(gl.createFramebuffer());
|
|
45
|
+
gl.bindFramebuffer(gl.FRAMEBUFFER, framebuffer);
|
|
46
|
+
gl.framebufferRenderbuffer(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.RENDERBUFFER, color);
|
|
47
|
+
gl.framebufferRenderbuffer(gl.FRAMEBUFFER, gl.DEPTH_ATTACHMENT, gl.RENDERBUFFER, depth);
|
|
48
|
+
if (gl.checkFramebufferStatus(gl.FRAMEBUFFER) === gl.FRAMEBUFFER_COMPLETE) {
|
|
49
|
+
this.multisampleFramebuffer = framebuffer;
|
|
50
|
+
this.multisampleColor = color;
|
|
51
|
+
this.multisampleDepth = depth;
|
|
52
|
+
}
|
|
53
|
+
else {
|
|
54
|
+
gl.deleteFramebuffer(framebuffer);
|
|
55
|
+
gl.deleteRenderbuffer(color);
|
|
56
|
+
gl.deleteRenderbuffer(depth);
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
gl.bindRenderbuffer(gl.RENDERBUFFER, null);
|
|
60
|
+
gl.bindFramebuffer(gl.FRAMEBUFFER, null);
|
|
61
|
+
}
|
|
62
|
+
/** Bind where the pass draws, size the viewport and clear: colour to `clear`, depth to `depthClear`. */
|
|
63
|
+
begin(gl, clear, depthClear) {
|
|
64
|
+
gl.bindFramebuffer(gl.FRAMEBUFFER, this.multisampleFramebuffer ?? this.resolveFramebuffer);
|
|
65
|
+
gl.viewport(0, 0, this.width, this.height);
|
|
66
|
+
gl.clearColor(clear[0], clear[1], clear[2], 1);
|
|
67
|
+
gl.clearDepth(depthClear);
|
|
68
|
+
gl.depthMask(true);
|
|
69
|
+
gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
|
|
70
|
+
}
|
|
71
|
+
/** Resolve the multisampled colour into the texture, where there is any. */
|
|
72
|
+
end(gl) {
|
|
73
|
+
if (this.multisampleFramebuffer === null)
|
|
74
|
+
return;
|
|
75
|
+
gl.bindFramebuffer(gl.READ_FRAMEBUFFER, this.multisampleFramebuffer);
|
|
76
|
+
gl.bindFramebuffer(gl.DRAW_FRAMEBUFFER, this.resolveFramebuffer);
|
|
77
|
+
gl.blitFramebuffer(0, 0, this.width, this.height, 0, 0, this.width, this.height, gl.COLOR_BUFFER_BIT, gl.NEAREST);
|
|
78
|
+
gl.bindFramebuffer(gl.READ_FRAMEBUFFER, null);
|
|
79
|
+
gl.bindFramebuffer(gl.DRAW_FRAMEBUFFER, null);
|
|
80
|
+
}
|
|
81
|
+
dispose(gl) {
|
|
82
|
+
gl.deleteFramebuffer(this.resolveFramebuffer);
|
|
83
|
+
gl.deleteRenderbuffer(this.resolveDepth);
|
|
84
|
+
if (this.multisampleFramebuffer !== null)
|
|
85
|
+
gl.deleteFramebuffer(this.multisampleFramebuffer);
|
|
86
|
+
if (this.multisampleColor !== null)
|
|
87
|
+
gl.deleteRenderbuffer(this.multisampleColor);
|
|
88
|
+
if (this.multisampleDepth !== null)
|
|
89
|
+
gl.deleteRenderbuffer(this.multisampleDepth);
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
function must(made) {
|
|
93
|
+
if (made === null)
|
|
94
|
+
throw new Error('GlSceneCaptureTarget: a GL object could not be created');
|
|
95
|
+
return made;
|
|
96
|
+
}
|
|
@@ -2,6 +2,7 @@ import { DEPTH_COMPARE } from '../../depthConvention.js';
|
|
|
2
2
|
import { particleShaders } from '../../particleMaterial.js';
|
|
3
3
|
import { PARTICLE_BINDINGS } from '../../shaders/generated/particle.wgsl.js';
|
|
4
4
|
import { PARTICLE_BLADES, PARTICLE_INDICES, PARTICLE_VERTS } from '../../shaders/particle.js';
|
|
5
|
+
import { DEVICE_PARTICLE_FLOATS } from '../../particlePool.js';
|
|
5
6
|
import { packSpriteStream } from '../../particleBatch.js';
|
|
6
7
|
import { createParticleSort } from '../../particleSort.js';
|
|
7
8
|
import { DEPTH_FORMAT } from './flatPass.js';
|
|
@@ -45,7 +46,7 @@ const USAGE_UNIFORM_DST = 0x0040 | 0x0008;
|
|
|
45
46
|
* Floats per instance, in the order they are packed: position 3, size 1, spin 1, colour 3,
|
|
46
47
|
* alpha 1, age 1, seed 1, velocity 3, and a sprite's frame and half-height 2.
|
|
47
48
|
*/
|
|
48
|
-
const INSTANCE_FLOATS =
|
|
49
|
+
const INSTANCE_FLOATS = DEVICE_PARTICLE_FLOATS;
|
|
49
50
|
const INSTANCE_ATTRIBUTES = [
|
|
50
51
|
{ shaderLocation: 2, offset: 0, format: 'float32x3' },
|
|
51
52
|
{ shaderLocation: 3, offset: 12, format: 'float32' },
|
|
@@ -36,7 +36,7 @@ import { type GpuCaustics } from './causticsPass.ts';
|
|
|
36
36
|
import type { BoltSegments } from '../../boltPool.ts';
|
|
37
37
|
import type { LineSegments } from '../../linePoints.ts';
|
|
38
38
|
import { type ParticleBatchOptions } from '../../particleBatch.ts';
|
|
39
|
-
import type { ParticleInstances } from '../../particlePool.ts';
|
|
39
|
+
import type { DeviceParticles, ParticleInstances } from '../../particlePool.ts';
|
|
40
40
|
import type { PlumeOptions, PlumePlacement } from '../../plumeRenderer.ts';
|
|
41
41
|
import type { InstanceData } from '../../instancedMesh.ts';
|
|
42
42
|
import { type RenderQuality } from '../../renderQuality.ts';
|
|
@@ -1186,6 +1186,27 @@ export declare class WebGPURenderer implements RendererApi {
|
|
|
1186
1186
|
createSurfaceTextureArray(sources: readonly SurfaceSource[], options?: SurfaceTextureOptions): GpuSurfaceTexture;
|
|
1187
1187
|
/** A baked lightmap page, as a `lightmapModel` material's `modelMap`. See `RendererApi`. */
|
|
1188
1188
|
createLightmap(page: LightmapPage): GpuSurfaceTexture;
|
|
1189
|
+
/**
|
|
1190
|
+
* A texture the scene can be drawn into, for a material to show. See `RendererApi` and
|
|
1191
|
+
* `sceneCapture.ts`. At the world's own format, which under `hdrScene` is half floats.
|
|
1192
|
+
*/
|
|
1193
|
+
createSceneCapture(width: number, height: number): GpuSurfaceTexture;
|
|
1194
|
+
/** Each capture's attachments, beside the texture a material binds. */
|
|
1195
|
+
private readonly captureTargets;
|
|
1196
|
+
/**
|
|
1197
|
+
* Whether a scene capture is being drawn: an off-screen pass as a probe bake is, so the frame's
|
|
1198
|
+
* camera, jitter, motion, refraction copy, skin's spread and late routing are left alone, but
|
|
1199
|
+
* lit by the probes and DriftLight as the frame is. See `captureScene`.
|
|
1200
|
+
*/
|
|
1201
|
+
private capturePassActive;
|
|
1202
|
+
/**
|
|
1203
|
+
* Draw the scene into a capture from `camera`. See `RendererApi.captureScene`.
|
|
1204
|
+
*
|
|
1205
|
+
* **Its own encoder, as a probe bake's**: the frame's pass is set aside and restored, the rings
|
|
1206
|
+
* are marked and rewound around the submit, and the frame graph is flushed at both boundaries so
|
|
1207
|
+
* no recorded draw crosses one. **Radiance**: the output transform and exposure are held off.
|
|
1208
|
+
*/
|
|
1209
|
+
captureScene(capture: GpuSurfaceTexture, camera: Camera, clearColor: Vec3, draw: (camera: Camera) => void): boolean;
|
|
1189
1210
|
/**
|
|
1190
1211
|
* GPU textures an `updateSurfaceTexture` replaced, destroyed when the next frame begins.
|
|
1191
1212
|
*
|
|
@@ -1448,6 +1469,17 @@ export declare class WebGPURenderer implements RendererApi {
|
|
|
1448
1469
|
private gradeCode;
|
|
1449
1470
|
private gradeExposure;
|
|
1450
1471
|
drawParticles(batch: GpuParticles, data: ParticleInstances, camera: Camera, env: Environment, timeSeconds: number): void;
|
|
1472
|
+
/**
|
|
1473
|
+
* Draw particles a caller's own compute wrote, where they lie. See `DeviceParticles`.
|
|
1474
|
+
*
|
|
1475
|
+
* **The pool's draw with its instance stream swapped**: the same material, blend, flipbook, soft
|
|
1476
|
+
* edge and fog, reading the caller's buffer in place of the one the pool uploads into — so nothing
|
|
1477
|
+
* crosses back to the CPU. Not sorted, and a count past what the buffer holds is cut to it rather
|
|
1478
|
+
* than handed to a draw the device would refuse with the frame.
|
|
1479
|
+
*/
|
|
1480
|
+
drawDeviceParticles(batch: GpuParticles, particles: DeviceParticles, camera: Camera, env: Environment, timeSeconds: number): void;
|
|
1481
|
+
/** A pool's uniforms for this view, and its draw from `instances`: both entry points' tail. */
|
|
1482
|
+
private drawParticleInstances;
|
|
1451
1483
|
/**
|
|
1452
1484
|
* A sprite's flipbook and fades into its fragment block `f`, and the bind group holding the
|
|
1453
1485
|
* frame's depth copy its soft edge reads — built again only when that copy is replaced.
|
|
@@ -1767,6 +1799,8 @@ export declare class WebGPURenderer implements RendererApi {
|
|
|
1767
1799
|
private writeWind;
|
|
1768
1800
|
/** Refilled per frame rather than allocated; see `resolvePointLights`. */
|
|
1769
1801
|
private readonly lights;
|
|
1802
|
+
/** The fixed arm's rectangles as `uAreaLightSize` holds them. See `packAreaSizesAndDoors`. */
|
|
1803
|
+
private readonly areaSizesAndDoors;
|
|
1770
1804
|
/** Refilled per frame rather than allocated; see `resolveAtmosphere`. */
|
|
1771
1805
|
private readonly medium;
|
|
1772
1806
|
private readonly panelLayout;
|