reze-engine 0.10.2 → 0.11.0
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 +72 -13
- package/dist/engine.d.ts +170 -34
- package/dist/engine.d.ts.map +1 -1
- package/dist/engine.js +1080 -308
- package/dist/index.d.ts +2 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -1
- package/dist/shaders/body.d.ts +2 -0
- package/dist/shaders/body.d.ts.map +1 -0
- package/dist/shaders/body.js +209 -0
- package/dist/shaders/classify.d.ts +4 -0
- package/dist/shaders/classify.d.ts.map +1 -0
- package/dist/shaders/classify.js +12 -0
- package/dist/shaders/cloth_rough.d.ts +2 -0
- package/dist/shaders/cloth_rough.d.ts.map +1 -0
- package/dist/shaders/cloth_rough.js +172 -0
- package/dist/shaders/cloth_smooth.d.ts +2 -0
- package/dist/shaders/cloth_smooth.d.ts.map +1 -0
- package/dist/shaders/cloth_smooth.js +171 -0
- package/dist/shaders/default.d.ts +2 -0
- package/dist/shaders/default.d.ts.map +1 -0
- package/dist/shaders/default.js +168 -0
- package/dist/shaders/dfg_lut.d.ts +4 -0
- package/dist/shaders/dfg_lut.d.ts.map +1 -0
- package/dist/shaders/dfg_lut.js +125 -0
- package/dist/shaders/eye.d.ts +2 -0
- package/dist/shaders/eye.d.ts.map +1 -0
- package/dist/shaders/eye.js +142 -0
- package/dist/shaders/face.d.ts +2 -0
- package/dist/shaders/face.d.ts.map +1 -0
- package/dist/shaders/face.js +211 -0
- package/dist/shaders/hair.d.ts +2 -0
- package/dist/shaders/hair.d.ts.map +1 -0
- package/dist/shaders/hair.js +186 -0
- package/dist/shaders/ltc_mag_lut.d.ts +3 -0
- package/dist/shaders/ltc_mag_lut.d.ts.map +1 -0
- package/dist/shaders/ltc_mag_lut.js +1033 -0
- package/dist/shaders/metal.d.ts +2 -0
- package/dist/shaders/metal.d.ts.map +1 -0
- package/dist/shaders/metal.js +171 -0
- package/dist/shaders/nodes.d.ts +2 -0
- package/dist/shaders/nodes.d.ts.map +1 -0
- package/dist/shaders/nodes.js +423 -0
- package/dist/shaders/stockings.d.ts +2 -0
- package/dist/shaders/stockings.d.ts.map +1 -0
- package/dist/shaders/stockings.js +229 -0
- package/package.json +1 -1
- package/src/engine.ts +1281 -376
- package/src/index.ts +12 -2
- package/src/shaders/body.ts +211 -0
- package/src/shaders/classify.ts +25 -0
- package/src/shaders/cloth_rough.ts +174 -0
- package/src/shaders/cloth_smooth.ts +173 -0
- package/src/shaders/default.ts +169 -0
- package/src/shaders/dfg_lut.ts +127 -0
- package/src/shaders/eye.ts +143 -0
- package/src/shaders/face.ts +213 -0
- package/src/shaders/hair.ts +188 -0
- package/src/shaders/ltc_mag_lut.ts +1035 -0
- package/src/shaders/metal.ts +173 -0
- package/src/shaders/nodes.ts +424 -0
- package/src/shaders/stockings.ts +231 -0
|
@@ -0,0 +1,229 @@
|
|
|
1
|
+
// M_Stockings — 仿深空之眼渲染预设v1.0_by_小绿毛猫_material_graph_dump.json "M_Stockings".
|
|
2
|
+
// NPR mask (bbox gradient × facing rim) drives Mix Shader between an Emission (HSV-boosted texture)
|
|
3
|
+
// and a Principled BSDF with sheen. Mapping rotation + Generated-like coord approximated via UV,
|
|
4
|
+
// since our Y-up PMX engine has no object bbox; the gradient is a soft mask, not a hard landmark.
|
|
5
|
+
import { NODES_WGSL } from "./nodes";
|
|
6
|
+
export const STOCKINGS_SHADER_WGSL = /* wgsl */ `
|
|
7
|
+
|
|
8
|
+
${NODES_WGSL}
|
|
9
|
+
|
|
10
|
+
struct CameraUniforms {
|
|
11
|
+
view: mat4x4f,
|
|
12
|
+
projection: mat4x4f,
|
|
13
|
+
viewPos: vec3f,
|
|
14
|
+
_padding: f32,
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
struct Light {
|
|
18
|
+
direction: vec4f,
|
|
19
|
+
color: vec4f,
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
struct LightUniforms {
|
|
23
|
+
ambientColor: vec4f,
|
|
24
|
+
lights: array<Light, 4>,
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
struct MaterialUniforms {
|
|
28
|
+
diffuseColor: vec3f,
|
|
29
|
+
alpha: f32,
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
struct VertexOutput {
|
|
33
|
+
@builtin(position) position: vec4f,
|
|
34
|
+
@location(0) normal: vec3f,
|
|
35
|
+
@location(1) uv: vec2f,
|
|
36
|
+
@location(2) worldPos: vec3f,
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
struct LightVP { viewProj: mat4x4f, };
|
|
40
|
+
|
|
41
|
+
@group(0) @binding(0) var<uniform> camera: CameraUniforms;
|
|
42
|
+
@group(0) @binding(1) var<uniform> light: LightUniforms;
|
|
43
|
+
@group(0) @binding(2) var diffuseSampler: sampler;
|
|
44
|
+
@group(0) @binding(3) var shadowMap: texture_depth_2d;
|
|
45
|
+
@group(0) @binding(4) var shadowSampler: sampler_comparison;
|
|
46
|
+
@group(0) @binding(5) var<uniform> lightVP: LightVP;
|
|
47
|
+
@group(1) @binding(0) var<storage, read> skinMats: array<mat4x4f>;
|
|
48
|
+
@group(2) @binding(0) var diffuseTexture: texture_2d<f32>;
|
|
49
|
+
@group(2) @binding(1) var<uniform> material: MaterialUniforms;
|
|
50
|
+
|
|
51
|
+
fn sampleShadow(worldPos: vec3f, n: vec3f) -> f32 {
|
|
52
|
+
let biasedPos = worldPos + n * 0.08;
|
|
53
|
+
let lclip = lightVP.viewProj * vec4f(biasedPos, 1.0);
|
|
54
|
+
let ndc = lclip.xyz / max(lclip.w, 1e-6);
|
|
55
|
+
let suv = vec2f(ndc.x * 0.5 + 0.5, 0.5 - ndc.y * 0.5);
|
|
56
|
+
let cmpZ = ndc.z - 0.001;
|
|
57
|
+
let ts = 1.0 / 4096.0;
|
|
58
|
+
var vis = 0.0;
|
|
59
|
+
for (var y = -1; y <= 1; y++) {
|
|
60
|
+
for (var x = -1; x <= 1; x++) {
|
|
61
|
+
vis += textureSampleCompare(shadowMap, shadowSampler, suv + vec2f(f32(x), f32(y)) * ts, cmpZ);
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
return vis / 9.0;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
const PI_S: f32 = 3.141592653589793;
|
|
68
|
+
// Principled BSDF params from dump (Alpha=0.95 is intentionally dropped — see alpha-hash note below)
|
|
69
|
+
const STOCK_METALLIC: f32 = 0.1;
|
|
70
|
+
const STOCK_SPECULAR: f32 = 1.0;
|
|
71
|
+
const STOCK_ROUGHNESS: f32 = 0.5;
|
|
72
|
+
const STOCK_SHEEN: f32 = 0.7017999887466431;
|
|
73
|
+
const STOCK_SHEEN_TINT: f32 = 0.5;
|
|
74
|
+
// NPR mask ramps
|
|
75
|
+
const STOCK_RAMP002_P1: f32 = 0.9565; // EASE [0→black, 0.9565→white]
|
|
76
|
+
const STOCK_RAMPFACE_P1: f32 = 0.5435; // EASE [0→black, 0.5435→white]
|
|
77
|
+
const STOCK_LW_BLEND: f32 = 0.4; // Layer Weight Blend
|
|
78
|
+
|
|
79
|
+
// principled_sheen (gpu_shader_material_principled.glsl:8-14) — empirical NV curve
|
|
80
|
+
fn principled_sheen(NV: f32) -> f32 {
|
|
81
|
+
let f = 1.0 - NV;
|
|
82
|
+
return f * f * f * 0.077 + f * 0.01 + 0.00026;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
// Wyman & McGuire "Hashed Alpha Testing" (2017) — world-space hash with derivative-aware
|
|
86
|
+
// pixel-scale selection, matches Blender EEVEE prepass_frag.glsl::hashed_alpha_threshold.
|
|
87
|
+
// Key property: dither pattern is stable in object/world space (doesn't swim) and stays
|
|
88
|
+
// at one-pixel frequency regardless of view distance, which makes it tolerable without TAA.
|
|
89
|
+
fn _hash_wm(a: vec2f) -> f32 {
|
|
90
|
+
return fract(1e4 * sin(17.0 * a.x + 0.1 * a.y) * (0.1 + abs(sin(13.0 * a.y + a.x))));
|
|
91
|
+
}
|
|
92
|
+
fn _hash3d_wm(a: vec3f) -> f32 {
|
|
93
|
+
return _hash_wm(vec2f(_hash_wm(a.xy), a.z));
|
|
94
|
+
}
|
|
95
|
+
fn hashed_alpha_threshold(co: vec3f) -> f32 {
|
|
96
|
+
let alphaHashScale: f32 = 1.0;
|
|
97
|
+
let max_deriv = max(length(dpdx(co)), length(dpdy(co)));
|
|
98
|
+
let pix_scale = 1.0 / max(alphaHashScale * max_deriv, 1e-6);
|
|
99
|
+
let pix_scale_log = log2(pix_scale);
|
|
100
|
+
let px_lo = exp2(floor(pix_scale_log));
|
|
101
|
+
let px_hi = exp2(ceil(pix_scale_log));
|
|
102
|
+
let a_lo = _hash3d_wm(floor(px_lo * co));
|
|
103
|
+
let a_hi = _hash3d_wm(floor(px_hi * co));
|
|
104
|
+
let fac = fract(pix_scale_log);
|
|
105
|
+
let x = mix(a_lo, a_hi, fac);
|
|
106
|
+
// CDF remap so that discard-probability = (1 - alpha) uniformly across scale transitions
|
|
107
|
+
let a = min(fac, 1.0 - fac);
|
|
108
|
+
let one_a = 1.0 - a;
|
|
109
|
+
let denom = 1.0 / max(2.0 * a * one_a, 1e-6);
|
|
110
|
+
let one_x = 1.0 - x;
|
|
111
|
+
let case_lo = (x * x) * denom;
|
|
112
|
+
let case_mid = (x - 0.5 * a) / max(one_a, 1e-6);
|
|
113
|
+
let case_hi = 1.0 - (one_x * one_x) * denom;
|
|
114
|
+
var threshold = select(case_hi, select(case_lo, case_mid, x >= a), x < one_a);
|
|
115
|
+
return clamp(threshold, 1e-6, 1.0);
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
// Smoothstep-based EASE ramp (Blender VALTORGB EASE) — 2 stops, saturate+smoothstep between
|
|
119
|
+
fn ramp_ease_s(f: f32, p0: f32, p1: f32) -> f32 {
|
|
120
|
+
let t = saturate((f - p0) / max(p1 - p0, 1e-6));
|
|
121
|
+
return t * t * (3.0 - 2.0 * t);
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
@vertex fn vs(
|
|
125
|
+
@location(0) position: vec3f,
|
|
126
|
+
@location(1) normal: vec3f,
|
|
127
|
+
@location(2) uv: vec2f,
|
|
128
|
+
@location(3) joints0: vec4<u32>,
|
|
129
|
+
@location(4) weights0: vec4<f32>
|
|
130
|
+
) -> VertexOutput {
|
|
131
|
+
var output: VertexOutput;
|
|
132
|
+
let pos4 = vec4f(position, 1.0);
|
|
133
|
+
let weightSum = weights0.x + weights0.y + weights0.z + weights0.w;
|
|
134
|
+
let invWeightSum = select(1.0, 1.0 / weightSum, weightSum > 0.0001);
|
|
135
|
+
let nw = select(vec4f(1.0, 0.0, 0.0, 0.0), weights0 * invWeightSum, weightSum > 0.0001);
|
|
136
|
+
var skinnedPos = vec4f(0.0);
|
|
137
|
+
var skinnedNrm = vec3f(0.0);
|
|
138
|
+
for (var i = 0u; i < 4u; i++) {
|
|
139
|
+
let m = skinMats[joints0[i]];
|
|
140
|
+
let w = nw[i];
|
|
141
|
+
skinnedPos += (m * pos4) * w;
|
|
142
|
+
skinnedNrm += (mat3x3f(m[0].xyz, m[1].xyz, m[2].xyz) * normal) * w;
|
|
143
|
+
}
|
|
144
|
+
output.position = camera.projection * camera.view * vec4f(skinnedPos.xyz, 1.0);
|
|
145
|
+
output.normal = normalize(skinnedNrm);
|
|
146
|
+
output.uv = uv;
|
|
147
|
+
output.worldPos = skinnedPos.xyz;
|
|
148
|
+
return output;
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
@fragment fn fs(input: VertexOutput) -> @location(0) vec4f {
|
|
152
|
+
let n = normalize(input.normal);
|
|
153
|
+
let v = normalize(camera.viewPos - input.worldPos);
|
|
154
|
+
let l = -light.lights[0].direction.xyz;
|
|
155
|
+
let sun = light.lights[0].color.xyz * light.lights[0].color.w;
|
|
156
|
+
let amb = light.ambientColor.xyz;
|
|
157
|
+
let shadow = sampleShadow(input.worldPos, n);
|
|
158
|
+
|
|
159
|
+
let tex_s = textureSample(diffuseTexture, diffuseSampler, input.uv);
|
|
160
|
+
let tex_rgb = tex_s.rgb;
|
|
161
|
+
// Alpha HASHED (Blender EEVEE "Hashed" blend mode) per preset author's note —
|
|
162
|
+
// self-overlap on the stockings produces sort cracks under alpha blend. Wyman-style
|
|
163
|
+
// worldPos hash + depth-write is sort-independent. NOTE: Principled.Alpha=0.95 from
|
|
164
|
+
// the dump is DROPPED here — it relies on TAA to smooth the resulting 5%-everywhere
|
|
165
|
+
// dither, and without TAA it shows as a pervasive dot pattern. Hash now gates only
|
|
166
|
+
// on texture/material alpha, so solid stockings regions stay fully opaque.
|
|
167
|
+
let combined_alpha = material.alpha * tex_s.a;
|
|
168
|
+
if (combined_alpha < hashed_alpha_threshold(input.worldPos)) { discard; }
|
|
169
|
+
let out_alpha = 1.0;
|
|
170
|
+
|
|
171
|
+
// ═══ NPR MASK: TEX_COORD.Generated → Mapping(Rot=0,π/2,π/2, Loc=(1,1,1)) → Gradient Texture
|
|
172
|
+
// The Blender mapping reduces to gradient.x = 1 - input.y (rot swaps axes, loc offsets by 1).
|
|
173
|
+
// We approximate Generated with UV since Y-up PMX has no object bbox in pipeline state.
|
|
174
|
+
let gen_coord = vec3f(input.uv, 0.0);
|
|
175
|
+
let mapped = mapping_point(gen_coord, vec3f(1.0), vec3f(0.0, 1.5708, 1.5708), vec3f(1.0));
|
|
176
|
+
let gradient = tex_gradient_linear(mapped);
|
|
177
|
+
|
|
178
|
+
// Ramp.001 LINEAR [0→black, 0.5→white, 1.0→black] — triangular peak at 0.5
|
|
179
|
+
let ramp001 = 1.0 - abs(2.0 * gradient - 1.0);
|
|
180
|
+
// Ramp.002 EASE [0→black, 0.9565→white]
|
|
181
|
+
let ramp002 = ramp_ease_s(ramp001, 0.0, STOCK_RAMP002_P1);
|
|
182
|
+
|
|
183
|
+
// Layer Weight.Facing (Blend=0.4) → Ramp EASE [0→black, 0.5435→white]
|
|
184
|
+
let facing = layer_weight_facing(STOCK_LW_BLEND, n, v);
|
|
185
|
+
let ramp_face = ramp_ease_s(facing, 0.0, STOCK_RAMPFACE_P1);
|
|
186
|
+
|
|
187
|
+
// Mix.001: MIX blend Fac=0.5, A=white, B=ramp_face → (A,B) averaged 50/50
|
|
188
|
+
let mix001 = mix(1.0, ramp_face, 0.5);
|
|
189
|
+
// Mix: LIGHTEN blend Fac=0.5, A=mix001, B=ramp002 → A smoothly lightens toward max(A,B)
|
|
190
|
+
let lighten = max(mix001, ramp002);
|
|
191
|
+
let mask = mix(mix001, lighten, 0.5);
|
|
192
|
+
|
|
193
|
+
// ═══ EMISSION SHADER ═══
|
|
194
|
+
// Hue=0.5 (identity rotation), Sat=1.0, Val=5.0 (5× brightness boost), Fac=1; Strength=1
|
|
195
|
+
let emission = hue_sat(0.5, 1.0, 5.0, 1.0, tex_rgb);
|
|
196
|
+
|
|
197
|
+
// ═══ PRINCIPLED BSDF (EEVEE port) ═══
|
|
198
|
+
// base_color_tint, metallic f0, sheen coarse approx (scales diffuse radiance).
|
|
199
|
+
let NL = max(dot(n, l), 0.0);
|
|
200
|
+
let NV = max(dot(n, v), 1e-4);
|
|
201
|
+
|
|
202
|
+
// f0 = mix((0.08*spec)*dielectric_tint, base, metallic); dielectric_tint=1 since specular_tint=0.
|
|
203
|
+
let dielectric_f0 = vec3f(0.08 * STOCK_SPECULAR);
|
|
204
|
+
let f0 = mix(dielectric_f0, tex_rgb, STOCK_METALLIC);
|
|
205
|
+
let f90 = mix(f0, vec3f(1.0), sqrt(STOCK_SPECULAR));
|
|
206
|
+
let split_sum = brdf_lut_baked(NV, STOCK_ROUGHNESS);
|
|
207
|
+
let reflection_color = F_brdf_multi_scatter(f0, f90, split_sum);
|
|
208
|
+
|
|
209
|
+
let spec_direct = bsdf_ggx(n, l, v, STOCK_ROUGHNESS) * sun * shadow * ltc_brdf_scale(NV, STOCK_ROUGHNESS);
|
|
210
|
+
let spec_indirect = amb;
|
|
211
|
+
let spec_radiance = (spec_direct + spec_indirect) * reflection_color;
|
|
212
|
+
|
|
213
|
+
// Sheen coarse: diffuse_color += sheen * sheen_color * principled_sheen(NV).
|
|
214
|
+
let base_tint = tint_from_color(tex_rgb);
|
|
215
|
+
let sheen_color = mix(vec3f(1.0), base_tint, STOCK_SHEEN_TINT);
|
|
216
|
+
let diffuse_color = tex_rgb + STOCK_SHEEN * sheen_color * principled_sheen(NV);
|
|
217
|
+
|
|
218
|
+
// diffuse_weight = (1 - metallic). Indirect diffuse uses L_w (no π; see closure_eval_surface_lib:302).
|
|
219
|
+
let diffuse_weight = 1.0 - STOCK_METALLIC;
|
|
220
|
+
let diffuse_radiance = diffuse_color * (sun * NL * shadow / PI_S + amb) * diffuse_weight;
|
|
221
|
+
let principled = diffuse_radiance + spec_radiance;
|
|
222
|
+
|
|
223
|
+
// ═══ MIX SHADER: Shader=Emission, Shader_001=Principled, Fac=mask ═══
|
|
224
|
+
let final_color = mix(emission, principled, mask);
|
|
225
|
+
|
|
226
|
+
return vec4f(final_color, out_alpha);
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
`;
|