reze-engine 0.11.2 → 0.11.3

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 (104) hide show
  1. package/dist/engine.d.ts +4 -2
  2. package/dist/engine.d.ts.map +1 -1
  3. package/dist/engine.js +58 -426
  4. package/dist/index.d.ts +1 -2
  5. package/dist/index.d.ts.map +1 -1
  6. package/dist/shaders/body.d.ts +1 -1
  7. package/dist/shaders/body.d.ts.map +1 -1
  8. package/dist/shaders/body.js +7 -28
  9. package/dist/shaders/cloth_rough.d.ts +1 -1
  10. package/dist/shaders/cloth_rough.d.ts.map +1 -1
  11. package/dist/shaders/cloth_rough.js +4 -16
  12. package/dist/shaders/cloth_smooth.d.ts +1 -1
  13. package/dist/shaders/cloth_smooth.d.ts.map +1 -1
  14. package/dist/shaders/cloth_smooth.js +5 -17
  15. package/dist/shaders/default.d.ts +1 -1
  16. package/dist/shaders/default.d.ts.map +1 -1
  17. package/dist/shaders/eye.d.ts +1 -1
  18. package/dist/shaders/eye.d.ts.map +1 -1
  19. package/dist/shaders/face.d.ts +1 -1
  20. package/dist/shaders/face.d.ts.map +1 -1
  21. package/dist/shaders/face.js +21 -57
  22. package/dist/shaders/hair.d.ts +1 -1
  23. package/dist/shaders/hair.d.ts.map +1 -1
  24. package/dist/shaders/hair.js +7 -27
  25. package/dist/shaders/materials/body.d.ts +2 -0
  26. package/dist/shaders/materials/body.d.ts.map +1 -0
  27. package/dist/shaders/materials/body.js +199 -0
  28. package/dist/shaders/materials/cloth_rough.d.ts +2 -0
  29. package/dist/shaders/materials/cloth_rough.d.ts.map +1 -0
  30. package/dist/shaders/materials/cloth_rough.js +178 -0
  31. package/dist/shaders/materials/cloth_smooth.d.ts +2 -0
  32. package/dist/shaders/materials/cloth_smooth.d.ts.map +1 -0
  33. package/dist/shaders/materials/cloth_smooth.js +174 -0
  34. package/dist/shaders/materials/default.d.ts +2 -0
  35. package/dist/shaders/materials/default.d.ts.map +1 -0
  36. package/dist/shaders/materials/default.js +171 -0
  37. package/dist/shaders/materials/eye.d.ts +2 -0
  38. package/dist/shaders/materials/eye.d.ts.map +1 -0
  39. package/dist/shaders/materials/eye.js +146 -0
  40. package/dist/shaders/materials/face.d.ts +2 -0
  41. package/dist/shaders/materials/face.d.ts.map +1 -0
  42. package/dist/shaders/materials/face.js +199 -0
  43. package/dist/shaders/materials/hair.d.ts +2 -0
  44. package/dist/shaders/materials/hair.d.ts.map +1 -0
  45. package/dist/shaders/materials/hair.js +176 -0
  46. package/dist/shaders/materials/metal.d.ts +2 -0
  47. package/dist/shaders/materials/metal.d.ts.map +1 -0
  48. package/dist/shaders/materials/metal.js +183 -0
  49. package/dist/shaders/materials/nodes.d.ts +2 -0
  50. package/dist/shaders/materials/nodes.d.ts.map +1 -0
  51. package/{src/shaders/nodes.ts → dist/shaders/materials/nodes.js} +32 -16
  52. package/dist/shaders/materials/stockings.d.ts +2 -0
  53. package/dist/shaders/materials/stockings.d.ts.map +1 -0
  54. package/dist/shaders/materials/stockings.js +244 -0
  55. package/dist/shaders/metal.d.ts +1 -1
  56. package/dist/shaders/metal.d.ts.map +1 -1
  57. package/dist/shaders/metal.js +4 -17
  58. package/dist/shaders/nodes.d.ts +1 -1
  59. package/dist/shaders/nodes.d.ts.map +1 -1
  60. package/dist/shaders/nodes.js +0 -9
  61. package/dist/shaders/passes/bloom.d.ts +4 -0
  62. package/dist/shaders/passes/bloom.d.ts.map +1 -0
  63. package/dist/shaders/passes/bloom.js +117 -0
  64. package/dist/shaders/passes/composite.d.ts +2 -0
  65. package/dist/shaders/passes/composite.d.ts.map +1 -0
  66. package/dist/shaders/passes/composite.js +61 -0
  67. package/dist/shaders/passes/ground.d.ts +2 -0
  68. package/dist/shaders/passes/ground.d.ts.map +1 -0
  69. package/dist/shaders/passes/ground.js +93 -0
  70. package/dist/shaders/passes/mipmap.d.ts +2 -0
  71. package/dist/shaders/passes/mipmap.d.ts.map +1 -0
  72. package/dist/shaders/passes/mipmap.js +16 -0
  73. package/dist/shaders/passes/outline.d.ts +2 -0
  74. package/dist/shaders/passes/outline.d.ts.map +1 -0
  75. package/dist/shaders/passes/outline.js +83 -0
  76. package/dist/shaders/passes/pick.d.ts +2 -0
  77. package/dist/shaders/passes/pick.d.ts.map +1 -0
  78. package/dist/shaders/passes/pick.js +39 -0
  79. package/dist/shaders/passes/shadow.d.ts +2 -0
  80. package/dist/shaders/passes/shadow.d.ts.map +1 -0
  81. package/dist/shaders/passes/shadow.js +16 -0
  82. package/dist/shaders/stockings.d.ts +1 -1
  83. package/dist/shaders/stockings.d.ts.map +1 -1
  84. package/package.json +1 -1
  85. package/src/engine.ts +93 -438
  86. package/src/index.ts +3 -2
  87. package/src/shaders/{body.ts → materials/body.ts} +7 -28
  88. package/src/shaders/{cloth_rough.ts → materials/cloth_rough.ts} +4 -16
  89. package/src/shaders/{cloth_smooth.ts → materials/cloth_smooth.ts} +5 -17
  90. package/src/shaders/{face.ts → materials/face.ts} +21 -57
  91. package/src/shaders/{hair.ts → materials/hair.ts} +7 -27
  92. package/src/shaders/{metal.ts → materials/metal.ts} +15 -19
  93. package/src/shaders/materials/nodes.ts +483 -0
  94. package/src/shaders/passes/bloom.ts +121 -0
  95. package/src/shaders/passes/composite.ts +62 -0
  96. package/src/shaders/passes/ground.ts +94 -0
  97. package/src/shaders/passes/mipmap.ts +17 -0
  98. package/src/shaders/passes/outline.ts +84 -0
  99. package/src/shaders/passes/pick.ts +40 -0
  100. package/src/shaders/passes/shadow.ts +17 -0
  101. package/src/shaders/classify.ts +0 -25
  102. /package/src/shaders/{default.ts → materials/default.ts} +0 -0
  103. /package/src/shaders/{eye.ts → materials/eye.ts} +0 -0
  104. /package/src/shaders/{stockings.ts → materials/stockings.ts} +0 -0
@@ -71,11 +71,10 @@ fn sampleShadow(worldPos: vec3f, n: vec3f) -> f32 {
71
71
 
72
72
  const PI_B: f32 = 3.141592653589793;
73
73
  const BODY_ROUGHNESS: f32 = 0.3;
74
- // Dump: 层权重.002 Blend; 运算.007 POWER exponent Value_001; 背景 Color; 运算.004 after invert
75
74
  const BODY_RIM2_LAYER_BLEND: f32 = 0.20000000298023224;
76
75
  const BODY_RIM2_POW: f32 = 1.4300000667572021;
77
76
  const BODY_RIM2_BG: vec3f = vec3f(1.0, 0.4303792119026184, 0.3315804898738861);
78
- const BODY_WARM_AO_MUL: f32 = 0.30000001192092896;
77
+ const BODY_WARM_STR: f32 = 0.30000001192092896;
79
78
  const BODY_SPECULAR: f32 = 0.5;
80
79
  const BODY_MIX_NPR: f32 = 0.5;
81
80
  // EEVEE Light Clamp equivalent — caps firefly specular from noise-bumped NDF aliasing.
@@ -133,49 +132,33 @@ struct FSOut {
133
132
  let tex_color = textureSample(diffuseTexture, diffuseSampler, input.uv).rgb;
134
133
  let shadow = sampleShadow(input.worldPos, n);
135
134
 
136
- // ═══ TOON MASK: ShaderToRGB → ramp.008 CONSTANT [0→black, 0.2966→white] ═══
137
135
  let ndotl_raw = shader_to_rgb_diffuse(n, l, sun, light.ambientColor.xyz, shadow);
138
136
  let toon = ramp_constant(ndotl_raw, 0.0, vec4f(0,0,0,1), 0.2966, vec4f(1,1,1,1)).r;
139
137
 
140
- // ═══ TOON COLOR: Mix.004 A=HueSat, B=HueSat.001, Fac=ramp.008 (R) ═══
141
138
  let shadow_tint = hue_sat_id(2.0, 0.3499999940395355, 1.0, tex_color);
142
139
  let lit_tint = hue_sat_id(1.5, 1.0, 1.0, tex_color);
143
140
  let toon_color = mix_blend(toon, shadow_tint, lit_tint);
144
141
  let bc = bright_contrast(toon_color, 0.1, 0.2);
145
142
 
146
- // ═══ AO CHAIN: AO → ramp CONSTANT [0→white, 0.5995→black] → Mix.003 ═══
147
- let ao = 1.0; // ao_fake(n, v) — no SSAO yet; inline 1.0 so the ramp/mix chain folds at compile time.
148
- let ao_ramp = ramp_constant(ao, 0.0, vec4f(1,1,1,1), 0.5995, vec4f(0,0,0,1)).r;
149
- let ao_mixed = mix_blend(ao_ramp, bc, vec3f(0.8301780223846436, 0.3345769941806793, 0.27946099638938904));
143
+ let emission3 = bc * 4.0;
150
144
 
151
- // ═══ EMISSION.003 (strength=4.0) ═══
152
- let emission3 = ao_mixed * 4.0;
153
-
154
- // ═══ WARM: 颜色渐变.008 → 运算.006 ADD +0.5 (m_graphs) → clamp → 颜色渐变.003 ═══
155
- let ao_inv = invert_f(1.0, ao_ramp);
156
- let warm_str = ao_inv * BODY_WARM_AO_MUL;
157
145
  let warm_input = clamp(toon + 0.5, 0.0, 1.0);
158
146
  let warm_color = ramp_cardinal(warm_input, 0.2409,
159
147
  vec4f(0.2426, 0.068, 0.0588, 1.0), 0.4663,
160
148
  vec4f(0.6677, 0.5024, 0.5126, 1.0)).rgb;
161
- let warm_emission = warm_color * warm_str;
149
+ let warm_emission = warm_color * BODY_WARM_STR;
162
150
 
163
- // ═══ RIM 1: 菲涅尔 × 层权重.001 Facing Blend=0.24 → 自发光 Strength ═══
164
151
  let rim1_str = fresnel(2.0, n, v) * layer_weight_facing(0.24000005424022675, n, v);
165
152
  let rim1 = vec3f(0.984157919883728, 0.6110184788703918, 0.5736401677131653) * rim1_str;
166
153
 
167
- // ═══ RIM 2: 层权重.002 Facing → 运算.007 POWER → 颜色渐变.010 EASE → MixShader.002 Fac ═══
168
154
  let facing_raw = layer_weight_facing(BODY_RIM2_LAYER_BLEND, n, v);
169
155
  let facing_pow = math_power(facing_raw, BODY_RIM2_POW);
170
156
  let rim2_fac = ramp_ease(facing_pow, 0.0, vec4f(0,0,0,1), 0.5052, vec4f(1,1,1,1)).r;
171
157
  let rim2_mixed = mix(emission3, BODY_RIM2_BG, rim2_fac);
172
158
 
173
- // ═══ NPR STACK: AddShader chain (no bright gate in body) ═══
174
- let add0 = rim1 + rim2_mixed;
175
- let npr_stack = add0 + warm_emission;
159
+ let npr_stack = rim1 + rim2_mixed + warm_emission;
176
160
 
177
- // ═══ PRINCIPLED BSDF: noise bump, GGX specular, SSS from AO ═══
178
- // Mapping loc=rot=0 → plain scale multiply, inline.
161
+ // Noise bump Mapping loc=rot=0 folds to a plain scale multiply.
179
162
  let noise_val = tex_noise_d2(input.worldPos * vec3f(1.0, 1.0, 1.5), 1.0);
180
163
  let noise_ramp = ramp_linear(noise_val, 0.0, vec4f(0,0,0,1), 1.0, vec4f(1,1,1,1)).r;
181
164
  let bumped_n = bump_lh(0.324644535779953, noise_ramp, n, input.worldPos);
@@ -183,10 +166,7 @@ struct FSOut {
183
166
  let principled_base = mix_blend(noise_ramp, bc, vec3f(0.6831911206245422, 0.19474034011363983, 0.13732507824897766));
184
167
  let p_emission = bc * 0.2;
185
168
 
186
- // Reuse 'ao' (ao_fake(n, v) above) identical inputs, avoid a second procedural AO pass.
187
- let sss = ramp_linear(ao, 0.003, vec4f(0,0,0,1), 1.0, vec4f(0.0786, 0.0786, 0.0786, 1.0)).r;
188
-
189
- // 原理化BSDF (EEVEE port): metallic=0, specular=0.5, roughness=0.3, specular_tint=0.
169
+ // Principled BSDF (EEVEE port): metallic=0, specular=0.5, roughness=0.3, specular_tint=0.
190
170
  let NL = max(dot(bumped_n, l), 0.0);
191
171
  let NV = max(dot(bumped_n, v), 1e-4);
192
172
 
@@ -208,9 +188,8 @@ struct FSOut {
208
188
  // probe_evaluate_world_diff returns radiance (SH-projected, not cosine-convolved).
209
189
  // No (1-F) factor per EEVEE — it doesn't energy-conserve spec<->diffuse.
210
190
  let diffuse_radiance = principled_base * (sun * NL * shadow / PI_B + light.ambientColor.xyz);
211
- let principled = diffuse_radiance + spec_radiance + p_emission + vec3f(sss);
191
+ let principled = diffuse_radiance + spec_radiance + p_emission;
212
192
 
213
- // 混合着色器.001: Shader=相加着色器.001, Shader_001=原理化BSDF
214
193
  let final_color = mix(npr_stack, principled, BODY_MIX_NPR);
215
194
 
216
195
  var out: FSOut;
@@ -128,34 +128,22 @@ struct FSOut {
128
128
  let out_alpha = material.alpha * tex_s.a;
129
129
  if (out_alpha < 0.001) { discard; }
130
130
 
131
- // Shader→RGB → 颜色渐变.008 CONSTANT (edge AA terminator)
132
131
  let lum_shade = shader_to_rgb_diffuse(n, l, sun, amb, shadow);
132
+ // ramp_constant_edge_aa: avoids binary fac shimmer on terminator (fwidth + smoothstep).
133
133
  let ramp008 = ramp_constant_edge_aa(lum_shade, CLOTH_R_TOON_EDGE, vec4f(0,0,0,1), vec4f(1,1,1,1));
134
- let toon_r = ramp008.r;
135
- let mix04_fac = math_multiply(toon_r, CLOTH_R_MIX04_MUL);
134
+ let mix04_fac = math_multiply(ramp008.r, CLOTH_R_MIX04_MUL);
136
135
 
137
- // 混合.004: A=色相/饱和度/明度.002(Hue=0.5 Sat=1.0 Val=0.2), B=纹理
138
136
  let dark_tex = hue_sat_id(1.0, 0.19999998807907104, 1.0, tex_rgb);
139
137
  let mix04 = mix_blend(mix04_fac, dark_tex, tex_rgb);
140
138
 
141
- // 倒角.001.Z → 混合.003: A=混合.004, B=色相/饱和度/明度.002
142
139
  let bevel_z = clamp(n.y, 0.0, 1.0);
143
140
  let mix03 = mix_blend(bevel_z, mix04, dark_tex);
144
141
 
145
- // 环境光遮蔽 → 颜色渐变.001 LINEAR → 混合.001 (white/black) → 混合.002 OVERLAY Fac
146
- let ao = 1.0; // ao_fake(n, v) — no SSAO yet; inline 1.0 so the ramp/mix chain folds at compile time.
147
- let ao_ramp_c = ramp_linear(ao, 0.0, vec4f(1,1,1,1), 0.8808, vec4f(0,0,0,1));
148
- let mix01_fac = ao_ramp_c.r;
149
- let mix01_rgb = mix(vec3f(1.0), vec3f(0.0), mix01_fac);
150
-
151
- // 混合.002 OVERLAY: Fac=混合.001, A=混合.003, B=色相/饱和度/明度.004
152
142
  let hue004 = hue_sat_id(0.800000011920929, 2.0, 1.0, mix03);
153
- let overlay_fac = mix01_rgb.r;
154
- let npr_rgb = mix_overlay(overlay_fac, mix03, hue004);
143
+ let npr_rgb = mix_overlay(1.0, mix03, hue004);
155
144
  let npr_emission = npr_rgb * CLOTH_R_EMIT_STR;
156
145
 
157
- // 噪波→渐变→凹凸 (LIVE in M_Rough_Cloth — unlike Smooth Cloth): Strength=1.0, noise Scale=17.7.
158
- // mapping scale=(1,1,1), loc=rot=0 → identity; use worldPos directly.
146
+ // Noise bump is LIVE in M_Rough_Cloth — unlike Smooth Cloth where the subtree is dead.
159
147
  let noise_val = tex_noise_d2(input.worldPos, CLOTH_R_NOISE_SCALE);
160
148
  let noise_ramp = ramp_linear(noise_val, 0.0, vec4f(0,0,0,1), 1.0, vec4f(1,1,1,1)).r;
161
149
  let bumped_n = bump_lh(CLOTH_R_BUMP_STR, noise_ramp, n, input.worldPos);
@@ -123,35 +123,23 @@ struct FSOut {
123
123
  let out_alpha = material.alpha * tex_s.a;
124
124
  if (out_alpha < 0.001) { discard; }
125
125
 
126
- // Shader→RGB → 颜色渐变.008 CONSTANT — AA like face (same terminator artifact class)
127
126
  let lum_shade = shader_to_rgb_diffuse(n, l, sun, amb, shadow);
127
+ // ramp_constant_edge_aa: avoids binary fac shimmer on terminator (fwidth + smoothstep).
128
128
  let ramp008 = ramp_constant_edge_aa(lum_shade, CLOTH_TOON_EDGE, vec4f(0,0,0,1), vec4f(1,1,1,1));
129
- let toon_r = ramp008.r;
130
- // 颜色渐变.008 → 运算.004 MULTIPLY 0.5 → 混合.004 Factor
131
- let mix04_fac = math_multiply(toon_r, CLOTH_MIX04_MUL);
129
+ let mix04_fac = math_multiply(ramp008.r, CLOTH_MIX04_MUL);
132
130
 
133
- // 混合.004: A=色相/饱和度/明度.002, B=纹理
134
131
  let dark_tex = hue_sat_id(1.0, 0.19999998807907104, 1.0, tex_rgb);
135
132
  let mix04 = mix_blend(mix04_fac, dark_tex, tex_rgb);
136
133
 
137
- // 倒角.001→Z → 混合.003 Factor; A=混合.004, B=色相/饱和度/明度.002
138
134
  let bevel_z = clamp(n.y, 0.0, 1.0);
139
135
  let mix03 = mix_blend(bevel_z, mix04, dark_tex);
140
136
 
141
- // 环境光遮蔽 → 颜色渐变.001 LINEAR → 混合.001 (白/黑) → 混合.002 OVERLAY Fac
142
- let ao = 1.0; // ao_fake(n, v) — no SSAO yet; inline 1.0 so the ramp/mix chain folds at compile time.
143
- let ao_ramp_c = ramp_linear(ao, 0.0, vec4f(1,1,1,1), 0.8808, vec4f(0,0,0,1));
144
- let mix01_fac = ao_ramp_c.r;
145
- let mix01_rgb = mix(vec3f(1.0), vec3f(0.0), mix01_fac);
146
-
147
- // 混合.002 OVERLAY: Fac=混合.001, A=混合.003, B=色相/饱和度/明度.004
148
137
  let hue004 = hue_sat_id(0.800000011920929, 2.0, 1.0, mix03);
149
- let overlay_fac = mix01_rgb.r;
150
- let npr_rgb = mix_overlay(overlay_fac, mix03, hue004);
138
+ let npr_rgb = mix_overlay(1.0, mix03, hue004);
151
139
  let npr_emission = npr_rgb * NPR_EMIT_STR;
152
140
 
153
- // 原理化BSDF (EEVEE port): metallic=0, specular=0.8, roughness=0.5, specular_tint=0.
154
- // Bump subtree is dead in the Blender graph (噪波→凹凸 not linked to Principled.Normal).
141
+ // Principled BSDF (EEVEE port): metallic=0, specular=0.8, roughness=0.5, specular_tint=0.
142
+ // Bump subtree is dead in the Blender graph (noise→bump not linked to Principled.Normal).
155
143
  let principled_base = hue_sat_id(1.0, 0.800000011920929, 1.0, tex_rgb);
156
144
  let NL = max(dot(n, l), 0.0);
157
145
  let NV = max(dot(n, v), 1e-4);
@@ -73,12 +73,11 @@ fn sampleShadow(worldPos: vec3f, n: vec3f) -> f32 {
73
73
  const PI_F: f32 = 3.141592653589793;
74
74
  const FACE_SPECULAR: f32 = 0.5;
75
75
  const FACE_ROUGHNESS: f32 = 0.3;
76
- // Dump M_Face unlinked defaults (math op enum not serialized — warm clamp chain still from m_graphs)
77
76
  const FACE_RIM2_POW: f32 = 0.6300000548362732;
78
77
  const FACE_RIM2_BG: vec3f = vec3f(1.0, 0.4684903025627136, 0.3698573112487793);
79
- const FACE_WARM_AO_MUL: f32 = 0.30000001192092896; // 运算.004 MULTIPLY after invert (was 0.5 in older trace)
80
- const FACE_BRIGHT_TEX_THRESH: f32 = 0.9300000071525574; // 运算.005 GREATER_THAN Value_001
81
- const FACE_MIX_NPR: f32 = 0.5; // 混合着色器.001 Fac
78
+ const FACE_WARM_STR: f32 = 0.30000001192092896;
79
+ const FACE_BRIGHT_TEX_THRESH: f32 = 0.9300000071525574;
80
+ const FACE_MIX_NPR: f32 = 0.5;
82
81
  // EEVEE Light Clamp equivalent (Render Props → Sampling → Clamping). Caps direct
83
82
  // specular firefly from the noise-bumped normal's NDF aliasing — Blender hides this
84
83
  // via TAA, which we don't have. Value mirrors EEVEE's default Clamp Indirect=10.0.
@@ -112,10 +111,6 @@ const FACE_SPEC_CLAMP: f32 = 10.0;
112
111
  return output;
113
112
  }
114
113
 
115
- // Fragment: M_Face NPR + Principled hybrid
116
- // TEX → HueSat shadow/lit → toon gate → BrightContrast → AO chain → emission stack
117
- // Fresnel rims, warm AO emission, bright-texture gate, noise-bumped Principled
118
- // Final = mix(Principled, NPR, 0.5)
119
114
  struct FSOut {
120
115
  @location(0) color: vec4f,
121
116
  @location(1) mask: f32,
@@ -134,79 +129,49 @@ struct FSOut {
134
129
  let tex_color = textureSample(diffuseTexture, diffuseSampler, input.uv).rgb;
135
130
  let shadow = sampleShadow(input.worldPos, n);
136
131
 
137
- // ═══ SOURCES ═══
138
- // DiffuseBSDF(white) → ShaderToRGB (energy-matched); shadow on direct only
139
132
  let ndotl_raw = shader_to_rgb_diffuse(n, l, sun, light.ambientColor.xyz, shadow);
140
- // ramp.008 CONSTANT — edge AA avoids binary fac shimmer / white specks on terminator (fwidth + smoothstep)
133
+ // ramp_constant_edge_aa: avoids binary fac shimmer on terminator (fwidth + smoothstep).
141
134
  let toon = ramp_constant_edge_aa(ndotl_raw, 0.2966, vec4f(0,0,0,1), vec4f(1,1,1,1)).r;
142
- let ao = 1.0; // ao_fake(n, v) — no SSAO yet; inline 1.0 so the ramp/mix chain folds at compile time.
143
135
 
144
- // ═══ TOON COLOR ═══
145
- let shadow_tint = hue_sat(0.46000000834465027, 2.0, 0.3499999940395355, 1.0, tex_color); // HueSat.002
146
- let lit_tint = hue_sat(0.46000000834465027, 1.600000023841858, 1.5, 1.0, tex_color); // HueSat.001
147
- let toon_color = mix_blend(toon, shadow_tint, lit_tint); // Mix.004
136
+ let shadow_tint = hue_sat(0.46000000834465027, 2.0, 0.3499999940395355, 1.0, tex_color);
137
+ let lit_tint = hue_sat(0.46000000834465027, 1.600000023841858, 1.5, 1.0, tex_color);
138
+ let toon_color = mix_blend(toon, shadow_tint, lit_tint);
148
139
  let bc = bright_contrast(toon_color, 0.1, 0.2);
149
140
 
150
- // ═══ AO CHAIN ═══
151
- // ramp CONSTANT [0→white, 0.5995→black]
152
- let ao_ramp = ramp_constant(ao, 0.0, vec4f(1,1,1,1), 0.5995, vec4f(0,0,0,1)).r;
153
- // Mix.003(Factor=ao_ramp, A=bc, B=reddish tint)
154
- let ao_mixed = mix_blend(ao_ramp, bc, vec3f(0.8302, 0.3346, 0.2795));
141
+ let emission3 = bc * 2.5;
155
142
 
156
- // ═══ EMISSION 3 ═══
157
- let emission3 = ao_mixed * 2.5; // Emission.003(Strength=2.5)
158
-
159
- // ═══ WARM EMISSION ═══
160
- let ao_inv = invert_f(1.0, ao_ramp);
161
- let warm_str = ao_inv * FACE_WARM_AO_MUL; // 反转 → 运算.004 MULTIPLY Value_001
162
- let warm_input = clamp(toon * 0.5 + 0.5, 0.0, 1.0); // 运算.001→运算.006→Clamp
163
- // ramp.003 CARDINAL [0.2409→warm dark, 0.4663→warm light]
143
+ let warm_input = clamp(toon * 0.5 + 0.5, 0.0, 1.0);
164
144
  let warm_color = ramp_cardinal(warm_input, 0.2409,
165
145
  vec4f(0.2426, 0.068, 0.0588, 1.0), 0.4663,
166
146
  vec4f(0.6677, 0.5024, 0.5126, 1.0)).rgb;
167
- let warm_emission = warm_color * warm_str; // Emission.001
147
+ let warm_emission = warm_color * FACE_WARM_STR;
168
148
 
169
- // ═══ RIM 1 ═══
170
- // Fresnel(IOR=2.0) × LayerWeight.001(Facing, Blend=0.24)
171
149
  let rim1_str = fresnel(2.0, n, v) * layer_weight_facing(0.24, n, v);
172
150
  let rim1 = vec3f(0.984157919883728, 0.6110184788703918, 0.5736401677131653) * rim1_str;
173
151
 
174
- // ═══ RIM 2 ═══
175
- // Fresnel.001(IOR=1.45) × LayerWeight.002(Fresnel output, Blend=0.61)
176
152
  let rim2_raw = fresnel(1.45, n, v) * layer_weight_fresnel(0.61, n, v);
177
153
  let rim2_fac = math_power(rim2_raw, FACE_RIM2_POW);
178
- // MixShader.002: Shader=Emission.003, Shader_001=背景
179
154
  let rim2_mixed = mix(emission3, FACE_RIM2_BG, rim2_fac);
180
155
 
181
- // 转接点.005(tex)运算.005 GREATER_THAN Value_001
182
- // Blender implicitly converts Color Float via BT.601 grayscale when plugging a
183
- // color output into a Math node's Value input. Our earlier trace used tex_color.r,
184
- // which fires aggressively on R-dominant skin single near-white R pixels produced
185
- // firefly speckles. color_to_value matches the actual Blender socket semantic and
186
- // only fires on genuinely near-white painted features (the author's intent).
156
+ // Blender implicitly converts Color Float via BT.601 grayscale when plugging a color
157
+ // output into a Math node's Value input. An earlier trace used tex_color.r, which fires
158
+ // aggressively on R-dominant skin single near-white R pixels produced firefly speckles.
159
+ // color_to_value matches the Blender socket semantic and only fires on genuinely near-
160
+ // white painted features (the author's intent).
187
161
  let tex_gate = math_greater_than(color_to_value(tex_color), FACE_BRIGHT_TEX_THRESH);
188
- let bright_emit = vec3f(tex_gate) * 3.0; // Emission.002(Strength=3.0)
162
+ let bright_emit = vec3f(tex_gate) * 3.0;
189
163
 
190
- // ═══ NPR STACK (AddShader chain) ═══
191
- let add2 = rim2_mixed + bright_emit; // AddShader.002
192
- let add0 = rim1 + add2; // AddShader
193
- let npr_stack = add0 + warm_emission; // AddShader.001
164
+ let npr_stack = rim1 + rim2_mixed + bright_emit + warm_emission;
194
165
 
195
- // ═══ PRINCIPLED BSDF ═══
196
- // Noise-based bump normal. Mapping loc=rot=0 → plain scale multiply, inline.
166
+ // Noise bump Mapping loc=rot=0 folds to a plain scale multiply.
197
167
  let noise_val = tex_noise_d2(input.worldPos * vec3f(1.0, 1.0, 1.5), 1.0);
198
168
  let noise_ramp = ramp_linear(noise_val, 0.0, vec4f(0,0,0,1), 1.0, vec4f(1,1,1,1)).r;
199
- let bumped_n = bump_lh(0.324644535779953, noise_ramp, n, input.worldPos); // 凹凸 Strength; LH bump
169
+ let bumped_n = bump_lh(0.324644535779953, noise_ramp, n, input.worldPos);
200
170
 
201
- // Mix.001(Factor=noise_ramp, A=bc, B=dark red)
202
171
  let principled_base = mix_blend(noise_ramp, bc, vec3f(0.6832, 0.1947, 0.1373));
203
- // Emission input from reroute.011 (bc), Strength=0.2
204
172
  let p_emission = bc * 0.2;
205
- // AO.002 → ramp.005 LINEAR [0.003→black, 1.0→gray] for subsurface approx.
206
- // Reuse 'ao' (ao_fake(n, v) above) — identical inputs, avoid a second procedural AO pass.
207
- let sss = ramp_linear(ao, 0.003, vec4f(0,0,0,1), 1.0, vec4f(0.0786, 0.0786, 0.0786, 1.0)).r;
208
173
 
209
- // 原理化BSDF (EEVEE port): metallic=0, specular=0.5, roughness=0.3, specular_tint=0.
174
+ // Principled BSDF (EEVEE port): metallic=0, specular=0.5, roughness=0.3, specular_tint=0.
210
175
  let NL = max(dot(bumped_n, l), 0.0);
211
176
  let NV = max(dot(bumped_n, v), 1e-4);
212
177
 
@@ -223,9 +188,8 @@ struct FSOut {
223
188
  // Indirect diffuse = base_color × L_w per Blender closure_eval_surface_lib.glsl line 302;
224
189
  // probe_evaluate_world_diff returns radiance (SH-projected, not cosine-convolved).
225
190
  let diffuse_radiance = principled_base * (sun * NL * shadow / PI_F + light.ambientColor.xyz);
226
- let principled = diffuse_radiance + spec_radiance + p_emission + vec3f(sss);
191
+ let principled = diffuse_radiance + spec_radiance + p_emission;
227
192
 
228
- // 混合着色器.001: Shader=相加着色器.001, Shader_001=原理化BSDF — Fac blends toward second
229
193
  let final_color = mix(npr_stack, principled, FACE_MIX_NPR);
230
194
 
231
195
  var out: FSOut;
@@ -73,7 +73,6 @@ fn sampleShadow(worldPos: vec3f, n: vec3f) -> f32 {
73
73
  const PI_H: f32 = 3.141592653589793;
74
74
  const HAIR_SPECULAR: f32 = 1.0;
75
75
  const HAIR_ROUGHNESS: f32 = 0.3;
76
- // Dump M_Hair: 运算.004 GREATER_THAN second operand Value_001; 运算.007 POWER exponent Value_001; 背景 Color
77
76
  const HAIR_TEX_GATE_THRESH: f32 = 0.15000000596046448;
78
77
  const HAIR_RIM2_POW: f32 = 0.6300000548362732;
79
78
  const HAIR_MIX_BG: vec3f = vec3f(0.1673291176557541);
@@ -120,55 +119,37 @@ struct FSOut {
120
119
  let l = -light.lights[0].direction.xyz;
121
120
  let sun = light.lights[0].color.xyz * light.lights[0].color.w;
122
121
 
123
- // 图像纹理 ← 纹理坐标.UV → 映射 (default 1,1,1 scale per JSON)
124
122
  let tex_color = textureSample(diffuseTexture, diffuseSampler, input.uv).rgb;
125
123
  let shadow = sampleShadow(input.worldPos, n);
126
124
 
127
- // 色相/饱和度/明度 (Hue=0.5 Sat=1.2 Val=0.5 Fac=1) ← reroute from image
128
125
  let hue_sat_shadow = hue_sat_id(1.2, 0.5, 1.0, tex_color);
129
- // 色相/饱和度/明度.002 (0.48, 1.2, 0.7, 1) ← previous
130
126
  let hue_sat_002 = hue_sat(0.48, 1.2, 0.7, 1.0, hue_sat_shadow);
131
- // 色相/饱和度/明度.001 (0.5, 1.5, 1.0, 1) ← image reroute (lit path)
132
127
  let hue_sat_001 = hue_sat_id(1.5, 1.0, 1.0, tex_color);
133
128
 
134
- // 漫射 BSDF.002 → Shader --> RGB → 颜色渐变.008 CONSTANT [0→0, 0.2966→1]
135
129
  let ndotl_raw = shader_to_rgb_diffuse(n, l, sun, light.ambientColor.xyz, shadow);
136
130
  let ramp_008 = ramp_constant(ndotl_raw, 0.0, vec4f(0,0,0,1), 0.2966, vec4f(1,1,1,1)).r;
137
131
 
138
- // 混合.004 MIX Fac=ramp_008, A=hue_sat_002, B=hue_sat_001
139
132
  let mix_004 = mix_blend(ramp_008, hue_sat_002, hue_sat_001);
140
-
141
- // 亮度/对比度 (Bright=0.1 Contrast=0.2) ← mix_004 only (links: not bevel path)
142
133
  let bc = bright_contrast(mix_004, 0.1, 0.2);
143
134
 
144
- // 倒角.001 → 分离 XYZ.001 → Z → 混合.003 Factor; A=bc, B=hue_sat_002
145
135
  let bevel_z = clamp(n.y, 0.0, 1.0);
146
136
  let mix_003 = mix_blend(bevel_z, bc, hue_sat_002);
147
137
 
148
- // 环境光遮蔽 (AO).001 → 颜色渐变.001 → 混合.001 → 混合.002 chain collapses with fake AO=1:
149
- // ramp_constant(1, 0→white, 0.3756→black).r = 0 → ao_factor = mix(1,0,0) = 1 → mix_002 = mix_003.
150
- // hue_sat_004 becomes unreachable. When real SSAO lands, restore the original 5-line port.
151
- let emission3 = mix_003; // Emission.003 Strength=1.0 (×1 omitted)
152
-
153
- // 菲涅尔.001 × 层权重.002 → 运算.003 MULTIPLY → 运算.007 POWER(exponent Value_001) → MixShader.002 Fac
154
138
  let rim2_raw = fresnel(1.45, n, v) * layer_weight_fresnel(0.61, n, v);
155
139
  let rim2_fac = math_power(rim2_raw, HAIR_RIM2_POW);
156
- // MixShader.002: Shader=Emission.003, Shader_001=背景 — (1-Fac)*emission + Fac*bg
157
- let mix_shader_002 = mix(emission3, HAIR_MIX_BG, rim2_fac);
140
+ let mix_shader_002 = mix(mix_003, HAIR_MIX_BG, rim2_fac);
158
141
 
159
- // 运算.004 GREATER_THAN: 图像→Value, threshold Value_001. Blender converts Color→Float
160
- // via BT.601 luminance, not raw R — same socket-semantic fix as M_Face.
142
+ // Blender's GREATER_THAN converts Color→Float via BT.601 luminance, not raw R — same
143
+ // socket-semantic fix as M_Face.
161
144
  let tex_gate = math_greater_than(color_to_value(tex_color), HAIR_TEX_GATE_THRESH);
162
145
  let gate_emit = vec3f(tex_gate) * 0.1;
163
146
 
164
- // 相加着色器: MixShader.002 + gate emission (color sum in linear space)
165
147
  let add_shader = mix_shader_002 + gate_emit;
166
148
 
167
- // 原理化BSDF (EEVEE port): metallic=0, specular=1.0, roughness=0.3, specular_tint=0.
168
- // Blender graph has 噪波→法线贴图 Strength=0.1 on Principled.Normal, but MixShader.001
169
- // weights Principled at only 0.2; spec contribution × that weight is imperceptible in
170
- // A/B with the noise-bump port enabled, so we drop it and keep plain n saves a full
171
- // tex_noise + bump_lh per hair fragment.
149
+ // Principled BSDF (EEVEE port): metallic=0, specular=1.0, roughness=0.3, specular_tint=0.
150
+ // Graph has a noise→normal_map bump (Strength=0.1) on Principled.Normal, but MixShader.001
151
+ // weights Principled at only 0.2 — the bumped spec × that weight is imperceptible, so we
152
+ // drop the subtree and keep plain n (saves a tex_noise + bump_lh per hair fragment).
172
153
  let NL = max(dot(n, l), 0.0);
173
154
  let NV = max(dot(n, v), 1e-4);
174
155
 
@@ -186,7 +167,6 @@ struct FSOut {
186
167
  let diffuse_radiance = bc * (sun * NL * shadow / PI_H + light.ambientColor.xyz);
187
168
  let principled = diffuse_radiance + spec_radiance;
188
169
 
189
- // 混合着色器.001 Fac=0.2: first socket=相加着色器, second=原理化BSDF
190
170
  let final_color = mix(add_shader, principled, 0.2);
191
171
 
192
172
  var out: FSOut;
@@ -2,6 +2,12 @@
2
2
  // + NPR toon/AO emission stack (Strength=8.1), MixShader Fac=0.6967.
3
3
  // Base color uses a Voronoi pattern sampled in reflection-coord space (Blender 纹理坐标.Reflection)
4
4
  // to add subtle metallic sparkle variation. No Normal link in the graph.
5
+ //
6
+ // Graph's base color chain is: 纹理坐标.Reflection → 矢量运算.007(CROSS, Vec2=(0,1,0)) →
7
+ // 沃罗诺伊纹理(F1, Color out) → 颜色渐变(linear) → 混合.005. The dumper did not capture the
8
+ // VectorMath operation — CROSS is the assumed op based on the hardcoded (0,1,0) Vector_001
9
+ // constant (MULTIPLY would zero X/Z producing 1D bands; CROSS produces horizontal ring
10
+ // patterns consistent with metallic anisotropy).
5
11
 
6
12
  import { NODES_WGSL } from "./nodes"
7
13
 
@@ -129,40 +135,30 @@ struct FSOut {
129
135
  let out_alpha = material.alpha * tex_s.a;
130
136
  if (out_alpha < 0.001) { discard; }
131
137
 
132
- // ═══ NPR toon stack (图像 → HSV.007 Val=0.8 → 转接点.001) ═══
133
138
  let tex_tint = hue_sat_id(1.0, 0.800000011920929, 1.0, tex_rgb);
134
139
  let lum_shade = shader_to_rgb_diffuse(n, l, sun, amb, shadow);
135
140
  let ramp008 = ramp_constant_edge_aa(lum_shade, METAL_TOON_EDGE, vec4f(0,0,0,1), vec4f(1,1,1,1));
136
141
  let mix04_fac = math_multiply(ramp008.r, METAL_MIX04_MUL);
137
142
 
138
- // 混合.004: A=HSV.002(Val=0.2 dark), B=tex_tint
139
143
  let dark_tex = hue_sat_id(1.0, 0.19999998807907104, 1.0, tex_tint);
140
144
  let mix04 = mix_blend(mix04_fac, dark_tex, tex_tint);
141
145
 
142
- // AO white/black ramp → 混合.002 factor
143
- let ao = 1.0; // ao_fake(n, v) — no SSAO yet; inline 1.0 so the ramp/mix chain folds at compile time.
144
- let ao_ramp_c = ramp_linear(ao, 0.0, vec4f(1,1,1,1), 0.8808, vec4f(0,0,0,1));
145
- let overlay_fac = mix(1.0, 0.0, ao_ramp_c.r);
146
-
147
- // 混合.002 OVERLAY: A=HSV.008(Val=1.0 identity) ← mix04, B=HSV.004(Val=2.0 bright) ← mix04
148
- let hue008 = mix04; // identity HSV
149
146
  let hue004 = hue_sat_id(1.0, 2.0, 1.0, mix04);
150
- let npr_rgb = mix_overlay(overlay_fac, hue008, hue004);
147
+ let npr_rgb = mix_overlay(1.0, mix04, hue004);
151
148
  let npr_emission = npr_rgb * METAL_EMIT_STR;
152
149
 
153
- // ═══ Metallic Principled base color ═══
154
- // Reflection-coord Voronoi for metallic sparkle:
155
- // 纹理坐标.Reflection → 矢量运算 → 沃罗诺伊(Scale=4.3) → 颜色渐变 → 混合.005
150
+ // Reflection-coord Voronoi produces the metallic sparkle variation.
151
+ // VALTORGB takes Color Fac via Blender's BT.601 implicit color_to_value.
156
152
  let refl_dir = reflect(-v, n);
157
- let voro = tex_voronoi_f1(refl_dir, METAL_VORONOI_SCALE);
158
- let voro_ramp = ramp_linear(voro, 0.0, vec4f(0,0,0,1), 1.0, vec4f(1,1,1,1)).r;
159
- // 混合.005: Fac=voro_ramp, A=voro_color(grayscale), B=HSV.006(Hue=0.5 Sat=1.5 Val=1.3)
153
+ let voro_input = cross(refl_dir, vec3f(0.0, 1.0, 0.0));
154
+ let voro_rgb = tex_voronoi_color(voro_input, METAL_VORONOI_SCALE);
155
+ let voro_scalar = color_to_value(voro_rgb);
156
+ let voro_ramp = ramp_linear(voro_scalar, 0.0, vec4f(0,0,0,1), 1.0, vec4f(1,1,1,1)).r;
160
157
  let hue006 = hue_sat_id(1.5, 1.2999999523162842, 1.0, tex_tint);
161
158
  let albedo = mix_blend(voro_ramp, vec3f(voro_ramp), hue006);
162
159
 
163
- // 原理化BSDF (EEVEE port): metallic=1.0, specular=1.0, roughness=0.3.
164
- // Per Blender principled wiring: f0 = mix((0.08*spec)*dielectric, albedo, metallic) → with
165
- // metallic=1 this is just albedo (specular_tint is dielectric-only and ignored here).
160
+ // Principled BSDF (EEVEE port): metallic=1 collapses f0 = mix(dielectric, albedo, 1) = albedo;
161
+ // specular_tint is dielectric-only and ignored here.
166
162
  let f0 = albedo;
167
163
  let f90 = mix(f0, vec3f(1.0), sqrt(METAL_SPECULAR));
168
164
  let NL = max(dot(n, l), 0.0);