reze-engine 0.2.3 → 0.2.4
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 +104 -104
- package/dist/engine.d.ts +0 -2
- package/dist/engine.d.ts.map +1 -1
- package/dist/engine.js +393 -388
- package/package.json +1 -1
- package/src/camera.ts +358 -358
- package/src/engine.ts +2475 -2472
- package/src/math.ts +546 -546
- package/src/model.ts +421 -421
- package/src/pmx-loader.ts +1054 -1054
- package/src/vmd-loader.ts +179 -179
package/dist/engine.js
CHANGED
|
@@ -50,10 +50,8 @@ export class Engine {
|
|
|
50
50
|
this.canvas = canvas;
|
|
51
51
|
if (options) {
|
|
52
52
|
this.ambient = options.ambient ?? 1.0;
|
|
53
|
-
this.bloomThreshold = options.bloomThreshold ?? 0.3;
|
|
54
53
|
this.bloomIntensity = options.bloomIntensity ?? 0.12;
|
|
55
54
|
this.rimLightIntensity = options.rimLightIntensity ?? 0.45;
|
|
56
|
-
this.rimLightPower = options.rimLightPower ?? 2.0;
|
|
57
55
|
}
|
|
58
56
|
}
|
|
59
57
|
// Step 1: Get WebGPU device and context
|
|
@@ -92,125 +90,125 @@ export class Engine {
|
|
|
92
90
|
});
|
|
93
91
|
const shaderModule = this.device.createShaderModule({
|
|
94
92
|
label: "model shaders",
|
|
95
|
-
code: /* wgsl */ `
|
|
96
|
-
struct CameraUniforms {
|
|
97
|
-
view: mat4x4f,
|
|
98
|
-
projection: mat4x4f,
|
|
99
|
-
viewPos: vec3f,
|
|
100
|
-
_padding: f32,
|
|
101
|
-
};
|
|
102
|
-
|
|
103
|
-
struct Light {
|
|
104
|
-
direction: vec3f,
|
|
105
|
-
_padding1: f32,
|
|
106
|
-
color: vec3f,
|
|
107
|
-
intensity: f32,
|
|
108
|
-
};
|
|
109
|
-
|
|
110
|
-
struct LightUniforms {
|
|
111
|
-
ambient: f32,
|
|
112
|
-
lightCount: f32,
|
|
113
|
-
_padding1: f32,
|
|
114
|
-
_padding2: f32,
|
|
115
|
-
lights: array<Light, 4>,
|
|
116
|
-
};
|
|
117
|
-
|
|
118
|
-
struct MaterialUniforms {
|
|
119
|
-
alpha: f32,
|
|
120
|
-
alphaMultiplier: f32,
|
|
121
|
-
rimIntensity: f32,
|
|
122
|
-
rimPower: f32,
|
|
123
|
-
rimColor: vec3f,
|
|
124
|
-
isOverEyes: f32, // 1.0 if rendering over eyes, 0.0 otherwise
|
|
125
|
-
};
|
|
126
|
-
|
|
127
|
-
struct VertexOutput {
|
|
128
|
-
@builtin(position) position: vec4f,
|
|
129
|
-
@location(0) normal: vec3f,
|
|
130
|
-
@location(1) uv: vec2f,
|
|
131
|
-
@location(2) worldPos: vec3f,
|
|
132
|
-
};
|
|
133
|
-
|
|
134
|
-
@group(0) @binding(0) var<uniform> camera: CameraUniforms;
|
|
135
|
-
@group(0) @binding(1) var<uniform> light: LightUniforms;
|
|
136
|
-
@group(0) @binding(2) var diffuseTexture: texture_2d<f32>;
|
|
137
|
-
@group(0) @binding(3) var diffuseSampler: sampler;
|
|
138
|
-
@group(0) @binding(4) var<storage, read> skinMats: array<mat4x4f>;
|
|
139
|
-
@group(0) @binding(5) var toonTexture: texture_2d<f32>;
|
|
140
|
-
@group(0) @binding(6) var toonSampler: sampler;
|
|
141
|
-
@group(0) @binding(7) var<uniform> material: MaterialUniforms;
|
|
142
|
-
|
|
143
|
-
@vertex fn vs(
|
|
144
|
-
@location(0) position: vec3f,
|
|
145
|
-
@location(1) normal: vec3f,
|
|
146
|
-
@location(2) uv: vec2f,
|
|
147
|
-
@location(3) joints0: vec4<u32>,
|
|
148
|
-
@location(4) weights0: vec4<f32>
|
|
149
|
-
) -> VertexOutput {
|
|
150
|
-
var output: VertexOutput;
|
|
151
|
-
let pos4 = vec4f(position, 1.0);
|
|
152
|
-
|
|
153
|
-
// Normalize weights to ensure they sum to 1.0 (handles floating-point precision issues)
|
|
154
|
-
let weightSum = weights0.x + weights0.y + weights0.z + weights0.w;
|
|
155
|
-
var normalizedWeights: vec4f;
|
|
156
|
-
if (weightSum > 0.0001) {
|
|
157
|
-
normalizedWeights = weights0 / weightSum;
|
|
158
|
-
} else {
|
|
159
|
-
normalizedWeights = vec4f(1.0, 0.0, 0.0, 0.0);
|
|
160
|
-
}
|
|
161
|
-
|
|
162
|
-
var skinnedPos = vec4f(0.0, 0.0, 0.0, 0.0);
|
|
163
|
-
var skinnedNrm = vec3f(0.0, 0.0, 0.0);
|
|
164
|
-
for (var i = 0u; i < 4u; i++) {
|
|
165
|
-
let j = joints0[i];
|
|
166
|
-
let w = normalizedWeights[i];
|
|
167
|
-
let m = skinMats[j];
|
|
168
|
-
skinnedPos += (m * pos4) * w;
|
|
169
|
-
let r3 = mat3x3f(m[0].xyz, m[1].xyz, m[2].xyz);
|
|
170
|
-
skinnedNrm += (r3 * normal) * w;
|
|
171
|
-
}
|
|
172
|
-
let worldPos = skinnedPos.xyz;
|
|
173
|
-
output.position = camera.projection * camera.view * vec4f(worldPos, 1.0);
|
|
174
|
-
output.normal = normalize(skinnedNrm);
|
|
175
|
-
output.uv = uv;
|
|
176
|
-
output.worldPos = worldPos;
|
|
177
|
-
return output;
|
|
178
|
-
}
|
|
179
|
-
|
|
180
|
-
@fragment fn fs(input: VertexOutput) -> @location(0) vec4f {
|
|
181
|
-
let n = normalize(input.normal);
|
|
182
|
-
let albedo = textureSample(diffuseTexture, diffuseSampler, input.uv).rgb;
|
|
183
|
-
|
|
184
|
-
var lightAccum = vec3f(light.ambient);
|
|
185
|
-
let numLights = u32(light.lightCount);
|
|
186
|
-
for (var i = 0u; i < numLights; i++) {
|
|
187
|
-
let l = -light.lights[i].direction;
|
|
188
|
-
let nDotL = max(dot(n, l), 0.0);
|
|
189
|
-
let toonUV = vec2f(nDotL, 0.5);
|
|
190
|
-
let toonFactor = textureSample(toonTexture, toonSampler, toonUV).rgb;
|
|
191
|
-
let radiance = light.lights[i].color * light.lights[i].intensity;
|
|
192
|
-
lightAccum += toonFactor * radiance * nDotL;
|
|
193
|
-
}
|
|
194
|
-
|
|
195
|
-
// Rim light calculation
|
|
196
|
-
let viewDir = normalize(camera.viewPos - input.worldPos);
|
|
197
|
-
var rimFactor = 1.0 - max(dot(n, viewDir), 0.0);
|
|
198
|
-
rimFactor = pow(rimFactor, material.rimPower);
|
|
199
|
-
let rimLight = material.rimColor * material.rimIntensity * rimFactor;
|
|
200
|
-
|
|
201
|
-
let color = albedo * lightAccum + rimLight;
|
|
202
|
-
|
|
203
|
-
var finalAlpha = material.alpha * material.alphaMultiplier;
|
|
204
|
-
if (material.isOverEyes > 0.5) {
|
|
205
|
-
finalAlpha *= 0.5; // Hair over eyes gets 50% alpha
|
|
206
|
-
}
|
|
207
|
-
|
|
208
|
-
if (finalAlpha < 0.001) {
|
|
209
|
-
discard;
|
|
210
|
-
}
|
|
211
|
-
|
|
212
|
-
return vec4f(clamp(color, vec3f(0.0), vec3f(1.0)), finalAlpha);
|
|
213
|
-
}
|
|
93
|
+
code: /* wgsl */ `
|
|
94
|
+
struct CameraUniforms {
|
|
95
|
+
view: mat4x4f,
|
|
96
|
+
projection: mat4x4f,
|
|
97
|
+
viewPos: vec3f,
|
|
98
|
+
_padding: f32,
|
|
99
|
+
};
|
|
100
|
+
|
|
101
|
+
struct Light {
|
|
102
|
+
direction: vec3f,
|
|
103
|
+
_padding1: f32,
|
|
104
|
+
color: vec3f,
|
|
105
|
+
intensity: f32,
|
|
106
|
+
};
|
|
107
|
+
|
|
108
|
+
struct LightUniforms {
|
|
109
|
+
ambient: f32,
|
|
110
|
+
lightCount: f32,
|
|
111
|
+
_padding1: f32,
|
|
112
|
+
_padding2: f32,
|
|
113
|
+
lights: array<Light, 4>,
|
|
114
|
+
};
|
|
115
|
+
|
|
116
|
+
struct MaterialUniforms {
|
|
117
|
+
alpha: f32,
|
|
118
|
+
alphaMultiplier: f32,
|
|
119
|
+
rimIntensity: f32,
|
|
120
|
+
rimPower: f32,
|
|
121
|
+
rimColor: vec3f,
|
|
122
|
+
isOverEyes: f32, // 1.0 if rendering over eyes, 0.0 otherwise
|
|
123
|
+
};
|
|
124
|
+
|
|
125
|
+
struct VertexOutput {
|
|
126
|
+
@builtin(position) position: vec4f,
|
|
127
|
+
@location(0) normal: vec3f,
|
|
128
|
+
@location(1) uv: vec2f,
|
|
129
|
+
@location(2) worldPos: vec3f,
|
|
130
|
+
};
|
|
131
|
+
|
|
132
|
+
@group(0) @binding(0) var<uniform> camera: CameraUniforms;
|
|
133
|
+
@group(0) @binding(1) var<uniform> light: LightUniforms;
|
|
134
|
+
@group(0) @binding(2) var diffuseTexture: texture_2d<f32>;
|
|
135
|
+
@group(0) @binding(3) var diffuseSampler: sampler;
|
|
136
|
+
@group(0) @binding(4) var<storage, read> skinMats: array<mat4x4f>;
|
|
137
|
+
@group(0) @binding(5) var toonTexture: texture_2d<f32>;
|
|
138
|
+
@group(0) @binding(6) var toonSampler: sampler;
|
|
139
|
+
@group(0) @binding(7) var<uniform> material: MaterialUniforms;
|
|
140
|
+
|
|
141
|
+
@vertex fn vs(
|
|
142
|
+
@location(0) position: vec3f,
|
|
143
|
+
@location(1) normal: vec3f,
|
|
144
|
+
@location(2) uv: vec2f,
|
|
145
|
+
@location(3) joints0: vec4<u32>,
|
|
146
|
+
@location(4) weights0: vec4<f32>
|
|
147
|
+
) -> VertexOutput {
|
|
148
|
+
var output: VertexOutput;
|
|
149
|
+
let pos4 = vec4f(position, 1.0);
|
|
150
|
+
|
|
151
|
+
// Normalize weights to ensure they sum to 1.0 (handles floating-point precision issues)
|
|
152
|
+
let weightSum = weights0.x + weights0.y + weights0.z + weights0.w;
|
|
153
|
+
var normalizedWeights: vec4f;
|
|
154
|
+
if (weightSum > 0.0001) {
|
|
155
|
+
normalizedWeights = weights0 / weightSum;
|
|
156
|
+
} else {
|
|
157
|
+
normalizedWeights = vec4f(1.0, 0.0, 0.0, 0.0);
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
var skinnedPos = vec4f(0.0, 0.0, 0.0, 0.0);
|
|
161
|
+
var skinnedNrm = vec3f(0.0, 0.0, 0.0);
|
|
162
|
+
for (var i = 0u; i < 4u; i++) {
|
|
163
|
+
let j = joints0[i];
|
|
164
|
+
let w = normalizedWeights[i];
|
|
165
|
+
let m = skinMats[j];
|
|
166
|
+
skinnedPos += (m * pos4) * w;
|
|
167
|
+
let r3 = mat3x3f(m[0].xyz, m[1].xyz, m[2].xyz);
|
|
168
|
+
skinnedNrm += (r3 * normal) * w;
|
|
169
|
+
}
|
|
170
|
+
let worldPos = skinnedPos.xyz;
|
|
171
|
+
output.position = camera.projection * camera.view * vec4f(worldPos, 1.0);
|
|
172
|
+
output.normal = normalize(skinnedNrm);
|
|
173
|
+
output.uv = uv;
|
|
174
|
+
output.worldPos = worldPos;
|
|
175
|
+
return output;
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
@fragment fn fs(input: VertexOutput) -> @location(0) vec4f {
|
|
179
|
+
let n = normalize(input.normal);
|
|
180
|
+
let albedo = textureSample(diffuseTexture, diffuseSampler, input.uv).rgb;
|
|
181
|
+
|
|
182
|
+
var lightAccum = vec3f(light.ambient);
|
|
183
|
+
let numLights = u32(light.lightCount);
|
|
184
|
+
for (var i = 0u; i < numLights; i++) {
|
|
185
|
+
let l = -light.lights[i].direction;
|
|
186
|
+
let nDotL = max(dot(n, l), 0.0);
|
|
187
|
+
let toonUV = vec2f(nDotL, 0.5);
|
|
188
|
+
let toonFactor = textureSample(toonTexture, toonSampler, toonUV).rgb;
|
|
189
|
+
let radiance = light.lights[i].color * light.lights[i].intensity;
|
|
190
|
+
lightAccum += toonFactor * radiance * nDotL;
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
// Rim light calculation
|
|
194
|
+
let viewDir = normalize(camera.viewPos - input.worldPos);
|
|
195
|
+
var rimFactor = 1.0 - max(dot(n, viewDir), 0.0);
|
|
196
|
+
rimFactor = pow(rimFactor, material.rimPower);
|
|
197
|
+
let rimLight = material.rimColor * material.rimIntensity * rimFactor;
|
|
198
|
+
|
|
199
|
+
let color = albedo * lightAccum + rimLight;
|
|
200
|
+
|
|
201
|
+
var finalAlpha = material.alpha * material.alphaMultiplier;
|
|
202
|
+
if (material.isOverEyes > 0.5) {
|
|
203
|
+
finalAlpha *= 0.5; // Hair over eyes gets 50% alpha
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
if (finalAlpha < 0.001) {
|
|
207
|
+
discard;
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
return vec4f(clamp(color, vec3f(0.0), vec3f(1.0)), finalAlpha);
|
|
211
|
+
}
|
|
214
212
|
`,
|
|
215
213
|
});
|
|
216
214
|
// Create explicit bind group layout for all pipelines using the main shader
|
|
@@ -301,77 +299,77 @@ export class Engine {
|
|
|
301
299
|
});
|
|
302
300
|
const outlineShaderModule = this.device.createShaderModule({
|
|
303
301
|
label: "outline shaders",
|
|
304
|
-
code: /* wgsl */ `
|
|
305
|
-
struct CameraUniforms {
|
|
306
|
-
view: mat4x4f,
|
|
307
|
-
projection: mat4x4f,
|
|
308
|
-
viewPos: vec3f,
|
|
309
|
-
_padding: f32,
|
|
310
|
-
};
|
|
311
|
-
|
|
312
|
-
struct MaterialUniforms {
|
|
313
|
-
edgeColor: vec4f,
|
|
314
|
-
edgeSize: f32,
|
|
315
|
-
isOverEyes: f32, // 1.0 if rendering over eyes, 0.0 otherwise (for hair outlines)
|
|
316
|
-
_padding1: f32,
|
|
317
|
-
_padding2: f32,
|
|
318
|
-
};
|
|
319
|
-
|
|
320
|
-
@group(0) @binding(0) var<uniform> camera: CameraUniforms;
|
|
321
|
-
@group(0) @binding(1) var<uniform> material: MaterialUniforms;
|
|
322
|
-
@group(0) @binding(2) var<storage, read> skinMats: array<mat4x4f>;
|
|
323
|
-
|
|
324
|
-
struct VertexOutput {
|
|
325
|
-
@builtin(position) position: vec4f,
|
|
326
|
-
};
|
|
327
|
-
|
|
328
|
-
@vertex fn vs(
|
|
329
|
-
@location(0) position: vec3f,
|
|
330
|
-
@location(1) normal: vec3f,
|
|
331
|
-
@location(3) joints0: vec4<u32>,
|
|
332
|
-
@location(4) weights0: vec4<f32>
|
|
333
|
-
) -> VertexOutput {
|
|
334
|
-
var output: VertexOutput;
|
|
335
|
-
let pos4 = vec4f(position, 1.0);
|
|
336
|
-
|
|
337
|
-
// Normalize weights to ensure they sum to 1.0 (handles floating-point precision issues)
|
|
338
|
-
let weightSum = weights0.x + weights0.y + weights0.z + weights0.w;
|
|
339
|
-
var normalizedWeights: vec4f;
|
|
340
|
-
if (weightSum > 0.0001) {
|
|
341
|
-
normalizedWeights = weights0 / weightSum;
|
|
342
|
-
} else {
|
|
343
|
-
normalizedWeights = vec4f(1.0, 0.0, 0.0, 0.0);
|
|
344
|
-
}
|
|
345
|
-
|
|
346
|
-
var skinnedPos = vec4f(0.0, 0.0, 0.0, 0.0);
|
|
347
|
-
var skinnedNrm = vec3f(0.0, 0.0, 0.0);
|
|
348
|
-
for (var i = 0u; i < 4u; i++) {
|
|
349
|
-
let j = joints0[i];
|
|
350
|
-
let w = normalizedWeights[i];
|
|
351
|
-
let m = skinMats[j];
|
|
352
|
-
skinnedPos += (m * pos4) * w;
|
|
353
|
-
let r3 = mat3x3f(m[0].xyz, m[1].xyz, m[2].xyz);
|
|
354
|
-
skinnedNrm += (r3 * normal) * w;
|
|
355
|
-
}
|
|
356
|
-
let worldPos = skinnedPos.xyz;
|
|
357
|
-
let worldNormal = normalize(skinnedNrm);
|
|
358
|
-
|
|
359
|
-
// MMD invert hull: expand vertices outward along normals
|
|
360
|
-
let scaleFactor = 0.01;
|
|
361
|
-
let expandedPos = worldPos + worldNormal * material.edgeSize * scaleFactor;
|
|
362
|
-
output.position = camera.projection * camera.view * vec4f(expandedPos, 1.0);
|
|
363
|
-
return output;
|
|
364
|
-
}
|
|
365
|
-
|
|
366
|
-
@fragment fn fs() -> @location(0) vec4f {
|
|
367
|
-
var color = material.edgeColor;
|
|
368
|
-
|
|
369
|
-
if (material.isOverEyes > 0.5) {
|
|
370
|
-
color.a *= 0.5; // Hair outlines over eyes get 50% alpha
|
|
371
|
-
}
|
|
372
|
-
|
|
373
|
-
return color;
|
|
374
|
-
}
|
|
302
|
+
code: /* wgsl */ `
|
|
303
|
+
struct CameraUniforms {
|
|
304
|
+
view: mat4x4f,
|
|
305
|
+
projection: mat4x4f,
|
|
306
|
+
viewPos: vec3f,
|
|
307
|
+
_padding: f32,
|
|
308
|
+
};
|
|
309
|
+
|
|
310
|
+
struct MaterialUniforms {
|
|
311
|
+
edgeColor: vec4f,
|
|
312
|
+
edgeSize: f32,
|
|
313
|
+
isOverEyes: f32, // 1.0 if rendering over eyes, 0.0 otherwise (for hair outlines)
|
|
314
|
+
_padding1: f32,
|
|
315
|
+
_padding2: f32,
|
|
316
|
+
};
|
|
317
|
+
|
|
318
|
+
@group(0) @binding(0) var<uniform> camera: CameraUniforms;
|
|
319
|
+
@group(0) @binding(1) var<uniform> material: MaterialUniforms;
|
|
320
|
+
@group(0) @binding(2) var<storage, read> skinMats: array<mat4x4f>;
|
|
321
|
+
|
|
322
|
+
struct VertexOutput {
|
|
323
|
+
@builtin(position) position: vec4f,
|
|
324
|
+
};
|
|
325
|
+
|
|
326
|
+
@vertex fn vs(
|
|
327
|
+
@location(0) position: vec3f,
|
|
328
|
+
@location(1) normal: vec3f,
|
|
329
|
+
@location(3) joints0: vec4<u32>,
|
|
330
|
+
@location(4) weights0: vec4<f32>
|
|
331
|
+
) -> VertexOutput {
|
|
332
|
+
var output: VertexOutput;
|
|
333
|
+
let pos4 = vec4f(position, 1.0);
|
|
334
|
+
|
|
335
|
+
// Normalize weights to ensure they sum to 1.0 (handles floating-point precision issues)
|
|
336
|
+
let weightSum = weights0.x + weights0.y + weights0.z + weights0.w;
|
|
337
|
+
var normalizedWeights: vec4f;
|
|
338
|
+
if (weightSum > 0.0001) {
|
|
339
|
+
normalizedWeights = weights0 / weightSum;
|
|
340
|
+
} else {
|
|
341
|
+
normalizedWeights = vec4f(1.0, 0.0, 0.0, 0.0);
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
var skinnedPos = vec4f(0.0, 0.0, 0.0, 0.0);
|
|
345
|
+
var skinnedNrm = vec3f(0.0, 0.0, 0.0);
|
|
346
|
+
for (var i = 0u; i < 4u; i++) {
|
|
347
|
+
let j = joints0[i];
|
|
348
|
+
let w = normalizedWeights[i];
|
|
349
|
+
let m = skinMats[j];
|
|
350
|
+
skinnedPos += (m * pos4) * w;
|
|
351
|
+
let r3 = mat3x3f(m[0].xyz, m[1].xyz, m[2].xyz);
|
|
352
|
+
skinnedNrm += (r3 * normal) * w;
|
|
353
|
+
}
|
|
354
|
+
let worldPos = skinnedPos.xyz;
|
|
355
|
+
let worldNormal = normalize(skinnedNrm);
|
|
356
|
+
|
|
357
|
+
// MMD invert hull: expand vertices outward along normals
|
|
358
|
+
let scaleFactor = 0.01;
|
|
359
|
+
let expandedPos = worldPos + worldNormal * material.edgeSize * scaleFactor;
|
|
360
|
+
output.position = camera.projection * camera.view * vec4f(expandedPos, 1.0);
|
|
361
|
+
return output;
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
@fragment fn fs() -> @location(0) vec4f {
|
|
365
|
+
var color = material.edgeColor;
|
|
366
|
+
|
|
367
|
+
if (material.isOverEyes > 0.5) {
|
|
368
|
+
color.a *= 0.5; // Hair outlines over eyes get 50% alpha
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
return color;
|
|
372
|
+
}
|
|
375
373
|
`,
|
|
376
374
|
});
|
|
377
375
|
this.outlinePipeline = this.device.createRenderPipeline({
|
|
@@ -572,49 +570,49 @@ export class Engine {
|
|
|
572
570
|
// Depth-only shader for hair pre-pass (reduces overdraw by early depth rejection)
|
|
573
571
|
const depthOnlyShaderModule = this.device.createShaderModule({
|
|
574
572
|
label: "depth only shader",
|
|
575
|
-
code: /* wgsl */ `
|
|
576
|
-
struct CameraUniforms {
|
|
577
|
-
view: mat4x4f,
|
|
578
|
-
projection: mat4x4f,
|
|
579
|
-
viewPos: vec3f,
|
|
580
|
-
_padding: f32,
|
|
581
|
-
};
|
|
582
|
-
|
|
583
|
-
@group(0) @binding(0) var<uniform> camera: CameraUniforms;
|
|
584
|
-
@group(0) @binding(4) var<storage, read> skinMats: array<mat4x4f>;
|
|
585
|
-
|
|
586
|
-
@vertex fn vs(
|
|
587
|
-
@location(0) position: vec3f,
|
|
588
|
-
@location(1) normal: vec3f,
|
|
589
|
-
@location(3) joints0: vec4<u32>,
|
|
590
|
-
@location(4) weights0: vec4<f32>
|
|
591
|
-
) -> @builtin(position) vec4f {
|
|
592
|
-
let pos4 = vec4f(position, 1.0);
|
|
593
|
-
|
|
594
|
-
// Normalize weights
|
|
595
|
-
let weightSum = weights0.x + weights0.y + weights0.z + weights0.w;
|
|
596
|
-
var normalizedWeights: vec4f;
|
|
597
|
-
if (weightSum > 0.0001) {
|
|
598
|
-
normalizedWeights = weights0 / weightSum;
|
|
599
|
-
} else {
|
|
600
|
-
normalizedWeights = vec4f(1.0, 0.0, 0.0, 0.0);
|
|
601
|
-
}
|
|
602
|
-
|
|
603
|
-
var skinnedPos = vec4f(0.0, 0.0, 0.0, 0.0);
|
|
604
|
-
for (var i = 0u; i < 4u; i++) {
|
|
605
|
-
let j = joints0[i];
|
|
606
|
-
let w = normalizedWeights[i];
|
|
607
|
-
let m = skinMats[j];
|
|
608
|
-
skinnedPos += (m * pos4) * w;
|
|
609
|
-
}
|
|
610
|
-
let worldPos = skinnedPos.xyz;
|
|
611
|
-
let clipPos = camera.projection * camera.view * vec4f(worldPos, 1.0);
|
|
612
|
-
return clipPos;
|
|
613
|
-
}
|
|
614
|
-
|
|
615
|
-
@fragment fn fs() -> @location(0) vec4f {
|
|
616
|
-
return vec4f(0.0, 0.0, 0.0, 0.0); // Transparent - color writes disabled via writeMask
|
|
617
|
-
}
|
|
573
|
+
code: /* wgsl */ `
|
|
574
|
+
struct CameraUniforms {
|
|
575
|
+
view: mat4x4f,
|
|
576
|
+
projection: mat4x4f,
|
|
577
|
+
viewPos: vec3f,
|
|
578
|
+
_padding: f32,
|
|
579
|
+
};
|
|
580
|
+
|
|
581
|
+
@group(0) @binding(0) var<uniform> camera: CameraUniforms;
|
|
582
|
+
@group(0) @binding(4) var<storage, read> skinMats: array<mat4x4f>;
|
|
583
|
+
|
|
584
|
+
@vertex fn vs(
|
|
585
|
+
@location(0) position: vec3f,
|
|
586
|
+
@location(1) normal: vec3f,
|
|
587
|
+
@location(3) joints0: vec4<u32>,
|
|
588
|
+
@location(4) weights0: vec4<f32>
|
|
589
|
+
) -> @builtin(position) vec4f {
|
|
590
|
+
let pos4 = vec4f(position, 1.0);
|
|
591
|
+
|
|
592
|
+
// Normalize weights
|
|
593
|
+
let weightSum = weights0.x + weights0.y + weights0.z + weights0.w;
|
|
594
|
+
var normalizedWeights: vec4f;
|
|
595
|
+
if (weightSum > 0.0001) {
|
|
596
|
+
normalizedWeights = weights0 / weightSum;
|
|
597
|
+
} else {
|
|
598
|
+
normalizedWeights = vec4f(1.0, 0.0, 0.0, 0.0);
|
|
599
|
+
}
|
|
600
|
+
|
|
601
|
+
var skinnedPos = vec4f(0.0, 0.0, 0.0, 0.0);
|
|
602
|
+
for (var i = 0u; i < 4u; i++) {
|
|
603
|
+
let j = joints0[i];
|
|
604
|
+
let w = normalizedWeights[i];
|
|
605
|
+
let m = skinMats[j];
|
|
606
|
+
skinnedPos += (m * pos4) * w;
|
|
607
|
+
}
|
|
608
|
+
let worldPos = skinnedPos.xyz;
|
|
609
|
+
let clipPos = camera.projection * camera.view * vec4f(worldPos, 1.0);
|
|
610
|
+
return clipPos;
|
|
611
|
+
}
|
|
612
|
+
|
|
613
|
+
@fragment fn fs() -> @location(0) vec4f {
|
|
614
|
+
return vec4f(0.0, 0.0, 0.0, 0.0); // Transparent - color writes disabled via writeMask
|
|
615
|
+
}
|
|
618
616
|
`,
|
|
619
617
|
});
|
|
620
618
|
// Hair depth pre-pass pipeline: depth-only with color writes disabled to eliminate overdraw
|
|
@@ -794,31 +792,31 @@ export class Engine {
|
|
|
794
792
|
createSkinMatrixComputePipeline() {
|
|
795
793
|
const computeShader = this.device.createShaderModule({
|
|
796
794
|
label: "skin matrix compute",
|
|
797
|
-
code: /* wgsl */ `
|
|
798
|
-
struct BoneCountUniform {
|
|
799
|
-
count: u32,
|
|
800
|
-
_padding1: u32,
|
|
801
|
-
_padding2: u32,
|
|
802
|
-
_padding3: u32,
|
|
803
|
-
_padding4: vec4<u32>,
|
|
804
|
-
};
|
|
805
|
-
|
|
806
|
-
@group(0) @binding(0) var<uniform> boneCount: BoneCountUniform;
|
|
807
|
-
@group(0) @binding(1) var<storage, read> worldMatrices: array<mat4x4f>;
|
|
808
|
-
@group(0) @binding(2) var<storage, read> inverseBindMatrices: array<mat4x4f>;
|
|
809
|
-
@group(0) @binding(3) var<storage, read_write> skinMatrices: array<mat4x4f>;
|
|
810
|
-
|
|
811
|
-
@compute @workgroup_size(64)
|
|
812
|
-
fn main(@builtin(global_invocation_id) globalId: vec3<u32>) {
|
|
813
|
-
let boneIndex = globalId.x;
|
|
814
|
-
// Bounds check: we dispatch workgroups (64 threads each), so some threads may be out of range
|
|
815
|
-
if (boneIndex >= boneCount.count) {
|
|
816
|
-
return;
|
|
817
|
-
}
|
|
818
|
-
let worldMat = worldMatrices[boneIndex];
|
|
819
|
-
let invBindMat = inverseBindMatrices[boneIndex];
|
|
820
|
-
skinMatrices[boneIndex] = worldMat * invBindMat;
|
|
821
|
-
}
|
|
795
|
+
code: /* wgsl */ `
|
|
796
|
+
struct BoneCountUniform {
|
|
797
|
+
count: u32,
|
|
798
|
+
_padding1: u32,
|
|
799
|
+
_padding2: u32,
|
|
800
|
+
_padding3: u32,
|
|
801
|
+
_padding4: vec4<u32>,
|
|
802
|
+
};
|
|
803
|
+
|
|
804
|
+
@group(0) @binding(0) var<uniform> boneCount: BoneCountUniform;
|
|
805
|
+
@group(0) @binding(1) var<storage, read> worldMatrices: array<mat4x4f>;
|
|
806
|
+
@group(0) @binding(2) var<storage, read> inverseBindMatrices: array<mat4x4f>;
|
|
807
|
+
@group(0) @binding(3) var<storage, read_write> skinMatrices: array<mat4x4f>;
|
|
808
|
+
|
|
809
|
+
@compute @workgroup_size(64)
|
|
810
|
+
fn main(@builtin(global_invocation_id) globalId: vec3<u32>) {
|
|
811
|
+
let boneIndex = globalId.x;
|
|
812
|
+
// Bounds check: we dispatch workgroups (64 threads each), so some threads may be out of range
|
|
813
|
+
if (boneIndex >= boneCount.count) {
|
|
814
|
+
return;
|
|
815
|
+
}
|
|
816
|
+
let worldMat = worldMatrices[boneIndex];
|
|
817
|
+
let invBindMat = inverseBindMatrices[boneIndex];
|
|
818
|
+
skinMatrices[boneIndex] = worldMat * invBindMat;
|
|
819
|
+
}
|
|
822
820
|
`,
|
|
823
821
|
});
|
|
824
822
|
this.skinMatrixComputePipeline = this.device.createComputePipeline({
|
|
@@ -872,143 +870,143 @@ export class Engine {
|
|
|
872
870
|
// Bloom extraction shader (extracts bright areas)
|
|
873
871
|
const bloomExtractShader = this.device.createShaderModule({
|
|
874
872
|
label: "bloom extract",
|
|
875
|
-
code: /* wgsl */ `
|
|
876
|
-
struct VertexOutput {
|
|
877
|
-
@builtin(position) position: vec4f,
|
|
878
|
-
@location(0) uv: vec2f,
|
|
879
|
-
};
|
|
880
|
-
|
|
881
|
-
@vertex fn vs(@builtin(vertex_index) vertexIndex: u32) -> VertexOutput {
|
|
882
|
-
var output: VertexOutput;
|
|
883
|
-
// Generate fullscreen quad from vertex index
|
|
884
|
-
let x = f32((vertexIndex << 1u) & 2u) * 2.0 - 1.0;
|
|
885
|
-
let y = f32(vertexIndex & 2u) * 2.0 - 1.0;
|
|
886
|
-
output.position = vec4f(x, y, 0.0, 1.0);
|
|
887
|
-
output.uv = vec2f(x * 0.5 + 0.5, 1.0 - (y * 0.5 + 0.5));
|
|
888
|
-
return output;
|
|
889
|
-
}
|
|
890
|
-
|
|
891
|
-
struct BloomExtractUniforms {
|
|
892
|
-
threshold: f32,
|
|
893
|
-
_padding1: f32,
|
|
894
|
-
_padding2: f32,
|
|
895
|
-
_padding3: f32,
|
|
896
|
-
_padding4: f32,
|
|
897
|
-
_padding5: f32,
|
|
898
|
-
_padding6: f32,
|
|
899
|
-
_padding7: f32,
|
|
900
|
-
};
|
|
901
|
-
|
|
902
|
-
@group(0) @binding(0) var inputTexture: texture_2d<f32>;
|
|
903
|
-
@group(0) @binding(1) var inputSampler: sampler;
|
|
904
|
-
@group(0) @binding(2) var<uniform> extractUniforms: BloomExtractUniforms;
|
|
905
|
-
|
|
906
|
-
@fragment fn fs(input: VertexOutput) -> @location(0) vec4f {
|
|
907
|
-
let color = textureSample(inputTexture, inputSampler, input.uv);
|
|
908
|
-
// Extract bright areas above threshold
|
|
909
|
-
let threshold = extractUniforms.threshold;
|
|
910
|
-
let bloom = max(vec3f(0.0), color.rgb - vec3f(threshold)) / max(0.001, 1.0 - threshold);
|
|
911
|
-
return vec4f(bloom, color.a);
|
|
912
|
-
}
|
|
873
|
+
code: /* wgsl */ `
|
|
874
|
+
struct VertexOutput {
|
|
875
|
+
@builtin(position) position: vec4f,
|
|
876
|
+
@location(0) uv: vec2f,
|
|
877
|
+
};
|
|
878
|
+
|
|
879
|
+
@vertex fn vs(@builtin(vertex_index) vertexIndex: u32) -> VertexOutput {
|
|
880
|
+
var output: VertexOutput;
|
|
881
|
+
// Generate fullscreen quad from vertex index
|
|
882
|
+
let x = f32((vertexIndex << 1u) & 2u) * 2.0 - 1.0;
|
|
883
|
+
let y = f32(vertexIndex & 2u) * 2.0 - 1.0;
|
|
884
|
+
output.position = vec4f(x, y, 0.0, 1.0);
|
|
885
|
+
output.uv = vec2f(x * 0.5 + 0.5, 1.0 - (y * 0.5 + 0.5));
|
|
886
|
+
return output;
|
|
887
|
+
}
|
|
888
|
+
|
|
889
|
+
struct BloomExtractUniforms {
|
|
890
|
+
threshold: f32,
|
|
891
|
+
_padding1: f32,
|
|
892
|
+
_padding2: f32,
|
|
893
|
+
_padding3: f32,
|
|
894
|
+
_padding4: f32,
|
|
895
|
+
_padding5: f32,
|
|
896
|
+
_padding6: f32,
|
|
897
|
+
_padding7: f32,
|
|
898
|
+
};
|
|
899
|
+
|
|
900
|
+
@group(0) @binding(0) var inputTexture: texture_2d<f32>;
|
|
901
|
+
@group(0) @binding(1) var inputSampler: sampler;
|
|
902
|
+
@group(0) @binding(2) var<uniform> extractUniforms: BloomExtractUniforms;
|
|
903
|
+
|
|
904
|
+
@fragment fn fs(input: VertexOutput) -> @location(0) vec4f {
|
|
905
|
+
let color = textureSample(inputTexture, inputSampler, input.uv);
|
|
906
|
+
// Extract bright areas above threshold
|
|
907
|
+
let threshold = extractUniforms.threshold;
|
|
908
|
+
let bloom = max(vec3f(0.0), color.rgb - vec3f(threshold)) / max(0.001, 1.0 - threshold);
|
|
909
|
+
return vec4f(bloom, color.a);
|
|
910
|
+
}
|
|
913
911
|
`,
|
|
914
912
|
});
|
|
915
913
|
// Bloom blur shader (gaussian blur - can be used for both horizontal and vertical)
|
|
916
914
|
const bloomBlurShader = this.device.createShaderModule({
|
|
917
915
|
label: "bloom blur",
|
|
918
|
-
code: /* wgsl */ `
|
|
919
|
-
struct VertexOutput {
|
|
920
|
-
@builtin(position) position: vec4f,
|
|
921
|
-
@location(0) uv: vec2f,
|
|
922
|
-
};
|
|
923
|
-
|
|
924
|
-
@vertex fn vs(@builtin(vertex_index) vertexIndex: u32) -> VertexOutput {
|
|
925
|
-
var output: VertexOutput;
|
|
926
|
-
let x = f32((vertexIndex << 1u) & 2u) * 2.0 - 1.0;
|
|
927
|
-
let y = f32(vertexIndex & 2u) * 2.0 - 1.0;
|
|
928
|
-
output.position = vec4f(x, y, 0.0, 1.0);
|
|
929
|
-
output.uv = vec2f(x * 0.5 + 0.5, 1.0 - (y * 0.5 + 0.5));
|
|
930
|
-
return output;
|
|
931
|
-
}
|
|
932
|
-
|
|
933
|
-
struct BlurUniforms {
|
|
934
|
-
direction: vec2f,
|
|
935
|
-
_padding1: f32,
|
|
936
|
-
_padding2: f32,
|
|
937
|
-
_padding3: f32,
|
|
938
|
-
_padding4: f32,
|
|
939
|
-
_padding5: f32,
|
|
940
|
-
_padding6: f32,
|
|
941
|
-
};
|
|
942
|
-
|
|
943
|
-
@group(0) @binding(0) var inputTexture: texture_2d<f32>;
|
|
944
|
-
@group(0) @binding(1) var inputSampler: sampler;
|
|
945
|
-
@group(0) @binding(2) var<uniform> blurUniforms: BlurUniforms;
|
|
946
|
-
|
|
947
|
-
// 9-tap gaussian blur
|
|
948
|
-
@fragment fn fs(input: VertexOutput) -> @location(0) vec4f {
|
|
949
|
-
let texelSize = 1.0 / vec2f(textureDimensions(inputTexture));
|
|
950
|
-
var result = vec4f(0.0);
|
|
951
|
-
|
|
952
|
-
// Gaussian weights for 9-tap filter
|
|
953
|
-
let weights = array<f32, 9>(
|
|
954
|
-
0.01621622, 0.05405405, 0.12162162,
|
|
955
|
-
0.19459459, 0.22702703,
|
|
956
|
-
0.19459459, 0.12162162, 0.05405405, 0.01621622
|
|
957
|
-
);
|
|
958
|
-
|
|
959
|
-
let offsets = array<f32, 9>(-4.0, -3.0, -2.0, -1.0, 0.0, 1.0, 2.0, 3.0, 4.0);
|
|
960
|
-
|
|
961
|
-
for (var i = 0u; i < 9u; i++) {
|
|
962
|
-
let offset = offsets[i] * texelSize * blurUniforms.direction;
|
|
963
|
-
result += textureSample(inputTexture, inputSampler, input.uv + offset) * weights[i];
|
|
964
|
-
}
|
|
965
|
-
|
|
966
|
-
return result;
|
|
967
|
-
}
|
|
916
|
+
code: /* wgsl */ `
|
|
917
|
+
struct VertexOutput {
|
|
918
|
+
@builtin(position) position: vec4f,
|
|
919
|
+
@location(0) uv: vec2f,
|
|
920
|
+
};
|
|
921
|
+
|
|
922
|
+
@vertex fn vs(@builtin(vertex_index) vertexIndex: u32) -> VertexOutput {
|
|
923
|
+
var output: VertexOutput;
|
|
924
|
+
let x = f32((vertexIndex << 1u) & 2u) * 2.0 - 1.0;
|
|
925
|
+
let y = f32(vertexIndex & 2u) * 2.0 - 1.0;
|
|
926
|
+
output.position = vec4f(x, y, 0.0, 1.0);
|
|
927
|
+
output.uv = vec2f(x * 0.5 + 0.5, 1.0 - (y * 0.5 + 0.5));
|
|
928
|
+
return output;
|
|
929
|
+
}
|
|
930
|
+
|
|
931
|
+
struct BlurUniforms {
|
|
932
|
+
direction: vec2f,
|
|
933
|
+
_padding1: f32,
|
|
934
|
+
_padding2: f32,
|
|
935
|
+
_padding3: f32,
|
|
936
|
+
_padding4: f32,
|
|
937
|
+
_padding5: f32,
|
|
938
|
+
_padding6: f32,
|
|
939
|
+
};
|
|
940
|
+
|
|
941
|
+
@group(0) @binding(0) var inputTexture: texture_2d<f32>;
|
|
942
|
+
@group(0) @binding(1) var inputSampler: sampler;
|
|
943
|
+
@group(0) @binding(2) var<uniform> blurUniforms: BlurUniforms;
|
|
944
|
+
|
|
945
|
+
// 9-tap gaussian blur
|
|
946
|
+
@fragment fn fs(input: VertexOutput) -> @location(0) vec4f {
|
|
947
|
+
let texelSize = 1.0 / vec2f(textureDimensions(inputTexture));
|
|
948
|
+
var result = vec4f(0.0);
|
|
949
|
+
|
|
950
|
+
// Gaussian weights for 9-tap filter
|
|
951
|
+
let weights = array<f32, 9>(
|
|
952
|
+
0.01621622, 0.05405405, 0.12162162,
|
|
953
|
+
0.19459459, 0.22702703,
|
|
954
|
+
0.19459459, 0.12162162, 0.05405405, 0.01621622
|
|
955
|
+
);
|
|
956
|
+
|
|
957
|
+
let offsets = array<f32, 9>(-4.0, -3.0, -2.0, -1.0, 0.0, 1.0, 2.0, 3.0, 4.0);
|
|
958
|
+
|
|
959
|
+
for (var i = 0u; i < 9u; i++) {
|
|
960
|
+
let offset = offsets[i] * texelSize * blurUniforms.direction;
|
|
961
|
+
result += textureSample(inputTexture, inputSampler, input.uv + offset) * weights[i];
|
|
962
|
+
}
|
|
963
|
+
|
|
964
|
+
return result;
|
|
965
|
+
}
|
|
968
966
|
`,
|
|
969
967
|
});
|
|
970
968
|
// Bloom composition shader (combines original scene with bloom)
|
|
971
969
|
const bloomComposeShader = this.device.createShaderModule({
|
|
972
970
|
label: "bloom compose",
|
|
973
|
-
code: /* wgsl */ `
|
|
974
|
-
struct VertexOutput {
|
|
975
|
-
@builtin(position) position: vec4f,
|
|
976
|
-
@location(0) uv: vec2f,
|
|
977
|
-
};
|
|
978
|
-
|
|
979
|
-
@vertex fn vs(@builtin(vertex_index) vertexIndex: u32) -> VertexOutput {
|
|
980
|
-
var output: VertexOutput;
|
|
981
|
-
let x = f32((vertexIndex << 1u) & 2u) * 2.0 - 1.0;
|
|
982
|
-
let y = f32(vertexIndex & 2u) * 2.0 - 1.0;
|
|
983
|
-
output.position = vec4f(x, y, 0.0, 1.0);
|
|
984
|
-
output.uv = vec2f(x * 0.5 + 0.5, 1.0 - (y * 0.5 + 0.5));
|
|
985
|
-
return output;
|
|
986
|
-
}
|
|
987
|
-
|
|
988
|
-
struct BloomComposeUniforms {
|
|
989
|
-
intensity: f32,
|
|
990
|
-
_padding1: f32,
|
|
991
|
-
_padding2: f32,
|
|
992
|
-
_padding3: f32,
|
|
993
|
-
_padding4: f32,
|
|
994
|
-
_padding5: f32,
|
|
995
|
-
_padding6: f32,
|
|
996
|
-
_padding7: f32,
|
|
997
|
-
};
|
|
998
|
-
|
|
999
|
-
@group(0) @binding(0) var sceneTexture: texture_2d<f32>;
|
|
1000
|
-
@group(0) @binding(1) var sceneSampler: sampler;
|
|
1001
|
-
@group(0) @binding(2) var bloomTexture: texture_2d<f32>;
|
|
1002
|
-
@group(0) @binding(3) var bloomSampler: sampler;
|
|
1003
|
-
@group(0) @binding(4) var<uniform> composeUniforms: BloomComposeUniforms;
|
|
1004
|
-
|
|
1005
|
-
@fragment fn fs(input: VertexOutput) -> @location(0) vec4f {
|
|
1006
|
-
let scene = textureSample(sceneTexture, sceneSampler, input.uv);
|
|
1007
|
-
let bloom = textureSample(bloomTexture, bloomSampler, input.uv);
|
|
1008
|
-
// Additive blending with intensity control
|
|
1009
|
-
let result = scene.rgb + bloom.rgb * composeUniforms.intensity;
|
|
1010
|
-
return vec4f(result, scene.a);
|
|
1011
|
-
}
|
|
971
|
+
code: /* wgsl */ `
|
|
972
|
+
struct VertexOutput {
|
|
973
|
+
@builtin(position) position: vec4f,
|
|
974
|
+
@location(0) uv: vec2f,
|
|
975
|
+
};
|
|
976
|
+
|
|
977
|
+
@vertex fn vs(@builtin(vertex_index) vertexIndex: u32) -> VertexOutput {
|
|
978
|
+
var output: VertexOutput;
|
|
979
|
+
let x = f32((vertexIndex << 1u) & 2u) * 2.0 - 1.0;
|
|
980
|
+
let y = f32(vertexIndex & 2u) * 2.0 - 1.0;
|
|
981
|
+
output.position = vec4f(x, y, 0.0, 1.0);
|
|
982
|
+
output.uv = vec2f(x * 0.5 + 0.5, 1.0 - (y * 0.5 + 0.5));
|
|
983
|
+
return output;
|
|
984
|
+
}
|
|
985
|
+
|
|
986
|
+
struct BloomComposeUniforms {
|
|
987
|
+
intensity: f32,
|
|
988
|
+
_padding1: f32,
|
|
989
|
+
_padding2: f32,
|
|
990
|
+
_padding3: f32,
|
|
991
|
+
_padding4: f32,
|
|
992
|
+
_padding5: f32,
|
|
993
|
+
_padding6: f32,
|
|
994
|
+
_padding7: f32,
|
|
995
|
+
};
|
|
996
|
+
|
|
997
|
+
@group(0) @binding(0) var sceneTexture: texture_2d<f32>;
|
|
998
|
+
@group(0) @binding(1) var sceneSampler: sampler;
|
|
999
|
+
@group(0) @binding(2) var bloomTexture: texture_2d<f32>;
|
|
1000
|
+
@group(0) @binding(3) var bloomSampler: sampler;
|
|
1001
|
+
@group(0) @binding(4) var<uniform> composeUniforms: BloomComposeUniforms;
|
|
1002
|
+
|
|
1003
|
+
@fragment fn fs(input: VertexOutput) -> @location(0) vec4f {
|
|
1004
|
+
let scene = textureSample(sceneTexture, sceneSampler, input.uv);
|
|
1005
|
+
let bloom = textureSample(bloomTexture, bloomSampler, input.uv);
|
|
1006
|
+
// Additive blending with intensity control
|
|
1007
|
+
let result = scene.rgb + bloom.rgb * composeUniforms.intensity;
|
|
1008
|
+
return vec4f(result, scene.a);
|
|
1009
|
+
}
|
|
1012
1010
|
`,
|
|
1013
1011
|
});
|
|
1014
1012
|
// Create uniform buffer for blur direction (minimum 32 bytes for WebGPU)
|
|
@@ -1272,7 +1270,6 @@ export class Engine {
|
|
|
1272
1270
|
async loadAnimation(url) {
|
|
1273
1271
|
const frames = await VMDLoader.load(url);
|
|
1274
1272
|
this.animationFrames = frames;
|
|
1275
|
-
console.log(this.animationFrames);
|
|
1276
1273
|
}
|
|
1277
1274
|
playAnimation() {
|
|
1278
1275
|
if (this.animationFrames.length === 0)
|
|
@@ -1408,6 +1405,14 @@ export class Engine {
|
|
|
1408
1405
|
const dir = pathParts.join("/") + "/";
|
|
1409
1406
|
this.modelDir = dir;
|
|
1410
1407
|
const model = await PmxLoader.load(path);
|
|
1408
|
+
// console.log({
|
|
1409
|
+
// vertices: Array.from(model.getVertices()),
|
|
1410
|
+
// indices: Array.from(model.getIndices()),
|
|
1411
|
+
// materials: model.getMaterials(),
|
|
1412
|
+
// textures: model.getTextures(),
|
|
1413
|
+
// bones: model.getSkeleton().bones,
|
|
1414
|
+
// skinning: { joints: Array.from(model.getSkinning().joints), weights: Array.from(model.getSkinning().weights) },
|
|
1415
|
+
// })
|
|
1411
1416
|
this.physics = new Physics(model.getRigidbodies(), model.getJoints());
|
|
1412
1417
|
await this.setupModelBuffers(model);
|
|
1413
1418
|
}
|