reze-engine 0.12.1 → 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/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/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,77 +1,15 @@
|
|
|
1
|
-
// M_Stockings — 仿深空之眼渲染预设v1.0_by_小绿毛猫
|
|
2
|
-
//
|
|
3
|
-
//
|
|
4
|
-
//
|
|
1
|
+
// M_Stockings — 仿深空之眼渲染预设v1.0_by_小绿毛猫 "M_Stockings". A bbox-gradient ×
|
|
2
|
+
// facing-rim mask drives a Mix Shader between an HSV-boosted emission and a sheen
|
|
3
|
+
// Principled BSDF. Wyman hashed-alpha testing replaces the graph's Alpha=0.95 (which
|
|
4
|
+
// would require TAA to hide the dither dots across every pixel).
|
|
5
5
|
import { NODES_WGSL } from "./nodes";
|
|
6
|
+
import { COMMON_MATERIAL_PRELUDE_WGSL } from "./common";
|
|
6
7
|
export const STOCKINGS_SHADER_WGSL = /* wgsl */ `
|
|
7
8
|
|
|
8
9
|
${NODES_WGSL}
|
|
10
|
+
${COMMON_MATERIAL_PRELUDE_WGSL}
|
|
9
11
|
|
|
10
|
-
|
|
11
|
-
view: mat4x4f,
|
|
12
|
-
projection: mat4x4f,
|
|
13
|
-
viewPos: vec3f,
|
|
14
|
-
_padding: f32,
|
|
15
|
-
};
|
|
16
|
-
|
|
17
|
-
struct Light {
|
|
18
|
-
direction: vec4f,
|
|
19
|
-
color: vec4f,
|
|
20
|
-
};
|
|
21
|
-
|
|
22
|
-
struct LightUniforms {
|
|
23
|
-
ambientColor: vec4f,
|
|
24
|
-
lights: array<Light, 4>,
|
|
25
|
-
};
|
|
26
|
-
|
|
27
|
-
struct MaterialUniforms {
|
|
28
|
-
diffuseColor: vec3f,
|
|
29
|
-
alpha: f32,
|
|
30
|
-
};
|
|
31
|
-
|
|
32
|
-
struct VertexOutput {
|
|
33
|
-
@builtin(position) position: vec4f,
|
|
34
|
-
@location(0) normal: vec3f,
|
|
35
|
-
@location(1) uv: vec2f,
|
|
36
|
-
@location(2) worldPos: vec3f,
|
|
37
|
-
};
|
|
38
|
-
|
|
39
|
-
struct LightVP { viewProj: mat4x4f, };
|
|
40
|
-
|
|
41
|
-
@group(0) @binding(0) var<uniform> camera: CameraUniforms;
|
|
42
|
-
@group(0) @binding(1) var<uniform> light: LightUniforms;
|
|
43
|
-
@group(0) @binding(2) var diffuseSampler: sampler;
|
|
44
|
-
@group(0) @binding(3) var shadowMap: texture_depth_2d;
|
|
45
|
-
@group(0) @binding(4) var shadowSampler: sampler_comparison;
|
|
46
|
-
@group(0) @binding(5) var<uniform> lightVP: LightVP;
|
|
47
|
-
@group(1) @binding(0) var<storage, read> skinMats: array<mat4x4f>;
|
|
48
|
-
@group(2) @binding(0) var diffuseTexture: texture_2d<f32>;
|
|
49
|
-
@group(2) @binding(1) var<uniform> material: MaterialUniforms;
|
|
50
|
-
|
|
51
|
-
fn sampleShadow(worldPos: vec3f, n: vec3f) -> f32 {
|
|
52
|
-
// Back-facing to key light: direct contribution is zero anyway, skip 9 texture samples.
|
|
53
|
-
if (dot(n, -light.lights[0].direction.xyz) <= 0.0) { return 0.0; }
|
|
54
|
-
let biasedPos = worldPos + n * 0.08;
|
|
55
|
-
let lclip = lightVP.viewProj * vec4f(biasedPos, 1.0);
|
|
56
|
-
let ndc = lclip.xyz / max(lclip.w, 1e-6);
|
|
57
|
-
let suv = vec2f(ndc.x * 0.5 + 0.5, 0.5 - ndc.y * 0.5);
|
|
58
|
-
let cmpZ = ndc.z - 0.001;
|
|
59
|
-
let ts = 1.0 / 2048.0;
|
|
60
|
-
// 3x3 PCF unrolled — Safari's Metal backend doesn't unroll nested shadow loops reliably.
|
|
61
|
-
let s00 = textureSampleCompareLevel(shadowMap, shadowSampler, suv + vec2f(-ts, -ts), cmpZ);
|
|
62
|
-
let s10 = textureSampleCompareLevel(shadowMap, shadowSampler, suv + vec2f(0.0, -ts), cmpZ);
|
|
63
|
-
let s20 = textureSampleCompareLevel(shadowMap, shadowSampler, suv + vec2f( ts, -ts), cmpZ);
|
|
64
|
-
let s01 = textureSampleCompareLevel(shadowMap, shadowSampler, suv + vec2f(-ts, 0.0), cmpZ);
|
|
65
|
-
let s11 = textureSampleCompareLevel(shadowMap, shadowSampler, suv, cmpZ);
|
|
66
|
-
let s21 = textureSampleCompareLevel(shadowMap, shadowSampler, suv + vec2f( ts, 0.0), cmpZ);
|
|
67
|
-
let s02 = textureSampleCompareLevel(shadowMap, shadowSampler, suv + vec2f(-ts, ts), cmpZ);
|
|
68
|
-
let s12 = textureSampleCompareLevel(shadowMap, shadowSampler, suv + vec2f(0.0, ts), cmpZ);
|
|
69
|
-
let s22 = textureSampleCompareLevel(shadowMap, shadowSampler, suv + vec2f( ts, ts), cmpZ);
|
|
70
|
-
return (s00 + s10 + s20 + s01 + s11 + s21 + s02 + s12 + s22) * (1.0 / 9.0);
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
const PI_S: f32 = 3.141592653589793;
|
|
74
|
-
// Principled BSDF params from dump (Alpha=0.95 is intentionally dropped — see alpha-hash note below)
|
|
12
|
+
// Principled params from dump (Alpha=0.95 intentionally dropped — see hash note below).
|
|
75
13
|
const STOCK_METALLIC: f32 = 0.1;
|
|
76
14
|
const STOCK_SPECULAR: f32 = 1.0;
|
|
77
15
|
const STOCK_ROUGHNESS: f32 = 0.5;
|
|
@@ -82,12 +20,6 @@ const STOCK_RAMP002_P1: f32 = 0.9565; // EASE [0→black, 0.9565→white]
|
|
|
82
20
|
const STOCK_RAMPFACE_P1: f32 = 0.5435; // EASE [0→black, 0.5435→white]
|
|
83
21
|
const STOCK_LW_BLEND: f32 = 0.4; // Layer Weight Blend
|
|
84
22
|
|
|
85
|
-
// principled_sheen (gpu_shader_material_principled.glsl:8-14) — empirical NV curve
|
|
86
|
-
fn principled_sheen(NV: f32) -> f32 {
|
|
87
|
-
let f = 1.0 - NV;
|
|
88
|
-
return f * f * f * 0.077 + f * 0.01 + 0.00026;
|
|
89
|
-
}
|
|
90
|
-
|
|
91
23
|
// Wyman & McGuire "Hashed Alpha Testing" (2017) — world-space hash with derivative-aware
|
|
92
24
|
// pixel-scale selection, matches Blender EEVEE prepass_frag.glsl::hashed_alpha_threshold.
|
|
93
25
|
// Key property: dither pattern is stable in object/world space (doesn't swim) and stays
|
|
@@ -109,7 +41,7 @@ fn hashed_alpha_threshold(co: vec3f) -> f32 {
|
|
|
109
41
|
let a_hi = _hash3d_wm(floor(px_hi * co));
|
|
110
42
|
let fac = fract(pix_scale_log);
|
|
111
43
|
let x = mix(a_lo, a_hi, fac);
|
|
112
|
-
// CDF remap so
|
|
44
|
+
// CDF remap so discard-probability = (1 - alpha) uniformly across scale transitions
|
|
113
45
|
let a = min(fac, 1.0 - fac);
|
|
114
46
|
let one_a = 1.0 - a;
|
|
115
47
|
let denom = 1.0 / max(2.0 * a * one_a, 1e-6);
|
|
@@ -127,39 +59,6 @@ fn ramp_ease_s(f: f32, p0: f32, p1: f32) -> f32 {
|
|
|
127
59
|
return t * t * (3.0 - 2.0 * t);
|
|
128
60
|
}
|
|
129
61
|
|
|
130
|
-
@vertex fn vs(
|
|
131
|
-
@location(0) position: vec3f,
|
|
132
|
-
@location(1) normal: vec3f,
|
|
133
|
-
@location(2) uv: vec2f,
|
|
134
|
-
@location(3) joints0: vec4<u32>,
|
|
135
|
-
@location(4) weights0: vec4<f32>
|
|
136
|
-
) -> VertexOutput {
|
|
137
|
-
var output: VertexOutput;
|
|
138
|
-
let pos4 = vec4f(position, 1.0);
|
|
139
|
-
let weightSum = weights0.x + weights0.y + weights0.z + weights0.w;
|
|
140
|
-
let invWeightSum = select(1.0, 1.0 / weightSum, weightSum > 0.0001);
|
|
141
|
-
let nw = select(vec4f(1.0, 0.0, 0.0, 0.0), weights0 * invWeightSum, weightSum > 0.0001);
|
|
142
|
-
var skinnedPos = vec4f(0.0);
|
|
143
|
-
var skinnedNrm = vec3f(0.0);
|
|
144
|
-
for (var i = 0u; i < 4u; i++) {
|
|
145
|
-
let m = skinMats[joints0[i]];
|
|
146
|
-
let w = nw[i];
|
|
147
|
-
skinnedPos += (m * pos4) * w;
|
|
148
|
-
skinnedNrm += (mat3x3f(m[0].xyz, m[1].xyz, m[2].xyz) * normal) * w;
|
|
149
|
-
}
|
|
150
|
-
output.position = camera.projection * camera.view * vec4f(skinnedPos.xyz, 1.0);
|
|
151
|
-
// Skip VS normalize — interpolation denormalizes anyway, and FS always does normalize(input.normal).
|
|
152
|
-
output.normal = skinnedNrm;
|
|
153
|
-
output.uv = uv;
|
|
154
|
-
output.worldPos = skinnedPos.xyz;
|
|
155
|
-
return output;
|
|
156
|
-
}
|
|
157
|
-
|
|
158
|
-
struct FSOut {
|
|
159
|
-
@location(0) color: vec4f,
|
|
160
|
-
@location(1) bloom_mask: f32,
|
|
161
|
-
};
|
|
162
|
-
|
|
163
62
|
@fragment fn fs(input: VertexOutput) -> FSOut {
|
|
164
63
|
let n = normalize(input.normal);
|
|
165
64
|
let v = normalize(camera.viewPos - input.worldPos);
|
|
@@ -170,18 +69,16 @@ struct FSOut {
|
|
|
170
69
|
|
|
171
70
|
let tex_s = textureSample(diffuseTexture, diffuseSampler, input.uv);
|
|
172
71
|
let tex_rgb = tex_s.rgb;
|
|
173
|
-
// Alpha HASHED (Blender EEVEE "Hashed" blend mode) per preset author's note —
|
|
174
|
-
//
|
|
175
|
-
//
|
|
176
|
-
//
|
|
177
|
-
//
|
|
178
|
-
// on texture/material alpha, so solid stockings regions stay fully opaque.
|
|
72
|
+
// Alpha HASHED (Blender EEVEE "Hashed" blend mode) per preset author's note — self-overlap
|
|
73
|
+
// on the stockings produces sort cracks under alpha blend. Wyman-style worldPos hash +
|
|
74
|
+
// depth-write is sort-independent. NOTE: Principled.Alpha=0.95 from the dump is DROPPED;
|
|
75
|
+
// it relies on TAA to smooth the 5%-everywhere dither, and without TAA it shows as a
|
|
76
|
+
// pervasive dot pattern. Hash now gates only on texture/material alpha.
|
|
179
77
|
let combined_alpha = material.alpha * tex_s.a;
|
|
180
78
|
if (combined_alpha < hashed_alpha_threshold(input.worldPos)) { discard; }
|
|
181
|
-
let out_alpha = 1.0;
|
|
182
79
|
|
|
183
|
-
// ═══ NPR MASK
|
|
184
|
-
// The Blender mapping reduces to gradient.x = 1 - input.y (rot swaps axes, loc offsets
|
|
80
|
+
// ═══ NPR MASK ═══ TEX_COORD.Generated → Mapping(Rot=0,π/2,π/2, Loc=(1,1,1)) → Gradient.
|
|
81
|
+
// The Blender mapping reduces to gradient.x = 1 - input.y (rot swaps axes, loc offsets).
|
|
185
82
|
// We approximate Generated with UV since Y-up PMX has no object bbox in pipeline state.
|
|
186
83
|
let gen_coord = vec3f(input.uv, 0.0);
|
|
187
84
|
let mapped = mapping_point(gen_coord, vec3f(1.0), vec3f(0.0, 1.5708, 1.5708), vec3f(1.0));
|
|
@@ -189,55 +86,32 @@ struct FSOut {
|
|
|
189
86
|
|
|
190
87
|
// Ramp.001 LINEAR [0→black, 0.5→white, 1.0→black] — triangular peak at 0.5
|
|
191
88
|
let ramp001 = 1.0 - abs(2.0 * gradient - 1.0);
|
|
192
|
-
// Ramp.002 EASE [0→black, 0.9565→white]
|
|
193
89
|
let ramp002 = ramp_ease_s(ramp001, 0.0, STOCK_RAMP002_P1);
|
|
194
90
|
|
|
195
|
-
// Layer Weight.Facing (Blend=0.4) → Ramp EASE [0→black, 0.5435→white]
|
|
196
91
|
let facing = layer_weight_facing(STOCK_LW_BLEND, n, v);
|
|
197
92
|
let ramp_face = ramp_ease_s(facing, 0.0, STOCK_RAMPFACE_P1);
|
|
198
93
|
|
|
199
|
-
// Mix.001: MIX blend Fac=0.5, A=white, B=ramp_face
|
|
94
|
+
// Mix.001: MIX blend Fac=0.5, A=white, B=ramp_face
|
|
200
95
|
let mix001 = mix(1.0, ramp_face, 0.5);
|
|
201
|
-
// Mix: LIGHTEN blend Fac=0.5, A=mix001, B=ramp002
|
|
96
|
+
// Mix: LIGHTEN blend Fac=0.5, A=mix001, B=ramp002
|
|
202
97
|
let lighten = max(mix001, ramp002);
|
|
203
98
|
let mask = mix(mix001, lighten, 0.5);
|
|
204
99
|
|
|
205
|
-
// ═══ EMISSION SHADER ═══
|
|
206
|
-
// Hue=0.5 (identity rotation), Sat=1.0, Val=5.0 (5× brightness boost), Fac=1; Strength=1
|
|
100
|
+
// ═══ EMISSION SHADER ═══ Hue=0.5 (identity), Sat=1.0, Val=5.0 (5× brightness), Fac=1.
|
|
207
101
|
let emission = hue_sat_id(1.0, 5.0, 1.0, tex_rgb);
|
|
208
102
|
|
|
209
|
-
// ═══ PRINCIPLED BSDF
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
// f0 = mix((0.08*spec)*dielectric_tint, base, metallic); dielectric_tint=1 since specular_tint=0.
|
|
215
|
-
let dielectric_f0 = vec3f(0.08 * STOCK_SPECULAR);
|
|
216
|
-
let f0 = mix(dielectric_f0, tex_rgb, STOCK_METALLIC);
|
|
217
|
-
let f90 = mix(f0, vec3f(1.0), sqrt(STOCK_SPECULAR));
|
|
218
|
-
let brdf_lut = brdf_lut_sample(NV, STOCK_ROUGHNESS);
|
|
219
|
-
let reflection_color = F_brdf_multi_scatter(f0, f90, brdf_lut.xy);
|
|
220
|
-
|
|
221
|
-
let spec_direct = bsdf_ggx(n, l, v, NL, NV, STOCK_ROUGHNESS) * sun * shadow * ltc_brdf_scale_from_lut(brdf_lut);
|
|
222
|
-
let spec_indirect = amb;
|
|
223
|
-
let spec_radiance = (spec_direct + spec_indirect) * reflection_color;
|
|
224
|
-
|
|
225
|
-
// Sheen coarse: diffuse_color += sheen * sheen_color * principled_sheen(NV).
|
|
226
|
-
let base_tint = tint_from_color(tex_rgb);
|
|
227
|
-
let sheen_color = mix(vec3f(1.0), base_tint, STOCK_SHEEN_TINT);
|
|
228
|
-
let diffuse_color = tex_rgb + STOCK_SHEEN * sheen_color * principled_sheen(NV);
|
|
229
|
-
|
|
230
|
-
// diffuse_weight = (1 - metallic). Indirect diffuse uses L_w (no π; see closure_eval_surface_lib:302).
|
|
231
|
-
let diffuse_weight = 1.0 - STOCK_METALLIC;
|
|
232
|
-
let diffuse_radiance = diffuse_color * (sun * NL * shadow / PI_S + amb) * diffuse_weight;
|
|
233
|
-
let principled = diffuse_radiance + spec_radiance;
|
|
103
|
+
// ═══ PRINCIPLED BSDF with sheen ═══ metallic=0.1, sheen=0.7, sheen_tint=0.5.
|
|
104
|
+
let principled = eval_principled(
|
|
105
|
+
PrincipledIn(tex_rgb, STOCK_METALLIC, STOCK_SPECULAR, STOCK_ROUGHNESS, 1e30, STOCK_SHEEN, STOCK_SHEEN_TINT),
|
|
106
|
+
n, l, v, sun, amb, shadow
|
|
107
|
+
);
|
|
234
108
|
|
|
235
|
-
//
|
|
109
|
+
// MIX SHADER: Shader=Emission, Shader_001=Principled, Fac=mask
|
|
236
110
|
let final_color = mix(emission, principled, mask);
|
|
237
111
|
|
|
238
112
|
var out: FSOut;
|
|
239
|
-
out.color = vec4f(final_color,
|
|
240
|
-
out.
|
|
113
|
+
out.color = vec4f(final_color, 1.0);
|
|
114
|
+
out.mask = 1.0;
|
|
241
115
|
return out;
|
|
242
116
|
}
|
|
243
117
|
|
package/package.json
CHANGED
|
@@ -1,201 +1,90 @@
|
|
|
1
|
-
// M_Body — 仿深空之眼渲染预设v1.0_by_小绿毛猫
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
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
|
-
let
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
let
|
|
57
|
-
|
|
58
|
-
let
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
let
|
|
62
|
-
let
|
|
63
|
-
let
|
|
64
|
-
let
|
|
65
|
-
|
|
66
|
-
let
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
return
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
@location(0) position: vec3f,
|
|
92
|
-
@location(1) normal: vec3f,
|
|
93
|
-
@location(2) uv: vec2f,
|
|
94
|
-
@location(3) joints0: vec4<u32>,
|
|
95
|
-
@location(4) weights0: vec4<f32>
|
|
96
|
-
) -> VertexOutput {
|
|
97
|
-
var output: VertexOutput;
|
|
98
|
-
let pos4 = vec4f(position, 1.0);
|
|
99
|
-
let weightSum = weights0.x + weights0.y + weights0.z + weights0.w;
|
|
100
|
-
let invWeightSum = select(1.0, 1.0 / weightSum, weightSum > 0.0001);
|
|
101
|
-
let nw = select(vec4f(1.0, 0.0, 0.0, 0.0), weights0 * invWeightSum, weightSum > 0.0001);
|
|
102
|
-
var skinnedPos = vec4f(0.0);
|
|
103
|
-
var skinnedNrm = vec3f(0.0);
|
|
104
|
-
for (var i = 0u; i < 4u; i++) {
|
|
105
|
-
let m = skinMats[joints0[i]];
|
|
106
|
-
let w = nw[i];
|
|
107
|
-
skinnedPos += (m * pos4) * w;
|
|
108
|
-
skinnedNrm += (mat3x3f(m[0].xyz, m[1].xyz, m[2].xyz) * normal) * w;
|
|
109
|
-
}
|
|
110
|
-
output.position = camera.projection * camera.view * vec4f(skinnedPos.xyz, 1.0);
|
|
111
|
-
// Skip VS normalize — interpolation denormalizes anyway, and FS always does normalize(input.normal).
|
|
112
|
-
output.normal = skinnedNrm;
|
|
113
|
-
output.uv = uv;
|
|
114
|
-
output.worldPos = skinnedPos.xyz;
|
|
115
|
-
return output;
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
struct FSOut {
|
|
119
|
-
@location(0) color: vec4f,
|
|
120
|
-
@location(1) mask: f32,
|
|
121
|
-
};
|
|
122
|
-
|
|
123
|
-
@fragment fn fs(input: VertexOutput) -> FSOut {
|
|
124
|
-
let alpha = material.alpha;
|
|
125
|
-
if (alpha < 0.001) { discard; }
|
|
126
|
-
|
|
127
|
-
let n = normalize(input.normal);
|
|
128
|
-
let v = normalize(camera.viewPos - input.worldPos);
|
|
129
|
-
let l = -light.lights[0].direction.xyz;
|
|
130
|
-
let sun = light.lights[0].color.xyz * light.lights[0].color.w;
|
|
131
|
-
|
|
132
|
-
let tex_color = textureSample(diffuseTexture, diffuseSampler, input.uv).rgb;
|
|
133
|
-
let shadow = sampleShadow(input.worldPos, n);
|
|
134
|
-
|
|
135
|
-
let ndotl_raw = shader_to_rgb_diffuse(n, l, sun, light.ambientColor.xyz, shadow);
|
|
136
|
-
let toon = ramp_constant(ndotl_raw, 0.0, vec4f(0,0,0,1), 0.2966, vec4f(1,1,1,1)).r;
|
|
137
|
-
|
|
138
|
-
let shadow_tint = hue_sat_id(2.0, 0.3499999940395355, 1.0, tex_color);
|
|
139
|
-
let lit_tint = hue_sat_id(1.5, 1.0, 1.0, tex_color);
|
|
140
|
-
let toon_color = mix_blend(toon, shadow_tint, lit_tint);
|
|
141
|
-
let bc = bright_contrast(toon_color, 0.1, 0.2);
|
|
142
|
-
|
|
143
|
-
let emission3 = bc * 4.0;
|
|
144
|
-
|
|
145
|
-
let warm_input = clamp(toon + 0.5, 0.0, 1.0);
|
|
146
|
-
let warm_color = ramp_cardinal(warm_input, 0.2409,
|
|
147
|
-
vec4f(0.2426, 0.068, 0.0588, 1.0), 0.4663,
|
|
148
|
-
vec4f(0.6677, 0.5024, 0.5126, 1.0)).rgb;
|
|
149
|
-
let warm_emission = warm_color * BODY_WARM_STR;
|
|
150
|
-
|
|
151
|
-
let rim1_str = fresnel(2.0, n, v) * layer_weight_facing(0.24000005424022675, n, v);
|
|
152
|
-
let rim1 = vec3f(0.984157919883728, 0.6110184788703918, 0.5736401677131653) * rim1_str;
|
|
153
|
-
|
|
154
|
-
let facing_raw = layer_weight_facing(BODY_RIM2_LAYER_BLEND, n, v);
|
|
155
|
-
let facing_pow = math_power(facing_raw, BODY_RIM2_POW);
|
|
156
|
-
let rim2_fac = ramp_ease(facing_pow, 0.0, vec4f(0,0,0,1), 0.5052, vec4f(1,1,1,1)).r;
|
|
157
|
-
let rim2_mixed = mix(emission3, BODY_RIM2_BG, rim2_fac);
|
|
158
|
-
|
|
159
|
-
let npr_stack = rim1 + rim2_mixed + warm_emission;
|
|
160
|
-
|
|
161
|
-
// Noise bump — Mapping loc=rot=0 folds to a plain scale multiply.
|
|
162
|
-
let noise_val = tex_noise_d2(input.worldPos * vec3f(1.0, 1.0, 1.5), 1.0);
|
|
163
|
-
let noise_ramp = ramp_linear(noise_val, 0.0, vec4f(0,0,0,1), 1.0, vec4f(1,1,1,1)).r;
|
|
164
|
-
let bumped_n = bump_lh(0.324644535779953, noise_ramp, n, input.worldPos);
|
|
165
|
-
|
|
166
|
-
let principled_base = mix_blend(noise_ramp, bc, vec3f(0.6831911206245422, 0.19474034011363983, 0.13732507824897766));
|
|
167
|
-
let p_emission = bc * 0.2;
|
|
168
|
-
|
|
169
|
-
// Principled BSDF (EEVEE port): metallic=0, specular=0.5, roughness=0.3, specular_tint=0.
|
|
170
|
-
let NL = max(dot(bumped_n, l), 0.0);
|
|
171
|
-
let NV = max(dot(bumped_n, v), 1e-4);
|
|
172
|
-
|
|
173
|
-
// f0/f90 per gpu_shader_material_principled.glsl — specular_tint=0 → dielectric_f0_color=white.
|
|
174
|
-
let f0 = vec3f(0.08 * BODY_SPECULAR);
|
|
175
|
-
let f90 = mix(f0, vec3f(1.0), sqrt(BODY_SPECULAR));
|
|
176
|
-
let brdf_lut = brdf_lut_sample(NV, BODY_ROUGHNESS);
|
|
177
|
-
let reflection_color = F_brdf_multi_scatter(f0, f90, brdf_lut.xy);
|
|
178
|
-
|
|
179
|
-
// Direct glossy — bsdf_ggx already includes NL; no F applied here (tinted after accum).
|
|
180
|
-
// ltc_brdf_scale: EEVEE direct path uses LTC; split-sum LUT path is rescaled to match.
|
|
181
|
-
let spec_direct_raw = bsdf_ggx(bumped_n, l, v, NL, NV, BODY_ROUGHNESS) * sun * shadow * ltc_brdf_scale_from_lut(brdf_lut);
|
|
182
|
-
let spec_direct = min(spec_direct_raw, vec3f(BODY_SPEC_CLAMP));
|
|
183
|
-
// Indirect glossy — flat world probe (solid color). Phase 2 adds cubemap.
|
|
184
|
-
let spec_indirect = light.ambientColor.xyz;
|
|
185
|
-
let spec_radiance = (spec_direct + spec_indirect) * reflection_color;
|
|
186
|
-
|
|
187
|
-
// Indirect diffuse = base_color × L_w per Blender closure_eval_surface_lib.glsl line 302;
|
|
188
|
-
// probe_evaluate_world_diff returns radiance (SH-projected, not cosine-convolved).
|
|
189
|
-
// No (1-F) factor per EEVEE — it doesn't energy-conserve spec<->diffuse.
|
|
190
|
-
let diffuse_radiance = principled_base * (sun * NL * shadow / PI_B + light.ambientColor.xyz);
|
|
191
|
-
let principled = diffuse_radiance + spec_radiance + p_emission;
|
|
192
|
-
|
|
193
|
-
let final_color = mix(npr_stack, principled, BODY_MIX_NPR);
|
|
194
|
-
|
|
195
|
-
var out: FSOut;
|
|
196
|
-
out.color = vec4f(final_color, alpha);
|
|
197
|
-
out.mask = 1.0;
|
|
198
|
-
return out;
|
|
199
|
-
}
|
|
200
|
-
|
|
201
|
-
`
|
|
1
|
+
// M_Body — 仿深空之眼渲染预设v1.0_by_小绿毛猫 "M_Body". Toon + warm rim + rim1/rim2
|
|
2
|
+
// stack mixed 50/50 against a Principled BSDF with noise-bumped normal.
|
|
3
|
+
|
|
4
|
+
import { NODES_WGSL } from "./nodes"
|
|
5
|
+
import { COMMON_MATERIAL_PRELUDE_WGSL } from "./common"
|
|
6
|
+
|
|
7
|
+
export const BODY_SHADER_WGSL = /* wgsl */ `
|
|
8
|
+
|
|
9
|
+
${NODES_WGSL}
|
|
10
|
+
${COMMON_MATERIAL_PRELUDE_WGSL}
|
|
11
|
+
|
|
12
|
+
const BODY_ROUGHNESS: f32 = 0.3;
|
|
13
|
+
const BODY_SPECULAR: f32 = 0.5;
|
|
14
|
+
const BODY_MIX_NPR: f32 = 0.5;
|
|
15
|
+
const BODY_SPEC_CLAMP: f32 = 10.0;
|
|
16
|
+
const BODY_RIM2_LAYER_BLEND: f32 = 0.20000000298023224;
|
|
17
|
+
const BODY_RIM2_POW: f32 = 1.4300000667572021;
|
|
18
|
+
const BODY_RIM2_BG: vec3f = vec3f(1.0, 0.4303792119026184, 0.3315804898738861);
|
|
19
|
+
const BODY_WARM_STR: f32 = 0.30000001192092896;
|
|
20
|
+
|
|
21
|
+
// smoothstep-based ramp: t*t*(3-2*t) between two color stops
|
|
22
|
+
fn ramp_ease(f: f32, p0: f32, c0: vec4f, p1: f32, c1: vec4f) -> vec4f {
|
|
23
|
+
let t = saturate((f - p0) / max(p1 - p0, 1e-6));
|
|
24
|
+
let ss = t * t * (3.0 - 2.0 * t);
|
|
25
|
+
return mix(c0, c1, ss);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
@fragment fn fs(input: VertexOutput) -> FSOut {
|
|
29
|
+
let alpha = material.alpha;
|
|
30
|
+
if (alpha < 0.001) { discard; }
|
|
31
|
+
|
|
32
|
+
let n = normalize(input.normal);
|
|
33
|
+
let v = normalize(camera.viewPos - input.worldPos);
|
|
34
|
+
let l = -light.lights[0].direction.xyz;
|
|
35
|
+
let sun = light.lights[0].color.xyz * light.lights[0].color.w;
|
|
36
|
+
let amb = light.ambientColor.xyz;
|
|
37
|
+
let shadow = sampleShadow(input.worldPos, n);
|
|
38
|
+
|
|
39
|
+
let tex_color = textureSample(diffuseTexture, diffuseSampler, input.uv).rgb;
|
|
40
|
+
|
|
41
|
+
// ═══ NPR STACK ═══
|
|
42
|
+
let ndotl_raw = shader_to_rgb_diffuse(n, l, sun, amb, shadow);
|
|
43
|
+
let toon = ramp_constant(ndotl_raw, 0.0, vec4f(0,0,0,1), 0.2966, vec4f(1,1,1,1)).r;
|
|
44
|
+
|
|
45
|
+
let shadow_tint = hue_sat_id(2.0, 0.3499999940395355, 1.0, tex_color);
|
|
46
|
+
let lit_tint = hue_sat_id(1.5, 1.0, 1.0, tex_color);
|
|
47
|
+
let toon_color = mix_blend(toon, shadow_tint, lit_tint);
|
|
48
|
+
let bc = bright_contrast(toon_color, 0.1, 0.2);
|
|
49
|
+
|
|
50
|
+
let emission3 = bc * 4.0;
|
|
51
|
+
|
|
52
|
+
let warm_input = clamp(toon + 0.5, 0.0, 1.0);
|
|
53
|
+
let warm_color = ramp_cardinal(warm_input, 0.2409,
|
|
54
|
+
vec4f(0.2426, 0.068, 0.0588, 1.0), 0.4663,
|
|
55
|
+
vec4f(0.6677, 0.5024, 0.5126, 1.0)).rgb;
|
|
56
|
+
let warm_emission = warm_color * BODY_WARM_STR;
|
|
57
|
+
|
|
58
|
+
let rim1_str = fresnel(2.0, n, v) * layer_weight_facing(0.24000005424022675, n, v);
|
|
59
|
+
let rim1 = vec3f(0.984157919883728, 0.6110184788703918, 0.5736401677131653) * rim1_str;
|
|
60
|
+
|
|
61
|
+
let facing_raw = layer_weight_facing(BODY_RIM2_LAYER_BLEND, n, v);
|
|
62
|
+
let facing_pow = math_power(facing_raw, BODY_RIM2_POW);
|
|
63
|
+
let rim2_fac = ramp_ease(facing_pow, 0.0, vec4f(0,0,0,1), 0.5052, vec4f(1,1,1,1)).r;
|
|
64
|
+
let rim2_mixed = mix(emission3, BODY_RIM2_BG, rim2_fac);
|
|
65
|
+
|
|
66
|
+
let npr_stack = rim1 + rim2_mixed + warm_emission;
|
|
67
|
+
|
|
68
|
+
// ═══ PRINCIPLED BSDF with noise bump ═══
|
|
69
|
+
// Mapping loc=rot=0 in the Blender graph folds to a plain scale multiply.
|
|
70
|
+
let noise_val = tex_noise_d2(input.worldPos * vec3f(1.0, 1.0, 1.5), 1.0);
|
|
71
|
+
let noise_ramp = ramp_linear(noise_val, 0.0, vec4f(0,0,0,1), 1.0, vec4f(1,1,1,1)).r;
|
|
72
|
+
let bumped_n = bump_lh(0.324644535779953, noise_ramp, n, input.worldPos);
|
|
73
|
+
|
|
74
|
+
let principled_base = mix_blend(noise_ramp, bc, vec3f(0.6831911206245422, 0.19474034011363983, 0.13732507824897766));
|
|
75
|
+
let p_emission = bc * 0.2;
|
|
76
|
+
|
|
77
|
+
let principled = eval_principled(
|
|
78
|
+
PrincipledIn(principled_base, 0.0, BODY_SPECULAR, BODY_ROUGHNESS, BODY_SPEC_CLAMP, 0.0, 0.0),
|
|
79
|
+
bumped_n, l, v, sun, amb, shadow
|
|
80
|
+
) + p_emission;
|
|
81
|
+
|
|
82
|
+
let final_color = mix(npr_stack, principled, BODY_MIX_NPR);
|
|
83
|
+
|
|
84
|
+
var out: FSOut;
|
|
85
|
+
out.color = vec4f(final_color, alpha);
|
|
86
|
+
out.mask = 1.0;
|
|
87
|
+
return out;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
`
|