reze-engine 0.12.1 → 0.12.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 (120) hide show
  1. package/README.md +102 -61
  2. package/dist/engine.d.ts +9 -4
  3. package/dist/engine.d.ts.map +1 -1
  4. package/dist/engine.js +62 -18
  5. package/dist/shaders/body.d.ts +1 -1
  6. package/dist/shaders/body.d.ts.map +1 -1
  7. package/dist/shaders/body.js +7 -28
  8. package/dist/shaders/cloth_rough.d.ts +1 -1
  9. package/dist/shaders/cloth_rough.d.ts.map +1 -1
  10. package/dist/shaders/cloth_rough.js +4 -16
  11. package/dist/shaders/cloth_smooth.d.ts +1 -1
  12. package/dist/shaders/cloth_smooth.d.ts.map +1 -1
  13. package/dist/shaders/cloth_smooth.js +5 -17
  14. package/dist/shaders/default.d.ts +1 -1
  15. package/dist/shaders/default.d.ts.map +1 -1
  16. package/dist/shaders/eye.d.ts +1 -1
  17. package/dist/shaders/eye.d.ts.map +1 -1
  18. package/dist/shaders/face.d.ts +1 -1
  19. package/dist/shaders/face.d.ts.map +1 -1
  20. package/dist/shaders/face.js +21 -57
  21. package/dist/shaders/hair.d.ts +1 -1
  22. package/dist/shaders/hair.d.ts.map +1 -1
  23. package/dist/shaders/hair.js +7 -27
  24. package/dist/shaders/materials/body.d.ts +1 -1
  25. package/dist/shaders/materials/body.d.ts.map +1 -1
  26. package/dist/shaders/materials/body.js +86 -197
  27. package/dist/shaders/materials/cloth_rough.d.ts +1 -1
  28. package/dist/shaders/materials/cloth_rough.d.ts.map +1 -1
  29. package/dist/shaders/materials/cloth_rough.js +11 -122
  30. package/dist/shaders/materials/cloth_smooth.d.ts +1 -1
  31. package/dist/shaders/materials/cloth_smooth.d.ts.map +1 -1
  32. package/dist/shaders/materials/cloth_smooth.js +59 -172
  33. package/dist/shaders/materials/common.d.ts +6 -0
  34. package/dist/shaders/materials/common.d.ts.map +1 -0
  35. package/dist/shaders/materials/common.js +160 -0
  36. package/dist/shaders/materials/default.d.ts +1 -1
  37. package/dist/shaders/materials/default.d.ts.map +1 -1
  38. package/dist/shaders/materials/default.js +13 -146
  39. package/dist/shaders/materials/eye.d.ts +1 -1
  40. package/dist/shaders/materials/eye.d.ts.map +1 -1
  41. package/dist/shaders/materials/eye.js +13 -118
  42. package/dist/shaders/materials/face.d.ts +1 -1
  43. package/dist/shaders/materials/face.d.ts.map +1 -1
  44. package/dist/shaders/materials/face.js +85 -197
  45. package/dist/shaders/materials/hair.d.ts +1 -1
  46. package/dist/shaders/materials/hair.d.ts.map +1 -1
  47. package/dist/shaders/materials/hair.js +78 -183
  48. package/dist/shaders/materials/metal.d.ts +1 -1
  49. package/dist/shaders/materials/metal.d.ts.map +1 -1
  50. package/dist/shaders/materials/metal.js +16 -122
  51. package/dist/shaders/materials/nodes.d.ts +1 -1
  52. package/dist/shaders/materials/nodes.d.ts.map +1 -1
  53. package/dist/shaders/materials/nodes.js +77 -0
  54. package/dist/shaders/materials/stockings.d.ts +1 -1
  55. package/dist/shaders/materials/stockings.d.ts.map +1 -1
  56. package/dist/shaders/materials/stockings.js +26 -152
  57. package/dist/shaders/metal.d.ts +1 -1
  58. package/dist/shaders/metal.d.ts.map +1 -1
  59. package/dist/shaders/metal.js +4 -17
  60. package/dist/shaders/nodes.d.ts +1 -1
  61. package/dist/shaders/nodes.d.ts.map +1 -1
  62. package/dist/shaders/nodes.js +0 -9
  63. package/dist/shaders/passes/bloom.d.ts +1 -1
  64. package/dist/shaders/passes/bloom.d.ts.map +1 -1
  65. package/dist/shaders/passes/bloom.js +7 -4
  66. package/dist/shaders/passes/composite.d.ts +1 -1
  67. package/dist/shaders/passes/composite.d.ts.map +1 -1
  68. package/dist/shaders/passes/composite.js +11 -4
  69. package/dist/shaders/passes/ground.d.ts +1 -1
  70. package/dist/shaders/passes/ground.d.ts.map +1 -1
  71. package/dist/shaders/passes/ground.js +6 -2
  72. package/dist/shaders/passes/outline.d.ts +1 -1
  73. package/dist/shaders/passes/outline.d.ts.map +1 -1
  74. package/dist/shaders/passes/outline.js +2 -2
  75. package/dist/shaders/stockings.d.ts +1 -1
  76. package/dist/shaders/stockings.d.ts.map +1 -1
  77. package/package.json +1 -1
  78. package/src/engine.ts +60 -18
  79. package/src/shaders/materials/body.ts +90 -201
  80. package/src/shaders/materials/cloth_rough.ts +11 -122
  81. package/src/shaders/materials/cloth_smooth.ts +63 -176
  82. package/src/shaders/materials/common.ts +171 -0
  83. package/src/shaders/materials/default.ts +13 -146
  84. package/src/shaders/materials/eye.ts +13 -118
  85. package/src/shaders/materials/face.ts +89 -201
  86. package/src/shaders/materials/hair.ts +82 -187
  87. package/src/shaders/materials/metal.ts +16 -122
  88. package/src/shaders/materials/nodes.ts +77 -0
  89. package/src/shaders/materials/stockings.ts +27 -153
  90. package/src/shaders/passes/bloom.ts +7 -4
  91. package/src/shaders/passes/composite.ts +11 -4
  92. package/src/shaders/passes/ground.ts +6 -2
  93. package/src/shaders/passes/outline.ts +2 -2
  94. package/dist/bezier-interpolate.d.ts +0 -15
  95. package/dist/bezier-interpolate.d.ts.map +0 -1
  96. package/dist/bezier-interpolate.js +0 -40
  97. package/dist/engine_ts.d.ts +0 -143
  98. package/dist/engine_ts.d.ts.map +0 -1
  99. package/dist/engine_ts.js +0 -1575
  100. package/dist/ik.d.ts +0 -32
  101. package/dist/ik.d.ts.map +0 -1
  102. package/dist/ik.js +0 -337
  103. package/dist/player.d.ts +0 -64
  104. package/dist/player.d.ts.map +0 -1
  105. package/dist/player.js +0 -220
  106. package/dist/pool-scene.d.ts +0 -52
  107. package/dist/pool-scene.d.ts.map +0 -1
  108. package/dist/pool-scene.js +0 -1122
  109. package/dist/pool.d.ts +0 -38
  110. package/dist/pool.d.ts.map +0 -1
  111. package/dist/pool.js +0 -422
  112. package/dist/rzm-converter.d.ts +0 -12
  113. package/dist/rzm-converter.d.ts.map +0 -1
  114. package/dist/rzm-converter.js +0 -40
  115. package/dist/rzm-loader.d.ts +0 -24
  116. package/dist/rzm-loader.d.ts.map +0 -1
  117. package/dist/rzm-loader.js +0 -488
  118. package/dist/rzm-writer.d.ts +0 -27
  119. package/dist/rzm-writer.d.ts.map +0 -1
  120. package/dist/rzm-writer.js +0 -701
@@ -1,201 +1,90 @@
1
- // M_Body — 仿深空之眼渲染预设v1.0_by_小绿毛猫_material_graph_dump.json "M_Body"; VALTORGB / math ops from m_graphs where dump omits them.
2
-
3
- import { NODES_WGSL } from "./nodes"
4
-
5
- export const BODY_SHADER_WGSL = /* wgsl */ `
6
-
7
- ${NODES_WGSL}
8
-
9
- struct CameraUniforms {
10
- view: mat4x4f,
11
- projection: mat4x4f,
12
- viewPos: vec3f,
13
- _padding: f32,
14
- };
15
-
16
- struct Light {
17
- direction: vec4f,
18
- color: vec4f,
19
- };
20
-
21
- struct LightUniforms {
22
- ambientColor: vec4f,
23
- lights: array<Light, 4>,
24
- };
25
-
26
- struct MaterialUniforms {
27
- diffuseColor: vec3f,
28
- alpha: f32,
29
- };
30
-
31
- struct VertexOutput {
32
- @builtin(position) position: vec4f,
33
- @location(0) normal: vec3f,
34
- @location(1) uv: vec2f,
35
- @location(2) worldPos: vec3f,
36
- };
37
-
38
- struct LightVP { viewProj: mat4x4f, };
39
-
40
- @group(0) @binding(0) var<uniform> camera: CameraUniforms;
41
- @group(0) @binding(1) var<uniform> light: LightUniforms;
42
- @group(0) @binding(2) var diffuseSampler: sampler;
43
- @group(0) @binding(3) var shadowMap: texture_depth_2d;
44
- @group(0) @binding(4) var shadowSampler: sampler_comparison;
45
- @group(0) @binding(5) var<uniform> lightVP: LightVP;
46
- @group(1) @binding(0) var<storage, read> skinMats: array<mat4x4f>;
47
- @group(2) @binding(0) var diffuseTexture: texture_2d<f32>;
48
- @group(2) @binding(1) var<uniform> material: MaterialUniforms;
49
-
50
- fn sampleShadow(worldPos: vec3f, n: vec3f) -> f32 {
51
- // Back-facing to key light: direct contribution is zero anyway, skip 9 texture samples.
52
- if (dot(n, -light.lights[0].direction.xyz) <= 0.0) { return 0.0; }
53
- let biasedPos = worldPos + n * 0.08;
54
- let lclip = lightVP.viewProj * vec4f(biasedPos, 1.0);
55
- let ndc = lclip.xyz / max(lclip.w, 1e-6);
56
- let suv = vec2f(ndc.x * 0.5 + 0.5, 0.5 - ndc.y * 0.5);
57
- let cmpZ = ndc.z - 0.001;
58
- let ts = 1.0 / 2048.0;
59
- // 3x3 PCF unrolled Safari's Metal backend doesn't unroll nested shadow loops reliably.
60
- let s00 = textureSampleCompareLevel(shadowMap, shadowSampler, suv + vec2f(-ts, -ts), cmpZ);
61
- let s10 = textureSampleCompareLevel(shadowMap, shadowSampler, suv + vec2f(0.0, -ts), cmpZ);
62
- let s20 = textureSampleCompareLevel(shadowMap, shadowSampler, suv + vec2f( ts, -ts), cmpZ);
63
- let s01 = textureSampleCompareLevel(shadowMap, shadowSampler, suv + vec2f(-ts, 0.0), cmpZ);
64
- let s11 = textureSampleCompareLevel(shadowMap, shadowSampler, suv, cmpZ);
65
- let s21 = textureSampleCompareLevel(shadowMap, shadowSampler, suv + vec2f( ts, 0.0), cmpZ);
66
- let s02 = textureSampleCompareLevel(shadowMap, shadowSampler, suv + vec2f(-ts, ts), cmpZ);
67
- let s12 = textureSampleCompareLevel(shadowMap, shadowSampler, suv + vec2f(0.0, ts), cmpZ);
68
- let s22 = textureSampleCompareLevel(shadowMap, shadowSampler, suv + vec2f( ts, ts), cmpZ);
69
- return (s00 + s10 + s20 + s01 + s11 + s21 + s02 + s12 + s22) * (1.0 / 9.0);
70
- }
71
-
72
- const PI_B: f32 = 3.141592653589793;
73
- const BODY_ROUGHNESS: f32 = 0.3;
74
- const BODY_RIM2_LAYER_BLEND: f32 = 0.20000000298023224;
75
- const BODY_RIM2_POW: f32 = 1.4300000667572021;
76
- const BODY_RIM2_BG: vec3f = vec3f(1.0, 0.4303792119026184, 0.3315804898738861);
77
- const BODY_WARM_STR: f32 = 0.30000001192092896;
78
- const BODY_SPECULAR: f32 = 0.5;
79
- const BODY_MIX_NPR: f32 = 0.5;
80
- // EEVEE Light Clamp equivalent — caps firefly specular from noise-bumped NDF aliasing.
81
- const BODY_SPEC_CLAMP: f32 = 10.0;
82
-
83
- // smoothstep-based ramp: t*t*(3-2*t) between two color stops
84
- fn ramp_ease(f: f32, p0: f32, c0: vec4f, p1: f32, c1: vec4f) -> vec4f {
85
- let t = saturate((f - p0) / max(p1 - p0, 1e-6));
86
- let ss = t * t * (3.0 - 2.0 * t);
87
- return mix(c0, c1, ss);
88
- }
89
-
90
- @vertex fn vs(
91
- @location(0) position: vec3f,
92
- @location(1) normal: vec3f,
93
- @location(2) uv: vec2f,
94
- @location(3) joints0: vec4<u32>,
95
- @location(4) weights0: vec4<f32>
96
- ) -> VertexOutput {
97
- var output: VertexOutput;
98
- let pos4 = vec4f(position, 1.0);
99
- let weightSum = weights0.x + weights0.y + weights0.z + weights0.w;
100
- let invWeightSum = select(1.0, 1.0 / weightSum, weightSum > 0.0001);
101
- let nw = select(vec4f(1.0, 0.0, 0.0, 0.0), weights0 * invWeightSum, weightSum > 0.0001);
102
- var skinnedPos = vec4f(0.0);
103
- var skinnedNrm = vec3f(0.0);
104
- for (var i = 0u; i < 4u; i++) {
105
- let m = skinMats[joints0[i]];
106
- let w = nw[i];
107
- skinnedPos += (m * pos4) * w;
108
- skinnedNrm += (mat3x3f(m[0].xyz, m[1].xyz, m[2].xyz) * normal) * w;
109
- }
110
- output.position = camera.projection * camera.view * vec4f(skinnedPos.xyz, 1.0);
111
- // Skip VS normalize — interpolation denormalizes anyway, and FS always does normalize(input.normal).
112
- output.normal = skinnedNrm;
113
- output.uv = uv;
114
- output.worldPos = skinnedPos.xyz;
115
- return output;
116
- }
117
-
118
- struct FSOut {
119
- @location(0) color: vec4f,
120
- @location(1) mask: f32,
121
- };
122
-
123
- @fragment fn fs(input: VertexOutput) -> FSOut {
124
- let alpha = material.alpha;
125
- if (alpha < 0.001) { discard; }
126
-
127
- let n = normalize(input.normal);
128
- let v = normalize(camera.viewPos - input.worldPos);
129
- let l = -light.lights[0].direction.xyz;
130
- let sun = light.lights[0].color.xyz * light.lights[0].color.w;
131
-
132
- let tex_color = textureSample(diffuseTexture, diffuseSampler, input.uv).rgb;
133
- let shadow = sampleShadow(input.worldPos, n);
134
-
135
- let ndotl_raw = shader_to_rgb_diffuse(n, l, sun, light.ambientColor.xyz, shadow);
136
- let toon = ramp_constant(ndotl_raw, 0.0, vec4f(0,0,0,1), 0.2966, vec4f(1,1,1,1)).r;
137
-
138
- let shadow_tint = hue_sat_id(2.0, 0.3499999940395355, 1.0, tex_color);
139
- let lit_tint = hue_sat_id(1.5, 1.0, 1.0, tex_color);
140
- let toon_color = mix_blend(toon, shadow_tint, lit_tint);
141
- let bc = bright_contrast(toon_color, 0.1, 0.2);
142
-
143
- let emission3 = bc * 4.0;
144
-
145
- let warm_input = clamp(toon + 0.5, 0.0, 1.0);
146
- let warm_color = ramp_cardinal(warm_input, 0.2409,
147
- vec4f(0.2426, 0.068, 0.0588, 1.0), 0.4663,
148
- vec4f(0.6677, 0.5024, 0.5126, 1.0)).rgb;
149
- let warm_emission = warm_color * BODY_WARM_STR;
150
-
151
- let rim1_str = fresnel(2.0, n, v) * layer_weight_facing(0.24000005424022675, n, v);
152
- let rim1 = vec3f(0.984157919883728, 0.6110184788703918, 0.5736401677131653) * rim1_str;
153
-
154
- let facing_raw = layer_weight_facing(BODY_RIM2_LAYER_BLEND, n, v);
155
- let facing_pow = math_power(facing_raw, BODY_RIM2_POW);
156
- let rim2_fac = ramp_ease(facing_pow, 0.0, vec4f(0,0,0,1), 0.5052, vec4f(1,1,1,1)).r;
157
- let rim2_mixed = mix(emission3, BODY_RIM2_BG, rim2_fac);
158
-
159
- let npr_stack = rim1 + rim2_mixed + warm_emission;
160
-
161
- // Noise bump — Mapping loc=rot=0 folds to a plain scale multiply.
162
- let noise_val = tex_noise_d2(input.worldPos * vec3f(1.0, 1.0, 1.5), 1.0);
163
- let noise_ramp = ramp_linear(noise_val, 0.0, vec4f(0,0,0,1), 1.0, vec4f(1,1,1,1)).r;
164
- let bumped_n = bump_lh(0.324644535779953, noise_ramp, n, input.worldPos);
165
-
166
- let principled_base = mix_blend(noise_ramp, bc, vec3f(0.6831911206245422, 0.19474034011363983, 0.13732507824897766));
167
- let p_emission = bc * 0.2;
168
-
169
- // Principled BSDF (EEVEE port): metallic=0, specular=0.5, roughness=0.3, specular_tint=0.
170
- let NL = max(dot(bumped_n, l), 0.0);
171
- let NV = max(dot(bumped_n, v), 1e-4);
172
-
173
- // f0/f90 per gpu_shader_material_principled.glsl — specular_tint=0 → dielectric_f0_color=white.
174
- let f0 = vec3f(0.08 * BODY_SPECULAR);
175
- let f90 = mix(f0, vec3f(1.0), sqrt(BODY_SPECULAR));
176
- let brdf_lut = brdf_lut_sample(NV, BODY_ROUGHNESS);
177
- let reflection_color = F_brdf_multi_scatter(f0, f90, brdf_lut.xy);
178
-
179
- // Direct glossy — bsdf_ggx already includes NL; no F applied here (tinted after accum).
180
- // ltc_brdf_scale: EEVEE direct path uses LTC; split-sum LUT path is rescaled to match.
181
- let spec_direct_raw = bsdf_ggx(bumped_n, l, v, NL, NV, BODY_ROUGHNESS) * sun * shadow * ltc_brdf_scale_from_lut(brdf_lut);
182
- let spec_direct = min(spec_direct_raw, vec3f(BODY_SPEC_CLAMP));
183
- // Indirect glossy — flat world probe (solid color). Phase 2 adds cubemap.
184
- let spec_indirect = light.ambientColor.xyz;
185
- let spec_radiance = (spec_direct + spec_indirect) * reflection_color;
186
-
187
- // Indirect diffuse = base_color × L_w per Blender closure_eval_surface_lib.glsl line 302;
188
- // probe_evaluate_world_diff returns radiance (SH-projected, not cosine-convolved).
189
- // No (1-F) factor per EEVEE — it doesn't energy-conserve spec<->diffuse.
190
- let diffuse_radiance = principled_base * (sun * NL * shadow / PI_B + light.ambientColor.xyz);
191
- let principled = diffuse_radiance + spec_radiance + p_emission;
192
-
193
- let final_color = mix(npr_stack, principled, BODY_MIX_NPR);
194
-
195
- var out: FSOut;
196
- out.color = vec4f(final_color, alpha);
197
- out.mask = 1.0;
198
- return out;
199
- }
200
-
201
- `
1
+ // M_Body — 仿深空之眼渲染预设v1.0_by_小绿毛猫 "M_Body". Toon + warm rim + rim1/rim2
2
+ // stack mixed 50/50 against a Principled BSDF with noise-bumped normal.
3
+
4
+ import { NODES_WGSL } from "./nodes"
5
+ import { COMMON_MATERIAL_PRELUDE_WGSL } from "./common"
6
+
7
+ export const BODY_SHADER_WGSL = /* wgsl */ `
8
+
9
+ ${NODES_WGSL}
10
+ ${COMMON_MATERIAL_PRELUDE_WGSL}
11
+
12
+ const BODY_ROUGHNESS: f32 = 0.3;
13
+ const BODY_SPECULAR: f32 = 0.5;
14
+ const BODY_MIX_NPR: f32 = 0.5;
15
+ const BODY_SPEC_CLAMP: f32 = 10.0;
16
+ const BODY_RIM2_LAYER_BLEND: f32 = 0.20000000298023224;
17
+ const BODY_RIM2_POW: f32 = 1.4300000667572021;
18
+ const BODY_RIM2_BG: vec3f = vec3f(1.0, 0.4303792119026184, 0.3315804898738861);
19
+ const BODY_WARM_STR: f32 = 0.30000001192092896;
20
+
21
+ // smoothstep-based ramp: t*t*(3-2*t) between two color stops
22
+ fn ramp_ease(f: f32, p0: f32, c0: vec4f, p1: f32, c1: vec4f) -> vec4f {
23
+ let t = saturate((f - p0) / max(p1 - p0, 1e-6));
24
+ let ss = t * t * (3.0 - 2.0 * t);
25
+ return mix(c0, c1, ss);
26
+ }
27
+
28
+ @fragment fn fs(input: VertexOutput) -> FSOut {
29
+ let alpha = material.alpha;
30
+ if (alpha < 0.001) { discard; }
31
+
32
+ let n = normalize(input.normal);
33
+ let v = normalize(camera.viewPos - input.worldPos);
34
+ let l = -light.lights[0].direction.xyz;
35
+ let sun = light.lights[0].color.xyz * light.lights[0].color.w;
36
+ let amb = light.ambientColor.xyz;
37
+ let shadow = sampleShadow(input.worldPos, n);
38
+
39
+ let tex_color = textureSample(diffuseTexture, diffuseSampler, input.uv).rgb;
40
+
41
+ // ═══ NPR STACK ═══
42
+ let ndotl_raw = shader_to_rgb_diffuse(n, l, sun, amb, shadow);
43
+ let toon = ramp_constant(ndotl_raw, 0.0, vec4f(0,0,0,1), 0.2966, vec4f(1,1,1,1)).r;
44
+
45
+ let shadow_tint = hue_sat_id(2.0, 0.3499999940395355, 1.0, tex_color);
46
+ let lit_tint = hue_sat_id(1.5, 1.0, 1.0, tex_color);
47
+ let toon_color = mix_blend(toon, shadow_tint, lit_tint);
48
+ let bc = bright_contrast(toon_color, 0.1, 0.2);
49
+
50
+ let emission3 = bc * 4.0;
51
+
52
+ let warm_input = clamp(toon + 0.5, 0.0, 1.0);
53
+ let warm_color = ramp_cardinal(warm_input, 0.2409,
54
+ vec4f(0.2426, 0.068, 0.0588, 1.0), 0.4663,
55
+ vec4f(0.6677, 0.5024, 0.5126, 1.0)).rgb;
56
+ let warm_emission = warm_color * BODY_WARM_STR;
57
+
58
+ let rim1_str = fresnel(2.0, n, v) * layer_weight_facing(0.24000005424022675, n, v);
59
+ let rim1 = vec3f(0.984157919883728, 0.6110184788703918, 0.5736401677131653) * rim1_str;
60
+
61
+ let facing_raw = layer_weight_facing(BODY_RIM2_LAYER_BLEND, n, v);
62
+ let facing_pow = math_power(facing_raw, BODY_RIM2_POW);
63
+ let rim2_fac = ramp_ease(facing_pow, 0.0, vec4f(0,0,0,1), 0.5052, vec4f(1,1,1,1)).r;
64
+ let rim2_mixed = mix(emission3, BODY_RIM2_BG, rim2_fac);
65
+
66
+ let npr_stack = rim1 + rim2_mixed + warm_emission;
67
+
68
+ // ═══ PRINCIPLED BSDF with noise bump ═══
69
+ // Mapping loc=rot=0 in the Blender graph folds to a plain scale multiply.
70
+ let noise_val = tex_noise_d2(input.worldPos * vec3f(1.0, 1.0, 1.5), 1.0);
71
+ let noise_ramp = ramp_linear(noise_val, 0.0, vec4f(0,0,0,1), 1.0, vec4f(1,1,1,1)).r;
72
+ let bumped_n = bump_lh(0.324644535779953, noise_ramp, n, input.worldPos);
73
+
74
+ let principled_base = mix_blend(noise_ramp, bc, vec3f(0.6831911206245422, 0.19474034011363983, 0.13732507824897766));
75
+ let p_emission = bc * 0.2;
76
+
77
+ let principled = eval_principled(
78
+ PrincipledIn(principled_base, 0.0, BODY_SPECULAR, BODY_ROUGHNESS, BODY_SPEC_CLAMP, 0.0, 0.0),
79
+ bumped_n, l, v, sun, amb, shadow
80
+ ) + p_emission;
81
+
82
+ let final_color = mix(npr_stack, principled, BODY_MIX_NPR);
83
+
84
+ var out: FSOut;
85
+ out.color = vec4f(final_color, alpha);
86
+ out.mask = vec4f(1.0, 1.0, 0.0, out.color.a);
87
+ return out;
88
+ }
89
+
90
+ `
@@ -1,76 +1,14 @@
1
- // M_Rough_Cloth — NPR graph identical to M_Smooth_Cloth but bump chain IS live
2
- // (噪波→渐变→凹凸.Normal 原理化BSDF.Normal in m_graphs) and Roughness=0.8187.
1
+ // M_Rough_Cloth — NPR graph identical to M_Smooth_Cloth, but the noise bump subtree
2
+ // IS live on Principled.Normal and Roughness is raised to 0.8187.
3
3
 
4
4
  import { NODES_WGSL } from "./nodes"
5
+ import { COMMON_MATERIAL_PRELUDE_WGSL } from "./common"
5
6
 
6
7
  export const CLOTH_ROUGH_SHADER_WGSL = /* wgsl */ `
7
8
 
8
9
  ${NODES_WGSL}
10
+ ${COMMON_MATERIAL_PRELUDE_WGSL}
9
11
 
10
- struct CameraUniforms {
11
- view: mat4x4f,
12
- projection: mat4x4f,
13
- viewPos: vec3f,
14
- _padding: f32,
15
- };
16
-
17
- struct Light {
18
- direction: vec4f,
19
- color: vec4f,
20
- };
21
-
22
- struct LightUniforms {
23
- ambientColor: vec4f,
24
- lights: array<Light, 4>,
25
- };
26
-
27
- struct MaterialUniforms {
28
- diffuseColor: vec3f,
29
- alpha: f32,
30
- };
31
-
32
- struct VertexOutput {
33
- @builtin(position) position: vec4f,
34
- @location(0) normal: vec3f,
35
- @location(1) uv: vec2f,
36
- @location(2) worldPos: vec3f,
37
- };
38
-
39
- struct LightVP { viewProj: mat4x4f, };
40
-
41
- @group(0) @binding(0) var<uniform> camera: CameraUniforms;
42
- @group(0) @binding(1) var<uniform> light: LightUniforms;
43
- @group(0) @binding(2) var diffuseSampler: sampler;
44
- @group(0) @binding(3) var shadowMap: texture_depth_2d;
45
- @group(0) @binding(4) var shadowSampler: sampler_comparison;
46
- @group(0) @binding(5) var<uniform> lightVP: LightVP;
47
- @group(1) @binding(0) var<storage, read> skinMats: array<mat4x4f>;
48
- @group(2) @binding(0) var diffuseTexture: texture_2d<f32>;
49
- @group(2) @binding(1) var<uniform> material: MaterialUniforms;
50
-
51
- fn sampleShadow(worldPos: vec3f, n: vec3f) -> f32 {
52
- // Back-facing to key light: direct contribution is zero anyway, skip 9 texture samples.
53
- if (dot(n, -light.lights[0].direction.xyz) <= 0.0) { return 0.0; }
54
- let biasedPos = worldPos + n * 0.08;
55
- let lclip = lightVP.viewProj * vec4f(biasedPos, 1.0);
56
- let ndc = lclip.xyz / max(lclip.w, 1e-6);
57
- let suv = vec2f(ndc.x * 0.5 + 0.5, 0.5 - ndc.y * 0.5);
58
- let cmpZ = ndc.z - 0.001;
59
- let ts = 1.0 / 2048.0;
60
- // 3x3 PCF unrolled — Safari's Metal backend doesn't unroll nested shadow loops reliably.
61
- let s00 = textureSampleCompareLevel(shadowMap, shadowSampler, suv + vec2f(-ts, -ts), cmpZ);
62
- let s10 = textureSampleCompareLevel(shadowMap, shadowSampler, suv + vec2f(0.0, -ts), cmpZ);
63
- let s20 = textureSampleCompareLevel(shadowMap, shadowSampler, suv + vec2f( ts, -ts), cmpZ);
64
- let s01 = textureSampleCompareLevel(shadowMap, shadowSampler, suv + vec2f(-ts, 0.0), cmpZ);
65
- let s11 = textureSampleCompareLevel(shadowMap, shadowSampler, suv, cmpZ);
66
- let s21 = textureSampleCompareLevel(shadowMap, shadowSampler, suv + vec2f( ts, 0.0), cmpZ);
67
- let s02 = textureSampleCompareLevel(shadowMap, shadowSampler, suv + vec2f(-ts, ts), cmpZ);
68
- let s12 = textureSampleCompareLevel(shadowMap, shadowSampler, suv + vec2f(0.0, ts), cmpZ);
69
- let s22 = textureSampleCompareLevel(shadowMap, shadowSampler, suv + vec2f( ts, ts), cmpZ);
70
- return (s00 + s10 + s20 + s01 + s11 + s21 + s02 + s12 + s22) * (1.0 / 9.0);
71
- }
72
-
73
- const PI_CR: f32 = 3.141592653589793;
74
12
  const CLOTH_R_SPECULAR: f32 = 0.8;
75
13
  const CLOTH_R_ROUGHNESS: f32 = 0.8187;
76
14
  const CLOTH_R_TOON_EDGE: f32 = 0.2966;
@@ -79,42 +17,8 @@ const CLOTH_R_EMIT_STR: f32 = 18.200000762939453;
79
17
  const CLOTH_R_MIX_SHADER_FAC: f32 = 0.8999999761581421;
80
18
  const CLOTH_R_NOISE_SCALE: f32 = 17.7;
81
19
  const CLOTH_R_BUMP_STR: f32 = 1.0;
82
- // EEVEE Light Clamp equivalent — caps firefly specular from noise-bumped NDF aliasing.
83
20
  const CLOTH_R_SPEC_CLAMP: f32 = 10.0;
84
21
 
85
- @vertex fn vs(
86
- @location(0) position: vec3f,
87
- @location(1) normal: vec3f,
88
- @location(2) uv: vec2f,
89
- @location(3) joints0: vec4<u32>,
90
- @location(4) weights0: vec4<f32>
91
- ) -> VertexOutput {
92
- var output: VertexOutput;
93
- let pos4 = vec4f(position, 1.0);
94
- let weightSum = weights0.x + weights0.y + weights0.z + weights0.w;
95
- let invWeightSum = select(1.0, 1.0 / weightSum, weightSum > 0.0001);
96
- let nw = select(vec4f(1.0, 0.0, 0.0, 0.0), weights0 * invWeightSum, weightSum > 0.0001);
97
- var skinnedPos = vec4f(0.0);
98
- var skinnedNrm = vec3f(0.0);
99
- for (var i = 0u; i < 4u; i++) {
100
- let m = skinMats[joints0[i]];
101
- let w = nw[i];
102
- skinnedPos += (m * pos4) * w;
103
- skinnedNrm += (mat3x3f(m[0].xyz, m[1].xyz, m[2].xyz) * normal) * w;
104
- }
105
- output.position = camera.projection * camera.view * vec4f(skinnedPos.xyz, 1.0);
106
- // Skip VS normalize — interpolation denormalizes anyway, and FS always does normalize(input.normal).
107
- output.normal = skinnedNrm;
108
- output.uv = uv;
109
- output.worldPos = skinnedPos.xyz;
110
- return output;
111
- }
112
-
113
- struct FSOut {
114
- @location(0) color: vec4f,
115
- @location(1) mask: f32,
116
- };
117
-
118
22
  @fragment fn fs(input: VertexOutput) -> FSOut {
119
23
  let n = normalize(input.normal);
120
24
  let v = normalize(camera.viewPos - input.worldPos);
@@ -128,8 +32,8 @@ struct FSOut {
128
32
  let out_alpha = material.alpha * tex_s.a;
129
33
  if (out_alpha < 0.001) { discard; }
130
34
 
35
+ // ═══ NPR STACK ═══
131
36
  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
37
  let ramp008 = ramp_constant_edge_aa(lum_shade, CLOTH_R_TOON_EDGE, vec4f(0,0,0,1), vec4f(1,1,1,1));
134
38
  let mix04_fac = math_multiply(ramp008.r, CLOTH_R_MIX04_MUL);
135
39
 
@@ -143,37 +47,22 @@ struct FSOut {
143
47
  let npr_rgb = mix_overlay(1.0, mix03, hue004);
144
48
  let npr_emission = npr_rgb * CLOTH_R_EMIT_STR;
145
49
 
146
- // Noise bump is LIVE in M_Rough_Cloth unlike Smooth Cloth where the subtree is dead.
50
+ // ═══ PRINCIPLED BSDF with noise bump (live in this preset) ═══
147
51
  let noise_val = tex_noise_d2(input.worldPos, CLOTH_R_NOISE_SCALE);
148
52
  let noise_ramp = ramp_linear(noise_val, 0.0, vec4f(0,0,0,1), 1.0, vec4f(1,1,1,1)).r;
149
53
  let bumped_n = bump_lh(CLOTH_R_BUMP_STR, noise_ramp, n, input.worldPos);
150
54
 
151
- // 原理化BSDF (EEVEE port): metallic=0, specular=0.8, roughness=0.8187, specular_tint=0.
152
55
  let principled_base = hue_sat_id(1.0, 0.800000011920929, 1.0, tex_rgb);
153
- let NL = max(dot(bumped_n, l), 0.0);
154
- let NV = max(dot(bumped_n, v), 1e-4);
155
-
156
- let f0 = vec3f(0.08 * CLOTH_R_SPECULAR);
157
- let f90 = mix(f0, vec3f(1.0), sqrt(CLOTH_R_SPECULAR));
158
- let brdf_lut = brdf_lut_sample(NV, CLOTH_R_ROUGHNESS);
159
- let reflection_color = F_brdf_multi_scatter(f0, f90, brdf_lut.xy);
160
-
161
- let spec_direct_raw = bsdf_ggx(bumped_n, l, v, NL, NV, CLOTH_R_ROUGHNESS) * sun * shadow * ltc_brdf_scale_from_lut(brdf_lut);
162
- let spec_direct = min(spec_direct_raw, vec3f(CLOTH_R_SPEC_CLAMP));
163
- let spec_indirect = amb;
164
- let spec_radiance = (spec_direct + spec_indirect) * reflection_color;
165
-
166
- // Indirect diffuse = base_color × L_w per Blender closure_eval_surface_lib.glsl line 302;
167
- // probe_evaluate_world_diff returns radiance (SH-projected, not cosine-convolved).
168
- let diffuse_radiance = principled_base * (sun * NL * shadow / PI_CR + amb);
169
- let principled = diffuse_radiance + spec_radiance;
56
+ let principled = eval_principled(
57
+ PrincipledIn(principled_base, 0.0, CLOTH_R_SPECULAR, CLOTH_R_ROUGHNESS, CLOTH_R_SPEC_CLAMP, 0.0, 0.0),
58
+ bumped_n, l, v, sun, amb, shadow
59
+ );
170
60
 
171
- // 混合着色器.001 Fac=0.9: Shader=自发光.005, Shader_001=原理化BSDF
172
61
  let final_color = mix(npr_emission, principled, CLOTH_R_MIX_SHADER_FAC);
173
62
 
174
63
  var out: FSOut;
175
64
  out.color = vec4f(final_color, out_alpha);
176
- out.mask = 1.0;
65
+ out.mask = vec4f(1.0, 1.0, 0.0, out.color.a);
177
66
  return out;
178
67
  }
179
68