reze-engine 0.11.1 → 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.
- package/dist/engine.d.ts +5 -3
- package/dist/engine.d.ts.map +1 -1
- package/dist/engine.js +72 -425
- package/dist/index.d.ts +1 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/shaders/body.d.ts +1 -1
- package/dist/shaders/body.d.ts.map +1 -1
- package/dist/shaders/body.js +27 -60
- 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 +4 -16
- 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 +5 -17
- package/dist/shaders/default.d.ts +1 -1
- package/dist/shaders/default.d.ts.map +1 -1
- package/dist/shaders/default.js +20 -34
- package/dist/shaders/dfg_lut.d.ts +1 -1
- package/dist/shaders/dfg_lut.d.ts.map +1 -1
- package/dist/shaders/dfg_lut.js +1 -1
- package/dist/shaders/eye.d.ts +1 -1
- package/dist/shaders/eye.d.ts.map +1 -1
- package/dist/shaders/eye.js +22 -35
- package/dist/shaders/face.d.ts +1 -1
- package/dist/shaders/face.d.ts.map +1 -1
- package/dist/shaders/face.js +21 -57
- package/dist/shaders/hair.d.ts +1 -1
- package/dist/shaders/hair.d.ts.map +1 -1
- package/dist/shaders/hair.js +7 -27
- package/dist/shaders/materials/body.d.ts +2 -0
- package/dist/shaders/materials/body.d.ts.map +1 -0
- package/dist/shaders/materials/body.js +199 -0
- package/dist/shaders/materials/cloth_rough.d.ts +2 -0
- package/dist/shaders/materials/cloth_rough.d.ts.map +1 -0
- package/dist/shaders/materials/cloth_rough.js +178 -0
- package/dist/shaders/materials/cloth_smooth.d.ts +2 -0
- package/dist/shaders/materials/cloth_smooth.d.ts.map +1 -0
- package/dist/shaders/materials/cloth_smooth.js +174 -0
- package/dist/shaders/materials/default.d.ts +2 -0
- package/dist/shaders/materials/default.d.ts.map +1 -0
- package/dist/shaders/materials/default.js +171 -0
- package/dist/shaders/materials/eye.d.ts +2 -0
- package/dist/shaders/materials/eye.d.ts.map +1 -0
- package/dist/shaders/materials/eye.js +146 -0
- package/dist/shaders/materials/face.d.ts +2 -0
- package/dist/shaders/materials/face.d.ts.map +1 -0
- package/dist/shaders/materials/face.js +199 -0
- package/dist/shaders/materials/hair.d.ts +2 -0
- package/dist/shaders/materials/hair.d.ts.map +1 -0
- package/dist/shaders/materials/hair.js +176 -0
- package/dist/shaders/materials/metal.d.ts +2 -0
- package/dist/shaders/materials/metal.d.ts.map +1 -0
- package/dist/shaders/materials/metal.js +183 -0
- package/dist/shaders/materials/nodes.d.ts +2 -0
- package/dist/shaders/materials/nodes.d.ts.map +1 -0
- package/{src/shaders/nodes.ts → dist/shaders/materials/nodes.js} +32 -16
- package/dist/shaders/materials/stockings.d.ts +2 -0
- package/dist/shaders/materials/stockings.d.ts.map +1 -0
- package/dist/shaders/materials/stockings.js +244 -0
- package/dist/shaders/metal.d.ts +1 -1
- package/dist/shaders/metal.d.ts.map +1 -1
- package/dist/shaders/metal.js +4 -17
- package/dist/shaders/nodes.d.ts +1 -1
- package/dist/shaders/nodes.d.ts.map +1 -1
- package/dist/shaders/nodes.js +0 -9
- package/dist/shaders/passes/bloom.d.ts +4 -0
- package/dist/shaders/passes/bloom.d.ts.map +1 -0
- package/dist/shaders/passes/bloom.js +117 -0
- package/dist/shaders/passes/composite.d.ts +2 -0
- package/dist/shaders/passes/composite.d.ts.map +1 -0
- package/dist/shaders/passes/composite.js +61 -0
- package/dist/shaders/passes/ground.d.ts +2 -0
- package/dist/shaders/passes/ground.d.ts.map +1 -0
- package/dist/shaders/passes/ground.js +93 -0
- package/dist/shaders/passes/mipmap.d.ts +2 -0
- package/dist/shaders/passes/mipmap.d.ts.map +1 -0
- package/dist/shaders/passes/mipmap.js +16 -0
- package/dist/shaders/passes/outline.d.ts +2 -0
- package/dist/shaders/passes/outline.d.ts.map +1 -0
- package/dist/shaders/passes/outline.js +83 -0
- package/dist/shaders/passes/pick.d.ts +2 -0
- package/dist/shaders/passes/pick.d.ts.map +1 -0
- package/dist/shaders/passes/pick.js +39 -0
- package/dist/shaders/passes/shadow.d.ts +2 -0
- package/dist/shaders/passes/shadow.d.ts.map +1 -0
- package/dist/shaders/passes/shadow.js +16 -0
- package/dist/shaders/stockings.d.ts +1 -1
- package/dist/shaders/stockings.d.ts.map +1 -1
- package/package.json +2 -2
- package/src/engine.ts +112 -449
- package/src/index.ts +3 -2
- package/src/shaders/dfg_lut.ts +1 -1
- package/src/shaders/{body.ts → materials/body.ts} +27 -60
- package/src/shaders/{cloth_rough.ts → materials/cloth_rough.ts} +4 -16
- package/src/shaders/{cloth_smooth.ts → materials/cloth_smooth.ts} +5 -17
- package/src/shaders/{default.ts → materials/default.ts} +21 -34
- package/src/shaders/{eye.ts → materials/eye.ts} +23 -35
- package/src/shaders/{face.ts → materials/face.ts} +21 -57
- package/src/shaders/{hair.ts → materials/hair.ts} +7 -27
- package/src/shaders/{metal.ts → materials/metal.ts} +15 -19
- package/src/shaders/materials/nodes.ts +483 -0
- package/src/shaders/passes/bloom.ts +121 -0
- package/src/shaders/passes/composite.ts +62 -0
- package/src/shaders/passes/ground.ts +94 -0
- package/src/shaders/passes/mipmap.ts +17 -0
- package/src/shaders/passes/outline.ts +84 -0
- package/src/shaders/passes/pick.ts +40 -0
- package/src/shaders/passes/shadow.ts +17 -0
- package/src/shaders/classify.ts +0 -25
- /package/src/shaders/{stockings.ts → materials/stockings.ts} +0 -0
package/src/index.ts
CHANGED
|
@@ -7,6 +7,8 @@ export {
|
|
|
7
7
|
type BloomOptions,
|
|
8
8
|
type ViewTransformOptions,
|
|
9
9
|
type LoadModelFromFilesOptions,
|
|
10
|
+
type MaterialPreset,
|
|
11
|
+
type MaterialPresetMap,
|
|
10
12
|
} from "./engine"
|
|
11
13
|
export { parsePmxFolderInput, pmxFileAtRelativePath, type PmxFolderInputResult } from "./folder-upload"
|
|
12
14
|
export { Model } from "./model"
|
|
@@ -21,5 +23,4 @@ export type {
|
|
|
21
23
|
ControlPoint,
|
|
22
24
|
} from "./animation"
|
|
23
25
|
export { FPS } from "./animation"
|
|
24
|
-
export { Physics, type PhysicsOptions } from "./physics"
|
|
25
|
-
export type { MaterialPreset, MaterialPresetMap } from "./shaders/classify"
|
|
26
|
+
export { Physics, type PhysicsOptions } from "./physics"
|
package/src/shaders/dfg_lut.ts
CHANGED
|
@@ -85,7 +85,7 @@ fn F_color_blend_zero(eta: f32, fresnel: f32) -> f32 {
|
|
|
85
85
|
let x_uv = floor(frag.x) / (LUT_SIZE - 1.0);
|
|
86
86
|
|
|
87
87
|
let NV = clamp(1.0 - y_uv * y_uv, 1e-4, 0.9999);
|
|
88
|
-
let a = x_uv
|
|
88
|
+
let a = max(x_uv, 1e-4);
|
|
89
89
|
let a2 = clamp(a * a, 1e-4, 0.9999);
|
|
90
90
|
|
|
91
91
|
let V = vec3f(sqrt(1.0 - NV * NV), 0.0, NV);
|
|
@@ -70,32 +70,16 @@ fn sampleShadow(worldPos: vec3f, n: vec3f) -> f32 {
|
|
|
70
70
|
}
|
|
71
71
|
|
|
72
72
|
const PI_B: f32 = 3.141592653589793;
|
|
73
|
-
const F0_BODY: f32 = 0.04;
|
|
74
73
|
const BODY_ROUGHNESS: f32 = 0.3;
|
|
75
|
-
// Dump: 层权重.002 Blend; 运算.007 POWER exponent Value_001; 背景 Color; 运算.004 after invert
|
|
76
74
|
const BODY_RIM2_LAYER_BLEND: f32 = 0.20000000298023224;
|
|
77
75
|
const BODY_RIM2_POW: f32 = 1.4300000667572021;
|
|
78
76
|
const BODY_RIM2_BG: vec3f = vec3f(1.0, 0.4303792119026184, 0.3315804898738861);
|
|
79
|
-
const
|
|
77
|
+
const BODY_WARM_STR: f32 = 0.30000001192092896;
|
|
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.
|
|
82
81
|
const BODY_SPEC_CLAMP: f32 = 10.0;
|
|
83
82
|
|
|
84
|
-
fn ggx_d_body(ndoth: f32, a2: f32) -> f32 {
|
|
85
|
-
let denom = ndoth * ndoth * (a2 - 1.0) + 1.0;
|
|
86
|
-
return a2 / (PI_B * denom * denom);
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
fn smith_g1_body(ndotx: f32, a2: f32) -> f32 {
|
|
90
|
-
return 2.0 * ndotx / (ndotx + sqrt(a2 + (1.0 - a2) * ndotx * ndotx));
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
fn fresnel_schlick_body(cosTheta: f32, f0: f32) -> f32 {
|
|
94
|
-
let m = 1.0 - cosTheta;
|
|
95
|
-
let m2 = m * m;
|
|
96
|
-
return f0 + (1.0 - f0) * (m2 * m2 * m);
|
|
97
|
-
}
|
|
98
|
-
|
|
99
83
|
// smoothstep-based ramp: t*t*(3-2*t) between two color stops
|
|
100
84
|
fn ramp_ease(f: f32, p0: f32, c0: vec4f, p1: f32, c1: vec4f) -> vec4f {
|
|
101
85
|
let t = saturate((f - p0) / max(p1 - p0, 1e-6));
|
|
@@ -148,49 +132,33 @@ struct FSOut {
|
|
|
148
132
|
let tex_color = textureSample(diffuseTexture, diffuseSampler, input.uv).rgb;
|
|
149
133
|
let shadow = sampleShadow(input.worldPos, n);
|
|
150
134
|
|
|
151
|
-
// ═══ TOON MASK: ShaderToRGB → ramp.008 CONSTANT [0→black, 0.2966→white] ═══
|
|
152
135
|
let ndotl_raw = shader_to_rgb_diffuse(n, l, sun, light.ambientColor.xyz, shadow);
|
|
153
136
|
let toon = ramp_constant(ndotl_raw, 0.0, vec4f(0,0,0,1), 0.2966, vec4f(1,1,1,1)).r;
|
|
154
137
|
|
|
155
|
-
// ═══ TOON COLOR: Mix.004 A=HueSat, B=HueSat.001, Fac=ramp.008 (R) ═══
|
|
156
138
|
let shadow_tint = hue_sat_id(2.0, 0.3499999940395355, 1.0, tex_color);
|
|
157
139
|
let lit_tint = hue_sat_id(1.5, 1.0, 1.0, tex_color);
|
|
158
140
|
let toon_color = mix_blend(toon, shadow_tint, lit_tint);
|
|
159
141
|
let bc = bright_contrast(toon_color, 0.1, 0.2);
|
|
160
142
|
|
|
161
|
-
|
|
162
|
-
let ao = 1.0; // ao_fake(n, v) — no SSAO yet; inline 1.0 so the ramp/mix chain folds at compile time.
|
|
163
|
-
let ao_ramp = ramp_constant(ao, 0.0, vec4f(1,1,1,1), 0.5995, vec4f(0,0,0,1)).r;
|
|
164
|
-
let ao_mixed = mix_blend(ao_ramp, bc, vec3f(0.8301780223846436, 0.3345769941806793, 0.27946099638938904));
|
|
165
|
-
|
|
166
|
-
// ═══ EMISSION.003 (strength=4.0) ═══
|
|
167
|
-
let emission3 = ao_mixed * 4.0;
|
|
143
|
+
let emission3 = bc * 4.0;
|
|
168
144
|
|
|
169
|
-
// ═══ WARM: 颜色渐变.008 → 运算.006 ADD +0.5 (m_graphs) → clamp → 颜色渐变.003 ═══
|
|
170
|
-
let ao_inv = invert_f(1.0, ao_ramp);
|
|
171
|
-
let warm_str = ao_inv * BODY_WARM_AO_MUL;
|
|
172
145
|
let warm_input = clamp(toon + 0.5, 0.0, 1.0);
|
|
173
146
|
let warm_color = ramp_cardinal(warm_input, 0.2409,
|
|
174
147
|
vec4f(0.2426, 0.068, 0.0588, 1.0), 0.4663,
|
|
175
148
|
vec4f(0.6677, 0.5024, 0.5126, 1.0)).rgb;
|
|
176
|
-
let warm_emission = warm_color *
|
|
149
|
+
let warm_emission = warm_color * BODY_WARM_STR;
|
|
177
150
|
|
|
178
|
-
// ═══ RIM 1: 菲涅尔 × 层权重.001 Facing Blend=0.24 → 自发光 Strength ═══
|
|
179
151
|
let rim1_str = fresnel(2.0, n, v) * layer_weight_facing(0.24000005424022675, n, v);
|
|
180
152
|
let rim1 = vec3f(0.984157919883728, 0.6110184788703918, 0.5736401677131653) * rim1_str;
|
|
181
153
|
|
|
182
|
-
// ═══ RIM 2: 层权重.002 Facing → 运算.007 POWER → 颜色渐变.010 EASE → MixShader.002 Fac ═══
|
|
183
154
|
let facing_raw = layer_weight_facing(BODY_RIM2_LAYER_BLEND, n, v);
|
|
184
155
|
let facing_pow = math_power(facing_raw, BODY_RIM2_POW);
|
|
185
156
|
let rim2_fac = ramp_ease(facing_pow, 0.0, vec4f(0,0,0,1), 0.5052, vec4f(1,1,1,1)).r;
|
|
186
157
|
let rim2_mixed = mix(emission3, BODY_RIM2_BG, rim2_fac);
|
|
187
158
|
|
|
188
|
-
|
|
189
|
-
let add0 = rim1 + rim2_mixed;
|
|
190
|
-
let npr_stack = add0 + warm_emission;
|
|
159
|
+
let npr_stack = rim1 + rim2_mixed + warm_emission;
|
|
191
160
|
|
|
192
|
-
//
|
|
193
|
-
// Mapping loc=rot=0 → plain scale multiply, inline.
|
|
161
|
+
// Noise bump — Mapping loc=rot=0 folds to a plain scale multiply.
|
|
194
162
|
let noise_val = tex_noise_d2(input.worldPos * vec3f(1.0, 1.0, 1.5), 1.0);
|
|
195
163
|
let noise_ramp = ramp_linear(noise_val, 0.0, vec4f(0,0,0,1), 1.0, vec4f(1,1,1,1)).r;
|
|
196
164
|
let bumped_n = bump_lh(0.324644535779953, noise_ramp, n, input.worldPos);
|
|
@@ -198,31 +166,30 @@ struct FSOut {
|
|
|
198
166
|
let principled_base = mix_blend(noise_ramp, bc, vec3f(0.6831911206245422, 0.19474034011363983, 0.13732507824897766));
|
|
199
167
|
let p_emission = bc * 0.2;
|
|
200
168
|
|
|
201
|
-
//
|
|
202
|
-
let
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
let
|
|
207
|
-
let
|
|
208
|
-
let
|
|
209
|
-
let
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
let
|
|
214
|
-
let
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
let spec_radiance =
|
|
218
|
-
|
|
219
|
-
let direct = kd * sun * p_ndotl * shadow + spec_clamped;
|
|
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
|
+
|
|
220
187
|
// Indirect diffuse = base_color × L_w per Blender closure_eval_surface_lib.glsl line 302;
|
|
221
188
|
// probe_evaluate_world_diff returns radiance (SH-projected, not cosine-convolved).
|
|
222
|
-
|
|
223
|
-
let
|
|
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;
|
|
224
192
|
|
|
225
|
-
// 混合着色器.001: Shader=相加着色器.001, Shader_001=原理化BSDF
|
|
226
193
|
let final_color = mix(npr_stack, principled, BODY_MIX_NPR);
|
|
227
194
|
|
|
228
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
|
|
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
|
|
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
|
-
//
|
|
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
|
|
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
|
|
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
|
-
//
|
|
154
|
-
// Bump subtree is dead in the Blender graph (
|
|
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);
|
|
@@ -2,10 +2,14 @@
|
|
|
2
2
|
// Metallic=0, Specular=0.5 (F0=0.04), Roughness=0.5.
|
|
3
3
|
// Tone mapping via LUT sampled from Blender's OCIO pipeline (exposure -0.3 baked in).
|
|
4
4
|
|
|
5
|
+
import { NODES_WGSL } from "./nodes"
|
|
6
|
+
|
|
5
7
|
export const DEFAULT_SHADER_WGSL = /* wgsl */ `
|
|
6
8
|
|
|
9
|
+
${NODES_WGSL}
|
|
10
|
+
|
|
7
11
|
const PI: f32 = 3.141592653589793;
|
|
8
|
-
const
|
|
12
|
+
const DEFAULT_SPECULAR: f32 = 0.5;
|
|
9
13
|
const ROUGHNESS: f32 = 0.5;
|
|
10
14
|
|
|
11
15
|
struct CameraUniforms {
|
|
@@ -52,23 +56,6 @@ struct LightVP { viewProj: mat4x4f, };
|
|
|
52
56
|
@group(2) @binding(0) var diffuseTexture: texture_2d<f32>;
|
|
53
57
|
@group(2) @binding(1) var<uniform> material: MaterialUniforms;
|
|
54
58
|
|
|
55
|
-
// ─── GGX specular helpers ───────────────────────────────────────────
|
|
56
|
-
|
|
57
|
-
fn ggx_d(ndoth: f32, a2: f32) -> f32 {
|
|
58
|
-
let denom = ndoth * ndoth * (a2 - 1.0) + 1.0;
|
|
59
|
-
return a2 / (PI * denom * denom);
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
fn smith_g1(ndotx: f32, a2: f32) -> f32 {
|
|
63
|
-
return 2.0 * ndotx / (ndotx + sqrt(a2 + (1.0 - a2) * ndotx * ndotx));
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
fn fresnel_schlick(cosTheta: f32, f0: f32) -> f32 {
|
|
67
|
-
let m = 1.0 - cosTheta;
|
|
68
|
-
let m2 = m * m;
|
|
69
|
-
return f0 + (1.0 - f0) * (m2 * m2 * m);
|
|
70
|
-
}
|
|
71
|
-
|
|
72
59
|
// ─── Filmic tone mapping (LUT extracted from Blender 3.6 OCIO) ─────
|
|
73
60
|
// View transform = Filmic, Look = Medium High Contrast, Exposure = -0.3.
|
|
74
61
|
// 14 samples at integer log2 stops from -10 to +3 (inclusive).
|
|
@@ -158,27 +145,27 @@ struct FSOut {
|
|
|
158
145
|
let albedo = textureSample(diffuseTexture, diffuseSampler, input.uv).rgb;
|
|
159
146
|
|
|
160
147
|
let l = -light.lights[0].direction.xyz;
|
|
161
|
-
let
|
|
162
|
-
let
|
|
148
|
+
let sun = light.lights[0].color.xyz * light.lights[0].color.w;
|
|
149
|
+
let amb = light.ambientColor.xyz;
|
|
150
|
+
let shadow = sampleShadow(input.worldPos, n);
|
|
163
151
|
|
|
164
|
-
|
|
165
|
-
let
|
|
166
|
-
let
|
|
167
|
-
let vdoth = max(dot(v, h), 0.0);
|
|
152
|
+
// 原理化BSDF (EEVEE port): metallic=0, specular=0.5, roughness=0.5, specular_tint=0.
|
|
153
|
+
let NL = max(dot(n, l), 0.0);
|
|
154
|
+
let NV = max(dot(n, v), 1e-4);
|
|
168
155
|
|
|
169
|
-
let
|
|
170
|
-
let
|
|
171
|
-
let
|
|
172
|
-
let
|
|
173
|
-
let spec = (D * G * F) / max(4.0 * ndotl * ndotv, 0.001);
|
|
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);
|
|
174
160
|
|
|
175
|
-
let
|
|
176
|
-
let
|
|
177
|
-
let
|
|
178
|
-
|
|
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;
|
|
164
|
+
|
|
165
|
+
let diffuse_radiance = albedo * (sun * NL * shadow / PI + amb);
|
|
179
166
|
|
|
180
167
|
var out: FSOut;
|
|
181
|
-
out.color = vec4f(
|
|
168
|
+
out.color = vec4f(diffuse_radiance + spec_radiance, alpha);
|
|
182
169
|
out.mask = 1.0;
|
|
183
170
|
return out;
|
|
184
171
|
}
|
|
@@ -1,12 +1,16 @@
|
|
|
1
|
-
// Eye preset — default Principled BSDF (
|
|
1
|
+
// Eye preset — default Principled BSDF (Specular=0.5, Roughness=0.5) + Emission socket set to albedo × 1.5.
|
|
2
2
|
// Matches the published preset's instruction: "keep eyes in the default nodegraph, add emission 1.5".
|
|
3
3
|
// Blender's Principled BSDF Emission socket is added on top of the shaded output (pre-tonemap, feeds bloom).
|
|
4
4
|
|
|
5
|
+
import { NODES_WGSL } from "./nodes"
|
|
6
|
+
|
|
5
7
|
export const EYE_SHADER_WGSL = /* wgsl */ `
|
|
6
8
|
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
const
|
|
9
|
+
${NODES_WGSL}
|
|
10
|
+
|
|
11
|
+
const PI_E: f32 = 3.141592653589793;
|
|
12
|
+
const EYE_SPECULAR: f32 = 0.5;
|
|
13
|
+
const EYE_ROUGHNESS: f32 = 0.5;
|
|
10
14
|
const EYE_EMISSION_STRENGTH: f32 = 1.5;
|
|
11
15
|
|
|
12
16
|
struct CameraUniforms {
|
|
@@ -50,21 +54,6 @@ struct LightVP { viewProj: mat4x4f, };
|
|
|
50
54
|
@group(2) @binding(0) var diffuseTexture: texture_2d<f32>;
|
|
51
55
|
@group(2) @binding(1) var<uniform> material: MaterialUniforms;
|
|
52
56
|
|
|
53
|
-
fn ggx_d(ndoth: f32, a2: f32) -> f32 {
|
|
54
|
-
let denom = ndoth * ndoth * (a2 - 1.0) + 1.0;
|
|
55
|
-
return a2 / (PI * denom * denom);
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
fn smith_g1(ndotx: f32, a2: f32) -> f32 {
|
|
59
|
-
return 2.0 * ndotx / (ndotx + sqrt(a2 + (1.0 - a2) * ndotx * ndotx));
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
fn fresnel_schlick(cosTheta: f32, f0: f32) -> f32 {
|
|
63
|
-
let m = 1.0 - cosTheta;
|
|
64
|
-
let m2 = m * m;
|
|
65
|
-
return f0 + (1.0 - f0) * (m2 * m2 * m);
|
|
66
|
-
}
|
|
67
|
-
|
|
68
57
|
fn sampleShadow(worldPos: vec3f, n: vec3f) -> f32 {
|
|
69
58
|
// Back-facing to key light: direct contribution is zero anyway, skip 9 texture samples.
|
|
70
59
|
if (dot(n, -light.lights[0].direction.xyz) <= 0.0) { return 0.0; }
|
|
@@ -129,30 +118,29 @@ struct FSOut {
|
|
|
129
118
|
let albedo = textureSample(diffuseTexture, diffuseSampler, input.uv).rgb;
|
|
130
119
|
|
|
131
120
|
let l = -light.lights[0].direction.xyz;
|
|
132
|
-
let
|
|
133
|
-
let
|
|
121
|
+
let sun = light.lights[0].color.xyz * light.lights[0].color.w;
|
|
122
|
+
let amb = light.ambientColor.xyz;
|
|
123
|
+
let shadow = sampleShadow(input.worldPos, n);
|
|
134
124
|
|
|
135
|
-
|
|
136
|
-
let
|
|
137
|
-
let
|
|
138
|
-
let vdoth = max(dot(v, h), 0.0);
|
|
125
|
+
// 原理化BSDF (EEVEE port): metallic=0, specular=0.5, roughness=0.5, specular_tint=0.
|
|
126
|
+
let NL = max(dot(n, l), 0.0);
|
|
127
|
+
let NV = max(dot(n, v), 1e-4);
|
|
139
128
|
|
|
140
|
-
let
|
|
141
|
-
let
|
|
142
|
-
let
|
|
143
|
-
let
|
|
144
|
-
let spec = (D * G * F) / max(4.0 * ndotl * ndotv, 0.001);
|
|
129
|
+
let f0 = vec3f(0.08 * EYE_SPECULAR);
|
|
130
|
+
let f90 = mix(f0, vec3f(1.0), sqrt(EYE_SPECULAR));
|
|
131
|
+
let brdf_lut = brdf_lut_sample(NV, EYE_ROUGHNESS);
|
|
132
|
+
let reflection_color = F_brdf_multi_scatter(f0, f90, brdf_lut.xy);
|
|
145
133
|
|
|
146
|
-
let
|
|
147
|
-
let
|
|
148
|
-
let
|
|
149
|
-
let ambient = albedo * light.ambientColor.xyz;
|
|
134
|
+
let spec_direct = bsdf_ggx(n, l, v, NL, NV, EYE_ROUGHNESS) * sun * shadow * ltc_brdf_scale_from_lut(brdf_lut);
|
|
135
|
+
let spec_indirect = amb;
|
|
136
|
+
let spec_radiance = (spec_direct + spec_indirect) * reflection_color;
|
|
150
137
|
|
|
138
|
+
let diffuse_radiance = albedo * (sun * NL * shadow / PI_E + amb);
|
|
151
139
|
// Principled Emission socket: emissive = emission_color × strength, added on top of shading.
|
|
152
140
|
let emission = albedo * EYE_EMISSION_STRENGTH;
|
|
153
141
|
|
|
154
142
|
var out: FSOut;
|
|
155
|
-
out.color = vec4f(
|
|
143
|
+
out.color = vec4f(diffuse_radiance + spec_radiance + emission, alpha);
|
|
156
144
|
out.mask = 1.0;
|
|
157
145
|
return out;
|
|
158
146
|
}
|
|
@@ -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
|
|
80
|
-
const FACE_BRIGHT_TEX_THRESH: f32 = 0.9300000071525574;
|
|
81
|
-
const FACE_MIX_NPR: f32 = 0.5;
|
|
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
|
-
//
|
|
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
|
-
|
|
145
|
-
let
|
|
146
|
-
let
|
|
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
|
-
|
|
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
|
-
|
|
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 *
|
|
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
|
-
//
|
|
182
|
-
//
|
|
183
|
-
//
|
|
184
|
-
//
|
|
185
|
-
//
|
|
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;
|
|
162
|
+
let bright_emit = vec3f(tex_gate) * 3.0;
|
|
189
163
|
|
|
190
|
-
|
|
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
|
-
//
|
|
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);
|
|
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
|
-
//
|
|
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
|
|
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
|
-
|
|
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
|
-
//
|
|
160
|
-
//
|
|
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
|
-
//
|
|
168
|
-
//
|
|
169
|
-
// weights Principled at only 0.2
|
|
170
|
-
//
|
|
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;
|