reze-engine 0.61.3 → 0.61.5
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/dist/engine.d.ts +74 -9
- package/dist/engine.d.ts.map +1 -1
- package/dist/engine.js +260 -34
- package/dist/graph/presets/body.js +1 -1
- package/dist/graph/presets/cloth_rough.js +1 -1
- package/dist/graph/presets/cloth_smooth.js +1 -1
- package/dist/graph/presets/eye.d.ts.map +1 -1
- package/dist/graph/presets/eye.js +19 -0
- package/dist/graph/presets/face.js +1 -1
- package/dist/graph/presets/hair.js +1 -1
- package/dist/graph/presets/metal.js +1 -1
- package/dist/graph/presets/pool_floor.d.ts +3 -0
- package/dist/graph/presets/pool_floor.d.ts.map +1 -0
- package/dist/graph/presets/pool_floor.js +42 -0
- package/dist/graph/presets/water.d.ts +3 -0
- package/dist/graph/presets/water.d.ts.map +1 -0
- package/dist/graph/presets/water.js +31 -0
- package/dist/graph/registry.d.ts.map +1 -1
- package/dist/graph/registry.js +11 -0
- package/dist/graph/slots.d.ts.map +1 -1
- package/dist/graph/slots.js +19 -5
- package/dist/physics/autofit.d.ts +147 -0
- package/dist/physics/autofit.d.ts.map +1 -0
- package/dist/physics/autofit.js +501 -0
- package/dist/shaders/materials/common.d.ts.map +1 -1
- package/dist/shaders/materials/common.js +10 -9
- package/dist/shaders/materials/nodes.d.ts +1 -1
- package/dist/shaders/materials/nodes.d.ts.map +1 -1
- package/dist/shaders/materials/nodes.js +12 -0
- package/dist/shaders/passes/field-blit.d.ts +26 -0
- package/dist/shaders/passes/field-blit.d.ts.map +1 -0
- package/dist/shaders/passes/field-blit.js +65 -0
- package/dist/shaders/passes/ground-noise.d.ts +7 -0
- package/dist/shaders/passes/ground-noise.d.ts.map +1 -0
- package/dist/shaders/passes/ground-noise.js +88 -0
- package/dist/shaders/passes/particles.js +6 -6
- package/dist/shaders/passes/scene-contract.js +1 -1
- package/dist/shaders/passes/sim.d.ts +34 -0
- package/dist/shaders/passes/sim.d.ts.map +1 -0
- package/dist/shaders/passes/sim.js +169 -0
- package/dist/shaders/passes/subsurface.d.ts +34 -0
- package/dist/shaders/passes/subsurface.d.ts.map +1 -0
- package/dist/shaders/passes/subsurface.js +134 -0
- package/dist/shaders/scene-light-api.d.ts.map +1 -1
- package/dist/shaders/scene-light-api.js +11 -0
- package/dist/shaders/score-api.d.ts +10 -0
- package/dist/shaders/score-api.d.ts.map +1 -0
- package/dist/shaders/score-api.js +114 -0
- package/dist/shaders/shadow-api.d.ts +29 -0
- package/dist/shaders/shadow-api.d.ts.map +1 -0
- package/dist/shaders/shadow-api.js +112 -0
- package/package.json +1 -1
- package/src/engine.ts +275 -36
- package/src/graph/presets/body.ts +1 -1
- package/src/graph/presets/cloth_rough.ts +1 -1
- package/src/graph/presets/cloth_smooth.ts +1 -1
- package/src/graph/presets/eye.ts +19 -0
- package/src/graph/presets/face.ts +1 -1
- package/src/graph/presets/hair.ts +1 -1
- package/src/graph/presets/metal.ts +1 -1
- package/src/graph/registry.ts +11 -0
- package/src/graph/slots.ts +19 -5
- package/src/shaders/materials/common.ts +10 -9
- package/src/shaders/materials/nodes.ts +12 -0
- package/src/shaders/passes/particles.ts +6 -6
- package/src/shaders/passes/scene-contract.ts +1 -1
- package/src/shaders/passes/subsurface.ts +136 -0
- package/src/shaders/scene-light-api.ts +11 -0
- package/dist/_dbg-loader.mjs +0 -1100
- package/dist/physics-debug.d.ts +0 -30
- package/dist/physics-debug.d.ts.map +0 -1
- package/dist/physics-debug.js +0 -526
- package/dist/shaders/materials/body.d.ts +0 -2
- package/dist/shaders/materials/body.d.ts.map +0 -1
- package/dist/shaders/materials/body.js +0 -95
- package/dist/shaders/materials/cloth_rough.d.ts +0 -2
- package/dist/shaders/materials/cloth_rough.d.ts.map +0 -1
- package/dist/shaders/materials/cloth_rough.js +0 -69
- package/dist/shaders/materials/cloth_smooth.d.ts +0 -2
- package/dist/shaders/materials/cloth_smooth.d.ts.map +0 -1
- package/dist/shaders/materials/cloth_smooth.js +0 -61
- package/dist/shaders/materials/default.d.ts +0 -2
- package/dist/shaders/materials/default.d.ts.map +0 -1
- package/dist/shaders/materials/default.js +0 -43
- package/dist/shaders/materials/eye.d.ts +0 -2
- package/dist/shaders/materials/eye.d.ts.map +0 -1
- package/dist/shaders/materials/eye.js +0 -60
- package/dist/shaders/materials/face.d.ts +0 -2
- package/dist/shaders/materials/face.d.ts.map +0 -1
- package/dist/shaders/materials/face.js +0 -95
- package/dist/shaders/materials/hair.d.ts +0 -2
- package/dist/shaders/materials/hair.d.ts.map +0 -1
- package/dist/shaders/materials/hair.js +0 -90
- package/dist/shaders/materials/metal.d.ts +0 -2
- package/dist/shaders/materials/metal.d.ts.map +0 -1
- package/dist/shaders/materials/metal.js +0 -77
- package/dist/shaders/materials/mmd_classic.d.ts +0 -2
- package/dist/shaders/materials/mmd_classic.d.ts.map +0 -1
- package/dist/shaders/materials/mmd_classic.js +0 -66
- package/dist/shaders/materials/stockings.d.ts +0 -2
- package/dist/shaders/materials/stockings.d.ts.map +0 -1
- package/dist/shaders/materials/stockings.js +0 -122
- package/dist/shaders/passes/physics-debug.d.ts +0 -2
- package/dist/shaders/passes/physics-debug.d.ts.map +0 -1
- package/dist/shaders/passes/physics-debug.js +0 -69
package/dist/engine.js
CHANGED
|
@@ -42,6 +42,7 @@ import { boneOverlay, boneMarkerPositions, buildOverlayShapes, jointOverlay, lin
|
|
|
42
42
|
import { BLOOM_BLIT_SHADER_WGSL, BLOOM_DOWNSAMPLE_SHADER_WGSL, BLOOM_UPSAMPLE_SHADER_WGSL, } from "./shaders/passes/bloom";
|
|
43
43
|
import { AGX_LUT_GZ, AGX_LUT_SIZE } from "./shaders/agx-lut";
|
|
44
44
|
import { buildCompositeShader, EFFECT_SCENE_API, buildFieldShader, EFFECT_ANCHORS, EFFECT_SUBJECTS, EFFECT_TRAIL_BASE, EFFECT_TRAIL_SAMPLES, } from "./shaders/passes/composite";
|
|
45
|
+
import { SUBSURFACE_WGSL, SSS_SPIKE } from "./shaders/passes/subsurface";
|
|
45
46
|
import { buildParticleComputeShader, buildParticleRenderShader, PARTICLE_LIGHT_BINDING, PARTICLE_POINTS_BINDING, particleEntryPoints, PARTICLE_INDIRECT_BINDING, PARTICLE_INDIRECT_BYTES, PARTICLE_INDIRECT_DRAW_OFFSET, PARTICLE_STRIDE, } from "./shaders/passes/particles";
|
|
46
47
|
import { MAX_EFFECT_POINTS, POINTS_FLOATS } from "./shaders/points-api";
|
|
47
48
|
import { bonesWithPrefix, pointsData, writeBonePoint } from "./effect-points";
|
|
@@ -1139,6 +1140,22 @@ export class Engine {
|
|
|
1139
1140
|
/** Scene-clock reading when the current effect was installed. The effect's
|
|
1140
1141
|
* `time` is measured from here — see where it is written. */
|
|
1141
1142
|
this.compositeBloomView = null;
|
|
1143
|
+
// Screen-space subsurface scattering (passes/subsurface.ts). The scratch target
|
|
1144
|
+
// is the X pass's output and lives with the canvas size; the bind groups name
|
|
1145
|
+
// it and the HDR resolve, so a resize drops them and the next frame rebuilds.
|
|
1146
|
+
this.sssPipelineX = null;
|
|
1147
|
+
this.sssPipelineY = null;
|
|
1148
|
+
this.sssScratch = null;
|
|
1149
|
+
this.sssBindGroupX = null;
|
|
1150
|
+
this.sssBindGroupY = null;
|
|
1151
|
+
this.sssUniformX = null;
|
|
1152
|
+
this.sssUniformY = null;
|
|
1153
|
+
this.sssUniformData = new Float32Array(8);
|
|
1154
|
+
// Model pipelines' descriptors, and the single-sided twins built from them —
|
|
1155
|
+
// one per pipeline per view (see sidedPipeline).
|
|
1156
|
+
this.pipelineDescs = new WeakMap();
|
|
1157
|
+
this.singleSided = new WeakMap();
|
|
1158
|
+
this.singleSidedMirror = new WeakMap();
|
|
1142
1159
|
this.bloomBlitUniformData = new Float32Array(4);
|
|
1143
1160
|
this.bloomUpsampleUniformData = new Float32Array(4);
|
|
1144
1161
|
this.bloomMipCount = 0;
|
|
@@ -4764,6 +4781,129 @@ export class Engine {
|
|
|
4764
4781
|
/** The user's field mounts, drawn at half resolution for the composite to
|
|
4765
4782
|
* upsample. Runs the whole quad — uniform control flow, so effects may use
|
|
4766
4783
|
* derivatives freely, which the old inline path had to forbid. */
|
|
4784
|
+
/** Whether any installed style group routes through a `subsurface` node —
|
|
4785
|
+
* the scattering pass, and the depth it reads, cost nothing otherwise. */
|
|
4786
|
+
subsurfaceInUse() {
|
|
4787
|
+
for (const inst of this.modelInstances.values()) {
|
|
4788
|
+
for (const g of inst.styleGroups.values()) {
|
|
4789
|
+
if (g.group.materials.length && g.group.graph.nodes.some((n) => n.type === "subsurface"))
|
|
4790
|
+
return true;
|
|
4791
|
+
}
|
|
4792
|
+
}
|
|
4793
|
+
return false;
|
|
4794
|
+
}
|
|
4795
|
+
/**
|
|
4796
|
+
* ray-mmd's skin scattering: a horizontal then a vertical depth-aware blur of
|
|
4797
|
+
* the pixels the scene pass marked as skin (aux .b), the second written back
|
|
4798
|
+
* into the HDR resolve through a blend that keeps ray-mmd's per-channel share
|
|
4799
|
+
* of the unblurred picture. See passes/subsurface.ts.
|
|
4800
|
+
*/
|
|
4801
|
+
renderSubsurface(encoder) {
|
|
4802
|
+
if (!this.device || !this.subsurfaceInUse())
|
|
4803
|
+
return;
|
|
4804
|
+
if (!this.hdrResolveTexture || !this.maskResolveView || !this.depthReadView || !this.dofUniformBuffer)
|
|
4805
|
+
return;
|
|
4806
|
+
const w = this.hdrResolveTexture.width;
|
|
4807
|
+
const h = this.hdrResolveTexture.height;
|
|
4808
|
+
if (!this.sssPipelineX || !this.sssPipelineY) {
|
|
4809
|
+
const module = this.device.createShaderModule({ label: "subsurface scattering", code: SUBSURFACE_WGSL });
|
|
4810
|
+
this.sssPipelineX = this.device.createRenderPipeline({
|
|
4811
|
+
label: "subsurface blur x",
|
|
4812
|
+
layout: "auto",
|
|
4813
|
+
vertex: { module, entryPoint: "vs" },
|
|
4814
|
+
fragment: { module, entryPoint: "fsX", targets: [{ format: this.hdrFormat }] },
|
|
4815
|
+
primitive: { topology: "triangle-list" },
|
|
4816
|
+
});
|
|
4817
|
+
// result = blurred·(1 − spike) + original·spike, per channel: the
|
|
4818
|
+
// destination is the original, and the blend constant holds the spike.
|
|
4819
|
+
this.sssPipelineY = this.device.createRenderPipeline({
|
|
4820
|
+
label: "subsurface blur y",
|
|
4821
|
+
layout: "auto",
|
|
4822
|
+
vertex: { module, entryPoint: "vs" },
|
|
4823
|
+
fragment: {
|
|
4824
|
+
module,
|
|
4825
|
+
entryPoint: "fsY",
|
|
4826
|
+
targets: [
|
|
4827
|
+
{
|
|
4828
|
+
format: this.hdrFormat,
|
|
4829
|
+
blend: {
|
|
4830
|
+
color: { srcFactor: "one-minus-constant", dstFactor: "constant", operation: "add" },
|
|
4831
|
+
alpha: { srcFactor: "zero", dstFactor: "one", operation: "add" },
|
|
4832
|
+
},
|
|
4833
|
+
},
|
|
4834
|
+
],
|
|
4835
|
+
},
|
|
4836
|
+
primitive: { topology: "triangle-list" },
|
|
4837
|
+
});
|
|
4838
|
+
this.sssBindGroupX = null;
|
|
4839
|
+
this.sssBindGroupY = null;
|
|
4840
|
+
}
|
|
4841
|
+
if (!this.sssUniformX || !this.sssUniformY) {
|
|
4842
|
+
const make = (label) => this.device.createBuffer({ label, size: 32, usage: GPUBufferUsage.UNIFORM | GPUBufferUsage.COPY_DST });
|
|
4843
|
+
this.sssUniformX = make("subsurface uniforms x");
|
|
4844
|
+
this.sssUniformY = make("subsurface uniforms y");
|
|
4845
|
+
}
|
|
4846
|
+
if (!this.sssScratch || this.sssScratch.width !== w || this.sssScratch.height !== h) {
|
|
4847
|
+
this.sssScratch?.destroy();
|
|
4848
|
+
this.sssScratch = this.device.createTexture({
|
|
4849
|
+
label: "subsurface scratch",
|
|
4850
|
+
size: [w, h],
|
|
4851
|
+
format: this.hdrFormat,
|
|
4852
|
+
usage: GPUTextureUsage.RENDER_ATTACHMENT | GPUTextureUsage.TEXTURE_BINDING,
|
|
4853
|
+
});
|
|
4854
|
+
this.sssBindGroupX = null;
|
|
4855
|
+
this.sssBindGroupY = null;
|
|
4856
|
+
}
|
|
4857
|
+
if (!this.sssBindGroupX || !this.sssBindGroupY) {
|
|
4858
|
+
const mask = this.maskResolveView;
|
|
4859
|
+
const depth = this.depthReadView;
|
|
4860
|
+
const dof = this.dofUniformBuffer;
|
|
4861
|
+
const group = (pipeline, src, uniforms, label) => this.device.createBindGroup({
|
|
4862
|
+
label,
|
|
4863
|
+
layout: pipeline.getBindGroupLayout(0),
|
|
4864
|
+
entries: [
|
|
4865
|
+
{ binding: 0, resource: src.createView() },
|
|
4866
|
+
{ binding: 1, resource: mask },
|
|
4867
|
+
{ binding: 2, resource: depth },
|
|
4868
|
+
{ binding: 3, resource: { buffer: dof } },
|
|
4869
|
+
{ binding: 4, resource: { buffer: uniforms } },
|
|
4870
|
+
],
|
|
4871
|
+
});
|
|
4872
|
+
this.sssBindGroupX = group(this.sssPipelineX, this.hdrResolveTexture, this.sssUniformX, "subsurface x");
|
|
4873
|
+
this.sssBindGroupY = group(this.sssPipelineY, this.sssScratch, this.sssUniformY, "subsurface y");
|
|
4874
|
+
}
|
|
4875
|
+
const tanY = Math.tan(this.camera.fov * 0.5);
|
|
4876
|
+
const u = this.sssUniformData;
|
|
4877
|
+
u[2] = 0;
|
|
4878
|
+
u[3] = 0;
|
|
4879
|
+
u[4] = tanY * this.camera.aspect;
|
|
4880
|
+
u[5] = tanY;
|
|
4881
|
+
u[6] = w;
|
|
4882
|
+
u[7] = h;
|
|
4883
|
+
u[0] = 1;
|
|
4884
|
+
u[1] = 0;
|
|
4885
|
+
this.device.queue.writeBuffer(this.sssUniformX, 0, u);
|
|
4886
|
+
u[0] = 0;
|
|
4887
|
+
u[1] = 1;
|
|
4888
|
+
this.device.queue.writeBuffer(this.sssUniformY, 0, u);
|
|
4889
|
+
const px = encoder.beginRenderPass({
|
|
4890
|
+
label: "subsurface blur x",
|
|
4891
|
+
colorAttachments: [{ view: this.sssScratch.createView(), loadOp: "clear", clearValue: [0, 0, 0, 0], storeOp: "store" }],
|
|
4892
|
+
});
|
|
4893
|
+
px.setPipeline(this.sssPipelineX);
|
|
4894
|
+
px.setBindGroup(0, this.sssBindGroupX);
|
|
4895
|
+
px.draw(3);
|
|
4896
|
+
px.end();
|
|
4897
|
+
const py = encoder.beginRenderPass({
|
|
4898
|
+
label: "subsurface blur y",
|
|
4899
|
+
colorAttachments: [{ view: this.hdrResolveTexture.createView(), loadOp: "load", storeOp: "store" }],
|
|
4900
|
+
});
|
|
4901
|
+
py.setPipeline(this.sssPipelineY);
|
|
4902
|
+
py.setBindGroup(0, this.sssBindGroupY);
|
|
4903
|
+
py.setBlendConstant({ r: SSS_SPIKE[0], g: SSS_SPIKE[1], b: SSS_SPIKE[2], a: 0 });
|
|
4904
|
+
py.draw(3);
|
|
4905
|
+
py.end();
|
|
4906
|
+
}
|
|
4767
4907
|
renderFieldPass(encoder) {
|
|
4768
4908
|
// TWO PREDICATES, deliberately, and they are not interchangeable.
|
|
4769
4909
|
//
|
|
@@ -5781,7 +5921,7 @@ export class Engine {
|
|
|
5781
5921
|
}
|
|
5782
5922
|
createRenderPipeline(config) {
|
|
5783
5923
|
const targets = config.fragmentTargets ?? (config.fragmentTarget ? [config.fragmentTarget] : undefined);
|
|
5784
|
-
return this.
|
|
5924
|
+
return this.createModelPipeline({
|
|
5785
5925
|
label: config.label,
|
|
5786
5926
|
layout: config.layout,
|
|
5787
5927
|
vertex: {
|
|
@@ -5984,7 +6124,7 @@ export class Engine {
|
|
|
5984
6124
|
this.device.queue.writeTexture({ texture: this.fallbackMaterialTexture }, new Uint8Array([255, 255, 255, 255]), { bytesPerRow: 4 }, [1, 1]);
|
|
5985
6125
|
this.noFillBuffer = this.device.createBuffer({
|
|
5986
6126
|
label: "model light (none)",
|
|
5987
|
-
size:
|
|
6127
|
+
size: 48,
|
|
5988
6128
|
usage: GPUBufferUsage.UNIFORM,
|
|
5989
6129
|
});
|
|
5990
6130
|
// Generic shared-toon ramp: lit white down to a soft cool shadow tone with
|
|
@@ -6195,7 +6335,7 @@ export class Engine {
|
|
|
6195
6335
|
depthCompare: this.depthAhead,
|
|
6196
6336
|
},
|
|
6197
6337
|
};
|
|
6198
|
-
this.depthPrepassPipeline = this.
|
|
6338
|
+
this.depthPrepassPipeline = this.createModelPipeline({
|
|
6199
6339
|
label: "opaque depth prepass",
|
|
6200
6340
|
...prepassDesc,
|
|
6201
6341
|
fragment: {
|
|
@@ -6207,7 +6347,7 @@ export class Engine {
|
|
|
6207
6347
|
// The SOLID prime: same module, cutoff forced to exactly 1.0. Only texels
|
|
6208
6348
|
// whose blend ignores the destination may pre-claim depth in the
|
|
6209
6349
|
// transparent phase — see the override's note in depth-prepass.ts.
|
|
6210
|
-
this.solidPrepassPipeline = this.
|
|
6350
|
+
this.solidPrepassPipeline = this.createModelPipeline({
|
|
6211
6351
|
label: "transparent solid prepass",
|
|
6212
6352
|
...prepassDesc,
|
|
6213
6353
|
fragment: {
|
|
@@ -6223,7 +6363,7 @@ export class Engine {
|
|
|
6223
6363
|
// hair depth from ever claiming the pixels the see-through-hair pass needs
|
|
6224
6364
|
// the eye to survive on. (Bundle draws use the PASS's stencil reference;
|
|
6225
6365
|
// only pipeline/bind/vertex state resets across executeBundles.)
|
|
6226
|
-
this.hairPrimePipeline = this.
|
|
6366
|
+
this.hairPrimePipeline = this.createModelPipeline({
|
|
6227
6367
|
label: "hair depth prime",
|
|
6228
6368
|
...prepassDesc,
|
|
6229
6369
|
depthStencil: {
|
|
@@ -7035,6 +7175,10 @@ export class Engine {
|
|
|
7035
7175
|
usage: GPUTextureUsage.RENDER_ATTACHMENT | GPUTextureUsage.TEXTURE_BINDING,
|
|
7036
7176
|
});
|
|
7037
7177
|
this.maskResolveView = this.maskResolveTexture.createView();
|
|
7178
|
+
this.sssScratch?.destroy();
|
|
7179
|
+
this.sssScratch = null;
|
|
7180
|
+
this.sssBindGroupX = null;
|
|
7181
|
+
this.sssBindGroupY = null;
|
|
7038
7182
|
// The floor mirror's targets — FULL resolution, the same attachment
|
|
7039
7183
|
// contract and sample count as the scene pass, which is what lets the
|
|
7040
7184
|
// mirror bundles reuse every scene pipeline unchanged. Half res was the
|
|
@@ -8739,6 +8883,8 @@ export class Engine {
|
|
|
8739
8883
|
bindGroup: this.groundShadowBindGroup,
|
|
8740
8884
|
materialName: "Ground",
|
|
8741
8885
|
groupId: null,
|
|
8886
|
+
// The ground's own pipeline decides its culling.
|
|
8887
|
+
doubleSided: true,
|
|
8742
8888
|
// The ground belongs to no model instance, so it is not in the cull list —
|
|
8743
8889
|
// cullIndex -1 leaves renderGround unconditional. Its box is filled in
|
|
8744
8890
|
// anyway rather than left a lie for whoever reads this next.
|
|
@@ -9255,8 +9401,12 @@ export class Engine {
|
|
|
9255
9401
|
this.modelInstances.set(newName, inst);
|
|
9256
9402
|
}
|
|
9257
9403
|
/**
|
|
9258
|
-
* Light one model with more than the world gives it: a FILL, linear RGB
|
|
9259
|
-
*
|
|
9404
|
+
* Light one model with more than the world gives it: a FILL, linear RGB times
|
|
9405
|
+
* its surface colour, added after its material graph. Null takes it away again.
|
|
9406
|
+
*
|
|
9407
|
+
* After the graph, not in its ambient: an NPR ramp read an ambient fill as
|
|
9408
|
+
* light, so sliding it moved her shadows and flipped whole regions across a
|
|
9409
|
+
* hard step. Added after, it brightens her evenly and every shadow stays put.
|
|
9260
9410
|
*
|
|
9261
9411
|
* A game lights its stage and its characters apart. Aether Gazer's rooms are
|
|
9262
9412
|
* lit by their lamps and a near-black ambient, while its characters take a
|
|
@@ -9281,16 +9431,31 @@ export class Engine {
|
|
|
9281
9431
|
setModelSun(name, sun) {
|
|
9282
9432
|
return this.writeModelLight(name, 4, sun);
|
|
9283
9433
|
}
|
|
9284
|
-
/**
|
|
9285
|
-
*
|
|
9286
|
-
*
|
|
9434
|
+
/**
|
|
9435
|
+
* Light one model by the sky's AVERAGE rather than its shape: the world's
|
|
9436
|
+
* colour and brightness from every direction alike. False gives it the
|
|
9437
|
+
* directional sky back.
|
|
9438
|
+
*
|
|
9439
|
+
* For a cast under an anime look. An HDRI's sky is brighter and warmer one
|
|
9440
|
+
* way than another, and taken at each normal it shades a face with soft
|
|
9441
|
+
* realistic gradients that no toon ramp drew — PBR on an anime face. Aether
|
|
9442
|
+
* Gazer gives its characters one flat base light instead. World strength and
|
|
9443
|
+
* colour still reach her; only the shape is gone. A flat World is its own
|
|
9444
|
+
* average, so without an HDRI this changes nothing.
|
|
9445
|
+
*/
|
|
9446
|
+
setModelFlatSky(name, flat) {
|
|
9447
|
+
return this.writeModelLight(name, 8, flat ? new Vec3(1, 0, 0) : null);
|
|
9448
|
+
}
|
|
9449
|
+
/** One part of a model's light, at `at` (0 the fill, 4 the sun, 8 its
|
|
9450
|
+
* options) in its ModelLight; w says whether that part is set. All unset
|
|
9451
|
+
* releases the buffer and the model takes the shared zero stand-in again. */
|
|
9287
9452
|
writeModelLight(name, at, value) {
|
|
9288
9453
|
const inst = this.modelInstances.get(name);
|
|
9289
9454
|
if (!inst || !this.device)
|
|
9290
9455
|
return false;
|
|
9291
|
-
const light = inst.modelLight ?? new Float32Array(
|
|
9456
|
+
const light = inst.modelLight ?? new Float32Array(12);
|
|
9292
9457
|
light.set(value ? [value.x, value.y, value.z, 1] : [0, 0, 0, 0], at);
|
|
9293
|
-
const any = light[3] > 0 || light[7] > 0;
|
|
9458
|
+
const any = light[3] > 0 || light[7] > 0 || light[11] > 0;
|
|
9294
9459
|
if (!any) {
|
|
9295
9460
|
if (!inst.lightBuffer)
|
|
9296
9461
|
return true;
|
|
@@ -9307,7 +9472,7 @@ export class Engine {
|
|
|
9307
9472
|
if (!inst.lightBuffer) {
|
|
9308
9473
|
inst.lightBuffer = this.device.createBuffer({
|
|
9309
9474
|
label: `${name}: light`,
|
|
9310
|
-
size:
|
|
9475
|
+
size: 48,
|
|
9311
9476
|
usage: GPUBufferUsage.UNIFORM | GPUBufferUsage.COPY_DST,
|
|
9312
9477
|
});
|
|
9313
9478
|
inst.mainPerInstanceBindGroup = this.perInstanceBindGroup(name, inst.skinMatrixBuffer, inst.lightBuffer);
|
|
@@ -12430,6 +12595,7 @@ export class Engine {
|
|
|
12430
12595
|
groupId: null,
|
|
12431
12596
|
baseBindGroupEntries,
|
|
12432
12597
|
castsShadow,
|
|
12598
|
+
doubleSided: inst.isPlane || (mat.edgeFlag & 0x01) !== 0,
|
|
12433
12599
|
outline,
|
|
12434
12600
|
bounds,
|
|
12435
12601
|
cullIndex: -1,
|
|
@@ -13727,7 +13893,10 @@ export class Engine {
|
|
|
13727
13893
|
// The two real readers are both in the composite and both have their own
|
|
13728
13894
|
// flag above: linearDepth() feeds the DoF gather and the depth handed to a
|
|
13729
13895
|
// foreground mount. Nothing else binds depthTex at all.
|
|
13730
|
-
|
|
13896
|
+
// The scattering pass is the third reader: it rejects taps across a depth
|
|
13897
|
+
// step and scales its width by distance, so a scene wearing a subsurface
|
|
13898
|
+
// look stores depth for as long as it does.
|
|
13899
|
+
const depthRead = dofOn || this.effects.some((e) => e.hasForeground) || this.subsurfaceInUse();
|
|
13731
13900
|
this.renderPassDescriptor.depthStencilAttachment.depthStoreOp = depthRead ? "store" : "discard";
|
|
13732
13901
|
if (depthRead)
|
|
13733
13902
|
this.writeDepthOfFieldUniforms();
|
|
@@ -13927,6 +14096,9 @@ export class Engine {
|
|
|
13927
14096
|
// kept ribbons out of bloom.
|
|
13928
14097
|
this.drawTrails(pass, "camera");
|
|
13929
14098
|
pass.end();
|
|
14099
|
+
// Skin scattering, on the resolved scene and before anything reads it:
|
|
14100
|
+
// bloom should bleed the scattered skin, not the raw one.
|
|
14101
|
+
this.renderSubsurface(encoder);
|
|
13930
14102
|
// The field mounts, likewise: after the scene so foregrounds can read its
|
|
13931
14103
|
// depth, before the composite that samples both layers.
|
|
13932
14104
|
this.renderFieldPass(encoder);
|
|
@@ -14711,11 +14883,53 @@ export class Engine {
|
|
|
14711
14883
|
stencilWriteMask: 0xff,
|
|
14712
14884
|
};
|
|
14713
14885
|
}
|
|
14714
|
-
|
|
14886
|
+
const desc = {
|
|
14715
14887
|
...base,
|
|
14716
14888
|
fragment: { module, constants, targets: blend === "additive" ? this.sceneTargetsAdditive : this.sceneTargets },
|
|
14717
14889
|
depthStencil,
|
|
14718
|
-
}
|
|
14890
|
+
};
|
|
14891
|
+
return this.device.createRenderPipelineAsync(desc).then((pipeline) => {
|
|
14892
|
+
this.pipelineDescs.set(pipeline, desc);
|
|
14893
|
+
return pipeline;
|
|
14894
|
+
});
|
|
14895
|
+
}
|
|
14896
|
+
/** A pipeline whose descriptor is kept, so a single-sided twin can be built
|
|
14897
|
+
* from it on demand (see sidedPipeline). */
|
|
14898
|
+
createModelPipeline(desc) {
|
|
14899
|
+
const pipeline = this.device.createRenderPipeline(desc);
|
|
14900
|
+
this.pipelineDescs.set(pipeline, desc);
|
|
14901
|
+
return pipeline;
|
|
14902
|
+
}
|
|
14903
|
+
/**
|
|
14904
|
+
* The pipeline a draw should use once its double-sided flag is honoured.
|
|
14905
|
+
*
|
|
14906
|
+
* A single-sided material culls its back faces, as MMD does. PMX winds a front
|
|
14907
|
+
* face clockwise, which this engine sees as a BACK face — the eye's front cull
|
|
14908
|
+
* and the outline hull's back cull are the same fact — so the cull is "front"
|
|
14909
|
+
* for the camera and "back" in the mirror, whose reflection flips winding.
|
|
14910
|
+
*
|
|
14911
|
+
* Every pipeline that draws a material's surface goes through here, depth
|
|
14912
|
+
* primes included: a prime that kept a culled face would write depth for a
|
|
14913
|
+
* surface nobody sees and hide what is behind it. A pipeline that already
|
|
14914
|
+
* culls (the eye) is returned as it is.
|
|
14915
|
+
*/
|
|
14916
|
+
sidedPipeline(pipeline, draw, mirrored) {
|
|
14917
|
+
if (draw.doubleSided)
|
|
14918
|
+
return pipeline;
|
|
14919
|
+
const desc = this.pipelineDescs.get(pipeline);
|
|
14920
|
+
if (!desc || (desc.primitive?.cullMode ?? "none") !== "none")
|
|
14921
|
+
return pipeline;
|
|
14922
|
+
const cache = mirrored ? this.singleSidedMirror : this.singleSided;
|
|
14923
|
+
let twin = cache.get(pipeline);
|
|
14924
|
+
if (!twin) {
|
|
14925
|
+
twin = this.device.createRenderPipeline({
|
|
14926
|
+
...desc,
|
|
14927
|
+
label: `${desc.label ?? "pipeline"} (single-sided${mirrored ? ", mirror" : ""})`,
|
|
14928
|
+
primitive: { ...desc.primitive, cullMode: mirrored ? "back" : "front" },
|
|
14929
|
+
});
|
|
14930
|
+
cache.set(pipeline, twin);
|
|
14931
|
+
}
|
|
14932
|
+
return twin;
|
|
14719
14933
|
}
|
|
14720
14934
|
// Pipeline for a material draw call: its group's compiled pipeline when grouped, else
|
|
14721
14935
|
// the neutral base (ungrouped materials render the default graph). Transparent-bucket
|
|
@@ -14771,7 +14985,8 @@ export class Engine {
|
|
|
14771
14985
|
pass.setBindGroup(1, inst.mainPerInstanceBindGroup);
|
|
14772
14986
|
bound = true;
|
|
14773
14987
|
}
|
|
14774
|
-
const
|
|
14988
|
+
const mirrored = view.args === "mirror";
|
|
14989
|
+
const pipeline = this.sidedPipeline(this.pipelineForDrawCall(inst, draw, mirrored), draw, mirrored);
|
|
14775
14990
|
if (pipeline !== currentPipeline) {
|
|
14776
14991
|
pass.setPipeline(pipeline);
|
|
14777
14992
|
currentPipeline = pipeline;
|
|
@@ -14873,15 +15088,18 @@ export class Engine {
|
|
|
14873
15088
|
/** Depth-only prime of the hair's alpha-1 texels, stencil-fenced off the eye
|
|
14874
15089
|
* silhouette. See the note at its call site and hairPrimePipeline. */
|
|
14875
15090
|
drawHairDepthPrime(pass, inst, view) {
|
|
14876
|
-
let bound =
|
|
15091
|
+
let bound = null;
|
|
14877
15092
|
for (const draw of inst.drawCalls) {
|
|
14878
15093
|
if (draw.type !== "opaque" || !this.isHairDraw(inst, draw))
|
|
14879
15094
|
continue;
|
|
14880
|
-
|
|
14881
|
-
|
|
14882
|
-
pass.
|
|
14883
|
-
|
|
14884
|
-
|
|
15095
|
+
const pipeline = this.sidedPipeline(this.hairPrimePipeline, draw, view.args === "mirror");
|
|
15096
|
+
if (bound !== pipeline) {
|
|
15097
|
+
pass.setPipeline(pipeline);
|
|
15098
|
+
if (!bound) {
|
|
15099
|
+
pass.setBindGroup(0, view.perFrame);
|
|
15100
|
+
pass.setBindGroup(1, inst.mainPerInstanceBindGroup);
|
|
15101
|
+
}
|
|
15102
|
+
bound = pipeline;
|
|
14885
15103
|
}
|
|
14886
15104
|
pass.setBindGroup(2, draw.bindGroup);
|
|
14887
15105
|
this.issueDraw(pass, draw, view.args);
|
|
@@ -14943,6 +15161,7 @@ export class Engine {
|
|
|
14943
15161
|
else if (install && install.alphaMode !== "opaque")
|
|
14944
15162
|
continue;
|
|
14945
15163
|
}
|
|
15164
|
+
pipeline = this.sidedPipeline(pipeline, draw, view.args === "mirror");
|
|
14946
15165
|
if (bound !== pipeline) {
|
|
14947
15166
|
pass.setPipeline(pipeline);
|
|
14948
15167
|
// One layout for both prepass pipelines, so groups 0 and 1 carry over.
|
|
@@ -14976,15 +15195,18 @@ export class Engine {
|
|
|
14976
15195
|
drawTransparentSolidPrepass(pass, inst, view) {
|
|
14977
15196
|
if (inst.isStage)
|
|
14978
15197
|
return;
|
|
14979
|
-
let bound =
|
|
15198
|
+
let bound = null;
|
|
14980
15199
|
for (const draw of inst.drawCalls) {
|
|
14981
15200
|
if (draw.type !== "transparent")
|
|
14982
15201
|
continue;
|
|
14983
|
-
|
|
14984
|
-
|
|
14985
|
-
pass.
|
|
14986
|
-
|
|
14987
|
-
|
|
15202
|
+
const pipeline = this.sidedPipeline(this.solidPrepassPipeline, draw, view.args === "mirror");
|
|
15203
|
+
if (bound !== pipeline) {
|
|
15204
|
+
pass.setPipeline(pipeline);
|
|
15205
|
+
if (!bound) {
|
|
15206
|
+
pass.setBindGroup(0, view.perFrame);
|
|
15207
|
+
pass.setBindGroup(1, inst.mainPerInstanceBindGroup);
|
|
15208
|
+
}
|
|
15209
|
+
bound = pipeline;
|
|
14988
15210
|
}
|
|
14989
15211
|
pass.setBindGroup(2, draw.bindGroup);
|
|
14990
15212
|
this.issueDraw(pass, draw, view.args);
|
|
@@ -15035,9 +15257,10 @@ export class Engine {
|
|
|
15035
15257
|
for (const draw of inst.drawCalls) {
|
|
15036
15258
|
if (draw.type !== "opaque")
|
|
15037
15259
|
continue;
|
|
15038
|
-
const
|
|
15039
|
-
if (!
|
|
15260
|
+
const hairOverEyes = this.overEyesPipelineFor(inst, draw);
|
|
15261
|
+
if (!hairOverEyes)
|
|
15040
15262
|
continue;
|
|
15263
|
+
const overEyes = this.sidedPipeline(hairOverEyes, draw, view.args === "mirror");
|
|
15041
15264
|
if (!bound) {
|
|
15042
15265
|
pass.setBindGroup(0, view.perFrame);
|
|
15043
15266
|
pass.setBindGroup(1, inst.mainPerInstanceBindGroup);
|
|
@@ -15607,15 +15830,18 @@ Engine.HDR_FORMAT_OVERRIDE = null;
|
|
|
15607
15830
|
/** Stencil value stamped by eye draws so hair can stencil-test against it and
|
|
15608
15831
|
* alpha-blend a second pass over eye silhouette pixels (see-through-hair effect). */
|
|
15609
15832
|
Engine.STENCIL_EYE_VALUE = 1;
|
|
15610
|
-
/** Aux MRT alongside HDR color.
|
|
15833
|
+
/** Aux MRT alongside HDR color. Three channels:
|
|
15611
15834
|
* .r — bloom mask (1 = model geometry, 0 = ground; sampled by bloom blit to gate prefilter).
|
|
15612
15835
|
* .g — accumulated alpha (the channel that used to live in hdr.a before the HDR format
|
|
15613
15836
|
* switched to rg11b10ufloat, which has no alpha). Sampled by composite/bloom to
|
|
15614
15837
|
* un-premultiply color for tonemap and to produce the canvas-drawable alpha used by
|
|
15615
15838
|
* the premultiplied alphaMode compositor (so the page background still shows through
|
|
15616
15839
|
* cleared / edge-faded regions like before).
|
|
15617
|
-
*
|
|
15618
|
-
|
|
15840
|
+
* .b — subsurface strength, set by a graph's `subsurface` node and 0 everywhere
|
|
15841
|
+
* else; the scattering pass reads it to find skin (passes/subsurface.ts).
|
|
15842
|
+
* rgba8unorm at 4× MSAA is 16 bytes/texel; it never leaves tile memory (storeOp
|
|
15843
|
+
* discard), only its single-sample resolve does. */
|
|
15844
|
+
Engine.BLOOM_MASK_FORMAT = "rgba8unorm";
|
|
15619
15845
|
/**
|
|
15620
15846
|
* The master switch for the id attachment. OFF — and off having been proven
|
|
15621
15847
|
* to work, not off because it was never finished.
|
|
@@ -16,7 +16,7 @@ export const BODY_GRAPH = {
|
|
|
16
16
|
{ id: "tex_base", type: "mix/multiply", inputs: { fac: 1.0 } },
|
|
17
17
|
{ id: "geo", type: "geometry" },
|
|
18
18
|
{ id: "str", type: "shader_to_rgb_diffuse" },
|
|
19
|
-
{ id: "toon", type: "
|
|
19
|
+
{ id: "toon", type: "ramp_cardinal", inputs: { pos0: 0.2466, pos1: 0.3466 } },
|
|
20
20
|
{ id: "shadow_tint", type: "hue_sat", inputs: { hue: 0.5, saturation: 2.0, value: 0.3499999940395355, fac: 1.0 } },
|
|
21
21
|
{ id: "lit_tint", type: "hue_sat", inputs: { hue: 0.5, saturation: 1.5, value: 1.0, fac: 1.0 } },
|
|
22
22
|
{ id: "toon_color", type: "mix/blend" },
|
|
@@ -16,7 +16,7 @@ export const CLOTH_ROUGH_GRAPH = {
|
|
|
16
16
|
{ id: "tex_base", type: "mix/multiply", inputs: { fac: 1.0 } },
|
|
17
17
|
{ id: "geo", type: "geometry" },
|
|
18
18
|
{ id: "str", type: "shader_to_rgb_diffuse" },
|
|
19
|
-
{ id: "ramp_008", type: "
|
|
19
|
+
{ id: "ramp_008", type: "ramp_cardinal", inputs: { pos0: 0.2466, pos1: 0.3466 } },
|
|
20
20
|
{ id: "mix04_fac", type: "math/multiply", inputs: { b: 0.5 } },
|
|
21
21
|
{ id: "dark_tex", type: "hue_sat", inputs: { hue: 0.5, saturation: 1.0, value: 0.19999998807907104, fac: 1.0 } },
|
|
22
22
|
{ id: "mix_004", type: "mix/blend" },
|
|
@@ -16,7 +16,7 @@ export const CLOTH_SMOOTH_GRAPH = {
|
|
|
16
16
|
{ id: "tex_base", type: "mix/multiply", inputs: { fac: 1.0 } },
|
|
17
17
|
{ id: "geo", type: "geometry" },
|
|
18
18
|
{ id: "str", type: "shader_to_rgb_diffuse" },
|
|
19
|
-
{ id: "ramp_008", type: "
|
|
19
|
+
{ id: "ramp_008", type: "ramp_cardinal", inputs: { pos0: 0.2466, pos1: 0.3466 } },
|
|
20
20
|
{ id: "mix04_fac", type: "math/multiply", inputs: { b: 0.5 } },
|
|
21
21
|
{ id: "dark_tex", type: "hue_sat", inputs: { hue: 0.5, saturation: 1.0, value: 0.19999998807907104, fac: 1.0 } },
|
|
22
22
|
{ id: "mix_004", type: "mix/blend" },
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"eye.d.ts","sourceRoot":"","sources":["../../../src/graph/presets/eye.ts"],"names":[],"mappings":"AASA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,WAAW,CAAA;AAE5C,eAAO,MAAM,SAAS,EAAE,
|
|
1
|
+
{"version":3,"file":"eye.d.ts","sourceRoot":"","sources":["../../../src/graph/presets/eye.ts"],"names":[],"mappings":"AASA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,WAAW,CAAA;AAE5C,eAAO,MAAM,SAAS,EAAE,WAgDvB,CAAA"}
|
|
@@ -25,6 +25,17 @@ export const EYE_GRAPH = {
|
|
|
25
25
|
},
|
|
26
26
|
{ id: "emission", type: "emission", inputs: { strength: 1.5 } },
|
|
27
27
|
{ id: "add", type: "add_shader" },
|
|
28
|
+
// The iris glow follows the key round her head: full (1.5) lit from the
|
|
29
|
+
// front, easing to 0.2 of it lit from behind - a fixed 1.5 left the eyes
|
|
30
|
+
// bright white in a backlit face. head_basis.forward · L, half-Lambert,
|
|
31
|
+
// times the sun's shadow at the eye, so an eye in a shadowed face dims too.
|
|
32
|
+
{ id: "eye_hb", type: "head_basis" },
|
|
33
|
+
{ id: "eye_lt", type: "light" },
|
|
34
|
+
{ id: "eye_fl", type: "vector_math/dot" },
|
|
35
|
+
{ id: "eye_half", type: "math/multiply_add", inputs: { b: 0.5, c: 0.5 } },
|
|
36
|
+
{ id: "eye_lvl", type: "map_range", inputs: { from_min: 0.0, from_max: 1.0, to_min: 0.2, to_max: 1.0 } },
|
|
37
|
+
{ id: "eye_sh", type: "math/multiply" },
|
|
38
|
+
{ id: "eye_str", type: "math/multiply", inputs: { b: 1.5 } },
|
|
28
39
|
],
|
|
29
40
|
links: [
|
|
30
41
|
{ from: { node: "tex", socket: "color" }, to: { node: "tex_base", socket: "a" } },
|
|
@@ -33,6 +44,14 @@ export const EYE_GRAPH = {
|
|
|
33
44
|
{ from: { node: "tex_base", socket: "color" }, to: { node: "emission", socket: "color" } },
|
|
34
45
|
{ from: { node: "principled", socket: "color" }, to: { node: "add", socket: "a" } },
|
|
35
46
|
{ from: { node: "emission", socket: "color" }, to: { node: "add", socket: "b" } },
|
|
47
|
+
{ from: { node: "eye_hb", socket: "forward" }, to: { node: "eye_fl", socket: "a" } },
|
|
48
|
+
{ from: { node: "eye_lt", socket: "direction" }, to: { node: "eye_fl", socket: "b" } },
|
|
49
|
+
{ from: { node: "eye_fl", socket: "value" }, to: { node: "eye_half", socket: "a" } },
|
|
50
|
+
{ from: { node: "eye_half", socket: "value" }, to: { node: "eye_sh", socket: "a" } },
|
|
51
|
+
{ from: { node: "eye_lt", socket: "shadow" }, to: { node: "eye_sh", socket: "b" } },
|
|
52
|
+
{ from: { node: "eye_sh", socket: "value" }, to: { node: "eye_lvl", socket: "value" } },
|
|
53
|
+
{ from: { node: "eye_lvl", socket: "value" }, to: { node: "eye_str", socket: "a" } },
|
|
54
|
+
{ from: { node: "eye_str", socket: "value" }, to: { node: "emission", socket: "strength" } },
|
|
36
55
|
],
|
|
37
56
|
output: { node: "add", socket: "color" },
|
|
38
57
|
};
|
|
@@ -20,7 +20,7 @@ export const FACE_GRAPH = {
|
|
|
20
20
|
{ id: "geo", type: "geometry" },
|
|
21
21
|
// ── toon shading ──
|
|
22
22
|
{ id: "str", type: "shader_to_rgb_diffuse" },
|
|
23
|
-
{ id: "toon", type: "
|
|
23
|
+
{ id: "toon", type: "ramp_cardinal", inputs: { pos0: 0.2466, pos1: 0.3466 } },
|
|
24
24
|
{ id: "shadow_tint", type: "hue_sat", inputs: { hue: 0.46000000834465027, saturation: 2.0, value: 0.3499999940395355, fac: 1.0 } },
|
|
25
25
|
{ id: "lit_tint", type: "hue_sat", inputs: { hue: 0.46000000834465027, saturation: 1.600000023841858, value: 1.5, fac: 1.0 } },
|
|
26
26
|
{ id: "toon_color", type: "mix/blend" },
|
|
@@ -26,7 +26,7 @@ export const HAIR_GRAPH = {
|
|
|
26
26
|
{ id: "hs_002", type: "hue_sat", inputs: { hue: 0.48, saturation: 1.2, value: 0.7, fac: 1.0 } },
|
|
27
27
|
{ id: "hs_001", type: "hue_sat", inputs: { hue: 0.5, saturation: 1.5, value: 1.0, fac: 1.0 } },
|
|
28
28
|
{ id: "str", type: "shader_to_rgb_diffuse" },
|
|
29
|
-
{ id: "ramp_008", type: "
|
|
29
|
+
{ id: "ramp_008", type: "ramp_cardinal", inputs: { pos0: 0.2466, color0: [0, 0, 0, 1], pos1: 0.3466, color1: [1, 1, 1, 1] } },
|
|
30
30
|
{ id: "mix_004", type: "mix/blend" },
|
|
31
31
|
{ id: "bc", type: "bright_contrast", inputs: { bright: 0.1, contrast: 0.2 } },
|
|
32
32
|
{ id: "sep_n", type: "separate_xyz" },
|
|
@@ -18,7 +18,7 @@ export const METAL_GRAPH = {
|
|
|
18
18
|
{ id: "geo", type: "geometry" },
|
|
19
19
|
{ id: "tex_tint", type: "hue_sat", inputs: { hue: 0.5, saturation: 1.0, value: 0.800000011920929, fac: 1.0 } },
|
|
20
20
|
{ id: "str", type: "shader_to_rgb_diffuse" },
|
|
21
|
-
{ id: "ramp_008", type: "
|
|
21
|
+
{ id: "ramp_008", type: "ramp_cardinal", inputs: { pos0: 0.2466, pos1: 0.3466 } },
|
|
22
22
|
{ id: "mix04_fac", type: "math/multiply", inputs: { b: 0.5 } },
|
|
23
23
|
{ id: "dark_tex", type: "hue_sat", inputs: { hue: 0.5, saturation: 1.0, value: 0.19999998807907104, fac: 1.0 } },
|
|
24
24
|
{ id: "mix_004", type: "mix/blend" },
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"pool_floor.d.ts","sourceRoot":"","sources":["../../../src/graph/presets/pool_floor.ts"],"names":[],"mappings":"AAaA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,WAAW,CAAA;AAE5C,eAAO,MAAM,gBAAgB,EAAE,WA6B9B,CAAA"}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
// Pool floor — the tiled bottom seen through water, with caustics on it.
|
|
2
|
+
//
|
|
3
|
+
// The other half of a pool, and the half that does most of the work. A water
|
|
4
|
+
// surface can be tuned for a long time without the shot reading as a pool;
|
|
5
|
+
// putting the focused-light web on the FLOOR is what settles it, because that
|
|
6
|
+
// is the cue a photograph actually carries. It is a floor material, so it is
|
|
7
|
+
// applied to the tile under the water rather than to the water itself.
|
|
8
|
+
//
|
|
9
|
+
// Caustics land between the albedo and the shading deliberately: they are
|
|
10
|
+
// light, so they should still be dimmed by the floor's own shadow rather than
|
|
11
|
+
// glowing through it. That is the difference between water-focused sun and a
|
|
12
|
+
// decal.
|
|
13
|
+
export const POOL_FLOOR_GRAPH = {
|
|
14
|
+
version: 1,
|
|
15
|
+
name: "Pool floor",
|
|
16
|
+
tags: ["stage", "water", "pool"],
|
|
17
|
+
nodes: [
|
|
18
|
+
{ id: "tex", type: "texture" },
|
|
19
|
+
{ id: "mat", type: "material_diffuse" },
|
|
20
|
+
{ id: "base", type: "mix/multiply", inputs: { fac: 1.0 } },
|
|
21
|
+
{ id: "caust", type: "caustics" },
|
|
22
|
+
{
|
|
23
|
+
id: "principled",
|
|
24
|
+
type: "principled",
|
|
25
|
+
inputs: { metallic: 0.0, specular_ior_level: 0.4, roughness: 0.45, spec_clamp: 10.0, sheen_weight: 0.0, sheen_tint: 0.0 },
|
|
26
|
+
},
|
|
27
|
+
],
|
|
28
|
+
links: [
|
|
29
|
+
{ from: { node: "tex", socket: "color" }, to: { node: "base", socket: "a" } },
|
|
30
|
+
{ from: { node: "mat", socket: "color" }, to: { node: "base", socket: "b" } },
|
|
31
|
+
{ from: { node: "base", socket: "color" }, to: { node: "caust", socket: "color" } },
|
|
32
|
+
{ from: { node: "caust", socket: "color" }, to: { node: "principled", socket: "base_color" } },
|
|
33
|
+
],
|
|
34
|
+
output: { node: "principled", socket: "color" },
|
|
35
|
+
params: [
|
|
36
|
+
{ id: "tint", label: "Light tint", target: { node: "caust", socket: "tint" }, kind: "color", default: [1.0, 1.0, 1.0] },
|
|
37
|
+
{ id: "scale", label: "Caustic scale", target: { node: "caust", socket: "scale" }, kind: "float", min: 0.01, max: 1, default: 0.12 },
|
|
38
|
+
{ id: "speed", label: "Caustic speed", target: { node: "caust", socket: "speed" }, kind: "float", min: 0, max: 1, default: 0.15 },
|
|
39
|
+
{ id: "strength", label: "Caustic strength", target: { node: "caust", socket: "strength" }, kind: "float", min: 0, max: 3, default: 0.6 },
|
|
40
|
+
{ id: "fade", label: "Depth fade", target: { node: "caust", socket: "fade" }, kind: "float", min: 1, max: 200, default: 30 },
|
|
41
|
+
],
|
|
42
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"water.d.ts","sourceRoot":"","sources":["../../../src/graph/presets/water.ts"],"names":[],"mappings":"AAeA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,WAAW,CAAA;AAE5C,eAAO,MAAM,WAAW,EAAE,WAgBzB,CAAA"}
|