@triforge/shader-core 0.2.1 → 0.4.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.
Files changed (46) hide show
  1. package/dist/bundle.js +196 -39
  2. package/dist/core/CompileContext.d.ts +2 -0
  3. package/dist/core/CompileContext.d.ts.map +1 -1
  4. package/dist/core/CompileContext.js +24 -9
  5. package/dist/core/OutputNode.d.ts.map +1 -1
  6. package/dist/core/OutputNode.js +1 -0
  7. package/dist/core/ShaderNode.d.ts +7 -0
  8. package/dist/core/ShaderNode.d.ts.map +1 -1
  9. package/dist/core/ShaderNode.js +23 -0
  10. package/dist/core/SocketType.d.ts +1 -1
  11. package/dist/core/SocketType.js +1 -1
  12. package/dist/index.d.ts +1 -0
  13. package/dist/index.d.ts.map +1 -1
  14. package/dist/index.js +1 -0
  15. package/dist/nodes/output/MaterialOutput.js +2 -2
  16. package/dist/nodes/process/AddShader.js +4 -4
  17. package/dist/nodes/process/Bump.d.ts +25 -2
  18. package/dist/nodes/process/Bump.d.ts.map +1 -1
  19. package/dist/nodes/process/Bump.js +113 -32
  20. package/dist/nodes/process/DiffuseBSDF.js +8 -8
  21. package/dist/nodes/process/Emission.js +4 -4
  22. package/dist/nodes/process/GlassBSDF.js +14 -14
  23. package/dist/nodes/process/GlossyBSDF.js +15 -15
  24. package/dist/nodes/process/MixShader.js +4 -4
  25. package/dist/nodes/process/PrincipledBSDF.d.ts +7 -0
  26. package/dist/nodes/process/PrincipledBSDF.d.ts.map +1 -1
  27. package/dist/nodes/process/PrincipledBSDF.js +55 -40
  28. package/dist/nodes/process/PrincipledHair.js +71 -71
  29. package/dist/nodes/process/PrincipledVolume.js +22 -22
  30. package/dist/nodes/process/RefractionBSDF.js +8 -8
  31. package/dist/nodes/process/ShaderScript.d.ts +2 -2
  32. package/dist/nodes/process/ShaderScript.d.ts.map +1 -1
  33. package/dist/nodes/process/ShaderScript.js +7 -3
  34. package/dist/nodes/process/ShaderToRGB.js +2 -2
  35. package/dist/nodes/process/SheenBSDF.js +13 -13
  36. package/dist/nodes/process/SpecularBSDF.js +16 -16
  37. package/dist/nodes/process/SubsurfaceScattering.js +8 -8
  38. package/dist/nodes/process/ToonBSDF.js +7 -7
  39. package/dist/nodes/process/TranslucentBSDF.js +6 -6
  40. package/dist/nodes/process/TransparentBSDF.d.ts +26 -0
  41. package/dist/nodes/process/TransparentBSDF.d.ts.map +1 -0
  42. package/dist/nodes/process/TransparentBSDF.js +29 -0
  43. package/dist/nodes/process/VolumeAbsorption.js +4 -4
  44. package/dist/nodes/process/VolumeScatter.js +8 -8
  45. package/dist/nodes/shader/RayPortal.js +1 -1
  46. package/package.json +1 -1
package/dist/bundle.js CHANGED
@@ -139,6 +139,15 @@ var ShaderNode = class {
139
139
  vertexInjections() {
140
140
  return [];
141
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
+ }
142
151
  /**
143
152
  * Returns the named output socket.
144
153
  * Throws ShaderNodeError if the socket does not exist.
@@ -228,6 +237,18 @@ var ShaderNode = class {
228
237
  enumerable: true,
229
238
  configurable: true
230
239
  });
240
+ const AXES = ["x", "y", "z"];
241
+ AXES.forEach((axis, idx) => {
242
+ Object.defineProperty(this.parameters, `${paramName}.${axis}`, {
243
+ get: () => uniforms[uniformName].value[idx],
244
+ set: (v) => {
245
+ const arr = uniforms[uniformName].value;
246
+ arr[idx] = finiteOr(v, 0);
247
+ },
248
+ enumerable: true,
249
+ configurable: true
250
+ });
251
+ });
231
252
  } else {
232
253
  Object.defineProperty(this.parameters, paramName, {
233
254
  get: () => uniforms[uniformName].value,
@@ -295,7 +316,8 @@ var CompileContext = class _CompileContext {
295
316
  vertexShader: this.buildVertexShader(),
296
317
  fragmentShader: this.buildFragmentShader(),
297
318
  uniforms: this._uniforms,
298
- nodes: this.order
319
+ nodes: this.order,
320
+ needsTransparency: this.order.some((node) => node.wantsTransparency())
299
321
  };
300
322
  }
301
323
  /**
@@ -315,12 +337,23 @@ var CompileContext = class _CompileContext {
315
337
  resolveInput(socket) {
316
338
  if (socket.connection) {
317
339
  const varName = this.outputVar(socket.connection.node, socket.connection.name);
318
- if (socket.type === "vector" && socket.connection.type !== "vector") {
340
+ const fromType = socket.connection.type;
341
+ const toType = socket.type;
342
+ if (toType === "vector" && fromType !== "vector") {
319
343
  return `(${varName}).xy`;
320
344
  }
321
- if (socket.type === "color" && socket.connection.type === "vector") {
345
+ if (toType === "color" && fromType === "vector") {
322
346
  return `vec3(${varName}, 0.0)`;
323
347
  }
348
+ if (toType === "color" && fromType === "shader") {
349
+ return `(${varName}).rgb`;
350
+ }
351
+ if (toType === "shader" && fromType === "color") {
352
+ return `vec4(${varName}, 1.0)`;
353
+ }
354
+ if (toType === "shader" && fromType === "vector") {
355
+ return `vec4(${varName}, 0.0, 1.0)`;
356
+ }
324
357
  return varName;
325
358
  }
326
359
  if (socket.uniformName !== null) {
@@ -436,7 +469,7 @@ var CompileContext = class _CompileContext {
436
469
  if (from === to) return true;
437
470
  if ((from === "shader" || from === "color") && (to === "shader" || to === "color")) return true;
438
471
  if ((from === "color" || from === "shader") && to === "vector") return true;
439
- if (from === "vector" && to === "color") return true;
472
+ if (from === "vector" && (to === "color" || to === "shader")) return true;
440
473
  return false;
441
474
  }
442
475
  // ── GLSL builders ─────────────────────────────────────────────────
@@ -494,7 +527,7 @@ ${callsBlock}
494
527
  case "vector":
495
528
  return "vec2(vUv)";
496
529
  case "shader":
497
- return "vec3(0.0)";
530
+ return "vec4(0.0, 0.0, 0.0, 1.0)";
498
531
  default:
499
532
  return "vec3(1.0)";
500
533
  }
@@ -553,7 +586,8 @@ var OutputNode = class extends ShaderNode {
553
586
  this.material = new ShaderMaterial({
554
587
  vertexShader: compiled.vertexShader,
555
588
  fragmentShader: compiled.fragmentShader,
556
- uniforms: compiled.uniforms
589
+ uniforms: compiled.uniforms,
590
+ transparent: compiled.needsTransparency
557
591
  });
558
592
  if (!this.material.uniforms.uSunDirection) {
559
593
  this.material.uniforms.uSunDirection = { value: new Vector3(1, 2, 1).normalize() };
@@ -577,7 +611,7 @@ var ProcessNode = class extends ShaderNode {
577
611
 
578
612
  // src/core/SocketType.ts
579
613
  var SOCKET_GLSL_TYPE = {
580
- shader: "vec3",
614
+ shader: "vec4",
581
615
  color: "vec3",
582
616
  vector: "vec2",
583
617
  float: "float"
@@ -1231,8 +1265,8 @@ var MaterialOutput = class extends OutputNode {
1231
1265
  return mat;
1232
1266
  }
1233
1267
  compileCall(ctx) {
1234
- const sv = this._inputs.surface.isConnected() ? ctx.outputVar(this._inputs.surface.connection.node, this._inputs.surface.connection.name) : "vec3(0.8)";
1235
- return `gl_FragColor = vec4(${sv}, 1.0);`;
1268
+ 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)";
1269
+ return `gl_FragColor = ${sv};`;
1236
1270
  }
1237
1271
  };
1238
1272
 
@@ -2731,13 +2765,19 @@ var Bump = class _Bump extends ProcessNode {
2731
2765
  return "Bump";
2732
2766
  }
2733
2767
  static {
2734
- this.instanceSpecificDef = false;
2768
+ // Both modes emit their own per-instance defs (a #ifndef-guarded shared function
2769
+ // for 'derivative', a per-instance uniform declaration for 'uv-offset' — see
2770
+ // compileDefs). instanceSpecificDef:true is required so multiple 'uv-offset'
2771
+ // instances each get their own uniform decl instead of being deduped by nodeType.
2772
+ this.instanceSpecificDef = true;
2735
2773
  }
2736
2774
  get metadata() {
2737
2775
  return { label: "Bump", category: "Vector", color: "#4a3a8a", cost: "medium" };
2738
2776
  }
2739
2777
  static {
2740
- this.glslFunction = `
2778
+ this.derivativeGlslFunction = `
2779
+ #ifndef _ST_BUMP_FN
2780
+ #define _ST_BUMP_FN
2741
2781
  vec3 _st_bump(float height, float strength, float distance, vec3 N) {
2742
2782
  // Cotangent frame bump \u2014 camera-independent.
2743
2783
  // We derive world-space surface tangent T and bitangent B from the UV
@@ -2764,15 +2804,34 @@ vec3 _st_bump(float height, float strength, float distance, vec3 N) {
2764
2804
 
2765
2805
  float scale = strength * distance * 50.0;
2766
2806
  return normalize(N - T * dhdu * scale - B * dhdv * scale);
2767
- }`;
2807
+ }
2808
+ #endif`;
2768
2809
  }
2769
2810
  constructor(inputs = {}) {
2770
2811
  super("Bump");
2812
+ this._method = inputs.method ?? "derivative";
2813
+ if (this._method === "uv-offset") {
2814
+ if (ShaderConfig.errorLevel !== "off" && !inputs.uniformName) {
2815
+ ShaderNodeError.throw({
2816
+ nodeType: "Bump",
2817
+ nodeId: this.id,
2818
+ problem: 'uniformName is required when method:"uv-offset".',
2819
+ fix: 'new Bump({ method: "uv-offset", uniformName: "uHeightTex" })\n After compile(): mat.material.uniforms.uHeightTex = { value: texture }'
2820
+ });
2821
+ }
2822
+ if (inputs.uniformName) assertGlslIdentifier(inputs.uniformName, "Bump uniformName");
2823
+ this._uniformName = inputs.uniformName ?? null;
2824
+ } else {
2825
+ this._uniformName = null;
2826
+ }
2771
2827
  this._inputs = this.createInputs(inputs, {
2772
2828
  strength: ["float", inputs.strength ?? 1],
2773
2829
  distance: ["float", inputs.distance ?? 1],
2774
2830
  height: ["float", 0.5],
2775
- normal: ["color", null]
2831
+ normal: ["color", null],
2832
+ vector: ["vector", null],
2833
+ texelSize: ["float", inputs.texelSize ?? 1 / 512],
2834
+ lod: ["float", inputs.lod ?? 0]
2776
2835
  });
2777
2836
  this._outputs = this.createOutputs({ Normal: "color" });
2778
2837
  }
@@ -2783,13 +2842,60 @@ vec3 _st_bump(float height, float strength, float distance, vec3 N) {
2783
2842
  return this._outputs;
2784
2843
  }
2785
2844
  compileDefs() {
2786
- return _Bump.glslFunction;
2845
+ if (this._method === "uv-offset") {
2846
+ return [
2847
+ "#ifndef _ST_BUMP_TEXLOD_EXT",
2848
+ "#define _ST_BUMP_TEXLOD_EXT",
2849
+ "#extension GL_EXT_shader_texture_lod : enable",
2850
+ "#endif",
2851
+ `uniform sampler2D ${this._uniformName};`
2852
+ ].join("\n");
2853
+ }
2854
+ return _Bump.derivativeGlslFunction;
2787
2855
  }
2788
2856
  compileCall(ctx) {
2789
- const height = ctx.resolveInput(this._inputs.height);
2790
2857
  const strength = ctx.resolveInput(this._inputs.strength);
2791
2858
  const distance = ctx.resolveInput(this._inputs.distance);
2792
2859
  const normal = this._inputs.normal.isConnected() ? ctx.outputVar(this._inputs.normal.connection.node, this._inputs.normal.connection.name) : "normalize(vNormal)";
2860
+ if (this._method === "uv-offset") {
2861
+ const uv = this._inputs.vector.isConnected() ? ctx.resolveInput(this._inputs.vector) : "vUv";
2862
+ const texelSize = ctx.resolveInput(this._inputs.texelSize);
2863
+ const lod = ctx.resolveInput(this._inputs.lod);
2864
+ const id = this.id;
2865
+ const hL = `_bmp_${id}_hL`, hR = `_bmp_${id}_hR`;
2866
+ const hD = `_bmp_${id}_hD`, hU = `_bmp_${id}_hU`;
2867
+ const dPdx = `_bmp_${id}_dPdx`, dPdy = `_bmp_${id}_dPdy`;
2868
+ const dUdx = `_bmp_${id}_dUdx`, dUdy = `_bmp_${id}_dUdy`;
2869
+ const det = `_bmp_${id}_det`, sgn = `_bmp_${id}_sgn`, invDet = `_bmp_${id}_invDet`;
2870
+ const T = `_bmp_${id}_T`, B = `_bmp_${id}_B`;
2871
+ const dhdu = `_bmp_${id}_dhdu`, dhdv = `_bmp_${id}_dhdv`;
2872
+ const scale = `_bmp_${id}_scale`;
2873
+ const ts = `_bmp_${id}_ts`;
2874
+ return [
2875
+ `float ${ts} = ${texelSize};`,
2876
+ `float ${hL} = texture2DLodEXT(${this._uniformName}, ${uv} - vec2(${ts}, 0.0), ${lod}).r;`,
2877
+ `float ${hR} = texture2DLodEXT(${this._uniformName}, ${uv} + vec2(${ts}, 0.0), ${lod}).r;`,
2878
+ `float ${hD} = texture2DLodEXT(${this._uniformName}, ${uv} - vec2(0.0, ${ts}), ${lod}).r;`,
2879
+ `float ${hU} = texture2DLodEXT(${this._uniformName}, ${uv} + vec2(0.0, ${ts}), ${lod}).r;`,
2880
+ // Cotangent frame from screen-space derivatives — this builds the tangent
2881
+ // basis (surface-fixed), not the height sample, so it doesn't carry the
2882
+ // camera-distance instability the uv-offset method exists to avoid.
2883
+ `vec3 ${dPdx} = dFdx(vPosition);`,
2884
+ `vec3 ${dPdy} = dFdy(vPosition);`,
2885
+ `vec2 ${dUdx} = dFdx(vUv);`,
2886
+ `vec2 ${dUdy} = dFdy(vUv);`,
2887
+ `float ${det} = ${dUdx}.x * ${dUdy}.y - ${dUdx}.y * ${dUdy}.x;`,
2888
+ `float ${sgn} = sign(${det});`,
2889
+ `float ${invDet} = ${sgn} / max(abs(${det}), 1e-6);`,
2890
+ `vec3 ${T} = normalize((${dUdy}.y * ${dPdx} - ${dUdx}.y * ${dPdy}) * ${invDet});`,
2891
+ `vec3 ${B} = normalize(cross(${normal}, ${T})) * ${sgn};`,
2892
+ `float ${dhdu} = (${hR} - ${hL}) / max(2.0 * ${ts}, 1e-6);`,
2893
+ `float ${dhdv} = (${hU} - ${hD}) / max(2.0 * ${ts}, 1e-6);`,
2894
+ `float ${scale} = ${strength} * ${distance} * 50.0;`,
2895
+ `vec3 ${ctx.outputVar(this, "Normal")} = normalize(${normal} - ${T} * ${dhdu} * ${scale} - ${B} * ${dhdv} * ${scale});`
2896
+ ].join("\n ");
2897
+ }
2898
+ const height = ctx.resolveInput(this._inputs.height);
2793
2899
  return `vec3 ${ctx.outputVar(this, "Normal")} = _st_bump(${height}, ${strength}, ${distance}, ${normal});`;
2794
2900
  }
2795
2901
  };
@@ -3381,7 +3487,7 @@ float _st_geo(float NdotX, float r) {
3381
3487
  float k = (r + 1.0) * (r + 1.0) / 8.0;
3382
3488
  return NdotX / max(NdotX * (1.0 - k) + k, 1e-7);
3383
3489
  }
3384
- vec3 _st_principledBSDF(vec3 base, float metallic, float roughness, float ior, float alpha, vec3 N) {
3490
+ vec3 _st_principledBSDF(vec3 base, float metallic, float roughness, float ior, vec3 N) {
3385
3491
  vec3 V = normalize(cameraPosition - vPosition);
3386
3492
  vec3 L = normalize(uSunDirection);
3387
3493
  vec3 H = normalize(L + V);
@@ -3422,9 +3528,23 @@ vec3 _st_principledBSDF(vec3 base, float metallic, float roughness, float ior, f
3422
3528
  compileDefs() {
3423
3529
  return this.glslFunction ?? _PrincipledBSDF.glslFunction;
3424
3530
  }
3531
+ /**
3532
+ * Alpha has no channel of its own in the underlying vec3 lighting function —
3533
+ * it's applied as the vec4 alpha component at the call site instead (see
3534
+ * compileCall). transparent:true is needed whenever alpha can read below 1.0:
3535
+ * a non-default literal, or any live/connected value (conservatively true).
3536
+ */
3537
+ wantsTransparency() {
3538
+ const alphaSocket = this._inputs.alpha;
3539
+ if (alphaSocket.isConnected()) return true;
3540
+ const v = alphaSocket.defaultValue;
3541
+ return typeof v === "number" && v < 1;
3542
+ }
3425
3543
  compileCall(ctx) {
3426
3544
  const normal = this._inputs.normal.isConnected() ? ctx.outputVar(this._inputs.normal.connection.node, this._inputs.normal.connection.name) : "normalize(vNormal)";
3427
- return `vec3 ${ctx.outputVar(this, "BSDF")} = _st_principledBSDF(${ctx.resolveInput(this._inputs.baseColor)}, ${ctx.resolveInput(this._inputs.metallic)}, ${ctx.resolveInput(this._inputs.roughness)}, ${ctx.resolveInput(this._inputs.ior)}, ${ctx.resolveInput(this._inputs.alpha)}, ${normal});`;
3545
+ 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})`;
3546
+ const alpha = ctx.resolveInput(this._inputs.alpha);
3547
+ return `vec4 ${ctx.outputVar(this, "BSDF")} = vec4(${rgb}, ${alpha});`;
3428
3548
  }
3429
3549
  };
3430
3550
 
@@ -3465,7 +3585,7 @@ vec3 _st_emission(vec3 color, float strength) {
3465
3585
  compileCall(ctx) {
3466
3586
  const color = ctx.resolveInput(this._inputs.color);
3467
3587
  const strength = ctx.resolveInput(this._inputs.strength);
3468
- return `vec3 ${ctx.outputVar(this, "BSDF")} = _st_emission(${color}, ${strength});`;
3588
+ return `vec4 ${ctx.outputVar(this, "BSDF")} = vec4(_st_emission(${color}, ${strength}), 1.0);`;
3469
3589
  }
3470
3590
  };
3471
3591
 
@@ -3506,7 +3626,7 @@ vec3 _st_volumeAbsorption(vec3 color, float density) {
3506
3626
  compileCall(ctx) {
3507
3627
  const color = ctx.resolveInput(this._inputs.color);
3508
3628
  const density = ctx.resolveInput(this._inputs.density);
3509
- return `vec3 ${ctx.outputVar(this, "Volume")} = _st_volumeAbsorption(${color}, ${density});`;
3629
+ return `vec4 ${ctx.outputVar(this, "Volume")} = vec4(_st_volumeAbsorption(${color}, ${density}), 1.0);`;
3510
3630
  }
3511
3631
  };
3512
3632
 
@@ -3553,7 +3673,7 @@ vec3 _st_volumeScatter(vec3 color, float density, float anisotropy) {
3553
3673
  const color = ctx.resolveInput(this._inputs.color);
3554
3674
  const density = ctx.resolveInput(this._inputs.density);
3555
3675
  const anisotropy = ctx.resolveInput(this._inputs.anisotropy);
3556
- return `vec3 ${ctx.outputVar(this, "Volume")} = _st_volumeScatter(${color}, ${density}, ${anisotropy});`;
3676
+ return `vec4 ${ctx.outputVar(this, "Volume")} = vec4(_st_volumeScatter(${color}, ${density}, ${anisotropy}), 1.0);`;
3557
3677
  }
3558
3678
  };
3559
3679
 
@@ -3624,7 +3744,7 @@ vec3 _st_principledVolume(
3624
3744
  const emissionStrength = ctx.resolveInput(this._inputs.emissionStrength);
3625
3745
  const blackbodyIntensity = ctx.resolveInput(this._inputs.blackbodyIntensity);
3626
3746
  const temperature = ctx.resolveInput(this._inputs.temperature);
3627
- return `vec3 ${ctx.outputVar(this, "Volume")} = _st_principledVolume(${color}, ${density}, ${anisotropy}, ${absorptionColor}, ${emissionColor}, ${emissionStrength}, ${blackbodyIntensity}, ${temperature});`;
3747
+ return `vec4 ${ctx.outputVar(this, "Volume")} = vec4(_st_principledVolume(${color}, ${density}, ${anisotropy}, ${absorptionColor}, ${emissionColor}, ${emissionStrength}, ${blackbodyIntensity}, ${temperature}), 1.0);`;
3628
3748
  }
3629
3749
  };
3630
3750
 
@@ -3646,7 +3766,7 @@ var MixShader = class _MixShader extends ProcessNode {
3646
3766
  }
3647
3767
  static {
3648
3768
  this.glslFunction = `
3649
- vec3 _st_mixShader(float fac, vec3 a, vec3 b) {
3769
+ vec4 _st_mixShader(float fac, vec4 a, vec4 b) {
3650
3770
  return mix(a, b, clamp(fac, 0.0, 1.0));
3651
3771
  }`;
3652
3772
  }
@@ -3672,7 +3792,7 @@ vec3 _st_mixShader(float fac, vec3 a, vec3 b) {
3672
3792
  const fac = ctx.resolveInput(this._inputs.fac);
3673
3793
  const shader1 = ctx.resolveInput(this._inputs.shader1);
3674
3794
  const shader2 = ctx.resolveInput(this._inputs.shader2);
3675
- return `vec3 ${ctx.outputVar(this, "BSDF")} = _st_mixShader(${fac}, ${shader1}, ${shader2});`;
3795
+ return `vec4 ${ctx.outputVar(this, "BSDF")} = _st_mixShader(${fac}, ${shader1}, ${shader2});`;
3676
3796
  }
3677
3797
  };
3678
3798
 
@@ -3689,8 +3809,8 @@ var AddShader = class _AddShader extends ProcessNode {
3689
3809
  }
3690
3810
  static {
3691
3811
  this.glslFunction = `
3692
- vec3 _st_addShader(vec3 a, vec3 b) {
3693
- return a + b;
3812
+ vec4 _st_addShader(vec4 a, vec4 b) {
3813
+ return vec4(a.rgb + b.rgb, max(a.a, b.a));
3694
3814
  }`;
3695
3815
  }
3696
3816
  constructor(inputs = {}) {
@@ -3713,7 +3833,7 @@ vec3 _st_addShader(vec3 a, vec3 b) {
3713
3833
  compileCall(ctx) {
3714
3834
  const s1 = ctx.resolveInput(this._inputs.shader1);
3715
3835
  const s2 = ctx.resolveInput(this._inputs.shader2);
3716
- return `vec3 ${ctx.outputVar(this, "BSDF")} = _st_addShader(${s1}, ${s2});`;
3836
+ return `vec4 ${ctx.outputVar(this, "BSDF")} = _st_addShader(${s1}, ${s2});`;
3717
3837
  }
3718
3838
  };
3719
3839
 
@@ -3810,7 +3930,7 @@ vec3 _st_diffuseBSDF(vec3 color, float roughness, vec3 N) {
3810
3930
  const color = ctx.resolveInput(this._inputs.color);
3811
3931
  const roughness = ctx.resolveInput(this._inputs.roughness);
3812
3932
  const normal = this._inputs.normal.isConnected() ? ctx.outputVar(this._inputs.normal.connection.node, this._inputs.normal.connection.name) : "normalize(vNormal)";
3813
- return `vec3 ${ctx.outputVar(this, "BSDF")} = _st_diffuseBSDF(${color}, ${roughness}, ${normal});`;
3933
+ return `vec4 ${ctx.outputVar(this, "BSDF")} = vec4(_st_diffuseBSDF(${color}, ${roughness}, ${normal}), 1.0);`;
3814
3934
  }
3815
3935
  };
3816
3936
 
@@ -3864,7 +3984,7 @@ vec3 _st_glossyBSDF(vec3 color, float roughness, vec3 N) {
3864
3984
  const color = ctx.resolveInput(this._inputs.color);
3865
3985
  const roughness = ctx.resolveInput(this._inputs.roughness);
3866
3986
  const normal = this._inputs.normal.isConnected() ? ctx.outputVar(this._inputs.normal.connection.node, this._inputs.normal.connection.name) : "normalize(vNormal)";
3867
- return `vec3 ${ctx.outputVar(this, "BSDF")} = _st_glossyBSDF(${color}, ${roughness}, ${normal});`;
3987
+ return `vec4 ${ctx.outputVar(this, "BSDF")} = vec4(_st_glossyBSDF(${color}, ${roughness}, ${normal}), 1.0);`;
3868
3988
  }
3869
3989
  };
3870
3990
 
@@ -3913,7 +4033,7 @@ vec3 _st_refractionBSDF(vec3 color, float roughness, float ior, vec3 N) {
3913
4033
  const roughness = ctx.resolveInput(this._inputs.roughness);
3914
4034
  const ior = ctx.resolveInput(this._inputs.ior);
3915
4035
  const normal = this._inputs.normal.isConnected() ? ctx.outputVar(this._inputs.normal.connection.node, this._inputs.normal.connection.name) : "normalize(vNormal)";
3916
- return `vec3 ${ctx.outputVar(this, "BSDF")} = _st_refractionBSDF(${color}, ${roughness}, ${ior}, ${normal});`;
4036
+ return `vec4 ${ctx.outputVar(this, "BSDF")} = vec4(_st_refractionBSDF(${color}, ${roughness}, ${ior}, ${normal}), 1.0);`;
3917
4037
  }
3918
4038
  };
3919
4039
 
@@ -3968,7 +4088,7 @@ vec3 _st_glassBSDF(vec3 color, float roughness, float ior, vec3 N) {
3968
4088
  const roughness = ctx.resolveInput(this._inputs.roughness);
3969
4089
  const ior = ctx.resolveInput(this._inputs.ior);
3970
4090
  const normal = this._inputs.normal.isConnected() ? ctx.outputVar(this._inputs.normal.connection.node, this._inputs.normal.connection.name) : "normalize(vNormal)";
3971
- return `vec3 ${ctx.outputVar(this, "BSDF")} = _st_glassBSDF(${color}, ${roughness}, ${ior}, ${normal});`;
4091
+ return `vec4 ${ctx.outputVar(this, "BSDF")} = vec4(_st_glassBSDF(${color}, ${roughness}, ${ior}, ${normal}), 1.0);`;
3972
4092
  }
3973
4093
  };
3974
4094
 
@@ -4020,7 +4140,7 @@ vec3 _st_sheenBSDF(vec3 color, float roughness, vec3 N) {
4020
4140
  const color = ctx.resolveInput(this._inputs.color);
4021
4141
  const roughness = ctx.resolveInput(this._inputs.roughness);
4022
4142
  const normal = this._inputs.normal.isConnected() ? ctx.outputVar(this._inputs.normal.connection.node, this._inputs.normal.connection.name) : "normalize(vNormal)";
4023
- return `vec3 ${ctx.outputVar(this, "BSDF")} = _st_sheenBSDF(${color}, ${roughness}, ${normal});`;
4143
+ return `vec4 ${ctx.outputVar(this, "BSDF")} = vec4(_st_sheenBSDF(${color}, ${roughness}, ${normal}), 1.0);`;
4024
4144
  }
4025
4145
  };
4026
4146
 
@@ -4069,7 +4189,7 @@ vec3 _st_sss(vec3 color, float scale, vec3 radius, vec3 N) {
4069
4189
  const scale = ctx.resolveInput(this._inputs.scale);
4070
4190
  const [r, g, b] = this.radius;
4071
4191
  const normal = this._inputs.normal.isConnected() ? ctx.outputVar(this._inputs.normal.connection.node, this._inputs.normal.connection.name) : "normalize(vNormal)";
4072
- return `vec3 ${ctx.outputVar(this, "BSDF")} = _st_sss(${color}, ${scale}, vec3(${r.toFixed(4)}, ${g.toFixed(4)}, ${b.toFixed(4)}), ${normal});`;
4192
+ return `vec4 ${ctx.outputVar(this, "BSDF")} = vec4(_st_sss(${color}, ${scale}, vec3(${r.toFixed(4)}, ${g.toFixed(4)}, ${b.toFixed(4)}), ${normal}), 1.0);`;
4073
4193
  }
4074
4194
  };
4075
4195
 
@@ -4117,7 +4237,7 @@ vec3 _st_toonBSDF(vec3 color, float size, float smoothWidth, vec3 N) {
4117
4237
  const size = ctx.resolveInput(this._inputs.size);
4118
4238
  const smoothWidth = ctx.resolveInput(this._inputs.smooth);
4119
4239
  const normal = this._inputs.normal.isConnected() ? ctx.outputVar(this._inputs.normal.connection.node, this._inputs.normal.connection.name) : "normalize(vNormal)";
4120
- return `vec3 ${ctx.outputVar(this, "BSDF")} = _st_toonBSDF(${color}, ${size}, ${smoothWidth}, ${normal});`;
4240
+ return `vec4 ${ctx.outputVar(this, "BSDF")} = vec4(_st_toonBSDF(${color}, ${size}, ${smoothWidth}, ${normal}), 1.0);`;
4121
4241
  }
4122
4242
  };
4123
4243
 
@@ -4174,7 +4294,7 @@ vec3 _st_specularBSDF(vec3 base, vec3 spec, float roughness, vec3 N) {
4174
4294
  const spec = ctx.resolveInput(this._inputs.specular);
4175
4295
  const roughness = ctx.resolveInput(this._inputs.roughness);
4176
4296
  const normal = this._inputs.normal.isConnected() ? ctx.outputVar(this._inputs.normal.connection.node, this._inputs.normal.connection.name) : "normalize(vNormal)";
4177
- return `vec3 ${ctx.outputVar(this, "BSDF")} = _st_specularBSDF(${base}, ${spec}, ${roughness}, ${normal});`;
4297
+ return `vec4 ${ctx.outputVar(this, "BSDF")} = vec4(_st_specularBSDF(${base}, ${spec}, ${roughness}, ${normal}), 1.0);`;
4178
4298
  }
4179
4299
  };
4180
4300
 
@@ -4217,7 +4337,41 @@ vec3 _st_translucentBSDF(vec3 color, vec3 N) {
4217
4337
  compileCall(ctx) {
4218
4338
  const color = ctx.resolveInput(this._inputs.color);
4219
4339
  const normal = this._inputs.normal.isConnected() ? ctx.outputVar(this._inputs.normal.connection.node, this._inputs.normal.connection.name) : "normalize(vNormal)";
4220
- return `vec3 ${ctx.outputVar(this, "BSDF")} = _st_translucentBSDF(${color}, ${normal});`;
4340
+ return `vec4 ${ctx.outputVar(this, "BSDF")} = vec4(_st_translucentBSDF(${color}, ${normal}), 1.0);`;
4341
+ }
4342
+ };
4343
+
4344
+ // src/nodes/process/TransparentBSDF.ts
4345
+ var TransparentBSDF = class extends ProcessNode {
4346
+ get nodeType() {
4347
+ return "TransparentBSDF";
4348
+ }
4349
+ static {
4350
+ this.instanceSpecificDef = false;
4351
+ }
4352
+ get metadata() {
4353
+ return { label: "Transparent BSDF", category: "Shader", color: "#336699", cost: "low" };
4354
+ }
4355
+ constructor() {
4356
+ super("TransparentBSDF");
4357
+ this._inputs = this.createInputs({}, {});
4358
+ this._outputs = this.createOutputs({ BSDF: "shader" });
4359
+ }
4360
+ getInputSockets() {
4361
+ return this._inputs;
4362
+ }
4363
+ getOutputSockets() {
4364
+ return this._outputs;
4365
+ }
4366
+ compileDefs() {
4367
+ return "";
4368
+ }
4369
+ /** Always fully transparent — a constant, not a dynamic value, so this always requests transparent:true. */
4370
+ wantsTransparency() {
4371
+ return true;
4372
+ }
4373
+ compileCall(ctx) {
4374
+ return `vec4 ${ctx.outputVar(this, "BSDF")} = vec4(0.0, 0.0, 0.0, 0.0);`;
4221
4375
  }
4222
4376
  };
4223
4377
 
@@ -4349,7 +4503,7 @@ vec3 _st_principledHair(
4349
4503
  const randomRoughness = ctx.resolveInput(this._inputs.randomRoughness);
4350
4504
  const random = ctx.resolveInput(this._inputs.random);
4351
4505
  const tangent = this._inputs.tangent.isConnected() ? ctx.outputVar(this._inputs.tangent.connection.node, this._inputs.tangent.connection.name) : "normalize(vStrandTangent)";
4352
- return `vec3 ${ctx.outputVar(this, "BSDF")} = _st_principledHair(${color}, ${roughness}, ${radialRoughness}, ${coat}, ${ior}, ${offset}, ${randomColor}, ${randomRoughness}, ${random}, ${tangent});`;
4506
+ return `vec4 ${ctx.outputVar(this, "BSDF")} = vec4(_st_principledHair(${color}, ${roughness}, ${radialRoughness}, ${coat}, ${ior}, ${offset}, ${randomColor}, ${randomRoughness}, ${random}, ${tangent}), 1.0);`;
4353
4507
  }
4354
4508
  };
4355
4509
 
@@ -4664,8 +4818,8 @@ var ShaderToRGB = class extends ProcessNode {
4664
4818
  const cv = ctx.outputVar(this, "Color");
4665
4819
  const av = ctx.outputVar(this, "Alpha");
4666
4820
  return [
4667
- `vec3 ${cv} = ${shader};`,
4668
- `float ${av} = 1.0;`
4821
+ `vec3 ${cv} = (${shader}).rgb;`,
4822
+ `float ${av} = (${shader}).a;`
4669
4823
  ].join("\n ");
4670
4824
  }
4671
4825
  };
@@ -4951,11 +5105,13 @@ var ShaderScript = class extends ProcessNode {
4951
5105
  _socketGlslType(type) {
4952
5106
  if (type === "float") return "float";
4953
5107
  if (type === "vector") return "vec2";
5108
+ if (type === "shader") return "vec4";
4954
5109
  return "vec3";
4955
5110
  }
4956
5111
  _declGlslType(type) {
4957
5112
  if (type === "float") return "float";
4958
5113
  if (type === "vector") return "vec2";
5114
+ if (type === "shader") return "vec4";
4959
5115
  return "vec3";
4960
5116
  }
4961
5117
  };
@@ -5537,7 +5693,7 @@ var RayPortal = class extends ProcessNode {
5537
5693
  const color = ctx.resolveInput(this._inputs.color);
5538
5694
  const rawBlend = this.parameters.blend;
5539
5695
  const blend = Number.isFinite(rawBlend) ? Math.min(Math.max(rawBlend, 0), 1).toFixed(4) : "1.0000";
5540
- return `vec3 ${ctx.outputVar(this, "BSDF")} = ${color} * ${blend};`;
5696
+ return `vec4 ${ctx.outputVar(this, "BSDF")} = vec4(${color} * ${blend}, 1.0);`;
5541
5697
  }
5542
5698
  };
5543
5699
  export {
@@ -5622,6 +5778,7 @@ export {
5622
5778
  TextureCoordinate,
5623
5779
  ToonBSDF,
5624
5780
  TranslucentBSDF,
5781
+ TransparentBSDF,
5625
5782
  UVMap,
5626
5783
  Value,
5627
5784
  VectorCurves,
@@ -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,EAAI,MAAM,CAAA;IACtB,cAAc,EAAE,MAAM,CAAA;IACtB,QAAQ,EAAQ,MAAM,CAAC,MAAM,EAAE;QAAE,KAAK,EAAE,OAAO,CAAA;KAAE,CAAC,CAAA;IAClD,KAAK,EAAW,UAAU,EAAE,CAAA;CAC7B;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;IAcjD;;;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;IAkClD,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"}
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
- // vec3 (color/shader) → vec2 (vector) implicit conversion, e.g. Mapping.Vector → ImageTexture.vector
58
- if (socket.type === 'vector' && socket.connection.type !== 'vector') {
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 conversion, e.g. TextureCoordinate.UV → Mapping.vector
62
- if (socket.type === 'color' && socket.connection.type === 'vector') {
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) {
@@ -200,15 +215,15 @@ export class CompileContext {
200
215
  typesCompatible(from, to) {
201
216
  if (from === to)
202
217
  return true;
203
- // shader and color are both vec3 — compatible
218
+ // shader (vec4) and color (vec3) freely convert — narrow via .rgb, widen via vec4(v, 1.0)
204
219
  if ((from === 'shader' || from === 'color') && (to === 'shader' || to === 'color'))
205
220
  return true;
206
- // 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
207
222
  // Mapping (vector output) → Image Texture Vector panning graph
208
223
  if ((from === 'color' || from === 'shader') && to === 'vector')
209
224
  return true;
210
- // vec2 (vector) implicitly widens to vec3 (color), e.g. Texture Coordinate UV → Mapping.vector
211
- if (from === 'vector' && to === 'color')
225
+ // vec2 (vector) implicitly widens to vec3 (color) or vec4 (shader)
226
+ if (from === 'vector' && (to === 'color' || to === 'shader'))
212
227
  return true;
213
228
  return false;
214
229
  }
@@ -277,7 +292,7 @@ ${callsBlock}
277
292
  case 'vector':
278
293
  return 'vec2(vUv)';
279
294
  case 'shader':
280
- return 'vec3(0.0)';
295
+ return 'vec4(0.0, 0.0, 0.0, 1.0)';
281
296
  default:
282
297
  return 'vec3(1.0)';
283
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;CAyB1B"}
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"}
@@ -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;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;;;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"}
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;CAqCpE"}