@triforge/shader-core 0.2.1 → 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.
Files changed (37) hide show
  1. package/dist/bundle.js +71 -33
  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 +7 -0
  10. package/dist/core/SocketType.d.ts +1 -1
  11. package/dist/core/SocketType.js +1 -1
  12. package/dist/nodes/output/MaterialOutput.js +2 -2
  13. package/dist/nodes/process/AddShader.js +3 -3
  14. package/dist/nodes/process/DiffuseBSDF.js +1 -1
  15. package/dist/nodes/process/Emission.js +1 -1
  16. package/dist/nodes/process/GlassBSDF.js +1 -1
  17. package/dist/nodes/process/GlossyBSDF.js +1 -1
  18. package/dist/nodes/process/MixShader.js +2 -2
  19. package/dist/nodes/process/PrincipledBSDF.d.ts +7 -0
  20. package/dist/nodes/process/PrincipledBSDF.d.ts.map +1 -1
  21. package/dist/nodes/process/PrincipledBSDF.js +17 -2
  22. package/dist/nodes/process/PrincipledHair.js +1 -1
  23. package/dist/nodes/process/PrincipledVolume.js +1 -1
  24. package/dist/nodes/process/RefractionBSDF.js +1 -1
  25. package/dist/nodes/process/ShaderScript.d.ts +2 -2
  26. package/dist/nodes/process/ShaderScript.d.ts.map +1 -1
  27. package/dist/nodes/process/ShaderScript.js +7 -3
  28. package/dist/nodes/process/ShaderToRGB.js +2 -2
  29. package/dist/nodes/process/SheenBSDF.js +1 -1
  30. package/dist/nodes/process/SpecularBSDF.js +1 -1
  31. package/dist/nodes/process/SubsurfaceScattering.js +1 -1
  32. package/dist/nodes/process/ToonBSDF.js +1 -1
  33. package/dist/nodes/process/TranslucentBSDF.js +1 -1
  34. package/dist/nodes/process/VolumeAbsorption.js +1 -1
  35. package/dist/nodes/process/VolumeScatter.js +1 -1
  36. package/dist/nodes/shader/RayPortal.js +1 -1
  37. 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.
@@ -295,7 +304,8 @@ var CompileContext = class _CompileContext {
295
304
  vertexShader: this.buildVertexShader(),
296
305
  fragmentShader: this.buildFragmentShader(),
297
306
  uniforms: this._uniforms,
298
- nodes: this.order
307
+ nodes: this.order,
308
+ needsTransparency: this.order.some((node) => node.wantsTransparency())
299
309
  };
300
310
  }
301
311
  /**
@@ -315,12 +325,23 @@ var CompileContext = class _CompileContext {
315
325
  resolveInput(socket) {
316
326
  if (socket.connection) {
317
327
  const varName = this.outputVar(socket.connection.node, socket.connection.name);
318
- if (socket.type === "vector" && socket.connection.type !== "vector") {
328
+ const fromType = socket.connection.type;
329
+ const toType = socket.type;
330
+ if (toType === "vector" && fromType !== "vector") {
319
331
  return `(${varName}).xy`;
320
332
  }
321
- if (socket.type === "color" && socket.connection.type === "vector") {
333
+ if (toType === "color" && fromType === "vector") {
322
334
  return `vec3(${varName}, 0.0)`;
323
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
+ }
324
345
  return varName;
325
346
  }
326
347
  if (socket.uniformName !== null) {
@@ -436,7 +457,7 @@ var CompileContext = class _CompileContext {
436
457
  if (from === to) return true;
437
458
  if ((from === "shader" || from === "color") && (to === "shader" || to === "color")) return true;
438
459
  if ((from === "color" || from === "shader") && to === "vector") return true;
439
- if (from === "vector" && to === "color") return true;
460
+ if (from === "vector" && (to === "color" || to === "shader")) return true;
440
461
  return false;
441
462
  }
442
463
  // ── GLSL builders ─────────────────────────────────────────────────
@@ -494,7 +515,7 @@ ${callsBlock}
494
515
  case "vector":
495
516
  return "vec2(vUv)";
496
517
  case "shader":
497
- return "vec3(0.0)";
518
+ return "vec4(0.0, 0.0, 0.0, 1.0)";
498
519
  default:
499
520
  return "vec3(1.0)";
500
521
  }
@@ -553,7 +574,8 @@ var OutputNode = class extends ShaderNode {
553
574
  this.material = new ShaderMaterial({
554
575
  vertexShader: compiled.vertexShader,
555
576
  fragmentShader: compiled.fragmentShader,
556
- uniforms: compiled.uniforms
577
+ uniforms: compiled.uniforms,
578
+ transparent: compiled.needsTransparency
557
579
  });
558
580
  if (!this.material.uniforms.uSunDirection) {
559
581
  this.material.uniforms.uSunDirection = { value: new Vector3(1, 2, 1).normalize() };
@@ -577,7 +599,7 @@ var ProcessNode = class extends ShaderNode {
577
599
 
578
600
  // src/core/SocketType.ts
579
601
  var SOCKET_GLSL_TYPE = {
580
- shader: "vec3",
602
+ shader: "vec4",
581
603
  color: "vec3",
582
604
  vector: "vec2",
583
605
  float: "float"
@@ -1231,8 +1253,8 @@ var MaterialOutput = class extends OutputNode {
1231
1253
  return mat;
1232
1254
  }
1233
1255
  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);`;
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};`;
1236
1258
  }
1237
1259
  };
1238
1260
 
@@ -3381,7 +3403,7 @@ float _st_geo(float NdotX, float r) {
3381
3403
  float k = (r + 1.0) * (r + 1.0) / 8.0;
3382
3404
  return NdotX / max(NdotX * (1.0 - k) + k, 1e-7);
3383
3405
  }
3384
- vec3 _st_principledBSDF(vec3 base, float metallic, float roughness, float ior, float alpha, vec3 N) {
3406
+ vec3 _st_principledBSDF(vec3 base, float metallic, float roughness, float ior, vec3 N) {
3385
3407
  vec3 V = normalize(cameraPosition - vPosition);
3386
3408
  vec3 L = normalize(uSunDirection);
3387
3409
  vec3 H = normalize(L + V);
@@ -3422,9 +3444,23 @@ vec3 _st_principledBSDF(vec3 base, float metallic, float roughness, float ior, f
3422
3444
  compileDefs() {
3423
3445
  return this.glslFunction ?? _PrincipledBSDF.glslFunction;
3424
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
+ }
3425
3459
  compileCall(ctx) {
3426
3460
  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});`;
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});`;
3428
3464
  }
3429
3465
  };
3430
3466
 
@@ -3465,7 +3501,7 @@ vec3 _st_emission(vec3 color, float strength) {
3465
3501
  compileCall(ctx) {
3466
3502
  const color = ctx.resolveInput(this._inputs.color);
3467
3503
  const strength = ctx.resolveInput(this._inputs.strength);
3468
- return `vec3 ${ctx.outputVar(this, "BSDF")} = _st_emission(${color}, ${strength});`;
3504
+ return `vec4 ${ctx.outputVar(this, "BSDF")} = vec4(_st_emission(${color}, ${strength}), 1.0);`;
3469
3505
  }
3470
3506
  };
3471
3507
 
@@ -3506,7 +3542,7 @@ vec3 _st_volumeAbsorption(vec3 color, float density) {
3506
3542
  compileCall(ctx) {
3507
3543
  const color = ctx.resolveInput(this._inputs.color);
3508
3544
  const density = ctx.resolveInput(this._inputs.density);
3509
- return `vec3 ${ctx.outputVar(this, "Volume")} = _st_volumeAbsorption(${color}, ${density});`;
3545
+ return `vec4 ${ctx.outputVar(this, "Volume")} = vec4(_st_volumeAbsorption(${color}, ${density}), 1.0);`;
3510
3546
  }
3511
3547
  };
3512
3548
 
@@ -3553,7 +3589,7 @@ vec3 _st_volumeScatter(vec3 color, float density, float anisotropy) {
3553
3589
  const color = ctx.resolveInput(this._inputs.color);
3554
3590
  const density = ctx.resolveInput(this._inputs.density);
3555
3591
  const anisotropy = ctx.resolveInput(this._inputs.anisotropy);
3556
- return `vec3 ${ctx.outputVar(this, "Volume")} = _st_volumeScatter(${color}, ${density}, ${anisotropy});`;
3592
+ return `vec4 ${ctx.outputVar(this, "Volume")} = vec4(_st_volumeScatter(${color}, ${density}, ${anisotropy}), 1.0);`;
3557
3593
  }
3558
3594
  };
3559
3595
 
@@ -3624,7 +3660,7 @@ vec3 _st_principledVolume(
3624
3660
  const emissionStrength = ctx.resolveInput(this._inputs.emissionStrength);
3625
3661
  const blackbodyIntensity = ctx.resolveInput(this._inputs.blackbodyIntensity);
3626
3662
  const temperature = ctx.resolveInput(this._inputs.temperature);
3627
- return `vec3 ${ctx.outputVar(this, "Volume")} = _st_principledVolume(${color}, ${density}, ${anisotropy}, ${absorptionColor}, ${emissionColor}, ${emissionStrength}, ${blackbodyIntensity}, ${temperature});`;
3663
+ return `vec4 ${ctx.outputVar(this, "Volume")} = vec4(_st_principledVolume(${color}, ${density}, ${anisotropy}, ${absorptionColor}, ${emissionColor}, ${emissionStrength}, ${blackbodyIntensity}, ${temperature}), 1.0);`;
3628
3664
  }
3629
3665
  };
3630
3666
 
@@ -3646,7 +3682,7 @@ var MixShader = class _MixShader extends ProcessNode {
3646
3682
  }
3647
3683
  static {
3648
3684
  this.glslFunction = `
3649
- vec3 _st_mixShader(float fac, vec3 a, vec3 b) {
3685
+ vec4 _st_mixShader(float fac, vec4 a, vec4 b) {
3650
3686
  return mix(a, b, clamp(fac, 0.0, 1.0));
3651
3687
  }`;
3652
3688
  }
@@ -3672,7 +3708,7 @@ vec3 _st_mixShader(float fac, vec3 a, vec3 b) {
3672
3708
  const fac = ctx.resolveInput(this._inputs.fac);
3673
3709
  const shader1 = ctx.resolveInput(this._inputs.shader1);
3674
3710
  const shader2 = ctx.resolveInput(this._inputs.shader2);
3675
- return `vec3 ${ctx.outputVar(this, "BSDF")} = _st_mixShader(${fac}, ${shader1}, ${shader2});`;
3711
+ return `vec4 ${ctx.outputVar(this, "BSDF")} = _st_mixShader(${fac}, ${shader1}, ${shader2});`;
3676
3712
  }
3677
3713
  };
3678
3714
 
@@ -3689,8 +3725,8 @@ var AddShader = class _AddShader extends ProcessNode {
3689
3725
  }
3690
3726
  static {
3691
3727
  this.glslFunction = `
3692
- vec3 _st_addShader(vec3 a, vec3 b) {
3693
- return a + b;
3728
+ vec4 _st_addShader(vec4 a, vec4 b) {
3729
+ return vec4(a.rgb + b.rgb, max(a.a, b.a));
3694
3730
  }`;
3695
3731
  }
3696
3732
  constructor(inputs = {}) {
@@ -3713,7 +3749,7 @@ vec3 _st_addShader(vec3 a, vec3 b) {
3713
3749
  compileCall(ctx) {
3714
3750
  const s1 = ctx.resolveInput(this._inputs.shader1);
3715
3751
  const s2 = ctx.resolveInput(this._inputs.shader2);
3716
- return `vec3 ${ctx.outputVar(this, "BSDF")} = _st_addShader(${s1}, ${s2});`;
3752
+ return `vec4 ${ctx.outputVar(this, "BSDF")} = _st_addShader(${s1}, ${s2});`;
3717
3753
  }
3718
3754
  };
3719
3755
 
@@ -3810,7 +3846,7 @@ vec3 _st_diffuseBSDF(vec3 color, float roughness, vec3 N) {
3810
3846
  const color = ctx.resolveInput(this._inputs.color);
3811
3847
  const roughness = ctx.resolveInput(this._inputs.roughness);
3812
3848
  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});`;
3849
+ return `vec4 ${ctx.outputVar(this, "BSDF")} = vec4(_st_diffuseBSDF(${color}, ${roughness}, ${normal}), 1.0);`;
3814
3850
  }
3815
3851
  };
3816
3852
 
@@ -3864,7 +3900,7 @@ vec3 _st_glossyBSDF(vec3 color, float roughness, vec3 N) {
3864
3900
  const color = ctx.resolveInput(this._inputs.color);
3865
3901
  const roughness = ctx.resolveInput(this._inputs.roughness);
3866
3902
  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});`;
3903
+ return `vec4 ${ctx.outputVar(this, "BSDF")} = vec4(_st_glossyBSDF(${color}, ${roughness}, ${normal}), 1.0);`;
3868
3904
  }
3869
3905
  };
3870
3906
 
@@ -3913,7 +3949,7 @@ vec3 _st_refractionBSDF(vec3 color, float roughness, float ior, vec3 N) {
3913
3949
  const roughness = ctx.resolveInput(this._inputs.roughness);
3914
3950
  const ior = ctx.resolveInput(this._inputs.ior);
3915
3951
  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});`;
3952
+ return `vec4 ${ctx.outputVar(this, "BSDF")} = vec4(_st_refractionBSDF(${color}, ${roughness}, ${ior}, ${normal}), 1.0);`;
3917
3953
  }
3918
3954
  };
3919
3955
 
@@ -3968,7 +4004,7 @@ vec3 _st_glassBSDF(vec3 color, float roughness, float ior, vec3 N) {
3968
4004
  const roughness = ctx.resolveInput(this._inputs.roughness);
3969
4005
  const ior = ctx.resolveInput(this._inputs.ior);
3970
4006
  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});`;
4007
+ return `vec4 ${ctx.outputVar(this, "BSDF")} = vec4(_st_glassBSDF(${color}, ${roughness}, ${ior}, ${normal}), 1.0);`;
3972
4008
  }
3973
4009
  };
3974
4010
 
@@ -4020,7 +4056,7 @@ vec3 _st_sheenBSDF(vec3 color, float roughness, vec3 N) {
4020
4056
  const color = ctx.resolveInput(this._inputs.color);
4021
4057
  const roughness = ctx.resolveInput(this._inputs.roughness);
4022
4058
  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});`;
4059
+ return `vec4 ${ctx.outputVar(this, "BSDF")} = vec4(_st_sheenBSDF(${color}, ${roughness}, ${normal}), 1.0);`;
4024
4060
  }
4025
4061
  };
4026
4062
 
@@ -4069,7 +4105,7 @@ vec3 _st_sss(vec3 color, float scale, vec3 radius, vec3 N) {
4069
4105
  const scale = ctx.resolveInput(this._inputs.scale);
4070
4106
  const [r, g, b] = this.radius;
4071
4107
  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});`;
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);`;
4073
4109
  }
4074
4110
  };
4075
4111
 
@@ -4117,7 +4153,7 @@ vec3 _st_toonBSDF(vec3 color, float size, float smoothWidth, vec3 N) {
4117
4153
  const size = ctx.resolveInput(this._inputs.size);
4118
4154
  const smoothWidth = ctx.resolveInput(this._inputs.smooth);
4119
4155
  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});`;
4156
+ return `vec4 ${ctx.outputVar(this, "BSDF")} = vec4(_st_toonBSDF(${color}, ${size}, ${smoothWidth}, ${normal}), 1.0);`;
4121
4157
  }
4122
4158
  };
4123
4159
 
@@ -4174,7 +4210,7 @@ vec3 _st_specularBSDF(vec3 base, vec3 spec, float roughness, vec3 N) {
4174
4210
  const spec = ctx.resolveInput(this._inputs.specular);
4175
4211
  const roughness = ctx.resolveInput(this._inputs.roughness);
4176
4212
  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});`;
4213
+ return `vec4 ${ctx.outputVar(this, "BSDF")} = vec4(_st_specularBSDF(${base}, ${spec}, ${roughness}, ${normal}), 1.0);`;
4178
4214
  }
4179
4215
  };
4180
4216
 
@@ -4217,7 +4253,7 @@ vec3 _st_translucentBSDF(vec3 color, vec3 N) {
4217
4253
  compileCall(ctx) {
4218
4254
  const color = ctx.resolveInput(this._inputs.color);
4219
4255
  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});`;
4256
+ return `vec4 ${ctx.outputVar(this, "BSDF")} = vec4(_st_translucentBSDF(${color}, ${normal}), 1.0);`;
4221
4257
  }
4222
4258
  };
4223
4259
 
@@ -4349,7 +4385,7 @@ vec3 _st_principledHair(
4349
4385
  const randomRoughness = ctx.resolveInput(this._inputs.randomRoughness);
4350
4386
  const random = ctx.resolveInput(this._inputs.random);
4351
4387
  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});`;
4388
+ return `vec4 ${ctx.outputVar(this, "BSDF")} = vec4(_st_principledHair(${color}, ${roughness}, ${radialRoughness}, ${coat}, ${ior}, ${offset}, ${randomColor}, ${randomRoughness}, ${random}, ${tangent}), 1.0);`;
4353
4389
  }
4354
4390
  };
4355
4391
 
@@ -4664,8 +4700,8 @@ var ShaderToRGB = class extends ProcessNode {
4664
4700
  const cv = ctx.outputVar(this, "Color");
4665
4701
  const av = ctx.outputVar(this, "Alpha");
4666
4702
  return [
4667
- `vec3 ${cv} = ${shader};`,
4668
- `float ${av} = 1.0;`
4703
+ `vec3 ${cv} = (${shader}).rgb;`,
4704
+ `float ${av} = (${shader}).a;`
4669
4705
  ].join("\n ");
4670
4706
  }
4671
4707
  };
@@ -4951,11 +4987,13 @@ var ShaderScript = class extends ProcessNode {
4951
4987
  _socketGlslType(type) {
4952
4988
  if (type === "float") return "float";
4953
4989
  if (type === "vector") return "vec2";
4990
+ if (type === "shader") return "vec4";
4954
4991
  return "vec3";
4955
4992
  }
4956
4993
  _declGlslType(type) {
4957
4994
  if (type === "float") return "float";
4958
4995
  if (type === "vector") return "vec2";
4996
+ if (type === "shader") return "vec4";
4959
4997
  return "vec3";
4960
4998
  }
4961
4999
  };
@@ -5537,7 +5575,7 @@ var RayPortal = class extends ProcessNode {
5537
5575
  const color = ctx.resolveInput(this._inputs.color);
5538
5576
  const rawBlend = this.parameters.blend;
5539
5577
  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};`;
5578
+ return `vec4 ${ctx.outputVar(this, "BSDF")} = vec4(${color} * ${blend}, 1.0);`;
5541
5579
  }
5542
5580
  };
5543
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,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;CAqBpE"}
@@ -60,6 +60,13 @@ export class ShaderNode {
60
60
  vertexInjections() {
61
61
  return [];
62
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; }
63
70
  /**
64
71
  * Returns the named output socket.
65
72
  * Throws ShaderNodeError if the socket does not exist.
@@ -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
@@ -1,6 +1,6 @@
1
1
  /** Maps SocketType to its GLSL primitive. */
2
2
  export const SOCKET_GLSL_TYPE = {
3
- shader: 'vec3',
3
+ shader: 'vec4',
4
4
  color: 'vec3',
5
5
  vector: 'vec2',
6
6
  float: 'float',
@@ -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
- : 'vec3(0.8)';
116
- return `gl_FragColor = vec4(${sv}, 1.0);`;
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 `vec3 ${ctx.outputVar(this, 'BSDF')} = _st_addShader(${s1}, ${s2});`;
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
- vec3 _st_addShader(vec3 a, vec3 b) {
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 `vec3 ${ctx.outputVar(this, 'BSDF')} = _st_diffuseBSDF(${color}, ${roughness}, ${normal});`;
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 `vec3 ${ctx.outputVar(this, 'BSDF')} = _st_emission(${color}, ${strength});`;
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 `vec3 ${ctx.outputVar(this, 'BSDF')} = _st_glassBSDF(${color}, ${roughness}, ${ior}, ${normal});`;
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 `vec3 ${ctx.outputVar(this, 'BSDF')} = _st_glossyBSDF(${color}, ${roughness}, ${normal});`;
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 `vec3 ${ctx.outputVar(this, 'BSDF')} = _st_mixShader(${fac}, ${shader1}, ${shader2});`;
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
- vec3 _st_mixShader(float fac, vec3 a, vec3 b) {
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;CAMzC"}
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
- 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});`;
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, float alpha, vec3 N) {
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 `vec3 ${ctx.outputVar(this, 'BSDF')} = _st_principledHair(${color}, ${roughness}, ${radialRoughness}, ${coat}, ${ior}, ${offset}, ${randomColor}, ${randomRoughness}, ${random}, ${tangent});`;
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 `vec3 ${ctx.outputVar(this, 'Volume')} = _st_principledVolume(${color}, ${density}, ${anisotropy}, ${absorptionColor}, ${emissionColor}, ${emissionStrength}, ${blackbodyIntensity}, ${temperature});`;
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 `vec3 ${ctx.outputVar(this, 'BSDF')} = _st_refractionBSDF(${color}, ${roughness}, ${ior}, ${normal});`;
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' → vec3 (BSDF connection)
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'/'shader' → vec3, 'vector' → vec2
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;IAMvB,OAAO,CAAC,aAAa;CAKtB"}
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'/'shader' → vec3, 'vector' → vec2
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
- return 'vec3'; // 'color' | 'shader'
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
- return 'vec3'; // 'color' | 'shader'
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} = 1.0;`,
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 `vec3 ${ctx.outputVar(this, 'BSDF')} = _st_sheenBSDF(${color}, ${roughness}, ${normal});`;
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 `vec3 ${ctx.outputVar(this, 'BSDF')} = _st_specularBSDF(${base}, ${spec}, ${roughness}, ${normal});`;
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 `vec3 ${ctx.outputVar(this, 'BSDF')} = _st_sss(${color}, ${scale}, vec3(${r.toFixed(4)}, ${g.toFixed(4)}, ${b.toFixed(4)}), ${normal});`;
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 `vec3 ${ctx.outputVar(this, 'BSDF')} = _st_toonBSDF(${color}, ${size}, ${smoothWidth}, ${normal});`;
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 `vec3 ${ctx.outputVar(this, 'BSDF')} = _st_translucentBSDF(${color}, ${normal});`;
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 `vec3 ${ctx.outputVar(this, 'Volume')} = _st_volumeAbsorption(${color}, ${density});`;
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 `vec3 ${ctx.outputVar(this, 'Volume')} = _st_volumeScatter(${color}, ${density}, ${anisotropy});`;
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 `vec3 ${ctx.outputVar(this, 'BSDF')} = ${color} * ${blend};`;
53
+ return `vec4 ${ctx.outputVar(this, 'BSDF')} = vec4(${color} * ${blend}, 1.0);`;
54
54
  }
55
55
  }
56
56
  RayPortal.instanceSpecificDef = false;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@triforge/shader-core",
3
- "version": "0.2.1",
3
+ "version": "0.3.0",
4
4
  "description": "GLSL shader library for Three.js with Blender workflow",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",