reze-engine 0.12.0 → 0.12.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +99 -58
- package/dist/bezier-interpolate.d.ts +15 -0
- package/dist/bezier-interpolate.d.ts.map +1 -0
- package/dist/bezier-interpolate.js +40 -0
- package/dist/engine_ts.d.ts +143 -0
- package/dist/engine_ts.d.ts.map +1 -0
- package/dist/engine_ts.js +1575 -0
- package/dist/ik.d.ts +32 -0
- package/dist/ik.d.ts.map +1 -0
- package/dist/ik.js +337 -0
- package/dist/player.d.ts +64 -0
- package/dist/player.d.ts.map +1 -0
- package/dist/player.js +220 -0
- package/dist/pool-scene.d.ts +52 -0
- package/dist/pool-scene.d.ts.map +1 -0
- package/dist/pool-scene.js +1122 -0
- package/dist/pool.d.ts +38 -0
- package/dist/pool.d.ts.map +1 -0
- package/dist/pool.js +422 -0
- package/dist/rzm-converter.d.ts +12 -0
- package/dist/rzm-converter.d.ts.map +1 -0
- package/dist/rzm-converter.js +40 -0
- package/dist/rzm-loader.d.ts +24 -0
- package/dist/rzm-loader.d.ts.map +1 -0
- package/dist/rzm-loader.js +488 -0
- package/dist/rzm-writer.d.ts +27 -0
- package/dist/rzm-writer.d.ts.map +1 -0
- package/dist/rzm-writer.js +701 -0
- package/dist/shaders/body.d.ts +1 -1
- package/dist/shaders/body.d.ts.map +1 -1
- package/dist/shaders/body.js +28 -7
- package/dist/shaders/cloth_rough.d.ts +1 -1
- package/dist/shaders/cloth_rough.d.ts.map +1 -1
- package/dist/shaders/cloth_rough.js +16 -4
- package/dist/shaders/cloth_smooth.d.ts +1 -1
- package/dist/shaders/cloth_smooth.d.ts.map +1 -1
- package/dist/shaders/cloth_smooth.js +17 -5
- package/dist/shaders/default.d.ts +1 -1
- package/dist/shaders/default.d.ts.map +1 -1
- package/dist/shaders/eye.d.ts +1 -1
- package/dist/shaders/eye.d.ts.map +1 -1
- package/dist/shaders/face.d.ts +1 -1
- package/dist/shaders/face.d.ts.map +1 -1
- package/dist/shaders/face.js +57 -21
- package/dist/shaders/hair.d.ts +1 -1
- package/dist/shaders/hair.d.ts.map +1 -1
- package/dist/shaders/hair.js +27 -7
- package/dist/shaders/materials/body.d.ts +1 -1
- package/dist/shaders/materials/body.d.ts.map +1 -1
- package/dist/shaders/materials/body.js +86 -197
- package/dist/shaders/materials/cloth_rough.d.ts +1 -1
- package/dist/shaders/materials/cloth_rough.d.ts.map +1 -1
- package/dist/shaders/materials/cloth_rough.js +10 -121
- package/dist/shaders/materials/cloth_smooth.d.ts +1 -1
- package/dist/shaders/materials/cloth_smooth.d.ts.map +1 -1
- package/dist/shaders/materials/cloth_smooth.js +59 -172
- package/dist/shaders/materials/common.d.ts +6 -0
- package/dist/shaders/materials/common.d.ts.map +1 -0
- package/dist/shaders/materials/common.js +144 -0
- package/dist/shaders/materials/default.d.ts +1 -1
- package/dist/shaders/materials/default.d.ts.map +1 -1
- package/dist/shaders/materials/default.js +12 -145
- package/dist/shaders/materials/eye.d.ts +1 -1
- package/dist/shaders/materials/eye.d.ts.map +1 -1
- package/dist/shaders/materials/eye.js +12 -117
- package/dist/shaders/materials/face.d.ts +1 -1
- package/dist/shaders/materials/face.d.ts.map +1 -1
- package/dist/shaders/materials/face.js +85 -197
- package/dist/shaders/materials/hair.d.ts +1 -1
- package/dist/shaders/materials/hair.d.ts.map +1 -1
- package/dist/shaders/materials/hair.js +78 -183
- package/dist/shaders/materials/metal.d.ts +1 -1
- package/dist/shaders/materials/metal.d.ts.map +1 -1
- package/dist/shaders/materials/metal.js +15 -121
- package/dist/shaders/materials/nodes.d.ts +1 -1
- package/dist/shaders/materials/nodes.d.ts.map +1 -1
- package/dist/shaders/materials/nodes.js +77 -0
- package/dist/shaders/materials/stockings.d.ts +1 -1
- package/dist/shaders/materials/stockings.d.ts.map +1 -1
- package/dist/shaders/materials/stockings.js +26 -152
- package/dist/shaders/metal.d.ts +1 -1
- package/dist/shaders/metal.d.ts.map +1 -1
- package/dist/shaders/metal.js +17 -4
- package/dist/shaders/nodes.d.ts +1 -1
- package/dist/shaders/nodes.d.ts.map +1 -1
- package/dist/shaders/nodes.js +9 -0
- package/dist/shaders/stockings.d.ts +1 -1
- package/dist/shaders/stockings.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/shaders/materials/body.ts +90 -201
- package/src/shaders/materials/cloth_rough.ts +10 -121
- package/src/shaders/materials/cloth_smooth.ts +63 -176
- package/src/shaders/materials/common.ts +155 -0
- package/src/shaders/materials/default.ts +12 -145
- package/src/shaders/materials/eye.ts +12 -117
- package/src/shaders/materials/face.ts +89 -201
- package/src/shaders/materials/hair.ts +82 -187
- package/src/shaders/materials/metal.ts +15 -121
- package/src/shaders/materials/nodes.ts +77 -0
- package/src/shaders/materials/stockings.ts +27 -153
|
@@ -1,187 +1,82 @@
|
|
|
1
|
-
// M_Hair —
|
|
2
|
-
//
|
|
3
|
-
|
|
4
|
-
import { NODES_WGSL } from "./nodes"
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
//
|
|
13
|
-
//
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
let
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
let
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
const HAIR_RIM2_POW: f32 = 0.6300000548362732;
|
|
84
|
-
const HAIR_MIX_BG: vec3f = vec3f(0.1673291176557541);
|
|
85
|
-
|
|
86
|
-
@vertex fn vs(
|
|
87
|
-
@location(0) position: vec3f,
|
|
88
|
-
@location(1) normal: vec3f,
|
|
89
|
-
@location(2) uv: vec2f,
|
|
90
|
-
@location(3) joints0: vec4<u32>,
|
|
91
|
-
@location(4) weights0: vec4<f32>
|
|
92
|
-
) -> VertexOutput {
|
|
93
|
-
var output: VertexOutput;
|
|
94
|
-
let pos4 = vec4f(position, 1.0);
|
|
95
|
-
let weightSum = weights0.x + weights0.y + weights0.z + weights0.w;
|
|
96
|
-
let invWeightSum = select(1.0, 1.0 / weightSum, weightSum > 0.0001);
|
|
97
|
-
let nw = select(vec4f(1.0, 0.0, 0.0, 0.0), weights0 * invWeightSum, weightSum > 0.0001);
|
|
98
|
-
var skinnedPos = vec4f(0.0);
|
|
99
|
-
var skinnedNrm = vec3f(0.0);
|
|
100
|
-
for (var i = 0u; i < 4u; i++) {
|
|
101
|
-
let m = skinMats[joints0[i]];
|
|
102
|
-
let w = nw[i];
|
|
103
|
-
skinnedPos += (m * pos4) * w;
|
|
104
|
-
skinnedNrm += (mat3x3f(m[0].xyz, m[1].xyz, m[2].xyz) * normal) * w;
|
|
105
|
-
}
|
|
106
|
-
output.position = camera.projection * camera.view * vec4f(skinnedPos.xyz, 1.0);
|
|
107
|
-
// Skip VS normalize — interpolation denormalizes anyway, and FS always does normalize(input.normal).
|
|
108
|
-
output.normal = skinnedNrm;
|
|
109
|
-
output.uv = uv;
|
|
110
|
-
output.worldPos = skinnedPos.xyz;
|
|
111
|
-
return output;
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
struct FSOut {
|
|
115
|
-
@location(0) color: vec4f,
|
|
116
|
-
@location(1) mask: f32,
|
|
117
|
-
};
|
|
118
|
-
|
|
119
|
-
@fragment fn fs(input: VertexOutput) -> FSOut {
|
|
120
|
-
let alpha = material.alpha;
|
|
121
|
-
if (alpha < 0.001) { discard; }
|
|
122
|
-
|
|
123
|
-
let n = normalize(input.normal);
|
|
124
|
-
let v = normalize(camera.viewPos - input.worldPos);
|
|
125
|
-
let l = -light.lights[0].direction.xyz;
|
|
126
|
-
let sun = light.lights[0].color.xyz * light.lights[0].color.w;
|
|
127
|
-
|
|
128
|
-
let tex_color = textureSample(diffuseTexture, diffuseSampler, input.uv).rgb;
|
|
129
|
-
let shadow = sampleShadow(input.worldPos, n);
|
|
130
|
-
|
|
131
|
-
let hue_sat_shadow = hue_sat_id(1.2, 0.5, 1.0, tex_color);
|
|
132
|
-
let hue_sat_002 = hue_sat(0.48, 1.2, 0.7, 1.0, hue_sat_shadow);
|
|
133
|
-
let hue_sat_001 = hue_sat_id(1.5, 1.0, 1.0, tex_color);
|
|
134
|
-
|
|
135
|
-
let ndotl_raw = shader_to_rgb_diffuse(n, l, sun, light.ambientColor.xyz, shadow);
|
|
136
|
-
let ramp_008 = ramp_constant(ndotl_raw, 0.0, vec4f(0,0,0,1), 0.2966, vec4f(1,1,1,1)).r;
|
|
137
|
-
|
|
138
|
-
let mix_004 = mix_blend(ramp_008, hue_sat_002, hue_sat_001);
|
|
139
|
-
let bc = bright_contrast(mix_004, 0.1, 0.2);
|
|
140
|
-
|
|
141
|
-
let bevel_z = clamp(n.y, 0.0, 1.0);
|
|
142
|
-
let mix_003 = mix_blend(bevel_z, bc, hue_sat_002);
|
|
143
|
-
|
|
144
|
-
let rim2_raw = fresnel(1.45, n, v) * layer_weight_fresnel(0.61, n, v);
|
|
145
|
-
let rim2_fac = math_power(rim2_raw, HAIR_RIM2_POW);
|
|
146
|
-
let mix_shader_002 = mix(mix_003, HAIR_MIX_BG, rim2_fac);
|
|
147
|
-
|
|
148
|
-
// Blender's GREATER_THAN converts Color→Float via BT.601 luminance, not raw R — same
|
|
149
|
-
// socket-semantic fix as M_Face.
|
|
150
|
-
let tex_gate = math_greater_than(color_to_value(tex_color), HAIR_TEX_GATE_THRESH);
|
|
151
|
-
let gate_emit = vec3f(tex_gate) * 0.1;
|
|
152
|
-
|
|
153
|
-
let add_shader = mix_shader_002 + gate_emit;
|
|
154
|
-
|
|
155
|
-
// Principled BSDF (EEVEE port): metallic=0, specular=1.0, roughness=0.3, specular_tint=0.
|
|
156
|
-
// Graph has a noise→normal_map bump (Strength=0.1) on Principled.Normal, but MixShader.001
|
|
157
|
-
// weights Principled at only 0.2 — the bumped spec × that weight is imperceptible, so we
|
|
158
|
-
// drop the subtree and keep plain n (saves a tex_noise + bump_lh per hair fragment).
|
|
159
|
-
let NL = max(dot(n, l), 0.0);
|
|
160
|
-
let NV = max(dot(n, v), 1e-4);
|
|
161
|
-
|
|
162
|
-
let f0 = vec3f(0.08 * HAIR_SPECULAR);
|
|
163
|
-
let f90 = mix(f0, vec3f(1.0), sqrt(HAIR_SPECULAR));
|
|
164
|
-
let brdf_lut = brdf_lut_sample(NV, HAIR_ROUGHNESS);
|
|
165
|
-
let reflection_color = F_brdf_multi_scatter(f0, f90, brdf_lut.xy);
|
|
166
|
-
|
|
167
|
-
let spec_direct = bsdf_ggx(n, l, v, NL, NV, HAIR_ROUGHNESS) * sun * shadow * ltc_brdf_scale_from_lut(brdf_lut);
|
|
168
|
-
let spec_indirect = light.ambientColor.xyz;
|
|
169
|
-
let spec_radiance = (spec_direct + spec_indirect) * reflection_color;
|
|
170
|
-
|
|
171
|
-
// Indirect diffuse = base_color × L_w per Blender closure_eval_surface_lib.glsl line 302;
|
|
172
|
-
// probe_evaluate_world_diff returns radiance (SH-projected, not cosine-convolved).
|
|
173
|
-
let diffuse_radiance = bc * (sun * NL * shadow / PI_H + light.ambientColor.xyz);
|
|
174
|
-
let principled = diffuse_radiance + spec_radiance;
|
|
175
|
-
|
|
176
|
-
let final_color = mix(add_shader, principled, 0.2);
|
|
177
|
-
|
|
178
|
-
var outAlpha = alpha;
|
|
179
|
-
if (IS_OVER_EYES) { outAlpha = alpha * 0.6; }
|
|
180
|
-
|
|
181
|
-
var out: FSOut;
|
|
182
|
-
out.color = vec4f(final_color, outAlpha);
|
|
183
|
-
out.mask = 1.0;
|
|
184
|
-
return out;
|
|
185
|
-
}
|
|
186
|
-
|
|
187
|
-
`
|
|
1
|
+
// M_Hair — 仿深空之眼渲染预设v1.0_by_小绿毛猫 "M_Hair". Toon + fresnel rim + bevel +
|
|
2
|
+
// bright-tex gate, mixed 80/20 NPR/PBR. MixShader.001 Fac=0.2 keeps Principled subtle.
|
|
3
|
+
|
|
4
|
+
import { NODES_WGSL } from "./nodes"
|
|
5
|
+
import { COMMON_MATERIAL_PRELUDE_WGSL } from "./common"
|
|
6
|
+
|
|
7
|
+
export const HAIR_SHADER_WGSL = /* wgsl */ `
|
|
8
|
+
|
|
9
|
+
${NODES_WGSL}
|
|
10
|
+
${COMMON_MATERIAL_PRELUDE_WGSL}
|
|
11
|
+
|
|
12
|
+
// Pipeline-override: the engine compiles two variants — the normal opaque hair pipeline
|
|
13
|
+
// (IS_OVER_EYES=false) and a second pipeline that re-draws hair fragments stencil-matched
|
|
14
|
+
// against the eye stamp with 50% alpha so eyes read through the hair silhouette. Resolved
|
|
15
|
+
// at pipeline-compile time; the dead branch is dropped by the shader compiler.
|
|
16
|
+
override IS_OVER_EYES: bool = false;
|
|
17
|
+
|
|
18
|
+
const HAIR_SPECULAR: f32 = 1.0;
|
|
19
|
+
const HAIR_ROUGHNESS: f32 = 0.3;
|
|
20
|
+
const HAIR_TEX_GATE_THRESH: f32 = 0.15000000596046448;
|
|
21
|
+
const HAIR_RIM2_POW: f32 = 0.6300000548362732;
|
|
22
|
+
const HAIR_MIX_BG: vec3f = vec3f(0.1673291176557541);
|
|
23
|
+
const HAIR_MIX_NPR: f32 = 0.2;
|
|
24
|
+
|
|
25
|
+
@fragment fn fs(input: VertexOutput) -> FSOut {
|
|
26
|
+
let alpha = material.alpha;
|
|
27
|
+
if (alpha < 0.001) { discard; }
|
|
28
|
+
|
|
29
|
+
let n = normalize(input.normal);
|
|
30
|
+
let v = normalize(camera.viewPos - input.worldPos);
|
|
31
|
+
let l = -light.lights[0].direction.xyz;
|
|
32
|
+
let sun = light.lights[0].color.xyz * light.lights[0].color.w;
|
|
33
|
+
let amb = light.ambientColor.xyz;
|
|
34
|
+
let shadow = sampleShadow(input.worldPos, n);
|
|
35
|
+
|
|
36
|
+
let tex_color = textureSample(diffuseTexture, diffuseSampler, input.uv).rgb;
|
|
37
|
+
|
|
38
|
+
// ═══ NPR STACK ═══
|
|
39
|
+
let hue_sat_shadow = hue_sat_id(1.2, 0.5, 1.0, tex_color);
|
|
40
|
+
let hue_sat_002 = hue_sat(0.48, 1.2, 0.7, 1.0, hue_sat_shadow);
|
|
41
|
+
let hue_sat_001 = hue_sat_id(1.5, 1.0, 1.0, tex_color);
|
|
42
|
+
|
|
43
|
+
let ndotl_raw = shader_to_rgb_diffuse(n, l, sun, amb, shadow);
|
|
44
|
+
let ramp_008 = ramp_constant(ndotl_raw, 0.0, vec4f(0,0,0,1), 0.2966, vec4f(1,1,1,1)).r;
|
|
45
|
+
|
|
46
|
+
let mix_004 = mix_blend(ramp_008, hue_sat_002, hue_sat_001);
|
|
47
|
+
let bc = bright_contrast(mix_004, 0.1, 0.2);
|
|
48
|
+
|
|
49
|
+
let bevel_z = clamp(n.y, 0.0, 1.0);
|
|
50
|
+
let mix_003 = mix_blend(bevel_z, bc, hue_sat_002);
|
|
51
|
+
|
|
52
|
+
let rim2_raw = fresnel(1.45, n, v) * layer_weight_fresnel(0.61, n, v);
|
|
53
|
+
let rim2_fac = math_power(rim2_raw, HAIR_RIM2_POW);
|
|
54
|
+
let mix_shader_002 = mix(mix_003, HAIR_MIX_BG, rim2_fac);
|
|
55
|
+
|
|
56
|
+
// GREATER_THAN on a color input uses BT.601 luminance — same socket-semantic fix as face.ts.
|
|
57
|
+
let tex_gate = math_greater_than(color_to_value(tex_color), HAIR_TEX_GATE_THRESH);
|
|
58
|
+
let gate_emit = vec3f(tex_gate) * 0.1;
|
|
59
|
+
|
|
60
|
+
let npr_stack = mix_shader_002 + gate_emit;
|
|
61
|
+
|
|
62
|
+
// ═══ PRINCIPLED BSDF ═══
|
|
63
|
+
// Graph has a noise→normal_map bump (Strength=0.1) on Principled.Normal, but MixShader.001
|
|
64
|
+
// weights Principled at only 0.2 — the bumped spec × that weight is imperceptible, so we
|
|
65
|
+
// drop the subtree and keep plain n (saves a tex_noise + bump_lh per hair fragment).
|
|
66
|
+
let principled = eval_principled(
|
|
67
|
+
PrincipledIn(bc, 0.0, HAIR_SPECULAR, HAIR_ROUGHNESS, 1e30, 0.0, 0.0),
|
|
68
|
+
n, l, v, sun, amb, shadow
|
|
69
|
+
);
|
|
70
|
+
|
|
71
|
+
let final_color = mix(npr_stack, principled, HAIR_MIX_NPR);
|
|
72
|
+
|
|
73
|
+
var outAlpha = alpha;
|
|
74
|
+
if (IS_OVER_EYES) { outAlpha = alpha * 0.5; }
|
|
75
|
+
|
|
76
|
+
var out: FSOut;
|
|
77
|
+
out.color = vec4f(final_color, outAlpha);
|
|
78
|
+
out.mask = 1.0;
|
|
79
|
+
return out;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
`
|
|
@@ -1,127 +1,30 @@
|
|
|
1
|
-
// M_Metal — Metallic Principled (Metallic=1.0, Specular=1.0,
|
|
2
|
-
//
|
|
3
|
-
//
|
|
4
|
-
// to add subtle metallic sparkle variation. No Normal link in the graph.
|
|
1
|
+
// M_Metal — Metallic Principled (Metallic=1.0, Specular=1.0, Roughness=0.3) with a
|
|
2
|
+
// reflection-coord Voronoi pattern driving base color for metallic sparkle, plus an
|
|
3
|
+
// NPR toon/overlay emission stack mixed at MixShader Fac=0.6967.
|
|
5
4
|
//
|
|
6
|
-
// Graph's base color chain
|
|
7
|
-
// 沃罗诺伊纹理(F1, Color out) → 颜色渐变(linear) → 混合.005. The dumper did not capture
|
|
8
|
-
// VectorMath
|
|
5
|
+
// Graph's base color chain: 纹理坐标.Reflection → 矢量运算.007(CROSS, Vec2=(0,1,0)) →
|
|
6
|
+
// 沃罗诺伊纹理(F1, Color out) → 颜色渐变(linear) → 混合.005. The dumper did not capture
|
|
7
|
+
// the VectorMath op — CROSS is assumed based on the hardcoded (0,1,0) Vector_001
|
|
9
8
|
// constant (MULTIPLY would zero X/Z producing 1D bands; CROSS produces horizontal ring
|
|
10
9
|
// patterns consistent with metallic anisotropy).
|
|
11
10
|
|
|
12
11
|
import { NODES_WGSL } from "./nodes"
|
|
12
|
+
import { COMMON_MATERIAL_PRELUDE_WGSL } from "./common"
|
|
13
13
|
|
|
14
14
|
export const METAL_SHADER_WGSL = /* wgsl */ `
|
|
15
15
|
|
|
16
16
|
${NODES_WGSL}
|
|
17
|
+
${COMMON_MATERIAL_PRELUDE_WGSL}
|
|
17
18
|
|
|
18
|
-
struct CameraUniforms {
|
|
19
|
-
view: mat4x4f,
|
|
20
|
-
projection: mat4x4f,
|
|
21
|
-
viewPos: vec3f,
|
|
22
|
-
_padding: f32,
|
|
23
|
-
};
|
|
24
|
-
|
|
25
|
-
struct Light {
|
|
26
|
-
direction: vec4f,
|
|
27
|
-
color: vec4f,
|
|
28
|
-
};
|
|
29
|
-
|
|
30
|
-
struct LightUniforms {
|
|
31
|
-
ambientColor: vec4f,
|
|
32
|
-
lights: array<Light, 4>,
|
|
33
|
-
};
|
|
34
|
-
|
|
35
|
-
struct MaterialUniforms {
|
|
36
|
-
diffuseColor: vec3f,
|
|
37
|
-
alpha: f32,
|
|
38
|
-
};
|
|
39
|
-
|
|
40
|
-
struct VertexOutput {
|
|
41
|
-
@builtin(position) position: vec4f,
|
|
42
|
-
@location(0) normal: vec3f,
|
|
43
|
-
@location(1) uv: vec2f,
|
|
44
|
-
@location(2) worldPos: vec3f,
|
|
45
|
-
};
|
|
46
|
-
|
|
47
|
-
struct LightVP { viewProj: mat4x4f, };
|
|
48
|
-
|
|
49
|
-
@group(0) @binding(0) var<uniform> camera: CameraUniforms;
|
|
50
|
-
@group(0) @binding(1) var<uniform> light: LightUniforms;
|
|
51
|
-
@group(0) @binding(2) var diffuseSampler: sampler;
|
|
52
|
-
@group(0) @binding(3) var shadowMap: texture_depth_2d;
|
|
53
|
-
@group(0) @binding(4) var shadowSampler: sampler_comparison;
|
|
54
|
-
@group(0) @binding(5) var<uniform> lightVP: LightVP;
|
|
55
|
-
@group(1) @binding(0) var<storage, read> skinMats: array<mat4x4f>;
|
|
56
|
-
@group(2) @binding(0) var diffuseTexture: texture_2d<f32>;
|
|
57
|
-
@group(2) @binding(1) var<uniform> material: MaterialUniforms;
|
|
58
|
-
|
|
59
|
-
fn sampleShadow(worldPos: vec3f, n: vec3f) -> f32 {
|
|
60
|
-
// Back-facing to key light: direct contribution is zero anyway, skip 9 texture samples.
|
|
61
|
-
if (dot(n, -light.lights[0].direction.xyz) <= 0.0) { return 0.0; }
|
|
62
|
-
let biasedPos = worldPos + n * 0.08;
|
|
63
|
-
let lclip = lightVP.viewProj * vec4f(biasedPos, 1.0);
|
|
64
|
-
let ndc = lclip.xyz / max(lclip.w, 1e-6);
|
|
65
|
-
let suv = vec2f(ndc.x * 0.5 + 0.5, 0.5 - ndc.y * 0.5);
|
|
66
|
-
let cmpZ = ndc.z - 0.001;
|
|
67
|
-
let ts = 1.0 / 2048.0;
|
|
68
|
-
// 3x3 PCF unrolled — Safari's Metal backend doesn't unroll nested shadow loops reliably.
|
|
69
|
-
let s00 = textureSampleCompareLevel(shadowMap, shadowSampler, suv + vec2f(-ts, -ts), cmpZ);
|
|
70
|
-
let s10 = textureSampleCompareLevel(shadowMap, shadowSampler, suv + vec2f(0.0, -ts), cmpZ);
|
|
71
|
-
let s20 = textureSampleCompareLevel(shadowMap, shadowSampler, suv + vec2f( ts, -ts), cmpZ);
|
|
72
|
-
let s01 = textureSampleCompareLevel(shadowMap, shadowSampler, suv + vec2f(-ts, 0.0), cmpZ);
|
|
73
|
-
let s11 = textureSampleCompareLevel(shadowMap, shadowSampler, suv, cmpZ);
|
|
74
|
-
let s21 = textureSampleCompareLevel(shadowMap, shadowSampler, suv + vec2f( ts, 0.0), cmpZ);
|
|
75
|
-
let s02 = textureSampleCompareLevel(shadowMap, shadowSampler, suv + vec2f(-ts, ts), cmpZ);
|
|
76
|
-
let s12 = textureSampleCompareLevel(shadowMap, shadowSampler, suv + vec2f(0.0, ts), cmpZ);
|
|
77
|
-
let s22 = textureSampleCompareLevel(shadowMap, shadowSampler, suv + vec2f( ts, ts), cmpZ);
|
|
78
|
-
return (s00 + s10 + s20 + s01 + s11 + s21 + s02 + s12 + s22) * (1.0 / 9.0);
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
const PI_M: f32 = 3.141592653589793;
|
|
82
19
|
const METAL_SPECULAR: f32 = 1.0;
|
|
83
20
|
const METAL_METALLIC: f32 = 1.0;
|
|
84
21
|
const METAL_ROUGHNESS: f32 = 0.3;
|
|
85
|
-
const METAL_SPECULAR_TINT: f32 = 0.114;
|
|
86
22
|
const METAL_TOON_EDGE: f32 = 0.2966;
|
|
87
23
|
const METAL_MIX04_MUL: f32 = 0.5;
|
|
88
24
|
const METAL_EMIT_STR: f32 = 8.100000381469727;
|
|
89
25
|
const METAL_MIX_SHADER_FAC: f32 = 0.6967;
|
|
90
26
|
const METAL_VORONOI_SCALE: f32 = 4.3;
|
|
91
27
|
|
|
92
|
-
@vertex fn vs(
|
|
93
|
-
@location(0) position: vec3f,
|
|
94
|
-
@location(1) normal: vec3f,
|
|
95
|
-
@location(2) uv: vec2f,
|
|
96
|
-
@location(3) joints0: vec4<u32>,
|
|
97
|
-
@location(4) weights0: vec4<f32>
|
|
98
|
-
) -> VertexOutput {
|
|
99
|
-
var output: VertexOutput;
|
|
100
|
-
let pos4 = vec4f(position, 1.0);
|
|
101
|
-
let weightSum = weights0.x + weights0.y + weights0.z + weights0.w;
|
|
102
|
-
let invWeightSum = select(1.0, 1.0 / weightSum, weightSum > 0.0001);
|
|
103
|
-
let nw = select(vec4f(1.0, 0.0, 0.0, 0.0), weights0 * invWeightSum, weightSum > 0.0001);
|
|
104
|
-
var skinnedPos = vec4f(0.0);
|
|
105
|
-
var skinnedNrm = vec3f(0.0);
|
|
106
|
-
for (var i = 0u; i < 4u; i++) {
|
|
107
|
-
let m = skinMats[joints0[i]];
|
|
108
|
-
let w = nw[i];
|
|
109
|
-
skinnedPos += (m * pos4) * w;
|
|
110
|
-
skinnedNrm += (mat3x3f(m[0].xyz, m[1].xyz, m[2].xyz) * normal) * w;
|
|
111
|
-
}
|
|
112
|
-
output.position = camera.projection * camera.view * vec4f(skinnedPos.xyz, 1.0);
|
|
113
|
-
// Skip VS normalize — interpolation denormalizes anyway, and FS always does normalize(input.normal).
|
|
114
|
-
output.normal = skinnedNrm;
|
|
115
|
-
output.uv = uv;
|
|
116
|
-
output.worldPos = skinnedPos.xyz;
|
|
117
|
-
return output;
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
struct FSOut {
|
|
121
|
-
@location(0) color: vec4f,
|
|
122
|
-
@location(1) mask: f32,
|
|
123
|
-
};
|
|
124
|
-
|
|
125
28
|
@fragment fn fs(input: VertexOutput) -> FSOut {
|
|
126
29
|
let n = normalize(input.normal);
|
|
127
30
|
let v = normalize(camera.viewPos - input.worldPos);
|
|
@@ -135,6 +38,7 @@ struct FSOut {
|
|
|
135
38
|
let out_alpha = material.alpha * tex_s.a;
|
|
136
39
|
if (out_alpha < 0.001) { discard; }
|
|
137
40
|
|
|
41
|
+
// ═══ NPR STACK ═══
|
|
138
42
|
let tex_tint = hue_sat_id(1.0, 0.800000011920929, 1.0, tex_rgb);
|
|
139
43
|
let lum_shade = shader_to_rgb_diffuse(n, l, sun, amb, shadow);
|
|
140
44
|
let ramp008 = ramp_constant_edge_aa(lum_shade, METAL_TOON_EDGE, vec4f(0,0,0,1), vec4f(1,1,1,1));
|
|
@@ -147,6 +51,7 @@ struct FSOut {
|
|
|
147
51
|
let npr_rgb = mix_overlay(1.0, mix04, hue004);
|
|
148
52
|
let npr_emission = npr_rgb * METAL_EMIT_STR;
|
|
149
53
|
|
|
54
|
+
// ═══ PRINCIPLED BSDF (metallic=1, voronoi-driven base) ═══
|
|
150
55
|
// Reflection-coord Voronoi produces the metallic sparkle variation.
|
|
151
56
|
// VALTORGB takes Color → Fac via Blender's BT.601 implicit color_to_value.
|
|
152
57
|
let refl_dir = reflect(-v, n);
|
|
@@ -157,23 +62,12 @@ struct FSOut {
|
|
|
157
62
|
let hue006 = hue_sat_id(1.5, 1.2999999523162842, 1.0, tex_tint);
|
|
158
63
|
let albedo = mix_blend(voro_ramp, vec3f(voro_ramp), hue006);
|
|
159
64
|
|
|
160
|
-
//
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
let NV = max(dot(n, v), 1e-4);
|
|
166
|
-
let brdf_lut = brdf_lut_sample(NV, METAL_ROUGHNESS);
|
|
167
|
-
let reflection_color = F_brdf_multi_scatter(f0, f90, brdf_lut.xy);
|
|
168
|
-
|
|
169
|
-
let spec_direct = bsdf_ggx(n, l, v, NL, NV, METAL_ROUGHNESS) * sun * shadow * ltc_brdf_scale_from_lut(brdf_lut);
|
|
170
|
-
let spec_indirect = amb;
|
|
171
|
-
let spec_radiance = (spec_direct + spec_indirect) * reflection_color;
|
|
172
|
-
|
|
173
|
-
// Pure metal — no diffuse lobe (diffuse_weight = (1 - metallic) = 0).
|
|
174
|
-
let principled = spec_radiance;
|
|
65
|
+
// metallic=1 collapses f0 = mix(dielectric, albedo, 1) = albedo; diffuse_weight = 0.
|
|
66
|
+
let principled = eval_principled(
|
|
67
|
+
PrincipledIn(albedo, METAL_METALLIC, METAL_SPECULAR, METAL_ROUGHNESS, 1e30, 0.0, 0.0),
|
|
68
|
+
n, l, v, sun, amb, shadow
|
|
69
|
+
);
|
|
175
70
|
|
|
176
|
-
// 混合着色器.001 Fac=0.6967: Shader=npr_emission, Shader_001=principled
|
|
177
71
|
let final_color = mix(npr_emission, principled, METAL_MIX_SHADER_FAC);
|
|
178
72
|
|
|
179
73
|
var out: FSOut;
|
|
@@ -480,4 +480,81 @@ fn tint_from_color(color: vec3f) -> vec3f {
|
|
|
480
480
|
return select(vec3f(1.0), color / lum, lum > 0.0);
|
|
481
481
|
}
|
|
482
482
|
|
|
483
|
+
// ─── Principled sheen (gpu_shader_material_principled.glsl:8-14) ────
|
|
484
|
+
// Empirical NV-only curve that approximates grazing retroreflection on cloth/velvet.
|
|
485
|
+
// Scales the sheen layer's diffuse contribution; no sheen call site has sheen=0
|
|
486
|
+
// shortcut because the multiplier is tiny at normal view angles anyway.
|
|
487
|
+
fn principled_sheen(NV: f32) -> f32 {
|
|
488
|
+
let f = 1.0 - NV;
|
|
489
|
+
return f * f * f * 0.077 + f * 0.01 + 0.00026;
|
|
490
|
+
}
|
|
491
|
+
|
|
492
|
+
// ─── Principled BSDF eval ───────────────────────────────────────────
|
|
493
|
+
// Shared EEVEE Principled path used by every material in the engine — metallic,
|
|
494
|
+
// dielectric, and sheen variants all fold into these ~15 lines via the struct
|
|
495
|
+
// fields. NPR materials still compute a separate toon/rim/warm stack on top and
|
|
496
|
+
// mix(npr_stack, eval_principled(...), fac); see body.ts / face.ts / etc.
|
|
497
|
+
//
|
|
498
|
+
// Field conventions:
|
|
499
|
+
// base — diffuse albedo. Mixed into f0 only when metallic > 0.
|
|
500
|
+
// metallic — 0 = dielectric (f0 from specular), 1 = pure metal (f0 = base).
|
|
501
|
+
// specular — Principled Specular input (0.5 default → f0 = 0.04). sqrt for f90.
|
|
502
|
+
// roughness — GGX roughness; drives BRDF LUT coord + bsdf_ggx.
|
|
503
|
+
// spec_clamp — EEVEE Light Clamp equivalent. Caps firefly spec from noise-bumped
|
|
504
|
+
// NDF aliasing (Blender hides this via TAA which we don't have).
|
|
505
|
+
// Pass 1e30 (effectively disabled) for materials that don't bump.
|
|
506
|
+
// sheen — 0 disables. Scales the sheen diffuse add; cloth/stockings use ~0.7.
|
|
507
|
+
// sheen_tint — 0 = white sheen, 1 = fully tinted by base. Multiplied by sheen,
|
|
508
|
+
// so value is don't-care when sheen=0.
|
|
509
|
+
struct PrincipledIn {
|
|
510
|
+
base: vec3f,
|
|
511
|
+
metallic: f32,
|
|
512
|
+
specular: f32,
|
|
513
|
+
roughness: f32,
|
|
514
|
+
spec_clamp: f32,
|
|
515
|
+
sheen: f32,
|
|
516
|
+
sheen_tint: f32,
|
|
517
|
+
};
|
|
518
|
+
|
|
519
|
+
fn eval_principled(
|
|
520
|
+
p: PrincipledIn,
|
|
521
|
+
N: vec3f, L: vec3f, V: vec3f,
|
|
522
|
+
sun_rgb: vec3f, amb_rgb: vec3f, shadow: f32
|
|
523
|
+
) -> vec3f {
|
|
524
|
+
let NL = max(dot(N, L), 0.0);
|
|
525
|
+
let NV = max(dot(N, V), 1e-4);
|
|
526
|
+
|
|
527
|
+
// f0/f90 per gpu_shader_material_principled.glsl. specular_tint=0 is assumed
|
|
528
|
+
// (all presets in this engine use the default white dielectric tint).
|
|
529
|
+
let dielectric_f0 = vec3f(0.08 * p.specular);
|
|
530
|
+
let f0 = mix(dielectric_f0, p.base, p.metallic);
|
|
531
|
+
let f90 = mix(f0, vec3f(1.0), sqrt(p.specular));
|
|
532
|
+
|
|
533
|
+
// Single LUT tap feeds both F_brdf_multi_scatter (split-sum DFG) and
|
|
534
|
+
// ltc_brdf_scale_from_lut (LTC mag in .ba). See nodes.ts brdf_lut_sample.
|
|
535
|
+
let lut = brdf_lut_sample(NV, p.roughness);
|
|
536
|
+
let reflection_color = F_brdf_multi_scatter(f0, f90, lut.xy);
|
|
537
|
+
|
|
538
|
+
// Direct glossy — bsdf_ggx already includes NL; no F applied here (tinted after
|
|
539
|
+
// accum with reflection_color). ltc_brdf_scale rescales direct to match the
|
|
540
|
+
// split-sum indirect path, matching EEVEE closure_eval_glossy_lib behavior.
|
|
541
|
+
let spec_direct_raw = bsdf_ggx(N, L, V, NL, NV, p.roughness)
|
|
542
|
+
* sun_rgb * shadow * ltc_brdf_scale_from_lut(lut);
|
|
543
|
+
let spec_direct = min(spec_direct_raw, vec3f(p.spec_clamp));
|
|
544
|
+
let spec_indirect = amb_rgb;
|
|
545
|
+
let spec_radiance = (spec_direct + spec_indirect) * reflection_color;
|
|
546
|
+
|
|
547
|
+
// Sheen add — when p.sheen=0 the whole term collapses, leaving diffuse_color=base.
|
|
548
|
+
let base_tint = tint_from_color(p.base);
|
|
549
|
+
let sheen_color = mix(vec3f(1.0), base_tint, p.sheen_tint);
|
|
550
|
+
let diffuse_color = p.base + p.sheen * sheen_color * principled_sheen(NV);
|
|
551
|
+
|
|
552
|
+
// diffuse_weight = (1-metallic). Indirect diffuse uses amb (L_w) with no π factor
|
|
553
|
+
// (probe_evaluate_world_diff returns SH-projected radiance, not cosine-convolved).
|
|
554
|
+
let diffuse_weight = 1.0 - p.metallic;
|
|
555
|
+
let diffuse_radiance = diffuse_color * (sun_rgb * NL * shadow / EEVEE_PI + amb_rgb) * diffuse_weight;
|
|
556
|
+
|
|
557
|
+
return diffuse_radiance + spec_radiance;
|
|
558
|
+
}
|
|
559
|
+
|
|
483
560
|
`;
|