reze-engine 0.25.0 → 0.25.1
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 +5 -1
- package/dist/engine.d.ts +78 -1
- package/dist/engine.d.ts.map +1 -1
- package/dist/engine.js +134 -14
- package/dist/graph/slots.d.ts.map +1 -1
- package/dist/graph/slots.js +10 -2
- package/dist/shaders/materials/common.d.ts +1 -1
- package/dist/shaders/materials/common.d.ts.map +1 -1
- package/dist/shaders/materials/common.js +143 -141
- package/dist/shaders/passes/depth-prepass.d.ts +2 -0
- package/dist/shaders/passes/depth-prepass.d.ts.map +1 -0
- package/dist/shaders/passes/depth-prepass.js +56 -0
- package/package.json +1 -1
- package/src/engine.ts +143 -15
- package/src/graph/slots.ts +11 -2
- package/src/shaders/materials/common.ts +207 -205
- package/src/shaders/passes/depth-prepass.ts +57 -0
|
@@ -18,148 +18,150 @@
|
|
|
18
18
|
// group(0): per-frame scene (camera, lights, shadow map, BRDF LUT via nodes.ts)
|
|
19
19
|
// group(1): per-model skinning
|
|
20
20
|
// group(2): per-material (diffuse texture + material uniforms)
|
|
21
|
-
export const COMMON_BINDINGS_WGSL = /* wgsl */ `
|
|
22
|
-
|
|
23
|
-
struct CameraUniforms {
|
|
24
|
-
view: mat4x4f,
|
|
25
|
-
projection: mat4x4f,
|
|
26
|
-
viewPos: vec3f,
|
|
27
|
-
_padding: f32,
|
|
28
|
-
};
|
|
29
|
-
|
|
30
|
-
struct Light {
|
|
31
|
-
direction: vec4f,
|
|
32
|
-
color: vec4f,
|
|
33
|
-
};
|
|
34
|
-
|
|
35
|
-
struct LightUniforms {
|
|
36
|
-
ambientColor: vec4f,
|
|
37
|
-
lights: array<Light, 4>,
|
|
38
|
-
};
|
|
39
|
-
|
|
40
|
-
// Per-material uniforms. Every material binds this layout even if it ignores fields;
|
|
41
|
-
// the engine keeps one bind group layout across all material pipelines. The PMX
|
|
42
|
-
// classic-material fields (ambient/specular/shininess) are carried for graph nodes that
|
|
43
|
-
// want them; most graphs read only diffuseColor + alpha.
|
|
44
|
-
struct MaterialUniforms {
|
|
45
|
-
diffuseColor: vec3f, // PMX diffuse rgb — the material_diffuse node reads this
|
|
46
|
-
alpha: f32, // 0 → discard; <1 → transparent draw call
|
|
47
|
-
ambient: vec3f, // PMX ambient rgb
|
|
48
|
-
shininess: f32, // PMX specular power
|
|
49
|
-
specular: vec3f, // PMX specular rgb
|
|
50
|
-
sphereMode: f32, // 0 none · 1 multiply (sph) · 2 add (spa)
|
|
51
|
-
// Skeleton index of the 頭 (head) bone, or -1. Lets the eye shader gate
|
|
52
|
-
// the post-alpha-eye stencil by camera-vs-face hemisphere.
|
|
53
|
-
headBoneIndex: f32,
|
|
54
|
-
_pad0: f32,
|
|
55
|
-
_pad1: f32,
|
|
56
|
-
_pad2: f32,
|
|
57
|
-
};
|
|
58
|
-
|
|
59
|
-
struct VertexOutput {
|
|
60
|
-
@builtin(position) position: vec4f,
|
|
61
|
-
@location(0) normal: vec3f,
|
|
62
|
-
@location(1) uv: vec2f,
|
|
63
|
-
@location(2) worldPos: vec3f,
|
|
64
|
-
// Bind-pose object-space position (the raw pre-skin vertex attribute). Procedural
|
|
65
|
-
// textures (noise bump, sparkle, Generated-coord gradients) key off this instead of
|
|
66
|
-
// worldPos so the pattern rides with the surface — otherwise the mesh swims through a
|
|
67
|
-
// world-static noise field under any skinning deformation or root (センター) motion.
|
|
68
|
-
// At rest skinMats are identity so restPos == worldPos, which is why existing noise-
|
|
69
|
-
// scale constants stay valid without retuning.
|
|
70
|
-
@location(3) restPos: vec3f,
|
|
71
|
-
};
|
|
72
|
-
|
|
73
|
-
struct LightVP { viewProj: mat4x4f, };
|
|
74
|
-
|
|
75
|
-
@group(0) @binding(0) var<uniform> camera: CameraUniforms;
|
|
76
|
-
@group(0) @binding(1) var<uniform> light: LightUniforms;
|
|
77
|
-
@group(0) @binding(2) var diffuseSampler: sampler;
|
|
78
|
-
@group(0) @binding(3) var shadowMap: texture_depth_2d;
|
|
79
|
-
@group(0) @binding(4) var shadowSampler: sampler_comparison;
|
|
80
|
-
@group(0) @binding(5) var<uniform> lightVP: LightVP;
|
|
81
|
-
// binding(9) brdfLut is declared inside NODES_WGSL (nodes.ts).
|
|
82
|
-
@group(1) @binding(0) var<storage, read> skinMats: array<mat4x4f>;
|
|
83
|
-
@group(2) @binding(0) var diffuseTexture: texture_2d<f32>;
|
|
84
|
-
@group(2) @binding(1) var<uniform> material: MaterialUniforms;
|
|
85
|
-
// Reserved for future sphere/toon graph nodes; graphs that don't read them get the
|
|
86
|
-
// 1×1 white fallback bound here.
|
|
87
|
-
@group(2) @binding(2) var toonTexture: texture_2d<f32>;
|
|
88
|
-
@group(2) @binding(3) var sphereTexture: texture_2d<f32>;
|
|
89
|
-
|
|
90
|
-
// Four-bone blended normals can cancel to ~zero on physics-driven parts
|
|
91
|
-
// (opposing bone rotations at 50/50 weights) — normalize(0) is 0/0 = NaN,
|
|
92
|
-
// which poisons the whole shading stack and flashes through bloom. Fall
|
|
93
|
-
// back to up for degenerate normals instead.
|
|
94
|
-
fn safe_normal(nIn: vec3f) -> vec3f {
|
|
95
|
-
let l2 = dot(nIn, nIn);
|
|
96
|
-
if (l2 < 1e-12) { return vec3f(0.0, 1.0, 0.0); }
|
|
97
|
-
return nIn * inverseSqrt(l2);
|
|
98
|
-
}
|
|
99
|
-
|
|
21
|
+
export const COMMON_BINDINGS_WGSL = /* wgsl */ `
|
|
22
|
+
|
|
23
|
+
struct CameraUniforms {
|
|
24
|
+
view: mat4x4f,
|
|
25
|
+
projection: mat4x4f,
|
|
26
|
+
viewPos: vec3f,
|
|
27
|
+
_padding: f32,
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
struct Light {
|
|
31
|
+
direction: vec4f,
|
|
32
|
+
color: vec4f,
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
struct LightUniforms {
|
|
36
|
+
ambientColor: vec4f,
|
|
37
|
+
lights: array<Light, 4>,
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
// Per-material uniforms. Every material binds this layout even if it ignores fields;
|
|
41
|
+
// the engine keeps one bind group layout across all material pipelines. The PMX
|
|
42
|
+
// classic-material fields (ambient/specular/shininess) are carried for graph nodes that
|
|
43
|
+
// want them; most graphs read only diffuseColor + alpha.
|
|
44
|
+
struct MaterialUniforms {
|
|
45
|
+
diffuseColor: vec3f, // PMX diffuse rgb — the material_diffuse node reads this
|
|
46
|
+
alpha: f32, // 0 → discard; <1 → transparent draw call
|
|
47
|
+
ambient: vec3f, // PMX ambient rgb
|
|
48
|
+
shininess: f32, // PMX specular power
|
|
49
|
+
specular: vec3f, // PMX specular rgb
|
|
50
|
+
sphereMode: f32, // 0 none · 1 multiply (sph) · 2 add (spa)
|
|
51
|
+
// Skeleton index of the 頭 (head) bone, or -1. Lets the eye shader gate
|
|
52
|
+
// the post-alpha-eye stencil by camera-vs-face hemisphere.
|
|
53
|
+
headBoneIndex: f32,
|
|
54
|
+
_pad0: f32,
|
|
55
|
+
_pad1: f32,
|
|
56
|
+
_pad2: f32,
|
|
57
|
+
};
|
|
58
|
+
|
|
59
|
+
struct VertexOutput {
|
|
60
|
+
@builtin(position) position: vec4f,
|
|
61
|
+
@location(0) normal: vec3f,
|
|
62
|
+
@location(1) uv: vec2f,
|
|
63
|
+
@location(2) worldPos: vec3f,
|
|
64
|
+
// Bind-pose object-space position (the raw pre-skin vertex attribute). Procedural
|
|
65
|
+
// textures (noise bump, sparkle, Generated-coord gradients) key off this instead of
|
|
66
|
+
// worldPos so the pattern rides with the surface — otherwise the mesh swims through a
|
|
67
|
+
// world-static noise field under any skinning deformation or root (センター) motion.
|
|
68
|
+
// At rest skinMats are identity so restPos == worldPos, which is why existing noise-
|
|
69
|
+
// scale constants stay valid without retuning.
|
|
70
|
+
@location(3) restPos: vec3f,
|
|
71
|
+
};
|
|
72
|
+
|
|
73
|
+
struct LightVP { viewProj: mat4x4f, };
|
|
74
|
+
|
|
75
|
+
@group(0) @binding(0) var<uniform> camera: CameraUniforms;
|
|
76
|
+
@group(0) @binding(1) var<uniform> light: LightUniforms;
|
|
77
|
+
@group(0) @binding(2) var diffuseSampler: sampler;
|
|
78
|
+
@group(0) @binding(3) var shadowMap: texture_depth_2d;
|
|
79
|
+
@group(0) @binding(4) var shadowSampler: sampler_comparison;
|
|
80
|
+
@group(0) @binding(5) var<uniform> lightVP: LightVP;
|
|
81
|
+
// binding(9) brdfLut is declared inside NODES_WGSL (nodes.ts).
|
|
82
|
+
@group(1) @binding(0) var<storage, read> skinMats: array<mat4x4f>;
|
|
83
|
+
@group(2) @binding(0) var diffuseTexture: texture_2d<f32>;
|
|
84
|
+
@group(2) @binding(1) var<uniform> material: MaterialUniforms;
|
|
85
|
+
// Reserved for future sphere/toon graph nodes; graphs that don't read them get the
|
|
86
|
+
// 1×1 white fallback bound here.
|
|
87
|
+
@group(2) @binding(2) var toonTexture: texture_2d<f32>;
|
|
88
|
+
@group(2) @binding(3) var sphereTexture: texture_2d<f32>;
|
|
89
|
+
|
|
90
|
+
// Four-bone blended normals can cancel to ~zero on physics-driven parts
|
|
91
|
+
// (opposing bone rotations at 50/50 weights) — normalize(0) is 0/0 = NaN,
|
|
92
|
+
// which poisons the whole shading stack and flashes through bloom. Fall
|
|
93
|
+
// back to up for degenerate normals instead.
|
|
94
|
+
fn safe_normal(nIn: vec3f) -> vec3f {
|
|
95
|
+
let l2 = dot(nIn, nIn);
|
|
96
|
+
if (l2 < 1e-12) { return vec3f(0.0, 1.0, 0.0); }
|
|
97
|
+
return nIn * inverseSqrt(l2);
|
|
98
|
+
}
|
|
99
|
+
|
|
100
100
|
`;
|
|
101
101
|
// ─── Shadow sampler (3×3 PCF) ───────────────────────────────────────
|
|
102
|
-
//
|
|
103
|
-
// doesn't unroll nested shadow loops
|
|
104
|
-
//
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
let
|
|
112
|
-
let
|
|
113
|
-
let
|
|
114
|
-
let
|
|
115
|
-
let
|
|
116
|
-
let
|
|
117
|
-
let
|
|
118
|
-
let
|
|
119
|
-
let
|
|
120
|
-
let
|
|
121
|
-
let
|
|
122
|
-
let
|
|
123
|
-
let
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
102
|
+
// 4096-map (MUST match Engine.SHADOW_MAP_SIZE), normal-bias 0.08, depth-bias
|
|
103
|
+
// 0.001. Unrolled — Safari's Metal backend doesn't unroll nested shadow loops
|
|
104
|
+
// reliably. The texel size was stale at 1/2048 after the map grew to 4096:
|
|
105
|
+
// PCF taps landed TWO texels apart, quantizing self-shadow edges into
|
|
106
|
+
// texel-sized gray/black squares that crawled with the animation.
|
|
107
|
+
export const SAMPLE_SHADOW_WGSL = /* wgsl */ `
|
|
108
|
+
|
|
109
|
+
fn sampleShadow(worldPos: vec3f, n: vec3f) -> f32 {
|
|
110
|
+
if (dot(n, -light.lights[0].direction.xyz) <= 0.0) { return 0.0; }
|
|
111
|
+
let biasedPos = worldPos + n * 0.08;
|
|
112
|
+
let lclip = lightVP.viewProj * vec4f(biasedPos, 1.0);
|
|
113
|
+
let ndc = lclip.xyz / max(lclip.w, 1e-6);
|
|
114
|
+
let suv = vec2f(ndc.x * 0.5 + 0.5, 0.5 - ndc.y * 0.5);
|
|
115
|
+
let cmpZ = ndc.z - 0.001;
|
|
116
|
+
let ts = 1.0 / 4096.0;
|
|
117
|
+
let s00 = textureSampleCompareLevel(shadowMap, shadowSampler, suv + vec2f(-ts, -ts), cmpZ);
|
|
118
|
+
let s10 = textureSampleCompareLevel(shadowMap, shadowSampler, suv + vec2f(0.0, -ts), cmpZ);
|
|
119
|
+
let s20 = textureSampleCompareLevel(shadowMap, shadowSampler, suv + vec2f( ts, -ts), cmpZ);
|
|
120
|
+
let s01 = textureSampleCompareLevel(shadowMap, shadowSampler, suv + vec2f(-ts, 0.0), cmpZ);
|
|
121
|
+
let s11 = textureSampleCompareLevel(shadowMap, shadowSampler, suv, cmpZ);
|
|
122
|
+
let s21 = textureSampleCompareLevel(shadowMap, shadowSampler, suv + vec2f( ts, 0.0), cmpZ);
|
|
123
|
+
let s02 = textureSampleCompareLevel(shadowMap, shadowSampler, suv + vec2f(-ts, ts), cmpZ);
|
|
124
|
+
let s12 = textureSampleCompareLevel(shadowMap, shadowSampler, suv + vec2f(0.0, ts), cmpZ);
|
|
125
|
+
let s22 = textureSampleCompareLevel(shadowMap, shadowSampler, suv + vec2f( ts, ts), cmpZ);
|
|
126
|
+
return (s00 + s10 + s20 + s01 + s11 + s21 + s02 + s12 + s22) * (1.0 / 9.0);
|
|
127
|
+
}
|
|
128
|
+
|
|
127
129
|
`;
|
|
128
130
|
// ─── Skinning vertex shader ─────────────────────────────────────────
|
|
129
131
|
// Four-bone linear blend skinning. Renormalizes weights when they don't sum to 1
|
|
130
132
|
// (PMX models occasionally ship with unnormalized weights on extras like hair tips).
|
|
131
133
|
// VS normalize on the outgoing normal is skipped — interpolation denormalizes it
|
|
132
134
|
// anyway and every fragment shader does `normalize(input.normal)` as its first line.
|
|
133
|
-
export const COMMON_VS_WGSL = /* wgsl */ `
|
|
134
|
-
|
|
135
|
-
@vertex fn vs(
|
|
136
|
-
@location(0) position: vec3f,
|
|
137
|
-
@location(1) normal: vec3f,
|
|
138
|
-
@location(2) uv: vec2f,
|
|
139
|
-
@location(3) joints0: vec4<u32>,
|
|
140
|
-
@location(4) weights0: vec4<f32>
|
|
141
|
-
) -> VertexOutput {
|
|
142
|
-
var output: VertexOutput;
|
|
143
|
-
let pos4 = vec4f(position, 1.0);
|
|
144
|
-
let weightSum = weights0.x + weights0.y + weights0.z + weights0.w;
|
|
145
|
-
let invWeightSum = select(1.0, 1.0 / weightSum, weightSum > 0.0001);
|
|
146
|
-
let nw = select(vec4f(1.0, 0.0, 0.0, 0.0), weights0 * invWeightSum, weightSum > 0.0001);
|
|
147
|
-
var skinnedPos = vec4f(0.0);
|
|
148
|
-
var skinnedNrm = vec3f(0.0);
|
|
149
|
-
for (var i = 0u; i < 4u; i++) {
|
|
150
|
-
let m = skinMats[joints0[i]];
|
|
151
|
-
let w = nw[i];
|
|
152
|
-
skinnedPos += (m * pos4) * w;
|
|
153
|
-
skinnedNrm += (mat3x3f(m[0].xyz, m[1].xyz, m[2].xyz) * normal) * w;
|
|
154
|
-
}
|
|
155
|
-
output.position = camera.projection * camera.view * vec4f(skinnedPos.xyz, 1.0);
|
|
156
|
-
output.normal = skinnedNrm;
|
|
157
|
-
output.uv = uv;
|
|
158
|
-
output.worldPos = skinnedPos.xyz;
|
|
159
|
-
output.restPos = position;
|
|
160
|
-
return output;
|
|
161
|
-
}
|
|
162
|
-
|
|
135
|
+
export const COMMON_VS_WGSL = /* wgsl */ `
|
|
136
|
+
|
|
137
|
+
@vertex fn vs(
|
|
138
|
+
@location(0) position: vec3f,
|
|
139
|
+
@location(1) normal: vec3f,
|
|
140
|
+
@location(2) uv: vec2f,
|
|
141
|
+
@location(3) joints0: vec4<u32>,
|
|
142
|
+
@location(4) weights0: vec4<f32>
|
|
143
|
+
) -> VertexOutput {
|
|
144
|
+
var output: VertexOutput;
|
|
145
|
+
let pos4 = vec4f(position, 1.0);
|
|
146
|
+
let weightSum = weights0.x + weights0.y + weights0.z + weights0.w;
|
|
147
|
+
let invWeightSum = select(1.0, 1.0 / weightSum, weightSum > 0.0001);
|
|
148
|
+
let nw = select(vec4f(1.0, 0.0, 0.0, 0.0), weights0 * invWeightSum, weightSum > 0.0001);
|
|
149
|
+
var skinnedPos = vec4f(0.0);
|
|
150
|
+
var skinnedNrm = vec3f(0.0);
|
|
151
|
+
for (var i = 0u; i < 4u; i++) {
|
|
152
|
+
let m = skinMats[joints0[i]];
|
|
153
|
+
let w = nw[i];
|
|
154
|
+
skinnedPos += (m * pos4) * w;
|
|
155
|
+
skinnedNrm += (mat3x3f(m[0].xyz, m[1].xyz, m[2].xyz) * normal) * w;
|
|
156
|
+
}
|
|
157
|
+
output.position = camera.projection * camera.view * vec4f(skinnedPos.xyz, 1.0);
|
|
158
|
+
output.normal = skinnedNrm;
|
|
159
|
+
output.uv = uv;
|
|
160
|
+
output.worldPos = skinnedPos.xyz;
|
|
161
|
+
output.restPos = position;
|
|
162
|
+
return output;
|
|
163
|
+
}
|
|
164
|
+
|
|
163
165
|
`;
|
|
164
166
|
// ─── FS output struct ───────────────────────────────────────────────
|
|
165
167
|
// Location 0: final radiance+alpha (blended into rg11b10ufloat; the HDR target
|
|
@@ -180,13 +182,13 @@ export const COMMON_VS_WGSL = /* wgsl */ `
|
|
|
180
182
|
// src-alpha blending enabled:
|
|
181
183
|
// out.r = mask_r · src.a + dst.r · (1-src.a) (bloom mask, weighted by alpha)
|
|
182
184
|
// out.g = 1.0 · src.a + dst.g · (1-src.a) (canonical premultiplied alpha-over)
|
|
183
|
-
export const COMMON_FS_OUT_WGSL = /* wgsl */ `
|
|
184
|
-
|
|
185
|
-
struct FSOut {
|
|
186
|
-
@location(0) color: vec4f,
|
|
187
|
-
@location(1) mask: vec4f,
|
|
188
|
-
};
|
|
189
|
-
|
|
185
|
+
export const COMMON_FS_OUT_WGSL = /* wgsl */ `
|
|
186
|
+
|
|
187
|
+
struct FSOut {
|
|
188
|
+
@location(0) color: vec4f,
|
|
189
|
+
@location(1) mask: vec4f,
|
|
190
|
+
};
|
|
191
|
+
|
|
190
192
|
`;
|
|
191
193
|
// ─── Convenience: full shared prelude ───────────────────────────────
|
|
192
194
|
// Material files compose this as `${NODES_WGSL}${COMMON_MATERIAL_PRELUDE_WGSL}` to
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
export declare const TRANSPARENT_DEPTH_PREPASS_WGSL = "\nstruct CameraUniforms { view: mat4x4f, projection: mat4x4f, viewPos: vec3f, _p: f32, };\nstruct MaterialUniforms {\n diffuseColor: vec3f,\n alpha: f32,\n};\n\n@group(0) @binding(0) var<uniform> camera: CameraUniforms;\n@group(0) @binding(2) var diffuseSampler: sampler;\n@group(1) @binding(0) var<storage, read> skinMats: array<mat4x4f>;\n@group(2) @binding(0) var diffuseTexture: texture_2d<f32>;\n@group(2) @binding(1) var<uniform> material: MaterialUniforms;\n\nstruct VSOut {\n @builtin(position) position: vec4f,\n @location(0) uv: vec2f,\n};\n\n@vertex fn vs(\n @location(0) position: vec3f,\n @location(1) normal: vec3f,\n @location(2) uv: vec2f,\n @location(3) joints0: vec4<u32>,\n @location(4) weights0: vec4<f32>,\n) -> VSOut {\n let pos4 = vec4f(position, 1.0);\n let weightSum = weights0.x + weights0.y + weights0.z + weights0.w;\n let invWeightSum = select(1.0, 1.0 / weightSum, weightSum > 0.0001);\n let w = select(vec4f(1.0, 0.0, 0.0, 0.0), weights0 * invWeightSum, weightSum > 0.0001);\n var skinned = vec4f(0.0);\n for (var i = 0u; i < 4u; i++) {\n skinned += (skinMats[joints0[i]] * pos4) * w[i];\n }\n var o: VSOut;\n o.position = camera.projection * camera.view * vec4f(skinned.xyz, 1.0);\n o.uv = uv;\n return o;\n}\n\n@fragment fn fs(in: VSOut) {\n let a = material.alpha * textureSample(diffuseTexture, diffuseSampler, in.uv).a;\n if (a < 0.5) { discard; }\n}\n";
|
|
2
|
+
//# sourceMappingURL=depth-prepass.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"depth-prepass.d.ts","sourceRoot":"","sources":["../../../src/shaders/passes/depth-prepass.ts"],"names":[],"mappings":"AAaA,eAAO,MAAM,8BAA8B,04CA2C1C,CAAA"}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
// Depth-only prepass for the TRANSPARENT bucket. Transparent color draws keep
|
|
2
|
+
// depth write OFF so self-overlapping sheer cloth blends both layers instead of
|
|
3
|
+
// a triangle-order patchwork — but that leaves no depth record, so anything
|
|
4
|
+
// drawn later (the outline hulls) shows straight through the fabric as black
|
|
5
|
+
// shapes. This pass re-draws the transparent geometry depth-only AFTER its
|
|
6
|
+
// color pass: solid-enough texels (alpha ≥ 0.5) write depth, so outlines get
|
|
7
|
+
// occluded behind fabric exactly like they are behind opaque cloth, while
|
|
8
|
+
// truly sheer texels (a veil) stay non-occluding.
|
|
9
|
+
//
|
|
10
|
+
// Reuses mainPipelineLayout: camera g0b0, diffuseSampler g0b2, skinMats g1b0,
|
|
11
|
+
// diffuse texture g2b0, material uniforms g2b1 — the same bind groups the
|
|
12
|
+
// color draws already set, so drawing it costs no extra binding work.
|
|
13
|
+
export const TRANSPARENT_DEPTH_PREPASS_WGSL = /* wgsl */ `
|
|
14
|
+
struct CameraUniforms { view: mat4x4f, projection: mat4x4f, viewPos: vec3f, _p: f32, };
|
|
15
|
+
struct MaterialUniforms {
|
|
16
|
+
diffuseColor: vec3f,
|
|
17
|
+
alpha: f32,
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
@group(0) @binding(0) var<uniform> camera: CameraUniforms;
|
|
21
|
+
@group(0) @binding(2) var diffuseSampler: sampler;
|
|
22
|
+
@group(1) @binding(0) var<storage, read> skinMats: array<mat4x4f>;
|
|
23
|
+
@group(2) @binding(0) var diffuseTexture: texture_2d<f32>;
|
|
24
|
+
@group(2) @binding(1) var<uniform> material: MaterialUniforms;
|
|
25
|
+
|
|
26
|
+
struct VSOut {
|
|
27
|
+
@builtin(position) position: vec4f,
|
|
28
|
+
@location(0) uv: vec2f,
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
@vertex fn vs(
|
|
32
|
+
@location(0) position: vec3f,
|
|
33
|
+
@location(1) normal: vec3f,
|
|
34
|
+
@location(2) uv: vec2f,
|
|
35
|
+
@location(3) joints0: vec4<u32>,
|
|
36
|
+
@location(4) weights0: vec4<f32>,
|
|
37
|
+
) -> VSOut {
|
|
38
|
+
let pos4 = vec4f(position, 1.0);
|
|
39
|
+
let weightSum = weights0.x + weights0.y + weights0.z + weights0.w;
|
|
40
|
+
let invWeightSum = select(1.0, 1.0 / weightSum, weightSum > 0.0001);
|
|
41
|
+
let w = select(vec4f(1.0, 0.0, 0.0, 0.0), weights0 * invWeightSum, weightSum > 0.0001);
|
|
42
|
+
var skinned = vec4f(0.0);
|
|
43
|
+
for (var i = 0u; i < 4u; i++) {
|
|
44
|
+
skinned += (skinMats[joints0[i]] * pos4) * w[i];
|
|
45
|
+
}
|
|
46
|
+
var o: VSOut;
|
|
47
|
+
o.position = camera.projection * camera.view * vec4f(skinned.xyz, 1.0);
|
|
48
|
+
o.uv = uv;
|
|
49
|
+
return o;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
@fragment fn fs(in: VSOut) {
|
|
53
|
+
let a = material.alpha * textureSample(diffuseTexture, diffuseSampler, in.uv).a;
|
|
54
|
+
if (a < 0.5) { discard; }
|
|
55
|
+
}
|
|
56
|
+
`;
|