@triforge/shader-core 0.2.0 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/bundle.js +88 -41
- package/dist/core/CompileContext.d.ts +2 -0
- package/dist/core/CompileContext.d.ts.map +1 -1
- package/dist/core/CompileContext.js +33 -13
- package/dist/core/OutputNode.d.ts.map +1 -1
- package/dist/core/OutputNode.js +1 -0
- package/dist/core/ShaderNode.d.ts +7 -0
- package/dist/core/ShaderNode.d.ts.map +1 -1
- package/dist/core/ShaderNode.js +20 -4
- package/dist/core/SocketType.d.ts +1 -1
- package/dist/core/SocketType.js +1 -1
- package/dist/nodes/output/MaterialOutput.js +2 -2
- package/dist/nodes/process/AddShader.js +3 -3
- package/dist/nodes/process/DiffuseBSDF.js +1 -1
- package/dist/nodes/process/Emission.js +1 -1
- package/dist/nodes/process/GlassBSDF.js +1 -1
- package/dist/nodes/process/GlossyBSDF.js +1 -1
- package/dist/nodes/process/MixShader.js +2 -2
- package/dist/nodes/process/PrincipledBSDF.d.ts +7 -0
- package/dist/nodes/process/PrincipledBSDF.d.ts.map +1 -1
- package/dist/nodes/process/PrincipledBSDF.js +17 -2
- package/dist/nodes/process/PrincipledHair.js +1 -1
- package/dist/nodes/process/PrincipledVolume.js +1 -1
- package/dist/nodes/process/RefractionBSDF.js +1 -1
- package/dist/nodes/process/ShaderScript.d.ts +2 -2
- package/dist/nodes/process/ShaderScript.d.ts.map +1 -1
- package/dist/nodes/process/ShaderScript.js +7 -3
- package/dist/nodes/process/ShaderToRGB.js +2 -2
- package/dist/nodes/process/SheenBSDF.js +1 -1
- package/dist/nodes/process/SpecularBSDF.js +1 -1
- package/dist/nodes/process/SubsurfaceScattering.js +1 -1
- package/dist/nodes/process/ToonBSDF.js +1 -1
- package/dist/nodes/process/TranslucentBSDF.js +1 -1
- package/dist/nodes/process/VolumeAbsorption.js +1 -1
- package/dist/nodes/process/VolumeScatter.js +1 -1
- package/dist/nodes/shader/RayPortal.js +1 -1
- package/package.json +1 -1
package/dist/bundle.js
CHANGED
|
@@ -84,6 +84,12 @@ var OutputSocket = class {
|
|
|
84
84
|
};
|
|
85
85
|
|
|
86
86
|
// src/core/ShaderNode.ts
|
|
87
|
+
function finiteOr(value, fallback) {
|
|
88
|
+
return Number.isFinite(value) ? value : fallback;
|
|
89
|
+
}
|
|
90
|
+
function sanitizeVec3(v) {
|
|
91
|
+
return [finiteOr(v[0], 0), finiteOr(v[1], 0), finiteOr(v[2], 0)];
|
|
92
|
+
}
|
|
87
93
|
function hexToRgb(hex) {
|
|
88
94
|
const h = hex.replace("#", "");
|
|
89
95
|
return [
|
|
@@ -133,6 +139,15 @@ var ShaderNode = class {
|
|
|
133
139
|
vertexInjections() {
|
|
134
140
|
return [];
|
|
135
141
|
}
|
|
142
|
+
/**
|
|
143
|
+
* Whether this node's contribution to the compiled material requires
|
|
144
|
+
* `transparent: true` on the resulting THREE.ShaderMaterial (e.g. a
|
|
145
|
+
* `shader`-socket alpha channel that can read below 1.0).
|
|
146
|
+
* Override in nodes that carry alpha semantics — see PrincipledBSDF.
|
|
147
|
+
*/
|
|
148
|
+
wantsTransparency() {
|
|
149
|
+
return false;
|
|
150
|
+
}
|
|
136
151
|
/**
|
|
137
152
|
* Returns the named output socket.
|
|
138
153
|
* Throws ShaderNodeError if the socket does not exist.
|
|
@@ -178,14 +193,14 @@ var ShaderNode = class {
|
|
|
178
193
|
if (type === "float" && typeof literal === "number") {
|
|
179
194
|
socket.uniformName = `u_${this.id}_${name}`;
|
|
180
195
|
this._uniformMap.set(name, socket.uniformName);
|
|
181
|
-
this.parameters[name] = literal;
|
|
196
|
+
this.parameters[name] = finiteOr(literal, 0);
|
|
182
197
|
}
|
|
183
198
|
if (type === "color" && !(value instanceof OutputSocket)) {
|
|
184
199
|
if (typeof literal === "string" || Array.isArray(literal)) {
|
|
185
200
|
socket.uniformName = `u_${this.id}_${name}`;
|
|
186
201
|
this._uniformMap.set(name, socket.uniformName);
|
|
187
202
|
this._colorParams.add(name);
|
|
188
|
-
this.parameters[name] = typeof literal === "string" ? hexToRgb(literal) : literal;
|
|
203
|
+
this.parameters[name] = typeof literal === "string" ? hexToRgb(literal) : sanitizeVec3(literal);
|
|
189
204
|
}
|
|
190
205
|
}
|
|
191
206
|
sockets[name] = socket;
|
|
@@ -217,7 +232,7 @@ var ShaderNode = class {
|
|
|
217
232
|
Object.defineProperty(this.parameters, paramName, {
|
|
218
233
|
get: () => uniforms[uniformName].value,
|
|
219
234
|
set: (v) => {
|
|
220
|
-
uniforms[uniformName].value = v;
|
|
235
|
+
uniforms[uniformName].value = sanitizeVec3(v);
|
|
221
236
|
},
|
|
222
237
|
enumerable: true,
|
|
223
238
|
configurable: true
|
|
@@ -226,7 +241,7 @@ var ShaderNode = class {
|
|
|
226
241
|
Object.defineProperty(this.parameters, paramName, {
|
|
227
242
|
get: () => uniforms[uniformName].value,
|
|
228
243
|
set: (v) => {
|
|
229
|
-
uniforms[uniformName].value = v;
|
|
244
|
+
uniforms[uniformName].value = finiteOr(v, 0);
|
|
230
245
|
},
|
|
231
246
|
enumerable: true,
|
|
232
247
|
configurable: true
|
|
@@ -289,7 +304,8 @@ var CompileContext = class _CompileContext {
|
|
|
289
304
|
vertexShader: this.buildVertexShader(),
|
|
290
305
|
fragmentShader: this.buildFragmentShader(),
|
|
291
306
|
uniforms: this._uniforms,
|
|
292
|
-
nodes: this.order
|
|
307
|
+
nodes: this.order,
|
|
308
|
+
needsTransparency: this.order.some((node) => node.wantsTransparency())
|
|
293
309
|
};
|
|
294
310
|
}
|
|
295
311
|
/**
|
|
@@ -309,12 +325,23 @@ var CompileContext = class _CompileContext {
|
|
|
309
325
|
resolveInput(socket) {
|
|
310
326
|
if (socket.connection) {
|
|
311
327
|
const varName = this.outputVar(socket.connection.node, socket.connection.name);
|
|
312
|
-
|
|
328
|
+
const fromType = socket.connection.type;
|
|
329
|
+
const toType = socket.type;
|
|
330
|
+
if (toType === "vector" && fromType !== "vector") {
|
|
313
331
|
return `(${varName}).xy`;
|
|
314
332
|
}
|
|
315
|
-
if (
|
|
333
|
+
if (toType === "color" && fromType === "vector") {
|
|
316
334
|
return `vec3(${varName}, 0.0)`;
|
|
317
335
|
}
|
|
336
|
+
if (toType === "color" && fromType === "shader") {
|
|
337
|
+
return `(${varName}).rgb`;
|
|
338
|
+
}
|
|
339
|
+
if (toType === "shader" && fromType === "color") {
|
|
340
|
+
return `vec4(${varName}, 1.0)`;
|
|
341
|
+
}
|
|
342
|
+
if (toType === "shader" && fromType === "vector") {
|
|
343
|
+
return `vec4(${varName}, 0.0, 1.0)`;
|
|
344
|
+
}
|
|
318
345
|
return varName;
|
|
319
346
|
}
|
|
320
347
|
if (socket.uniformName !== null) {
|
|
@@ -322,9 +349,10 @@ var CompileContext = class _CompileContext {
|
|
|
322
349
|
if (socket.type === "color") {
|
|
323
350
|
const raw = socket.defaultValue;
|
|
324
351
|
const rgb = typeof raw === "string" ? _CompileContext.hexToRgbArray(raw) : Array.isArray(raw) ? raw : [1, 1, 1];
|
|
325
|
-
this._uniforms[socket.uniformName] = { value: rgb };
|
|
352
|
+
this._uniforms[socket.uniformName] = { value: rgb.map((n) => Number.isFinite(n) ? n : 0) };
|
|
326
353
|
} else {
|
|
327
|
-
|
|
354
|
+
const n = socket.defaultValue;
|
|
355
|
+
this._uniforms[socket.uniformName] = { value: Number.isFinite(n) ? n : 0 };
|
|
328
356
|
}
|
|
329
357
|
}
|
|
330
358
|
return socket.uniformName;
|
|
@@ -429,7 +457,7 @@ var CompileContext = class _CompileContext {
|
|
|
429
457
|
if (from === to) return true;
|
|
430
458
|
if ((from === "shader" || from === "color") && (to === "shader" || to === "color")) return true;
|
|
431
459
|
if ((from === "color" || from === "shader") && to === "vector") return true;
|
|
432
|
-
if (from === "vector" && to === "color") return true;
|
|
460
|
+
if (from === "vector" && (to === "color" || to === "shader")) return true;
|
|
433
461
|
return false;
|
|
434
462
|
}
|
|
435
463
|
// ── GLSL builders ─────────────────────────────────────────────────
|
|
@@ -478,14 +506,16 @@ ${callsBlock}
|
|
|
478
506
|
// ── Literal conversion ────────────────────────────────────────────
|
|
479
507
|
toLiteral(value, type) {
|
|
480
508
|
switch (type) {
|
|
481
|
-
case "float":
|
|
482
|
-
|
|
509
|
+
case "float": {
|
|
510
|
+
const n = typeof value === "number" && Number.isFinite(value) ? value : 0;
|
|
511
|
+
return n.toFixed(4);
|
|
512
|
+
}
|
|
483
513
|
case "color":
|
|
484
514
|
return typeof value === "string" ? this.hexToVec3(value) : "vec3(1.0, 1.0, 1.0)";
|
|
485
515
|
case "vector":
|
|
486
516
|
return "vec2(vUv)";
|
|
487
517
|
case "shader":
|
|
488
|
-
return "
|
|
518
|
+
return "vec4(0.0, 0.0, 0.0, 1.0)";
|
|
489
519
|
default:
|
|
490
520
|
return "vec3(1.0)";
|
|
491
521
|
}
|
|
@@ -544,7 +574,8 @@ var OutputNode = class extends ShaderNode {
|
|
|
544
574
|
this.material = new ShaderMaterial({
|
|
545
575
|
vertexShader: compiled.vertexShader,
|
|
546
576
|
fragmentShader: compiled.fragmentShader,
|
|
547
|
-
uniforms: compiled.uniforms
|
|
577
|
+
uniforms: compiled.uniforms,
|
|
578
|
+
transparent: compiled.needsTransparency
|
|
548
579
|
});
|
|
549
580
|
if (!this.material.uniforms.uSunDirection) {
|
|
550
581
|
this.material.uniforms.uSunDirection = { value: new Vector3(1, 2, 1).normalize() };
|
|
@@ -568,7 +599,7 @@ var ProcessNode = class extends ShaderNode {
|
|
|
568
599
|
|
|
569
600
|
// src/core/SocketType.ts
|
|
570
601
|
var SOCKET_GLSL_TYPE = {
|
|
571
|
-
shader: "
|
|
602
|
+
shader: "vec4",
|
|
572
603
|
color: "vec3",
|
|
573
604
|
vector: "vec2",
|
|
574
605
|
float: "float"
|
|
@@ -1222,8 +1253,8 @@ var MaterialOutput = class extends OutputNode {
|
|
|
1222
1253
|
return mat;
|
|
1223
1254
|
}
|
|
1224
1255
|
compileCall(ctx) {
|
|
1225
|
-
const sv = this._inputs.surface.isConnected() ? ctx.outputVar(this._inputs.surface.connection.node, this._inputs.surface.connection.name) : "
|
|
1226
|
-
return `gl_FragColor =
|
|
1256
|
+
const sv = this._inputs.surface.isConnected() ? ctx.outputVar(this._inputs.surface.connection.node, this._inputs.surface.connection.name) : "vec4(0.8, 0.8, 0.8, 1.0)";
|
|
1257
|
+
return `gl_FragColor = ${sv};`;
|
|
1227
1258
|
}
|
|
1228
1259
|
};
|
|
1229
1260
|
|
|
@@ -3372,7 +3403,7 @@ float _st_geo(float NdotX, float r) {
|
|
|
3372
3403
|
float k = (r + 1.0) * (r + 1.0) / 8.0;
|
|
3373
3404
|
return NdotX / max(NdotX * (1.0 - k) + k, 1e-7);
|
|
3374
3405
|
}
|
|
3375
|
-
vec3 _st_principledBSDF(vec3 base, float metallic, float roughness, float ior,
|
|
3406
|
+
vec3 _st_principledBSDF(vec3 base, float metallic, float roughness, float ior, vec3 N) {
|
|
3376
3407
|
vec3 V = normalize(cameraPosition - vPosition);
|
|
3377
3408
|
vec3 L = normalize(uSunDirection);
|
|
3378
3409
|
vec3 H = normalize(L + V);
|
|
@@ -3413,9 +3444,23 @@ vec3 _st_principledBSDF(vec3 base, float metallic, float roughness, float ior, f
|
|
|
3413
3444
|
compileDefs() {
|
|
3414
3445
|
return this.glslFunction ?? _PrincipledBSDF.glslFunction;
|
|
3415
3446
|
}
|
|
3447
|
+
/**
|
|
3448
|
+
* Alpha has no channel of its own in the underlying vec3 lighting function —
|
|
3449
|
+
* it's applied as the vec4 alpha component at the call site instead (see
|
|
3450
|
+
* compileCall). transparent:true is needed whenever alpha can read below 1.0:
|
|
3451
|
+
* a non-default literal, or any live/connected value (conservatively true).
|
|
3452
|
+
*/
|
|
3453
|
+
wantsTransparency() {
|
|
3454
|
+
const alphaSocket = this._inputs.alpha;
|
|
3455
|
+
if (alphaSocket.isConnected()) return true;
|
|
3456
|
+
const v = alphaSocket.defaultValue;
|
|
3457
|
+
return typeof v === "number" && v < 1;
|
|
3458
|
+
}
|
|
3416
3459
|
compileCall(ctx) {
|
|
3417
3460
|
const normal = this._inputs.normal.isConnected() ? ctx.outputVar(this._inputs.normal.connection.node, this._inputs.normal.connection.name) : "normalize(vNormal)";
|
|
3418
|
-
|
|
3461
|
+
const rgb = `_st_principledBSDF(${ctx.resolveInput(this._inputs.baseColor)}, ${ctx.resolveInput(this._inputs.metallic)}, ${ctx.resolveInput(this._inputs.roughness)}, ${ctx.resolveInput(this._inputs.ior)}, ${normal})`;
|
|
3462
|
+
const alpha = ctx.resolveInput(this._inputs.alpha);
|
|
3463
|
+
return `vec4 ${ctx.outputVar(this, "BSDF")} = vec4(${rgb}, ${alpha});`;
|
|
3419
3464
|
}
|
|
3420
3465
|
};
|
|
3421
3466
|
|
|
@@ -3456,7 +3501,7 @@ vec3 _st_emission(vec3 color, float strength) {
|
|
|
3456
3501
|
compileCall(ctx) {
|
|
3457
3502
|
const color = ctx.resolveInput(this._inputs.color);
|
|
3458
3503
|
const strength = ctx.resolveInput(this._inputs.strength);
|
|
3459
|
-
return `
|
|
3504
|
+
return `vec4 ${ctx.outputVar(this, "BSDF")} = vec4(_st_emission(${color}, ${strength}), 1.0);`;
|
|
3460
3505
|
}
|
|
3461
3506
|
};
|
|
3462
3507
|
|
|
@@ -3497,7 +3542,7 @@ vec3 _st_volumeAbsorption(vec3 color, float density) {
|
|
|
3497
3542
|
compileCall(ctx) {
|
|
3498
3543
|
const color = ctx.resolveInput(this._inputs.color);
|
|
3499
3544
|
const density = ctx.resolveInput(this._inputs.density);
|
|
3500
|
-
return `
|
|
3545
|
+
return `vec4 ${ctx.outputVar(this, "Volume")} = vec4(_st_volumeAbsorption(${color}, ${density}), 1.0);`;
|
|
3501
3546
|
}
|
|
3502
3547
|
};
|
|
3503
3548
|
|
|
@@ -3544,7 +3589,7 @@ vec3 _st_volumeScatter(vec3 color, float density, float anisotropy) {
|
|
|
3544
3589
|
const color = ctx.resolveInput(this._inputs.color);
|
|
3545
3590
|
const density = ctx.resolveInput(this._inputs.density);
|
|
3546
3591
|
const anisotropy = ctx.resolveInput(this._inputs.anisotropy);
|
|
3547
|
-
return `
|
|
3592
|
+
return `vec4 ${ctx.outputVar(this, "Volume")} = vec4(_st_volumeScatter(${color}, ${density}, ${anisotropy}), 1.0);`;
|
|
3548
3593
|
}
|
|
3549
3594
|
};
|
|
3550
3595
|
|
|
@@ -3615,7 +3660,7 @@ vec3 _st_principledVolume(
|
|
|
3615
3660
|
const emissionStrength = ctx.resolveInput(this._inputs.emissionStrength);
|
|
3616
3661
|
const blackbodyIntensity = ctx.resolveInput(this._inputs.blackbodyIntensity);
|
|
3617
3662
|
const temperature = ctx.resolveInput(this._inputs.temperature);
|
|
3618
|
-
return `
|
|
3663
|
+
return `vec4 ${ctx.outputVar(this, "Volume")} = vec4(_st_principledVolume(${color}, ${density}, ${anisotropy}, ${absorptionColor}, ${emissionColor}, ${emissionStrength}, ${blackbodyIntensity}, ${temperature}), 1.0);`;
|
|
3619
3664
|
}
|
|
3620
3665
|
};
|
|
3621
3666
|
|
|
@@ -3637,7 +3682,7 @@ var MixShader = class _MixShader extends ProcessNode {
|
|
|
3637
3682
|
}
|
|
3638
3683
|
static {
|
|
3639
3684
|
this.glslFunction = `
|
|
3640
|
-
|
|
3685
|
+
vec4 _st_mixShader(float fac, vec4 a, vec4 b) {
|
|
3641
3686
|
return mix(a, b, clamp(fac, 0.0, 1.0));
|
|
3642
3687
|
}`;
|
|
3643
3688
|
}
|
|
@@ -3663,7 +3708,7 @@ vec3 _st_mixShader(float fac, vec3 a, vec3 b) {
|
|
|
3663
3708
|
const fac = ctx.resolveInput(this._inputs.fac);
|
|
3664
3709
|
const shader1 = ctx.resolveInput(this._inputs.shader1);
|
|
3665
3710
|
const shader2 = ctx.resolveInput(this._inputs.shader2);
|
|
3666
|
-
return `
|
|
3711
|
+
return `vec4 ${ctx.outputVar(this, "BSDF")} = _st_mixShader(${fac}, ${shader1}, ${shader2});`;
|
|
3667
3712
|
}
|
|
3668
3713
|
};
|
|
3669
3714
|
|
|
@@ -3680,8 +3725,8 @@ var AddShader = class _AddShader extends ProcessNode {
|
|
|
3680
3725
|
}
|
|
3681
3726
|
static {
|
|
3682
3727
|
this.glslFunction = `
|
|
3683
|
-
|
|
3684
|
-
return a + b;
|
|
3728
|
+
vec4 _st_addShader(vec4 a, vec4 b) {
|
|
3729
|
+
return vec4(a.rgb + b.rgb, max(a.a, b.a));
|
|
3685
3730
|
}`;
|
|
3686
3731
|
}
|
|
3687
3732
|
constructor(inputs = {}) {
|
|
@@ -3704,7 +3749,7 @@ vec3 _st_addShader(vec3 a, vec3 b) {
|
|
|
3704
3749
|
compileCall(ctx) {
|
|
3705
3750
|
const s1 = ctx.resolveInput(this._inputs.shader1);
|
|
3706
3751
|
const s2 = ctx.resolveInput(this._inputs.shader2);
|
|
3707
|
-
return `
|
|
3752
|
+
return `vec4 ${ctx.outputVar(this, "BSDF")} = _st_addShader(${s1}, ${s2});`;
|
|
3708
3753
|
}
|
|
3709
3754
|
};
|
|
3710
3755
|
|
|
@@ -3801,7 +3846,7 @@ vec3 _st_diffuseBSDF(vec3 color, float roughness, vec3 N) {
|
|
|
3801
3846
|
const color = ctx.resolveInput(this._inputs.color);
|
|
3802
3847
|
const roughness = ctx.resolveInput(this._inputs.roughness);
|
|
3803
3848
|
const normal = this._inputs.normal.isConnected() ? ctx.outputVar(this._inputs.normal.connection.node, this._inputs.normal.connection.name) : "normalize(vNormal)";
|
|
3804
|
-
return `
|
|
3849
|
+
return `vec4 ${ctx.outputVar(this, "BSDF")} = vec4(_st_diffuseBSDF(${color}, ${roughness}, ${normal}), 1.0);`;
|
|
3805
3850
|
}
|
|
3806
3851
|
};
|
|
3807
3852
|
|
|
@@ -3855,7 +3900,7 @@ vec3 _st_glossyBSDF(vec3 color, float roughness, vec3 N) {
|
|
|
3855
3900
|
const color = ctx.resolveInput(this._inputs.color);
|
|
3856
3901
|
const roughness = ctx.resolveInput(this._inputs.roughness);
|
|
3857
3902
|
const normal = this._inputs.normal.isConnected() ? ctx.outputVar(this._inputs.normal.connection.node, this._inputs.normal.connection.name) : "normalize(vNormal)";
|
|
3858
|
-
return `
|
|
3903
|
+
return `vec4 ${ctx.outputVar(this, "BSDF")} = vec4(_st_glossyBSDF(${color}, ${roughness}, ${normal}), 1.0);`;
|
|
3859
3904
|
}
|
|
3860
3905
|
};
|
|
3861
3906
|
|
|
@@ -3904,7 +3949,7 @@ vec3 _st_refractionBSDF(vec3 color, float roughness, float ior, vec3 N) {
|
|
|
3904
3949
|
const roughness = ctx.resolveInput(this._inputs.roughness);
|
|
3905
3950
|
const ior = ctx.resolveInput(this._inputs.ior);
|
|
3906
3951
|
const normal = this._inputs.normal.isConnected() ? ctx.outputVar(this._inputs.normal.connection.node, this._inputs.normal.connection.name) : "normalize(vNormal)";
|
|
3907
|
-
return `
|
|
3952
|
+
return `vec4 ${ctx.outputVar(this, "BSDF")} = vec4(_st_refractionBSDF(${color}, ${roughness}, ${ior}, ${normal}), 1.0);`;
|
|
3908
3953
|
}
|
|
3909
3954
|
};
|
|
3910
3955
|
|
|
@@ -3959,7 +4004,7 @@ vec3 _st_glassBSDF(vec3 color, float roughness, float ior, vec3 N) {
|
|
|
3959
4004
|
const roughness = ctx.resolveInput(this._inputs.roughness);
|
|
3960
4005
|
const ior = ctx.resolveInput(this._inputs.ior);
|
|
3961
4006
|
const normal = this._inputs.normal.isConnected() ? ctx.outputVar(this._inputs.normal.connection.node, this._inputs.normal.connection.name) : "normalize(vNormal)";
|
|
3962
|
-
return `
|
|
4007
|
+
return `vec4 ${ctx.outputVar(this, "BSDF")} = vec4(_st_glassBSDF(${color}, ${roughness}, ${ior}, ${normal}), 1.0);`;
|
|
3963
4008
|
}
|
|
3964
4009
|
};
|
|
3965
4010
|
|
|
@@ -4011,7 +4056,7 @@ vec3 _st_sheenBSDF(vec3 color, float roughness, vec3 N) {
|
|
|
4011
4056
|
const color = ctx.resolveInput(this._inputs.color);
|
|
4012
4057
|
const roughness = ctx.resolveInput(this._inputs.roughness);
|
|
4013
4058
|
const normal = this._inputs.normal.isConnected() ? ctx.outputVar(this._inputs.normal.connection.node, this._inputs.normal.connection.name) : "normalize(vNormal)";
|
|
4014
|
-
return `
|
|
4059
|
+
return `vec4 ${ctx.outputVar(this, "BSDF")} = vec4(_st_sheenBSDF(${color}, ${roughness}, ${normal}), 1.0);`;
|
|
4015
4060
|
}
|
|
4016
4061
|
};
|
|
4017
4062
|
|
|
@@ -4060,7 +4105,7 @@ vec3 _st_sss(vec3 color, float scale, vec3 radius, vec3 N) {
|
|
|
4060
4105
|
const scale = ctx.resolveInput(this._inputs.scale);
|
|
4061
4106
|
const [r, g, b] = this.radius;
|
|
4062
4107
|
const normal = this._inputs.normal.isConnected() ? ctx.outputVar(this._inputs.normal.connection.node, this._inputs.normal.connection.name) : "normalize(vNormal)";
|
|
4063
|
-
return `
|
|
4108
|
+
return `vec4 ${ctx.outputVar(this, "BSDF")} = vec4(_st_sss(${color}, ${scale}, vec3(${r.toFixed(4)}, ${g.toFixed(4)}, ${b.toFixed(4)}), ${normal}), 1.0);`;
|
|
4064
4109
|
}
|
|
4065
4110
|
};
|
|
4066
4111
|
|
|
@@ -4108,7 +4153,7 @@ vec3 _st_toonBSDF(vec3 color, float size, float smoothWidth, vec3 N) {
|
|
|
4108
4153
|
const size = ctx.resolveInput(this._inputs.size);
|
|
4109
4154
|
const smoothWidth = ctx.resolveInput(this._inputs.smooth);
|
|
4110
4155
|
const normal = this._inputs.normal.isConnected() ? ctx.outputVar(this._inputs.normal.connection.node, this._inputs.normal.connection.name) : "normalize(vNormal)";
|
|
4111
|
-
return `
|
|
4156
|
+
return `vec4 ${ctx.outputVar(this, "BSDF")} = vec4(_st_toonBSDF(${color}, ${size}, ${smoothWidth}, ${normal}), 1.0);`;
|
|
4112
4157
|
}
|
|
4113
4158
|
};
|
|
4114
4159
|
|
|
@@ -4165,7 +4210,7 @@ vec3 _st_specularBSDF(vec3 base, vec3 spec, float roughness, vec3 N) {
|
|
|
4165
4210
|
const spec = ctx.resolveInput(this._inputs.specular);
|
|
4166
4211
|
const roughness = ctx.resolveInput(this._inputs.roughness);
|
|
4167
4212
|
const normal = this._inputs.normal.isConnected() ? ctx.outputVar(this._inputs.normal.connection.node, this._inputs.normal.connection.name) : "normalize(vNormal)";
|
|
4168
|
-
return `
|
|
4213
|
+
return `vec4 ${ctx.outputVar(this, "BSDF")} = vec4(_st_specularBSDF(${base}, ${spec}, ${roughness}, ${normal}), 1.0);`;
|
|
4169
4214
|
}
|
|
4170
4215
|
};
|
|
4171
4216
|
|
|
@@ -4208,7 +4253,7 @@ vec3 _st_translucentBSDF(vec3 color, vec3 N) {
|
|
|
4208
4253
|
compileCall(ctx) {
|
|
4209
4254
|
const color = ctx.resolveInput(this._inputs.color);
|
|
4210
4255
|
const normal = this._inputs.normal.isConnected() ? ctx.outputVar(this._inputs.normal.connection.node, this._inputs.normal.connection.name) : "normalize(vNormal)";
|
|
4211
|
-
return `
|
|
4256
|
+
return `vec4 ${ctx.outputVar(this, "BSDF")} = vec4(_st_translucentBSDF(${color}, ${normal}), 1.0);`;
|
|
4212
4257
|
}
|
|
4213
4258
|
};
|
|
4214
4259
|
|
|
@@ -4340,7 +4385,7 @@ vec3 _st_principledHair(
|
|
|
4340
4385
|
const randomRoughness = ctx.resolveInput(this._inputs.randomRoughness);
|
|
4341
4386
|
const random = ctx.resolveInput(this._inputs.random);
|
|
4342
4387
|
const tangent = this._inputs.tangent.isConnected() ? ctx.outputVar(this._inputs.tangent.connection.node, this._inputs.tangent.connection.name) : "normalize(vStrandTangent)";
|
|
4343
|
-
return `
|
|
4388
|
+
return `vec4 ${ctx.outputVar(this, "BSDF")} = vec4(_st_principledHair(${color}, ${roughness}, ${radialRoughness}, ${coat}, ${ior}, ${offset}, ${randomColor}, ${randomRoughness}, ${random}, ${tangent}), 1.0);`;
|
|
4344
4389
|
}
|
|
4345
4390
|
};
|
|
4346
4391
|
|
|
@@ -4655,8 +4700,8 @@ var ShaderToRGB = class extends ProcessNode {
|
|
|
4655
4700
|
const cv = ctx.outputVar(this, "Color");
|
|
4656
4701
|
const av = ctx.outputVar(this, "Alpha");
|
|
4657
4702
|
return [
|
|
4658
|
-
`vec3 ${cv} = ${shader};`,
|
|
4659
|
-
`float ${av} =
|
|
4703
|
+
`vec3 ${cv} = (${shader}).rgb;`,
|
|
4704
|
+
`float ${av} = (${shader}).a;`
|
|
4660
4705
|
].join("\n ");
|
|
4661
4706
|
}
|
|
4662
4707
|
};
|
|
@@ -4942,11 +4987,13 @@ var ShaderScript = class extends ProcessNode {
|
|
|
4942
4987
|
_socketGlslType(type) {
|
|
4943
4988
|
if (type === "float") return "float";
|
|
4944
4989
|
if (type === "vector") return "vec2";
|
|
4990
|
+
if (type === "shader") return "vec4";
|
|
4945
4991
|
return "vec3";
|
|
4946
4992
|
}
|
|
4947
4993
|
_declGlslType(type) {
|
|
4948
4994
|
if (type === "float") return "float";
|
|
4949
4995
|
if (type === "vector") return "vec2";
|
|
4996
|
+
if (type === "shader") return "vec4";
|
|
4950
4997
|
return "vec3";
|
|
4951
4998
|
}
|
|
4952
4999
|
};
|
|
@@ -5528,7 +5575,7 @@ var RayPortal = class extends ProcessNode {
|
|
|
5528
5575
|
const color = ctx.resolveInput(this._inputs.color);
|
|
5529
5576
|
const rawBlend = this.parameters.blend;
|
|
5530
5577
|
const blend = Number.isFinite(rawBlend) ? Math.min(Math.max(rawBlend, 0), 1).toFixed(4) : "1.0000";
|
|
5531
|
-
return `
|
|
5578
|
+
return `vec4 ${ctx.outputVar(this, "BSDF")} = vec4(${color} * ${blend}, 1.0);`;
|
|
5532
5579
|
}
|
|
5533
5580
|
};
|
|
5534
5581
|
export {
|
|
@@ -8,6 +8,8 @@ export interface CompiledMaterial {
|
|
|
8
8
|
value: unknown;
|
|
9
9
|
}>;
|
|
10
10
|
nodes: ShaderNode[];
|
|
11
|
+
/** True when any node in the graph carries alpha that can read below 1.0 (see ShaderNode.wantsTransparency). */
|
|
12
|
+
needsTransparency: boolean;
|
|
11
13
|
}
|
|
12
14
|
/**
|
|
13
15
|
* Walks the node graph from an OutputNode, validates all connections,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"CompileContext.d.ts","sourceRoot":"","sources":["../../src/core/CompileContext.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAA;AACjD,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAA;AACnD,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAA;AAEjD,MAAM,WAAW,gBAAgB;IAC/B,YAAY,
|
|
1
|
+
{"version":3,"file":"CompileContext.d.ts","sourceRoot":"","sources":["../../src/core/CompileContext.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAA;AACjD,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAA;AACnD,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAA;AAEjD,MAAM,WAAW,gBAAgB;IAC/B,YAAY,EAAO,MAAM,CAAA;IACzB,cAAc,EAAK,MAAM,CAAA;IACzB,QAAQ,EAAW,MAAM,CAAC,MAAM,EAAE;QAAE,KAAK,EAAE,OAAO,CAAA;KAAE,CAAC,CAAA;IACrD,KAAK,EAAc,UAAU,EAAE,CAAA;IAC/B,gHAAgH;IAChH,iBAAiB,EAAE,OAAO,CAAA;CAC3B;AAED;;;;;;;;;GASG;AACH,qBAAa,cAAc;IACzB,OAAO,CAAC,OAAO,CAAqD;IACpE,OAAO,CAAC,QAAQ,CAAoD;IACpE,OAAO,CAAC,KAAK,CAAgD;IAC7D,OAAO,CAAC,WAAW,CAAiD;IACpE,OAAO,CAAC,IAAI,CAAiD;IAC7D,OAAO,CAAC,KAAK,CAAgD;IAC7D,OAAO,CAAC,SAAS,CAA4C;IAC7D,OAAO,CAAC,SAAS,CAAuD;IACxE,0FAA0F;IAC1F,OAAO,CAAC,iBAAiB,CAAiE;IAI1F,OAAO,CAAC,UAAU,EAAE,UAAU,GAAG,gBAAgB;IAejD;;;OAGG;IACH,SAAS,CAAC,IAAI,EAAE,UAAU,EAAE,UAAU,EAAE,MAAM,GAAG,MAAM;IAKvD;;;;;OAKG;IACH,YAAY,CAAC,MAAM,EAAE,WAAW,CAAC,OAAO,CAAC,GAAG,MAAM;IAgDlD,OAAO,CAAC,KAAK;IA8Bb,OAAO,CAAC,IAAI;IAyBZ,OAAO,CAAC,QAAQ;IA0BhB,OAAO,CAAC,QAAQ;IAKhB,OAAO,CAAC,YAAY;IAsCpB,OAAO,CAAC,eAAe;IAcvB,OAAO,CAAC,iBAAiB;IAuBzB,OAAO,CAAC,wBAAwB;IAMhC,OAAO,CAAC,mBAAmB;IA2B3B,OAAO,CAAC,SAAS;IAwBjB,MAAM,CAAC,aAAa,CAAC,GAAG,EAAE,MAAM,GAAG,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC;IAS3D,MAAM,CAAC,SAAS,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM;IAQrC,OAAO,CAAC,SAAS;CAGlB"}
|
|
@@ -35,6 +35,7 @@ export class CompileContext {
|
|
|
35
35
|
fragmentShader: this.buildFragmentShader(),
|
|
36
36
|
uniforms: this._uniforms,
|
|
37
37
|
nodes: this.order,
|
|
38
|
+
needsTransparency: this.order.some(node => node.wantsTransparency()),
|
|
38
39
|
};
|
|
39
40
|
}
|
|
40
41
|
/**
|
|
@@ -54,14 +55,28 @@ export class CompileContext {
|
|
|
54
55
|
resolveInput(socket) {
|
|
55
56
|
if (socket.connection) {
|
|
56
57
|
const varName = this.outputVar(socket.connection.node, socket.connection.name);
|
|
57
|
-
|
|
58
|
-
|
|
58
|
+
const fromType = socket.connection.type;
|
|
59
|
+
const toType = socket.type;
|
|
60
|
+
// vec3/vec4 (color/shader) → vec2 (vector) implicit narrowing, e.g. Mapping.Vector → ImageTexture.vector
|
|
61
|
+
if (toType === 'vector' && fromType !== 'vector') {
|
|
59
62
|
return `(${varName}).xy`;
|
|
60
63
|
}
|
|
61
|
-
// vec2 (vector) → vec3 (color) implicit
|
|
62
|
-
if (
|
|
64
|
+
// vec2 (vector) → vec3 (color) implicit widening, e.g. TextureCoordinate.UV → Mapping.vector
|
|
65
|
+
if (toType === 'color' && fromType === 'vector') {
|
|
63
66
|
return `vec3(${varName}, 0.0)`;
|
|
64
67
|
}
|
|
68
|
+
// vec4 (shader) → vec3 (color) implicit narrowing — drop alpha, e.g. ShaderToRGB-style consumption
|
|
69
|
+
if (toType === 'color' && fromType === 'shader') {
|
|
70
|
+
return `(${varName}).rgb`;
|
|
71
|
+
}
|
|
72
|
+
// vec3 (color) → vec4 (shader) implicit widening — opaque alpha
|
|
73
|
+
if (toType === 'shader' && fromType === 'color') {
|
|
74
|
+
return `vec4(${varName}, 1.0)`;
|
|
75
|
+
}
|
|
76
|
+
// vec2 (vector) → vec4 (shader) implicit widening — opaque alpha
|
|
77
|
+
if (toType === 'shader' && fromType === 'vector') {
|
|
78
|
+
return `vec4(${varName}, 0.0, 1.0)`;
|
|
79
|
+
}
|
|
65
80
|
return varName;
|
|
66
81
|
}
|
|
67
82
|
if (socket.uniformName !== null) {
|
|
@@ -71,10 +86,12 @@ export class CompileContext {
|
|
|
71
86
|
const rgb = typeof raw === 'string'
|
|
72
87
|
? CompileContext.hexToRgbArray(raw)
|
|
73
88
|
: (Array.isArray(raw) ? raw : [1, 1, 1]);
|
|
74
|
-
|
|
89
|
+
// Guard against NaN/Infinity reaching the GPU uniform.
|
|
90
|
+
this._uniforms[socket.uniformName] = { value: rgb.map(n => Number.isFinite(n) ? n : 0) };
|
|
75
91
|
}
|
|
76
92
|
else {
|
|
77
|
-
|
|
93
|
+
const n = socket.defaultValue;
|
|
94
|
+
this._uniforms[socket.uniformName] = { value: Number.isFinite(n) ? n : 0 };
|
|
78
95
|
}
|
|
79
96
|
}
|
|
80
97
|
return socket.uniformName;
|
|
@@ -198,15 +215,15 @@ export class CompileContext {
|
|
|
198
215
|
typesCompatible(from, to) {
|
|
199
216
|
if (from === to)
|
|
200
217
|
return true;
|
|
201
|
-
// shader and color
|
|
218
|
+
// shader (vec4) and color (vec3) freely convert — narrow via .rgb, widen via vec4(v, 1.0)
|
|
202
219
|
if ((from === 'shader' || from === 'color') && (to === 'shader' || to === 'color'))
|
|
203
220
|
return true;
|
|
204
|
-
// vec3 (color/shader) implicitly narrows to vec2 (vector) via .xy, mirroring Blender's
|
|
221
|
+
// vec3/vec4 (color/shader) implicitly narrows to vec2 (vector) via .xy, mirroring Blender's
|
|
205
222
|
// Mapping (vector output) → Image Texture Vector panning graph
|
|
206
223
|
if ((from === 'color' || from === 'shader') && to === 'vector')
|
|
207
224
|
return true;
|
|
208
|
-
// vec2 (vector) implicitly widens to vec3 (color)
|
|
209
|
-
if (from === 'vector' && to === 'color')
|
|
225
|
+
// vec2 (vector) implicitly widens to vec3 (color) or vec4 (shader)
|
|
226
|
+
if (from === 'vector' && (to === 'color' || to === 'shader'))
|
|
210
227
|
return true;
|
|
211
228
|
return false;
|
|
212
229
|
}
|
|
@@ -263,8 +280,11 @@ ${callsBlock}
|
|
|
263
280
|
// ── Literal conversion ────────────────────────────────────────────
|
|
264
281
|
toLiteral(value, type) {
|
|
265
282
|
switch (type) {
|
|
266
|
-
case 'float':
|
|
267
|
-
|
|
283
|
+
case 'float': {
|
|
284
|
+
// Guard against NaN/Infinity reaching the shader source as an invalid GLSL literal.
|
|
285
|
+
const n = typeof value === 'number' && Number.isFinite(value) ? value : 0;
|
|
286
|
+
return n.toFixed(4);
|
|
287
|
+
}
|
|
268
288
|
case 'color':
|
|
269
289
|
return typeof value === 'string'
|
|
270
290
|
? this.hexToVec3(value)
|
|
@@ -272,7 +292,7 @@ ${callsBlock}
|
|
|
272
292
|
case 'vector':
|
|
273
293
|
return 'vec2(vUv)';
|
|
274
294
|
case 'shader':
|
|
275
|
-
return '
|
|
295
|
+
return 'vec4(0.0, 0.0, 0.0, 1.0)';
|
|
276
296
|
default:
|
|
277
297
|
return 'vec3(1.0)';
|
|
278
298
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"OutputNode.d.ts","sourceRoot":"","sources":["../../src/core/OutputNode.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAA;AAE5C,OAAO,EAAE,cAAc,EAAW,MAAM,OAAO,CAAA;AAC/C,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAA;AAErD;;;;GAIG;AACH,8BAAsB,UAAW,SAAQ,UAAU;IACjD,+EAA+E;IAC/E,QAAQ,EAAE,cAAc,GAAG,IAAI,CAAO;IAEtC,gBAAgB,IAAI,MAAM,CAAC,MAAM,EAAE,YAAY,CAAC;IAIhD,WAAW,IAAI,MAAM;IAIrB;;;;;;;;;;;;;OAaG;IACH,OAAO,IAAI,cAAc;
|
|
1
|
+
{"version":3,"file":"OutputNode.d.ts","sourceRoot":"","sources":["../../src/core/OutputNode.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAA;AAE5C,OAAO,EAAE,cAAc,EAAW,MAAM,OAAO,CAAA;AAC/C,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAA;AAErD;;;;GAIG;AACH,8BAAsB,UAAW,SAAQ,UAAU;IACjD,+EAA+E;IAC/E,QAAQ,EAAE,cAAc,GAAG,IAAI,CAAO;IAEtC,gBAAgB,IAAI,MAAM,CAAC,MAAM,EAAE,YAAY,CAAC;IAIhD,WAAW,IAAI,MAAM;IAIrB;;;;;;;;;;;;;OAaG;IACH,OAAO,IAAI,cAAc;CA0B1B"}
|
package/dist/core/OutputNode.js
CHANGED
|
@@ -39,6 +39,7 @@ export class OutputNode extends ShaderNode {
|
|
|
39
39
|
vertexShader: compiled.vertexShader,
|
|
40
40
|
fragmentShader: compiled.fragmentShader,
|
|
41
41
|
uniforms: compiled.uniforms,
|
|
42
|
+
transparent: compiled.needsTransparency,
|
|
42
43
|
});
|
|
43
44
|
// Inject default scene-lighting uniforms used by PrincipledBSDF.
|
|
44
45
|
// Users can override these after compile() to match their scene lights.
|
|
@@ -61,6 +61,13 @@ export declare abstract class ShaderNode {
|
|
|
61
61
|
attrType: 'float' | 'vec2' | 'vec3';
|
|
62
62
|
varyingName: string;
|
|
63
63
|
}>;
|
|
64
|
+
/**
|
|
65
|
+
* Whether this node's contribution to the compiled material requires
|
|
66
|
+
* `transparent: true` on the resulting THREE.ShaderMaterial (e.g. a
|
|
67
|
+
* `shader`-socket alpha channel that can read below 1.0).
|
|
68
|
+
* Override in nodes that carry alpha semantics — see PrincipledBSDF.
|
|
69
|
+
*/
|
|
70
|
+
wantsTransparency(): boolean;
|
|
64
71
|
/**
|
|
65
72
|
* Returns the named output socket.
|
|
66
73
|
* Throws ShaderNodeError if the socket does not exist.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ShaderNode.d.ts","sourceRoot":"","sources":["../../src/core/ShaderNode.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAA;AAC9C,OAAO,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAA;AAChD,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAA;
|
|
1
|
+
{"version":3,"file":"ShaderNode.d.ts","sourceRoot":"","sources":["../../src/core/ShaderNode.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAA;AAC9C,OAAO,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAA;AAChD,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAA;AAsBjD,6DAA6D;AAC7D,KAAK,QAAQ,GAAG,CAAC,UAAU,EAAE,OAAO,EAAE,OAAO,CAAC,CAAC,CAAA;AAE/C,8CAA8C;AAC9C,KAAK,SAAS,GAAG,UAAU,CAAA;AAE3B,MAAM,WAAW,YAAY;IAC3B,KAAK,EAAK,MAAM,CAAA;IAChB,QAAQ,EAAE,MAAM,CAAA;IAChB,KAAK,EAAK,MAAM,CAAA;IAChB,IAAI,EAAM,KAAK,GAAG,QAAQ,GAAG,MAAM,CAAA;IACnC,QAAQ,CAAC,EAAE,MAAM,CAAA;CAClB;AAED;;;GAGG;AACH,8BAAsB,UAAU;IAC9B,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAA;IAEnB,2EAA2E;IAC3E,YAAY,CAAC,EAAE,MAAM,CAAA;IAErB;;;;;OAKG;IACH,aAAa,IAAI,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC;IAEvC;;;;;;;;OAQG;IACH,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,CAAC,CAAK;IAElE,2EAA2E;IAC3E,OAAO,CAAC,WAAW,CAAmC;IACtD,yDAAyD;IACzD,OAAO,CAAC,YAAY,CAAkC;gBAE1C,QAAQ,EAAE,MAAM;IAI5B,QAAQ,KAAK,QAAQ,IAAI,MAAM,CAAA;IAC/B,QAAQ,KAAK,QAAQ,IAAI,YAAY,CAAA;IAErC,QAAQ,CAAC,eAAe,IAAK,MAAM,CAAC,MAAM,EAAE,WAAW,CAAC,OAAO,CAAC,CAAC;IACjE,QAAQ,CAAC,gBAAgB,IAAI,MAAM,CAAC,MAAM,EAAE,YAAY,CAAC;IAEzD;;;;;;;;;OASG;IACH,gBAAgB,IAAI,KAAK,CAAC;QAAE,QAAQ,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,OAAO,GAAG,MAAM,GAAG,MAAM,CAAC;QAAC,WAAW,EAAE,MAAM,CAAA;KAAE,CAAC;IAIzG;;;;;OAKG;IACH,iBAAiB,IAAI,OAAO;IAE5B;;;OAGG;IACH,MAAM,CAAC,IAAI,EAAE,MAAM,GAAG,YAAY;IAelC;;;OAGG;IACH,QAAQ,CAAC,WAAW,IAAI,MAAM;IAE9B;;;OAGG;IACH,QAAQ,CAAC,WAAW,CAAC,GAAG,EAAE,OAAO,qBAAqB,EAAE,cAAc,GAAG,MAAM;IAE/E;;;;OAIG;IACH,MAAM,CAAC,mBAAmB,UAAQ;IAElC;;;;;;;;;;OAUG;IACH,SAAS,CAAC,YAAY,CACpB,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EACjC,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,QAAQ,CAAC,GAC7B,MAAM,CAAC,MAAM,EAAE,WAAW,CAAC,OAAO,CAAC,CAAC;IAkCvC;;;;;OAKG;IACH,SAAS,CAAC,aAAa,CACrB,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,SAAS,CAAC,GAC9B,MAAM,CAAC,MAAM,EAAE,YAAY,CAAC;IAQ/B;;;;OAIG;IACH,eAAe,CAAC,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE;QAAE,KAAK,EAAE,OAAO,CAAA;KAAE,CAAC,GAAG,IAAI;CAqBpE"}
|
package/dist/core/ShaderNode.js
CHANGED
|
@@ -2,6 +2,13 @@ import { ShaderNodeError } from './ShaderNodeError.js';
|
|
|
2
2
|
import { ShaderConfig } from './ShaderConfig.js';
|
|
3
3
|
import { InputSocket } from './InputSocket.js';
|
|
4
4
|
import { OutputSocket } from './OutputSocket.js';
|
|
5
|
+
/** Guards against NaN/Infinity reaching a GLSL uniform (would emit invalid GLSL literals like "NaN"). */
|
|
6
|
+
function finiteOr(value, fallback) {
|
|
7
|
+
return Number.isFinite(value) ? value : fallback;
|
|
8
|
+
}
|
|
9
|
+
function sanitizeVec3(v) {
|
|
10
|
+
return [finiteOr(v[0], 0), finiteOr(v[1], 0), finiteOr(v[2], 0)];
|
|
11
|
+
}
|
|
5
12
|
function hexToRgb(hex) {
|
|
6
13
|
const h = hex.replace('#', '');
|
|
7
14
|
return [
|
|
@@ -53,6 +60,13 @@ export class ShaderNode {
|
|
|
53
60
|
vertexInjections() {
|
|
54
61
|
return [];
|
|
55
62
|
}
|
|
63
|
+
/**
|
|
64
|
+
* Whether this node's contribution to the compiled material requires
|
|
65
|
+
* `transparent: true` on the resulting THREE.ShaderMaterial (e.g. a
|
|
66
|
+
* `shader`-socket alpha channel that can read below 1.0).
|
|
67
|
+
* Override in nodes that carry alpha semantics — see PrincipledBSDF.
|
|
68
|
+
*/
|
|
69
|
+
wantsTransparency() { return false; }
|
|
56
70
|
/**
|
|
57
71
|
* Returns the named output socket.
|
|
58
72
|
* Throws ShaderNodeError if the socket does not exist.
|
|
@@ -92,7 +106,7 @@ export class ShaderNode {
|
|
|
92
106
|
if (type === 'float' && typeof literal === 'number') {
|
|
93
107
|
socket.uniformName = `u_${this.id}_${name}`;
|
|
94
108
|
this._uniformMap.set(name, socket.uniformName);
|
|
95
|
-
this.parameters[name] = literal;
|
|
109
|
+
this.parameters[name] = finiteOr(literal, 0);
|
|
96
110
|
}
|
|
97
111
|
// Auto-register unconnected color inputs as vec3 uniforms
|
|
98
112
|
if (type === 'color' && !(value instanceof OutputSocket)) {
|
|
@@ -100,7 +114,9 @@ export class ShaderNode {
|
|
|
100
114
|
socket.uniformName = `u_${this.id}_${name}`;
|
|
101
115
|
this._uniformMap.set(name, socket.uniformName);
|
|
102
116
|
this._colorParams.add(name);
|
|
103
|
-
this.parameters[name] = typeof literal === 'string'
|
|
117
|
+
this.parameters[name] = typeof literal === 'string'
|
|
118
|
+
? hexToRgb(literal)
|
|
119
|
+
: sanitizeVec3(literal);
|
|
104
120
|
}
|
|
105
121
|
}
|
|
106
122
|
sockets[name] = socket;
|
|
@@ -131,7 +147,7 @@ export class ShaderNode {
|
|
|
131
147
|
if (this._colorParams.has(paramName)) {
|
|
132
148
|
Object.defineProperty(this.parameters, paramName, {
|
|
133
149
|
get: () => uniforms[uniformName].value,
|
|
134
|
-
set: (v) => { uniforms[uniformName].value = v; },
|
|
150
|
+
set: (v) => { uniforms[uniformName].value = sanitizeVec3(v); },
|
|
135
151
|
enumerable: true,
|
|
136
152
|
configurable: true,
|
|
137
153
|
});
|
|
@@ -139,7 +155,7 @@ export class ShaderNode {
|
|
|
139
155
|
else {
|
|
140
156
|
Object.defineProperty(this.parameters, paramName, {
|
|
141
157
|
get: () => uniforms[uniformName].value,
|
|
142
|
-
set: (v) => { uniforms[uniformName].value = v; },
|
|
158
|
+
set: (v) => { uniforms[uniformName].value = finiteOr(v, 0); },
|
|
143
159
|
enumerable: true,
|
|
144
160
|
configurable: true,
|
|
145
161
|
});
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Socket types mirror Blender's socket colours.
|
|
3
3
|
*
|
|
4
|
-
* shader — yellow — BSDF / material output
|
|
4
|
+
* shader — yellow — BSDF / material output — vec4 RGB + alpha
|
|
5
5
|
* color — yellow-green — vec3 RGB
|
|
6
6
|
* vector — purple — vec2 / vec3 spatial
|
|
7
7
|
* float — grey — single scalar
|
package/dist/core/SocketType.js
CHANGED
|
@@ -112,7 +112,7 @@ export class MaterialOutput extends OutputNode {
|
|
|
112
112
|
compileCall(ctx) {
|
|
113
113
|
const sv = this._inputs.surface.isConnected()
|
|
114
114
|
? ctx.outputVar(this._inputs.surface.connection.node, this._inputs.surface.connection.name)
|
|
115
|
-
: '
|
|
116
|
-
return `gl_FragColor =
|
|
115
|
+
: 'vec4(0.8, 0.8, 0.8, 1.0)';
|
|
116
|
+
return `gl_FragColor = ${sv};`;
|
|
117
117
|
}
|
|
118
118
|
}
|
|
@@ -26,11 +26,11 @@ export class AddShader extends ProcessNode {
|
|
|
26
26
|
compileCall(ctx) {
|
|
27
27
|
const s1 = ctx.resolveInput(this._inputs.shader1);
|
|
28
28
|
const s2 = ctx.resolveInput(this._inputs.shader2);
|
|
29
|
-
return `
|
|
29
|
+
return `vec4 ${ctx.outputVar(this, 'BSDF')} = _st_addShader(${s1}, ${s2});`;
|
|
30
30
|
}
|
|
31
31
|
}
|
|
32
32
|
AddShader.instanceSpecificDef = false;
|
|
33
33
|
AddShader.glslFunction = `
|
|
34
|
-
|
|
35
|
-
return a + b;
|
|
34
|
+
vec4 _st_addShader(vec4 a, vec4 b) {
|
|
35
|
+
return vec4(a.rgb + b.rgb, max(a.a, b.a));
|
|
36
36
|
}`;
|
|
@@ -29,7 +29,7 @@ export class DiffuseBSDF extends ProcessNode {
|
|
|
29
29
|
const normal = this._inputs.normal.isConnected()
|
|
30
30
|
? ctx.outputVar(this._inputs.normal.connection.node, this._inputs.normal.connection.name)
|
|
31
31
|
: 'normalize(vNormal)';
|
|
32
|
-
return `
|
|
32
|
+
return `vec4 ${ctx.outputVar(this, 'BSDF')} = vec4(_st_diffuseBSDF(${color}, ${roughness}, ${normal}), 1.0);`;
|
|
33
33
|
}
|
|
34
34
|
}
|
|
35
35
|
DiffuseBSDF.instanceSpecificDef = false;
|
|
@@ -26,7 +26,7 @@ export class Emission extends ProcessNode {
|
|
|
26
26
|
compileCall(ctx) {
|
|
27
27
|
const color = ctx.resolveInput(this._inputs.color);
|
|
28
28
|
const strength = ctx.resolveInput(this._inputs.strength);
|
|
29
|
-
return `
|
|
29
|
+
return `vec4 ${ctx.outputVar(this, 'BSDF')} = vec4(_st_emission(${color}, ${strength}), 1.0);`;
|
|
30
30
|
}
|
|
31
31
|
}
|
|
32
32
|
Emission.instanceSpecificDef = false;
|
|
@@ -31,7 +31,7 @@ export class GlassBSDF extends ProcessNode {
|
|
|
31
31
|
const normal = this._inputs.normal.isConnected()
|
|
32
32
|
? ctx.outputVar(this._inputs.normal.connection.node, this._inputs.normal.connection.name)
|
|
33
33
|
: 'normalize(vNormal)';
|
|
34
|
-
return `
|
|
34
|
+
return `vec4 ${ctx.outputVar(this, 'BSDF')} = vec4(_st_glassBSDF(${color}, ${roughness}, ${ior}, ${normal}), 1.0);`;
|
|
35
35
|
}
|
|
36
36
|
}
|
|
37
37
|
GlassBSDF.instanceSpecificDef = false;
|
|
@@ -29,7 +29,7 @@ export class GlossyBSDF extends ProcessNode {
|
|
|
29
29
|
const normal = this._inputs.normal.isConnected()
|
|
30
30
|
? ctx.outputVar(this._inputs.normal.connection.node, this._inputs.normal.connection.name)
|
|
31
31
|
: 'normalize(vNormal)';
|
|
32
|
-
return `
|
|
32
|
+
return `vec4 ${ctx.outputVar(this, 'BSDF')} = vec4(_st_glossyBSDF(${color}, ${roughness}, ${normal}), 1.0);`;
|
|
33
33
|
}
|
|
34
34
|
}
|
|
35
35
|
GlossyBSDF.instanceSpecificDef = false;
|
|
@@ -37,11 +37,11 @@ export class MixShader extends ProcessNode {
|
|
|
37
37
|
const fac = ctx.resolveInput(this._inputs.fac);
|
|
38
38
|
const shader1 = ctx.resolveInput(this._inputs.shader1);
|
|
39
39
|
const shader2 = ctx.resolveInput(this._inputs.shader2);
|
|
40
|
-
return `
|
|
40
|
+
return `vec4 ${ctx.outputVar(this, 'BSDF')} = _st_mixShader(${fac}, ${shader1}, ${shader2});`;
|
|
41
41
|
}
|
|
42
42
|
}
|
|
43
43
|
MixShader.instanceSpecificDef = false;
|
|
44
44
|
MixShader.glslFunction = `
|
|
45
|
-
|
|
45
|
+
vec4 _st_mixShader(float fac, vec4 a, vec4 b) {
|
|
46
46
|
return mix(a, b, clamp(fac, 0.0, 1.0));
|
|
47
47
|
}`;
|
|
@@ -27,6 +27,13 @@ export declare class PrincipledBSDF extends ProcessNode {
|
|
|
27
27
|
getInputSockets(): Record<string, import("../../index.js").InputSocket<unknown>>;
|
|
28
28
|
getOutputSockets(): Record<string, OutputSocket>;
|
|
29
29
|
compileDefs(): string;
|
|
30
|
+
/**
|
|
31
|
+
* Alpha has no channel of its own in the underlying vec3 lighting function —
|
|
32
|
+
* it's applied as the vec4 alpha component at the call site instead (see
|
|
33
|
+
* compileCall). transparent:true is needed whenever alpha can read below 1.0:
|
|
34
|
+
* a non-default literal, or any live/connected value (conservatively true).
|
|
35
|
+
*/
|
|
36
|
+
wantsTransparency(): boolean;
|
|
30
37
|
compileCall(ctx: CompileContext): string;
|
|
31
38
|
}
|
|
32
39
|
//# sourceMappingURL=PrincipledBSDF.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"PrincipledBSDF.d.ts","sourceRoot":"","sources":["../../../src/nodes/process/PrincipledBSDF.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,2BAA2B,CAAA;AACvD,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,0BAA0B,CAAA;AAC5D,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,8BAA8B,CAAA;AAClE,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,4BAA4B,CAAA;AAE9D,MAAM,WAAW,oBAAoB;IACnC,SAAS,CAAC,EAAE,YAAY,GAAG,MAAM,CAAA;IACjC,QAAQ,CAAC,EAAG,YAAY,GAAG,MAAM,CAAA;IACjC,SAAS,CAAC,EAAE,YAAY,GAAG,MAAM,CAAA;IACjC,GAAG,CAAC,EAAQ,YAAY,GAAG,MAAM,CAAA;IACjC,KAAK,CAAC,EAAM,YAAY,GAAG,MAAM,CAAA;IACjC,MAAM,CAAC,EAAK,YAAY,CAAA;CACzB;AAED;;;;;GAKG;AACH,qBAAa,cAAe,SAAQ,WAAW;IAC7C,IAAI,QAAQ,WAA8B;IAC1C,MAAM,CAAC,mBAAmB,UAAQ;IAElC,IAAI,QAAQ,IAAI,YAAY,CAE3B;IAED,MAAM,CAAC,YAAY,SAuCnB;IAEA,OAAO,CAAC,QAAQ,CAAC,OAAO,CAA2E;IACnG,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAmE;gBAEhF,MAAM,GAAE,oBAAyB;IAa7C,eAAe;IACf,gBAAgB;IAChB,WAAW;IAEX,WAAW,CAAC,GAAG,EAAE,cAAc,GAAG,MAAM;
|
|
1
|
+
{"version":3,"file":"PrincipledBSDF.d.ts","sourceRoot":"","sources":["../../../src/nodes/process/PrincipledBSDF.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,2BAA2B,CAAA;AACvD,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,0BAA0B,CAAA;AAC5D,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,8BAA8B,CAAA;AAClE,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,4BAA4B,CAAA;AAE9D,MAAM,WAAW,oBAAoB;IACnC,SAAS,CAAC,EAAE,YAAY,GAAG,MAAM,CAAA;IACjC,QAAQ,CAAC,EAAG,YAAY,GAAG,MAAM,CAAA;IACjC,SAAS,CAAC,EAAE,YAAY,GAAG,MAAM,CAAA;IACjC,GAAG,CAAC,EAAQ,YAAY,GAAG,MAAM,CAAA;IACjC,KAAK,CAAC,EAAM,YAAY,GAAG,MAAM,CAAA;IACjC,MAAM,CAAC,EAAK,YAAY,CAAA;CACzB;AAED;;;;;GAKG;AACH,qBAAa,cAAe,SAAQ,WAAW;IAC7C,IAAI,QAAQ,WAA8B;IAC1C,MAAM,CAAC,mBAAmB,UAAQ;IAElC,IAAI,QAAQ,IAAI,YAAY,CAE3B;IAED,MAAM,CAAC,YAAY,SAuCnB;IAEA,OAAO,CAAC,QAAQ,CAAC,OAAO,CAA2E;IACnG,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAmE;gBAEhF,MAAM,GAAE,oBAAyB;IAa7C,eAAe;IACf,gBAAgB;IAChB,WAAW;IAEX;;;;;OAKG;IACH,iBAAiB,IAAI,OAAO;IAO5B,WAAW,CAAC,GAAG,EAAE,cAAc,GAAG,MAAM;CAQzC"}
|
|
@@ -25,11 +25,26 @@ export class PrincipledBSDF extends ProcessNode {
|
|
|
25
25
|
getInputSockets() { return this._inputs; }
|
|
26
26
|
getOutputSockets() { return this._outputs; }
|
|
27
27
|
compileDefs() { return this.glslFunction ?? PrincipledBSDF.glslFunction; }
|
|
28
|
+
/**
|
|
29
|
+
* Alpha has no channel of its own in the underlying vec3 lighting function —
|
|
30
|
+
* it's applied as the vec4 alpha component at the call site instead (see
|
|
31
|
+
* compileCall). transparent:true is needed whenever alpha can read below 1.0:
|
|
32
|
+
* a non-default literal, or any live/connected value (conservatively true).
|
|
33
|
+
*/
|
|
34
|
+
wantsTransparency() {
|
|
35
|
+
const alphaSocket = this._inputs.alpha;
|
|
36
|
+
if (alphaSocket.isConnected())
|
|
37
|
+
return true;
|
|
38
|
+
const v = alphaSocket.defaultValue;
|
|
39
|
+
return typeof v === 'number' && v < 1.0;
|
|
40
|
+
}
|
|
28
41
|
compileCall(ctx) {
|
|
29
42
|
const normal = this._inputs.normal.isConnected()
|
|
30
43
|
? ctx.outputVar(this._inputs.normal.connection.node, this._inputs.normal.connection.name)
|
|
31
44
|
: 'normalize(vNormal)';
|
|
32
|
-
|
|
45
|
+
const rgb = `_st_principledBSDF(${ctx.resolveInput(this._inputs.baseColor)}, ${ctx.resolveInput(this._inputs.metallic)}, ${ctx.resolveInput(this._inputs.roughness)}, ${ctx.resolveInput(this._inputs.ior)}, ${normal})`;
|
|
46
|
+
const alpha = ctx.resolveInput(this._inputs.alpha);
|
|
47
|
+
return `vec4 ${ctx.outputVar(this, 'BSDF')} = vec4(${rgb}, ${alpha});`;
|
|
33
48
|
}
|
|
34
49
|
}
|
|
35
50
|
PrincipledBSDF.instanceSpecificDef = false;
|
|
@@ -54,7 +69,7 @@ float _st_geo(float NdotX, float r) {
|
|
|
54
69
|
float k = (r + 1.0) * (r + 1.0) / 8.0;
|
|
55
70
|
return NdotX / max(NdotX * (1.0 - k) + k, 1e-7);
|
|
56
71
|
}
|
|
57
|
-
vec3 _st_principledBSDF(vec3 base, float metallic, float roughness, float ior,
|
|
72
|
+
vec3 _st_principledBSDF(vec3 base, float metallic, float roughness, float ior, vec3 N) {
|
|
58
73
|
vec3 V = normalize(cameraPosition - vPosition);
|
|
59
74
|
vec3 L = normalize(uSunDirection);
|
|
60
75
|
vec3 H = normalize(L + V);
|
|
@@ -59,7 +59,7 @@ export class PrincipledHair extends ProcessNode {
|
|
|
59
59
|
const tangent = this._inputs.tangent.isConnected()
|
|
60
60
|
? ctx.outputVar(this._inputs.tangent.connection.node, this._inputs.tangent.connection.name)
|
|
61
61
|
: 'normalize(vStrandTangent)'; // fallback: read attribute directly if not connected
|
|
62
|
-
return `
|
|
62
|
+
return `vec4 ${ctx.outputVar(this, 'BSDF')} = vec4(_st_principledHair(${color}, ${roughness}, ${radialRoughness}, ${coat}, ${ior}, ${offset}, ${randomColor}, ${randomRoughness}, ${random}, ${tangent}), 1.0);`;
|
|
63
63
|
}
|
|
64
64
|
}
|
|
65
65
|
PrincipledHair.instanceSpecificDef = false;
|
|
@@ -39,7 +39,7 @@ export class PrincipledVolume extends ProcessNode {
|
|
|
39
39
|
const emissionStrength = ctx.resolveInput(this._inputs.emissionStrength);
|
|
40
40
|
const blackbodyIntensity = ctx.resolveInput(this._inputs.blackbodyIntensity);
|
|
41
41
|
const temperature = ctx.resolveInput(this._inputs.temperature);
|
|
42
|
-
return `
|
|
42
|
+
return `vec4 ${ctx.outputVar(this, 'Volume')} = vec4(_st_principledVolume(${color}, ${density}, ${anisotropy}, ${absorptionColor}, ${emissionColor}, ${emissionStrength}, ${blackbodyIntensity}, ${temperature}), 1.0);`;
|
|
43
43
|
}
|
|
44
44
|
}
|
|
45
45
|
PrincipledVolume.instanceSpecificDef = false;
|
|
@@ -32,7 +32,7 @@ export class RefractionBSDF extends ProcessNode {
|
|
|
32
32
|
const normal = this._inputs.normal.isConnected()
|
|
33
33
|
? ctx.outputVar(this._inputs.normal.connection.node, this._inputs.normal.connection.name)
|
|
34
34
|
: 'normalize(vNormal)';
|
|
35
|
-
return `
|
|
35
|
+
return `vec4 ${ctx.outputVar(this, 'BSDF')} = vec4(_st_refractionBSDF(${color}, ${roughness}, ${ior}, ${normal}), 1.0);`;
|
|
36
36
|
}
|
|
37
37
|
}
|
|
38
38
|
RefractionBSDF.instanceSpecificDef = false;
|
|
@@ -7,7 +7,7 @@ import type { OutputSocket } from '../../core/OutputSocket.js';
|
|
|
7
7
|
* 'float' → float (uniform or literal)
|
|
8
8
|
* 'color' → vec3 (uniform or literal hex / [r,g,b])
|
|
9
9
|
* 'vector' → vec2 (UV coordinates)
|
|
10
|
-
* 'shader' →
|
|
10
|
+
* 'shader' → vec4 (BSDF connection, rgb + alpha)
|
|
11
11
|
*/
|
|
12
12
|
export type ScriptInputDecl = ['float' | 'color' | 'vector' | 'shader', unknown];
|
|
13
13
|
/** Output socket type map: socketName → glslType */
|
|
@@ -49,7 +49,7 @@ export interface ShaderScriptConfig {
|
|
|
49
49
|
* Inside the GLSL body:
|
|
50
50
|
* • Input sockets are available as local variables with the exact name you declared.
|
|
51
51
|
* • Output sockets must be assigned by name before the block ends.
|
|
52
|
-
* • GLSL types: 'float' → float, 'color'
|
|
52
|
+
* • GLSL types: 'float' → float, 'color' → vec3, 'shader' → vec4, 'vector' → vec2
|
|
53
53
|
*
|
|
54
54
|
* @example
|
|
55
55
|
* const ripple = new ShaderScript({
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ShaderScript.d.ts","sourceRoot":"","sources":["../../../src/nodes/process/ShaderScript.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,2BAA2B,CAAA;AAEvD,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,0BAA0B,CAAA;AAC5D,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,8BAA8B,CAAA;AAClE,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,4BAA4B,CAAA;AAK9D;;;;;;GAMG;AACH,MAAM,MAAM,eAAe,GAAG,CAAC,OAAO,GAAG,OAAO,GAAG,QAAQ,GAAG,QAAQ,EAAE,OAAO,CAAC,CAAA;AAEhF,oDAAoD;AACpD,MAAM,MAAM,gBAAgB,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,GAAG,OAAO,GAAG,QAAQ,GAAG,QAAQ,CAAC,CAAA;AAEtF,MAAM,WAAW,kBAAkB;IACjC;;;;OAIG;IACH,MAAM,CAAC,EAAG,MAAM,CAAC,MAAM,EAAE,eAAe,GAAG,YAAY,CAAC,CAAA;IAExD,2CAA2C;IAC3C,OAAO,CAAC,EAAE,gBAAgB,CAAA;IAE1B;;;;;;;;;OASG;IACH,IAAI,EAAE,MAAM,CAAA;IAEZ;;;OAGG;IACH,IAAI,CAAC,EAAE,MAAM,CAAA;IAEb,yDAAyD;IACzD,KAAK,CAAC,EAAE,MAAM,CAAA;CACf;AAID;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACH,qBAAa,YAAa,SAAQ,WAAW;IAC3C,IAAI,QAAQ,WAA4B;IACxC,MAAM,CAAC,mBAAmB,UAAO;IAEjC,IAAI,QAAQ,IAAI,YAAY,CAQ3B;IAED,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAe;IACrC,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAe;IACrC,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAc;IACrC,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAmB;IAC/C,OAAO,CAAC,QAAQ,CAAC,OAAO,CAA+E;IACvG,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAkC;gBAE/C,MAAM,EAAE,kBAAkB;IAmCtC,eAAe;IACf,gBAAgB;IAEhB,mDAAmD;IACnD,WAAW,IAAI,MAAM;IAErB;;;;;;OAMG;IACH,WAAW,CAAC,GAAG,EAAE,cAAc,GAAG,MAAM;IAkCxC,OAAO,CAAC,eAAe;
|
|
1
|
+
{"version":3,"file":"ShaderScript.d.ts","sourceRoot":"","sources":["../../../src/nodes/process/ShaderScript.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,2BAA2B,CAAA;AAEvD,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,0BAA0B,CAAA;AAC5D,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,8BAA8B,CAAA;AAClE,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,4BAA4B,CAAA;AAK9D;;;;;;GAMG;AACH,MAAM,MAAM,eAAe,GAAG,CAAC,OAAO,GAAG,OAAO,GAAG,QAAQ,GAAG,QAAQ,EAAE,OAAO,CAAC,CAAA;AAEhF,oDAAoD;AACpD,MAAM,MAAM,gBAAgB,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,GAAG,OAAO,GAAG,QAAQ,GAAG,QAAQ,CAAC,CAAA;AAEtF,MAAM,WAAW,kBAAkB;IACjC;;;;OAIG;IACH,MAAM,CAAC,EAAG,MAAM,CAAC,MAAM,EAAE,eAAe,GAAG,YAAY,CAAC,CAAA;IAExD,2CAA2C;IAC3C,OAAO,CAAC,EAAE,gBAAgB,CAAA;IAE1B;;;;;;;;;OASG;IACH,IAAI,EAAE,MAAM,CAAA;IAEZ;;;OAGG;IACH,IAAI,CAAC,EAAE,MAAM,CAAA;IAEb,yDAAyD;IACzD,KAAK,CAAC,EAAE,MAAM,CAAA;CACf;AAID;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACH,qBAAa,YAAa,SAAQ,WAAW;IAC3C,IAAI,QAAQ,WAA4B;IACxC,MAAM,CAAC,mBAAmB,UAAO;IAEjC,IAAI,QAAQ,IAAI,YAAY,CAQ3B;IAED,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAe;IACrC,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAe;IACrC,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAc;IACrC,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAmB;IAC/C,OAAO,CAAC,QAAQ,CAAC,OAAO,CAA+E;IACvG,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAkC;gBAE/C,MAAM,EAAE,kBAAkB;IAmCtC,eAAe;IACf,gBAAgB;IAEhB,mDAAmD;IACnD,WAAW,IAAI,MAAM;IAErB;;;;;;OAMG;IACH,WAAW,CAAC,GAAG,EAAE,cAAc,GAAG,MAAM;IAkCxC,OAAO,CAAC,eAAe;IAOvB,OAAO,CAAC,aAAa;CAMtB"}
|
|
@@ -10,7 +10,7 @@ import { assertGlslIdentifier } from '../../core/glslIdentifier.js';
|
|
|
10
10
|
* Inside the GLSL body:
|
|
11
11
|
* • Input sockets are available as local variables with the exact name you declared.
|
|
12
12
|
* • Output sockets must be assigned by name before the block ends.
|
|
13
|
-
* • GLSL types: 'float' → float, 'color'
|
|
13
|
+
* • GLSL types: 'float' → float, 'color' → vec3, 'shader' → vec4, 'vector' → vec2
|
|
14
14
|
*
|
|
15
15
|
* @example
|
|
16
16
|
* const ripple = new ShaderScript({
|
|
@@ -111,14 +111,18 @@ export class ShaderScript extends ProcessNode {
|
|
|
111
111
|
return 'float';
|
|
112
112
|
if (type === 'vector')
|
|
113
113
|
return 'vec2';
|
|
114
|
-
|
|
114
|
+
if (type === 'shader')
|
|
115
|
+
return 'vec4';
|
|
116
|
+
return 'vec3'; // 'color'
|
|
115
117
|
}
|
|
116
118
|
_declGlslType(type) {
|
|
117
119
|
if (type === 'float')
|
|
118
120
|
return 'float';
|
|
119
121
|
if (type === 'vector')
|
|
120
122
|
return 'vec2';
|
|
121
|
-
|
|
123
|
+
if (type === 'shader')
|
|
124
|
+
return 'vec4';
|
|
125
|
+
return 'vec3'; // 'color'
|
|
122
126
|
}
|
|
123
127
|
}
|
|
124
128
|
ShaderScript.instanceSpecificDef = true; // every script is unique — no dedup
|
|
@@ -25,8 +25,8 @@ export class ShaderToRGB extends ProcessNode {
|
|
|
25
25
|
const cv = ctx.outputVar(this, 'Color');
|
|
26
26
|
const av = ctx.outputVar(this, 'Alpha');
|
|
27
27
|
return [
|
|
28
|
-
`vec3 ${cv} = ${shader};`,
|
|
29
|
-
`float ${av} =
|
|
28
|
+
`vec3 ${cv} = (${shader}).rgb;`,
|
|
29
|
+
`float ${av} = (${shader}).a;`,
|
|
30
30
|
].join('\n ');
|
|
31
31
|
}
|
|
32
32
|
}
|
|
@@ -30,7 +30,7 @@ export class SheenBSDF extends ProcessNode {
|
|
|
30
30
|
const normal = this._inputs.normal.isConnected()
|
|
31
31
|
? ctx.outputVar(this._inputs.normal.connection.node, this._inputs.normal.connection.name)
|
|
32
32
|
: 'normalize(vNormal)';
|
|
33
|
-
return `
|
|
33
|
+
return `vec4 ${ctx.outputVar(this, 'BSDF')} = vec4(_st_sheenBSDF(${color}, ${roughness}, ${normal}), 1.0);`;
|
|
34
34
|
}
|
|
35
35
|
}
|
|
36
36
|
SheenBSDF.instanceSpecificDef = false;
|
|
@@ -32,7 +32,7 @@ export class SpecularBSDF extends ProcessNode {
|
|
|
32
32
|
const normal = this._inputs.normal.isConnected()
|
|
33
33
|
? ctx.outputVar(this._inputs.normal.connection.node, this._inputs.normal.connection.name)
|
|
34
34
|
: 'normalize(vNormal)';
|
|
35
|
-
return `
|
|
35
|
+
return `vec4 ${ctx.outputVar(this, 'BSDF')} = vec4(_st_specularBSDF(${base}, ${spec}, ${roughness}, ${normal}), 1.0);`;
|
|
36
36
|
}
|
|
37
37
|
}
|
|
38
38
|
SpecularBSDF.instanceSpecificDef = false;
|
|
@@ -32,7 +32,7 @@ export class SubsurfaceScattering extends ProcessNode {
|
|
|
32
32
|
const normal = this._inputs.normal.isConnected()
|
|
33
33
|
? ctx.outputVar(this._inputs.normal.connection.node, this._inputs.normal.connection.name)
|
|
34
34
|
: 'normalize(vNormal)';
|
|
35
|
-
return `
|
|
35
|
+
return `vec4 ${ctx.outputVar(this, 'BSDF')} = vec4(_st_sss(${color}, ${scale}, vec3(${r.toFixed(4)}, ${g.toFixed(4)}, ${b.toFixed(4)}), ${normal}), 1.0);`;
|
|
36
36
|
}
|
|
37
37
|
}
|
|
38
38
|
SubsurfaceScattering.instanceSpecificDef = false;
|
|
@@ -31,7 +31,7 @@ export class ToonBSDF extends ProcessNode {
|
|
|
31
31
|
const normal = this._inputs.normal.isConnected()
|
|
32
32
|
? ctx.outputVar(this._inputs.normal.connection.node, this._inputs.normal.connection.name)
|
|
33
33
|
: 'normalize(vNormal)';
|
|
34
|
-
return `
|
|
34
|
+
return `vec4 ${ctx.outputVar(this, 'BSDF')} = vec4(_st_toonBSDF(${color}, ${size}, ${smoothWidth}, ${normal}), 1.0);`;
|
|
35
35
|
}
|
|
36
36
|
}
|
|
37
37
|
ToonBSDF.instanceSpecificDef = false;
|
|
@@ -27,7 +27,7 @@ export class TranslucentBSDF extends ProcessNode {
|
|
|
27
27
|
const normal = this._inputs.normal.isConnected()
|
|
28
28
|
? ctx.outputVar(this._inputs.normal.connection.node, this._inputs.normal.connection.name)
|
|
29
29
|
: 'normalize(vNormal)';
|
|
30
|
-
return `
|
|
30
|
+
return `vec4 ${ctx.outputVar(this, 'BSDF')} = vec4(_st_translucentBSDF(${color}, ${normal}), 1.0);`;
|
|
31
31
|
}
|
|
32
32
|
}
|
|
33
33
|
TranslucentBSDF.instanceSpecificDef = false;
|
|
@@ -26,7 +26,7 @@ export class VolumeAbsorption extends ProcessNode {
|
|
|
26
26
|
compileCall(ctx) {
|
|
27
27
|
const color = ctx.resolveInput(this._inputs.color);
|
|
28
28
|
const density = ctx.resolveInput(this._inputs.density);
|
|
29
|
-
return `
|
|
29
|
+
return `vec4 ${ctx.outputVar(this, 'Volume')} = vec4(_st_volumeAbsorption(${color}, ${density}), 1.0);`;
|
|
30
30
|
}
|
|
31
31
|
}
|
|
32
32
|
VolumeAbsorption.instanceSpecificDef = false;
|
|
@@ -28,7 +28,7 @@ export class VolumeScatter extends ProcessNode {
|
|
|
28
28
|
const color = ctx.resolveInput(this._inputs.color);
|
|
29
29
|
const density = ctx.resolveInput(this._inputs.density);
|
|
30
30
|
const anisotropy = ctx.resolveInput(this._inputs.anisotropy);
|
|
31
|
-
return `
|
|
31
|
+
return `vec4 ${ctx.outputVar(this, 'Volume')} = vec4(_st_volumeScatter(${color}, ${density}, ${anisotropy}), 1.0);`;
|
|
32
32
|
}
|
|
33
33
|
}
|
|
34
34
|
VolumeScatter.instanceSpecificDef = false;
|
|
@@ -50,7 +50,7 @@ export class RayPortal extends ProcessNode {
|
|
|
50
50
|
const blend = Number.isFinite(rawBlend) ? Math.min(Math.max(rawBlend, 0.0), 1.0).toFixed(4) : '1.0000';
|
|
51
51
|
// Rasterizer passthrough: BSDF = color * blend
|
|
52
52
|
// Portal Transform is intentionally unused in this mode.
|
|
53
|
-
return `
|
|
53
|
+
return `vec4 ${ctx.outputVar(this, 'BSDF')} = vec4(${color} * ${blend}, 1.0);`;
|
|
54
54
|
}
|
|
55
55
|
}
|
|
56
56
|
RayPortal.instanceSpecificDef = false;
|