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,176 +1,63 @@
|
|
|
1
|
-
// M_Smooth_Cloth —
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
lights
|
|
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
|
-
let
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
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_C: f32 = 3.141592653589793;
|
|
73
|
-
const CLOTH_SPECULAR: f32 = 0.8;
|
|
74
|
-
const CLOTH_ROUGHNESS: f32 = 0.5;
|
|
75
|
-
const CLOTH_TOON_EDGE: f32 = 0.2966;
|
|
76
|
-
const CLOTH_MIX04_MUL: f32 = 0.5; // 运算.004 MULTIPLY Value_001 (dump)
|
|
77
|
-
const NPR_EMIT_STR: f32 = 18.200000762939453;
|
|
78
|
-
const NPR_MIX_SHADER_FAC: f32 = 0.8999999761581421;
|
|
79
|
-
|
|
80
|
-
@vertex fn vs(
|
|
81
|
-
@location(0) position: vec3f,
|
|
82
|
-
@location(1) normal: vec3f,
|
|
83
|
-
@location(2) uv: vec2f,
|
|
84
|
-
@location(3) joints0: vec4<u32>,
|
|
85
|
-
@location(4) weights0: vec4<f32>
|
|
86
|
-
) -> VertexOutput {
|
|
87
|
-
var output: VertexOutput;
|
|
88
|
-
let pos4 = vec4f(position, 1.0);
|
|
89
|
-
let weightSum = weights0.x + weights0.y + weights0.z + weights0.w;
|
|
90
|
-
let invWeightSum = select(1.0, 1.0 / weightSum, weightSum > 0.0001);
|
|
91
|
-
let nw = select(vec4f(1.0, 0.0, 0.0, 0.0), weights0 * invWeightSum, weightSum > 0.0001);
|
|
92
|
-
var skinnedPos = vec4f(0.0);
|
|
93
|
-
var skinnedNrm = vec3f(0.0);
|
|
94
|
-
for (var i = 0u; i < 4u; i++) {
|
|
95
|
-
let m = skinMats[joints0[i]];
|
|
96
|
-
let w = nw[i];
|
|
97
|
-
skinnedPos += (m * pos4) * w;
|
|
98
|
-
skinnedNrm += (mat3x3f(m[0].xyz, m[1].xyz, m[2].xyz) * normal) * w;
|
|
99
|
-
}
|
|
100
|
-
output.position = camera.projection * camera.view * vec4f(skinnedPos.xyz, 1.0);
|
|
101
|
-
// Skip VS normalize — interpolation denormalizes anyway, and FS always does normalize(input.normal).
|
|
102
|
-
output.normal = skinnedNrm;
|
|
103
|
-
output.uv = uv;
|
|
104
|
-
output.worldPos = skinnedPos.xyz;
|
|
105
|
-
return output;
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
struct FSOut {
|
|
109
|
-
@location(0) color: vec4f,
|
|
110
|
-
@location(1) mask: f32,
|
|
111
|
-
};
|
|
112
|
-
|
|
113
|
-
@fragment fn fs(input: VertexOutput) -> FSOut {
|
|
114
|
-
let n = normalize(input.normal);
|
|
115
|
-
let v = normalize(camera.viewPos - input.worldPos);
|
|
116
|
-
let l = -light.lights[0].direction.xyz;
|
|
117
|
-
let sun = light.lights[0].color.xyz * light.lights[0].color.w;
|
|
118
|
-
let amb = light.ambientColor.xyz;
|
|
119
|
-
let shadow = sampleShadow(input.worldPos, n);
|
|
120
|
-
|
|
121
|
-
let tex_s = textureSample(diffuseTexture, diffuseSampler, input.uv);
|
|
122
|
-
let tex_rgb = tex_s.rgb;
|
|
123
|
-
let out_alpha = material.alpha * tex_s.a;
|
|
124
|
-
if (out_alpha < 0.001) { discard; }
|
|
125
|
-
|
|
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
|
-
let ramp008 = ramp_constant_edge_aa(lum_shade, CLOTH_TOON_EDGE, vec4f(0,0,0,1), vec4f(1,1,1,1));
|
|
129
|
-
let mix04_fac = math_multiply(ramp008.r, CLOTH_MIX04_MUL);
|
|
130
|
-
|
|
131
|
-
let dark_tex = hue_sat_id(1.0, 0.19999998807907104, 1.0, tex_rgb);
|
|
132
|
-
let mix04 = mix_blend(mix04_fac, dark_tex, tex_rgb);
|
|
133
|
-
|
|
134
|
-
let bevel_z = clamp(n.y, 0.0, 1.0);
|
|
135
|
-
let mix03 = mix_blend(bevel_z, mix04, dark_tex);
|
|
136
|
-
|
|
137
|
-
let hue004 = hue_sat_id(0.800000011920929, 2.0, 1.0, mix03);
|
|
138
|
-
let npr_rgb = mix_overlay(1.0, mix03, hue004);
|
|
139
|
-
let npr_emission = npr_rgb * NPR_EMIT_STR;
|
|
140
|
-
|
|
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).
|
|
143
|
-
let principled_base = hue_sat_id(1.0, 0.800000011920929, 1.0, tex_rgb);
|
|
144
|
-
let NL = max(dot(n, l), 0.0);
|
|
145
|
-
let NV = max(dot(n, v), 1e-4);
|
|
146
|
-
|
|
147
|
-
// f0/f90 per gpu_shader_material_principled.glsl — specular_tint=0 → dielectric_f0_color=white.
|
|
148
|
-
let f0 = vec3f(0.08 * CLOTH_SPECULAR);
|
|
149
|
-
let f90 = mix(f0, vec3f(1.0), sqrt(CLOTH_SPECULAR));
|
|
150
|
-
let brdf_lut = brdf_lut_sample(NV, CLOTH_ROUGHNESS);
|
|
151
|
-
let reflection_color = F_brdf_multi_scatter(f0, f90, brdf_lut.xy);
|
|
152
|
-
|
|
153
|
-
// Direct glossy — bsdf_ggx already includes NL; no F applied here (tinted after accum).
|
|
154
|
-
// ltc_brdf_scale: EEVEE direct path uses LTC; split-sum LUT path is rescaled to match.
|
|
155
|
-
let spec_direct = bsdf_ggx(n, l, v, NL, NV, CLOTH_ROUGHNESS) * sun * shadow * ltc_brdf_scale_from_lut(brdf_lut);
|
|
156
|
-
// Indirect glossy — flat world probe (solid color). Phase 2 adds cubemap.
|
|
157
|
-
let spec_indirect = amb;
|
|
158
|
-
let spec_radiance = (spec_direct + spec_indirect) * reflection_color;
|
|
159
|
-
|
|
160
|
-
// Diffuse (Lambert), no (1-F) factor per EEVEE — it doesn't energy-conserve spec<->diffuse.
|
|
161
|
-
// probe_evaluate_world_diff returns radiance L_w (SH projected, not cosine-convolved); in
|
|
162
|
-
// closure_eval_surface_lib line 302: closure.radiance += diffuse_accum * L_w * diffuse.color.
|
|
163
|
-
// So indirect diffuse = base_color × L_w, no π factor.
|
|
164
|
-
let diffuse_radiance = principled_base * (sun * NL * shadow / PI_C + amb);
|
|
165
|
-
let principled = diffuse_radiance + spec_radiance;
|
|
166
|
-
|
|
167
|
-
// 混合着色器.001: Shader=自发光.005, Shader_001=原理化BSDF, Fac=0.9
|
|
168
|
-
let final_color = mix(npr_emission, principled, NPR_MIX_SHADER_FAC);
|
|
169
|
-
|
|
170
|
-
var out: FSOut;
|
|
171
|
-
out.color = vec4f(final_color, out_alpha);
|
|
172
|
-
out.mask = 1.0;
|
|
173
|
-
return out;
|
|
174
|
-
}
|
|
175
|
-
|
|
176
|
-
`
|
|
1
|
+
// M_Smooth_Cloth — NPR toon + bevel + overlay-boosted emission mixed 10/90 against
|
|
2
|
+
// a plain Principled BSDF. Bump subtree is dead in the Blender graph.
|
|
3
|
+
|
|
4
|
+
import { NODES_WGSL } from "./nodes"
|
|
5
|
+
import { COMMON_MATERIAL_PRELUDE_WGSL } from "./common"
|
|
6
|
+
|
|
7
|
+
export const CLOTH_SMOOTH_SHADER_WGSL = /* wgsl */ `
|
|
8
|
+
|
|
9
|
+
${NODES_WGSL}
|
|
10
|
+
${COMMON_MATERIAL_PRELUDE_WGSL}
|
|
11
|
+
|
|
12
|
+
const CLOTH_SPECULAR: f32 = 0.8;
|
|
13
|
+
const CLOTH_ROUGHNESS: f32 = 0.5;
|
|
14
|
+
const CLOTH_TOON_EDGE: f32 = 0.2966;
|
|
15
|
+
const CLOTH_MIX04_MUL: f32 = 0.5;
|
|
16
|
+
const NPR_EMIT_STR: f32 = 18.200000762939453;
|
|
17
|
+
const NPR_MIX_SHADER_FAC: f32 = 0.8999999761581421;
|
|
18
|
+
|
|
19
|
+
@fragment fn fs(input: VertexOutput) -> FSOut {
|
|
20
|
+
let n = normalize(input.normal);
|
|
21
|
+
let v = normalize(camera.viewPos - input.worldPos);
|
|
22
|
+
let l = -light.lights[0].direction.xyz;
|
|
23
|
+
let sun = light.lights[0].color.xyz * light.lights[0].color.w;
|
|
24
|
+
let amb = light.ambientColor.xyz;
|
|
25
|
+
let shadow = sampleShadow(input.worldPos, n);
|
|
26
|
+
|
|
27
|
+
let tex_s = textureSample(diffuseTexture, diffuseSampler, input.uv);
|
|
28
|
+
let tex_rgb = tex_s.rgb;
|
|
29
|
+
let out_alpha = material.alpha * tex_s.a;
|
|
30
|
+
if (out_alpha < 0.001) { discard; }
|
|
31
|
+
|
|
32
|
+
// ═══ NPR STACK ═══
|
|
33
|
+
let lum_shade = shader_to_rgb_diffuse(n, l, sun, amb, shadow);
|
|
34
|
+
let ramp008 = ramp_constant_edge_aa(lum_shade, CLOTH_TOON_EDGE, vec4f(0,0,0,1), vec4f(1,1,1,1));
|
|
35
|
+
let mix04_fac = math_multiply(ramp008.r, CLOTH_MIX04_MUL);
|
|
36
|
+
|
|
37
|
+
let dark_tex = hue_sat_id(1.0, 0.19999998807907104, 1.0, tex_rgb);
|
|
38
|
+
let mix04 = mix_blend(mix04_fac, dark_tex, tex_rgb);
|
|
39
|
+
|
|
40
|
+
let bevel_z = clamp(n.y, 0.0, 1.0);
|
|
41
|
+
let mix03 = mix_blend(bevel_z, mix04, dark_tex);
|
|
42
|
+
|
|
43
|
+
let hue004 = hue_sat_id(0.800000011920929, 2.0, 1.0, mix03);
|
|
44
|
+
let npr_rgb = mix_overlay(1.0, mix03, hue004);
|
|
45
|
+
let npr_emission = npr_rgb * NPR_EMIT_STR;
|
|
46
|
+
|
|
47
|
+
// ═══ PRINCIPLED BSDF ═══
|
|
48
|
+
let principled_base = hue_sat_id(1.0, 0.800000011920929, 1.0, tex_rgb);
|
|
49
|
+
let principled = eval_principled(
|
|
50
|
+
PrincipledIn(principled_base, 0.0, CLOTH_SPECULAR, CLOTH_ROUGHNESS, 1e30, 0.0, 0.0),
|
|
51
|
+
n, l, v, sun, amb, shadow
|
|
52
|
+
);
|
|
53
|
+
|
|
54
|
+
// MixShader.001: Shader=自发光.005, Shader_001=原理化BSDF, Fac=0.9
|
|
55
|
+
let final_color = mix(npr_emission, principled, NPR_MIX_SHADER_FAC);
|
|
56
|
+
|
|
57
|
+
var out: FSOut;
|
|
58
|
+
out.color = vec4f(final_color, out_alpha);
|
|
59
|
+
out.mask = 1.0;
|
|
60
|
+
return out;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
`
|
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
// Shared WGSL blocks concatenated by every material shader.
|
|
2
|
+
// Splits the boilerplate (uniform structs, bind group layout, skinning VS, PCF shadow)
|
|
3
|
+
// away from the per-material fragment code so each material file only contains what
|
|
4
|
+
// makes it visually distinct.
|
|
5
|
+
//
|
|
6
|
+
// Concat order in every material:
|
|
7
|
+
// NODES_WGSL (nodes.ts — math/noise/BSDF helpers)
|
|
8
|
+
// COMMON_BINDINGS_WGSL (uniform structs + @group/@binding declarations)
|
|
9
|
+
// SAMPLE_SHADOW_WGSL (3×3 PCF shadow sampler; reads bindings above)
|
|
10
|
+
// COMMON_VS_WGSL (skinning vertex shader; reads bindings above)
|
|
11
|
+
// <material's own constants + @fragment fn fs>
|
|
12
|
+
//
|
|
13
|
+
// WGSL is a whole-module compile — declaration order at module scope doesn't matter,
|
|
14
|
+
// but the readable order is: types → bindings → helpers → entry points.
|
|
15
|
+
|
|
16
|
+
// ─── Uniform structs + bind group layout ────────────────────────────
|
|
17
|
+
// Every material pipeline uses the same bind group layout, so the same bindings are
|
|
18
|
+
// declared here once. Groups:
|
|
19
|
+
// group(0): per-frame scene (camera, lights, shadow map, BRDF LUT via nodes.ts)
|
|
20
|
+
// group(1): per-model skinning
|
|
21
|
+
// group(2): per-material (diffuse texture + material uniforms)
|
|
22
|
+
|
|
23
|
+
export const COMMON_BINDINGS_WGSL = /* wgsl */ `
|
|
24
|
+
|
|
25
|
+
struct CameraUniforms {
|
|
26
|
+
view: mat4x4f,
|
|
27
|
+
projection: mat4x4f,
|
|
28
|
+
viewPos: vec3f,
|
|
29
|
+
_padding: f32,
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
struct Light {
|
|
33
|
+
direction: vec4f,
|
|
34
|
+
color: vec4f,
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
struct LightUniforms {
|
|
38
|
+
ambientColor: vec4f,
|
|
39
|
+
lights: array<Light, 4>,
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
// Per-material uniforms. Every material binds this layout even if it ignores fields;
|
|
43
|
+
// the engine keeps one bind group layout across all material pipelines.
|
|
44
|
+
struct MaterialUniforms {
|
|
45
|
+
diffuseColor: vec3f, // tint; reserved (currently unused by all material fs)
|
|
46
|
+
alpha: f32, // 0 → discard; <1 → transparent draw call
|
|
47
|
+
};
|
|
48
|
+
|
|
49
|
+
struct VertexOutput {
|
|
50
|
+
@builtin(position) position: vec4f,
|
|
51
|
+
@location(0) normal: vec3f,
|
|
52
|
+
@location(1) uv: vec2f,
|
|
53
|
+
@location(2) worldPos: vec3f,
|
|
54
|
+
};
|
|
55
|
+
|
|
56
|
+
struct LightVP { viewProj: mat4x4f, };
|
|
57
|
+
|
|
58
|
+
@group(0) @binding(0) var<uniform> camera: CameraUniforms;
|
|
59
|
+
@group(0) @binding(1) var<uniform> light: LightUniforms;
|
|
60
|
+
@group(0) @binding(2) var diffuseSampler: sampler;
|
|
61
|
+
@group(0) @binding(3) var shadowMap: texture_depth_2d;
|
|
62
|
+
@group(0) @binding(4) var shadowSampler: sampler_comparison;
|
|
63
|
+
@group(0) @binding(5) var<uniform> lightVP: LightVP;
|
|
64
|
+
// binding(9) brdfLut is declared inside NODES_WGSL (nodes.ts).
|
|
65
|
+
@group(1) @binding(0) var<storage, read> skinMats: array<mat4x4f>;
|
|
66
|
+
@group(2) @binding(0) var diffuseTexture: texture_2d<f32>;
|
|
67
|
+
@group(2) @binding(1) var<uniform> material: MaterialUniforms;
|
|
68
|
+
|
|
69
|
+
`;
|
|
70
|
+
|
|
71
|
+
// ─── Shadow sampler (3×3 PCF) ───────────────────────────────────────
|
|
72
|
+
// 2048-map, normal-bias 0.08, depth-bias 0.001. Unrolled — Safari's Metal backend
|
|
73
|
+
// doesn't unroll nested shadow loops reliably, and the early out on back-facing
|
|
74
|
+
// fragments saves 9 texture taps per skipped pixel.
|
|
75
|
+
|
|
76
|
+
export const SAMPLE_SHADOW_WGSL = /* wgsl */ `
|
|
77
|
+
|
|
78
|
+
fn sampleShadow(worldPos: vec3f, n: vec3f) -> f32 {
|
|
79
|
+
if (dot(n, -light.lights[0].direction.xyz) <= 0.0) { return 0.0; }
|
|
80
|
+
let biasedPos = worldPos + n * 0.08;
|
|
81
|
+
let lclip = lightVP.viewProj * vec4f(biasedPos, 1.0);
|
|
82
|
+
let ndc = lclip.xyz / max(lclip.w, 1e-6);
|
|
83
|
+
let suv = vec2f(ndc.x * 0.5 + 0.5, 0.5 - ndc.y * 0.5);
|
|
84
|
+
let cmpZ = ndc.z - 0.001;
|
|
85
|
+
let ts = 1.0 / 2048.0;
|
|
86
|
+
let s00 = textureSampleCompareLevel(shadowMap, shadowSampler, suv + vec2f(-ts, -ts), cmpZ);
|
|
87
|
+
let s10 = textureSampleCompareLevel(shadowMap, shadowSampler, suv + vec2f(0.0, -ts), cmpZ);
|
|
88
|
+
let s20 = textureSampleCompareLevel(shadowMap, shadowSampler, suv + vec2f( ts, -ts), cmpZ);
|
|
89
|
+
let s01 = textureSampleCompareLevel(shadowMap, shadowSampler, suv + vec2f(-ts, 0.0), cmpZ);
|
|
90
|
+
let s11 = textureSampleCompareLevel(shadowMap, shadowSampler, suv, cmpZ);
|
|
91
|
+
let s21 = textureSampleCompareLevel(shadowMap, shadowSampler, suv + vec2f( ts, 0.0), cmpZ);
|
|
92
|
+
let s02 = textureSampleCompareLevel(shadowMap, shadowSampler, suv + vec2f(-ts, ts), cmpZ);
|
|
93
|
+
let s12 = textureSampleCompareLevel(shadowMap, shadowSampler, suv + vec2f(0.0, ts), cmpZ);
|
|
94
|
+
let s22 = textureSampleCompareLevel(shadowMap, shadowSampler, suv + vec2f( ts, ts), cmpZ);
|
|
95
|
+
return (s00 + s10 + s20 + s01 + s11 + s21 + s02 + s12 + s22) * (1.0 / 9.0);
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
`;
|
|
99
|
+
|
|
100
|
+
// ─── Skinning vertex shader ─────────────────────────────────────────
|
|
101
|
+
// Four-bone linear blend skinning. Renormalizes weights when they don't sum to 1
|
|
102
|
+
// (PMX models occasionally ship with unnormalized weights on extras like hair tips).
|
|
103
|
+
// VS normalize on the outgoing normal is skipped — interpolation denormalizes it
|
|
104
|
+
// anyway and every fragment shader does `normalize(input.normal)` as its first line.
|
|
105
|
+
|
|
106
|
+
export const COMMON_VS_WGSL = /* wgsl */ `
|
|
107
|
+
|
|
108
|
+
@vertex fn vs(
|
|
109
|
+
@location(0) position: vec3f,
|
|
110
|
+
@location(1) normal: vec3f,
|
|
111
|
+
@location(2) uv: vec2f,
|
|
112
|
+
@location(3) joints0: vec4<u32>,
|
|
113
|
+
@location(4) weights0: vec4<f32>
|
|
114
|
+
) -> VertexOutput {
|
|
115
|
+
var output: VertexOutput;
|
|
116
|
+
let pos4 = vec4f(position, 1.0);
|
|
117
|
+
let weightSum = weights0.x + weights0.y + weights0.z + weights0.w;
|
|
118
|
+
let invWeightSum = select(1.0, 1.0 / weightSum, weightSum > 0.0001);
|
|
119
|
+
let nw = select(vec4f(1.0, 0.0, 0.0, 0.0), weights0 * invWeightSum, weightSum > 0.0001);
|
|
120
|
+
var skinnedPos = vec4f(0.0);
|
|
121
|
+
var skinnedNrm = vec3f(0.0);
|
|
122
|
+
for (var i = 0u; i < 4u; i++) {
|
|
123
|
+
let m = skinMats[joints0[i]];
|
|
124
|
+
let w = nw[i];
|
|
125
|
+
skinnedPos += (m * pos4) * w;
|
|
126
|
+
skinnedNrm += (mat3x3f(m[0].xyz, m[1].xyz, m[2].xyz) * normal) * w;
|
|
127
|
+
}
|
|
128
|
+
output.position = camera.projection * camera.view * vec4f(skinnedPos.xyz, 1.0);
|
|
129
|
+
output.normal = skinnedNrm;
|
|
130
|
+
output.uv = uv;
|
|
131
|
+
output.worldPos = skinnedPos.xyz;
|
|
132
|
+
return output;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
`;
|
|
136
|
+
|
|
137
|
+
// ─── FS output struct ───────────────────────────────────────────────
|
|
138
|
+
// Location 0: final radiance+alpha. Location 1: bloom/highlight mask (single f32,
|
|
139
|
+
// currently 1.0 everywhere — slot is reserved for future per-material bloom control).
|
|
140
|
+
|
|
141
|
+
export const COMMON_FS_OUT_WGSL = /* wgsl */ `
|
|
142
|
+
|
|
143
|
+
struct FSOut {
|
|
144
|
+
@location(0) color: vec4f,
|
|
145
|
+
@location(1) mask: f32,
|
|
146
|
+
};
|
|
147
|
+
|
|
148
|
+
`;
|
|
149
|
+
|
|
150
|
+
// ─── Convenience: full shared prelude ───────────────────────────────
|
|
151
|
+
// Material files compose this as `${NODES_WGSL}${COMMON_MATERIAL_PRELUDE_WGSL}` to
|
|
152
|
+
// pull in everything structural. Each material then adds its own constants + fs().
|
|
153
|
+
|
|
154
|
+
export const COMMON_MATERIAL_PRELUDE_WGSL =
|
|
155
|
+
COMMON_BINDINGS_WGSL + SAMPLE_SHADOW_WGSL + COMMON_VS_WGSL + COMMON_FS_OUT_WGSL
|
|
@@ -1,140 +1,17 @@
|
|
|
1
|
-
// Blender 3.6 Principled BSDF defaults
|
|
2
|
-
// Metallic=0, Specular=0.5 (F0=0.04), Roughness=0.5.
|
|
3
|
-
//
|
|
1
|
+
// Default material — Blender 3.6 Principled BSDF defaults, no NPR stack.
|
|
2
|
+
// Metallic=0, Specular=0.5 (F0=0.04), Roughness=0.5. Serves as the EEVEE reference
|
|
3
|
+
// path that every NPR material mixes against in its final stage.
|
|
4
4
|
|
|
5
5
|
import { NODES_WGSL } from "./nodes"
|
|
6
|
+
import { COMMON_MATERIAL_PRELUDE_WGSL } from "./common"
|
|
6
7
|
|
|
7
8
|
export const DEFAULT_SHADER_WGSL = /* wgsl */ `
|
|
8
9
|
|
|
9
10
|
${NODES_WGSL}
|
|
11
|
+
${COMMON_MATERIAL_PRELUDE_WGSL}
|
|
10
12
|
|
|
11
|
-
const PI: f32 = 3.141592653589793;
|
|
12
13
|
const DEFAULT_SPECULAR: f32 = 0.5;
|
|
13
|
-
const
|
|
14
|
-
|
|
15
|
-
struct CameraUniforms {
|
|
16
|
-
view: mat4x4f,
|
|
17
|
-
projection: mat4x4f,
|
|
18
|
-
viewPos: vec3f,
|
|
19
|
-
_padding: f32,
|
|
20
|
-
};
|
|
21
|
-
|
|
22
|
-
struct Light {
|
|
23
|
-
direction: vec4f,
|
|
24
|
-
color: vec4f,
|
|
25
|
-
};
|
|
26
|
-
|
|
27
|
-
struct LightUniforms {
|
|
28
|
-
ambientColor: vec4f,
|
|
29
|
-
lights: array<Light, 4>,
|
|
30
|
-
};
|
|
31
|
-
|
|
32
|
-
// Per-material uniforms. Add fields here only when a shader actually reads them;
|
|
33
|
-
// preset-specific shaders (face.ts, future hair.ts) share this struct so the
|
|
34
|
-
// engine can use one material bind-group layout.
|
|
35
|
-
struct MaterialUniforms {
|
|
36
|
-
diffuseColor: vec3f, // tint; multiplies sampled albedo (unused by current fs, reserved)
|
|
37
|
-
alpha: f32, // 0 → discard; <1 → transparent draw call
|
|
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
|
-
// ─── Filmic tone mapping (LUT extracted from Blender 3.6 OCIO) ─────
|
|
60
|
-
// View transform = Filmic, Look = Medium High Contrast, Exposure = -0.3.
|
|
61
|
-
// 14 samples at integer log2 stops from -10 to +3 (inclusive).
|
|
62
|
-
// Extracted via scripts/extract_filmic_lut.py → probe image through scene
|
|
63
|
-
// color management. Input: linear scene-referred. Output: sRGB display.
|
|
64
|
-
|
|
65
|
-
fn filmic(x: f32) -> f32 {
|
|
66
|
-
var lut = array<f32, 14>(
|
|
67
|
-
0.0067, 0.0141, 0.0272, 0.0499, 0.0885, 0.1512, 0.2462,
|
|
68
|
-
0.3753, 0.5273, 0.6776, 0.8031, 0.8929, 0.9495, 0.9814
|
|
69
|
-
);
|
|
70
|
-
let t = clamp(log2(max(x, 1e-10)) + 10.0, 0.0, 13.0);
|
|
71
|
-
let i = u32(t);
|
|
72
|
-
let j = min(i + 1u, 13u);
|
|
73
|
-
return mix(lut[i], lut[j], t - f32(i));
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
fn tonemap(hdr: vec3f) -> vec3f {
|
|
77
|
-
return vec3f(filmic(hdr.x), filmic(hdr.y), filmic(hdr.z));
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
// ─── Shadow sampling (3×3 PCF) ──────────────────────────────────────
|
|
81
|
-
|
|
82
|
-
fn sampleShadow(worldPos: vec3f, n: vec3f) -> f32 {
|
|
83
|
-
// Back-facing to key light: direct contribution is zero anyway, skip 9 texture samples.
|
|
84
|
-
if (dot(n, -light.lights[0].direction.xyz) <= 0.0) { return 0.0; }
|
|
85
|
-
let biasedPos = worldPos + n * 0.08;
|
|
86
|
-
let lclip = lightVP.viewProj * vec4f(biasedPos, 1.0);
|
|
87
|
-
let ndc = lclip.xyz / max(lclip.w, 1e-6);
|
|
88
|
-
let suv = vec2f(ndc.x * 0.5 + 0.5, 0.5 - ndc.y * 0.5);
|
|
89
|
-
let cmpZ = ndc.z - 0.001;
|
|
90
|
-
let ts = 1.0 / 2048.0;
|
|
91
|
-
// 3x3 PCF unrolled — Safari's Metal backend doesn't unroll nested shadow loops reliably.
|
|
92
|
-
let s00 = textureSampleCompareLevel(shadowMap, shadowSampler, suv + vec2f(-ts, -ts), cmpZ);
|
|
93
|
-
let s10 = textureSampleCompareLevel(shadowMap, shadowSampler, suv + vec2f(0.0, -ts), cmpZ);
|
|
94
|
-
let s20 = textureSampleCompareLevel(shadowMap, shadowSampler, suv + vec2f( ts, -ts), cmpZ);
|
|
95
|
-
let s01 = textureSampleCompareLevel(shadowMap, shadowSampler, suv + vec2f(-ts, 0.0), cmpZ);
|
|
96
|
-
let s11 = textureSampleCompareLevel(shadowMap, shadowSampler, suv, cmpZ);
|
|
97
|
-
let s21 = textureSampleCompareLevel(shadowMap, shadowSampler, suv + vec2f( ts, 0.0), cmpZ);
|
|
98
|
-
let s02 = textureSampleCompareLevel(shadowMap, shadowSampler, suv + vec2f(-ts, ts), cmpZ);
|
|
99
|
-
let s12 = textureSampleCompareLevel(shadowMap, shadowSampler, suv + vec2f(0.0, ts), cmpZ);
|
|
100
|
-
let s22 = textureSampleCompareLevel(shadowMap, shadowSampler, suv + vec2f( ts, ts), cmpZ);
|
|
101
|
-
return (s00 + s10 + s20 + s01 + s11 + s21 + s02 + s12 + s22) * (1.0 / 9.0);
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
// ─── Vertex / Fragment ──────────────────────────────────────────────
|
|
105
|
-
|
|
106
|
-
@vertex fn vs(
|
|
107
|
-
@location(0) position: vec3f,
|
|
108
|
-
@location(1) normal: vec3f,
|
|
109
|
-
@location(2) uv: vec2f,
|
|
110
|
-
@location(3) joints0: vec4<u32>,
|
|
111
|
-
@location(4) weights0: vec4<f32>
|
|
112
|
-
) -> VertexOutput {
|
|
113
|
-
var output: VertexOutput;
|
|
114
|
-
let pos4 = vec4f(position, 1.0);
|
|
115
|
-
let weightSum = weights0.x + weights0.y + weights0.z + weights0.w;
|
|
116
|
-
let invWeightSum = select(1.0, 1.0 / weightSum, weightSum > 0.0001);
|
|
117
|
-
let nw = select(vec4f(1.0, 0.0, 0.0, 0.0), weights0 * invWeightSum, weightSum > 0.0001);
|
|
118
|
-
var skinnedPos = vec4f(0.0);
|
|
119
|
-
var skinnedNrm = vec3f(0.0);
|
|
120
|
-
for (var i = 0u; i < 4u; i++) {
|
|
121
|
-
let m = skinMats[joints0[i]];
|
|
122
|
-
let w = nw[i];
|
|
123
|
-
skinnedPos += (m * pos4) * w;
|
|
124
|
-
skinnedNrm += (mat3x3f(m[0].xyz, m[1].xyz, m[2].xyz) * normal) * w;
|
|
125
|
-
}
|
|
126
|
-
output.position = camera.projection * camera.view * vec4f(skinnedPos.xyz, 1.0);
|
|
127
|
-
// Skip VS normalize — interpolation denormalizes anyway, and FS always does normalize(input.normal).
|
|
128
|
-
output.normal = skinnedNrm;
|
|
129
|
-
output.uv = uv;
|
|
130
|
-
output.worldPos = skinnedPos.xyz;
|
|
131
|
-
return output;
|
|
132
|
-
}
|
|
133
|
-
|
|
134
|
-
struct FSOut {
|
|
135
|
-
@location(0) color: vec4f,
|
|
136
|
-
@location(1) mask: f32,
|
|
137
|
-
};
|
|
14
|
+
const DEFAULT_ROUGHNESS: f32 = 0.5;
|
|
138
15
|
|
|
139
16
|
@fragment fn fs(input: VertexOutput) -> FSOut {
|
|
140
17
|
let alpha = material.alpha;
|
|
@@ -142,30 +19,20 @@ struct FSOut {
|
|
|
142
19
|
|
|
143
20
|
let n = normalize(input.normal);
|
|
144
21
|
let v = normalize(camera.viewPos - input.worldPos);
|
|
145
|
-
let albedo = textureSample(diffuseTexture, diffuseSampler, input.uv).rgb;
|
|
146
|
-
|
|
147
22
|
let l = -light.lights[0].direction.xyz;
|
|
148
23
|
let sun = light.lights[0].color.xyz * light.lights[0].color.w;
|
|
149
24
|
let amb = light.ambientColor.xyz;
|
|
150
25
|
let shadow = sampleShadow(input.worldPos, n);
|
|
151
26
|
|
|
152
|
-
|
|
153
|
-
let NL = max(dot(n, l), 0.0);
|
|
154
|
-
let NV = max(dot(n, v), 1e-4);
|
|
155
|
-
|
|
156
|
-
let f0 = vec3f(0.08 * DEFAULT_SPECULAR);
|
|
157
|
-
let f90 = mix(f0, vec3f(1.0), sqrt(DEFAULT_SPECULAR));
|
|
158
|
-
let brdf_lut = brdf_lut_sample(NV, ROUGHNESS);
|
|
159
|
-
let reflection_color = F_brdf_multi_scatter(f0, f90, brdf_lut.xy);
|
|
160
|
-
|
|
161
|
-
let spec_direct = bsdf_ggx(n, l, v, NL, NV, ROUGHNESS) * sun * shadow * ltc_brdf_scale_from_lut(brdf_lut);
|
|
162
|
-
let spec_indirect = amb;
|
|
163
|
-
let spec_radiance = (spec_direct + spec_indirect) * reflection_color;
|
|
27
|
+
let albedo = textureSample(diffuseTexture, diffuseSampler, input.uv).rgb;
|
|
164
28
|
|
|
165
|
-
let
|
|
29
|
+
let color = eval_principled(
|
|
30
|
+
PrincipledIn(albedo, 0.0, DEFAULT_SPECULAR, DEFAULT_ROUGHNESS, 1e30, 0.0, 0.0),
|
|
31
|
+
n, l, v, sun, amb, shadow
|
|
32
|
+
);
|
|
166
33
|
|
|
167
34
|
var out: FSOut;
|
|
168
|
-
out.color = vec4f(
|
|
35
|
+
out.color = vec4f(color, alpha);
|
|
169
36
|
out.mask = 1.0;
|
|
170
37
|
return out;
|
|
171
38
|
}
|