reze-engine 0.11.2 → 0.11.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/engine.d.ts +4 -2
- package/dist/engine.d.ts.map +1 -1
- package/dist/engine.js +58 -426
- package/dist/index.d.ts +1 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/shaders/body.d.ts +1 -1
- package/dist/shaders/body.d.ts.map +1 -1
- package/dist/shaders/body.js +7 -28
- package/dist/shaders/cloth_rough.d.ts +1 -1
- package/dist/shaders/cloth_rough.d.ts.map +1 -1
- package/dist/shaders/cloth_rough.js +4 -16
- package/dist/shaders/cloth_smooth.d.ts +1 -1
- package/dist/shaders/cloth_smooth.d.ts.map +1 -1
- package/dist/shaders/cloth_smooth.js +5 -17
- package/dist/shaders/default.d.ts +1 -1
- package/dist/shaders/default.d.ts.map +1 -1
- package/dist/shaders/eye.d.ts +1 -1
- package/dist/shaders/eye.d.ts.map +1 -1
- package/dist/shaders/face.d.ts +1 -1
- package/dist/shaders/face.d.ts.map +1 -1
- package/dist/shaders/face.js +21 -57
- package/dist/shaders/hair.d.ts +1 -1
- package/dist/shaders/hair.d.ts.map +1 -1
- package/dist/shaders/hair.js +7 -27
- package/dist/shaders/materials/body.d.ts +2 -0
- package/dist/shaders/materials/body.d.ts.map +1 -0
- package/dist/shaders/materials/body.js +199 -0
- package/dist/shaders/materials/cloth_rough.d.ts +2 -0
- package/dist/shaders/materials/cloth_rough.d.ts.map +1 -0
- package/dist/shaders/materials/cloth_rough.js +178 -0
- package/dist/shaders/materials/cloth_smooth.d.ts +2 -0
- package/dist/shaders/materials/cloth_smooth.d.ts.map +1 -0
- package/dist/shaders/materials/cloth_smooth.js +174 -0
- package/dist/shaders/materials/default.d.ts +2 -0
- package/dist/shaders/materials/default.d.ts.map +1 -0
- package/dist/shaders/materials/default.js +171 -0
- package/dist/shaders/materials/eye.d.ts +2 -0
- package/dist/shaders/materials/eye.d.ts.map +1 -0
- package/dist/shaders/materials/eye.js +146 -0
- package/dist/shaders/materials/face.d.ts +2 -0
- package/dist/shaders/materials/face.d.ts.map +1 -0
- package/dist/shaders/materials/face.js +199 -0
- package/dist/shaders/materials/hair.d.ts +2 -0
- package/dist/shaders/materials/hair.d.ts.map +1 -0
- package/dist/shaders/materials/hair.js +176 -0
- package/dist/shaders/materials/metal.d.ts +2 -0
- package/dist/shaders/materials/metal.d.ts.map +1 -0
- package/dist/shaders/materials/metal.js +183 -0
- package/dist/shaders/materials/nodes.d.ts +2 -0
- package/dist/shaders/materials/nodes.d.ts.map +1 -0
- package/{src/shaders/nodes.ts → dist/shaders/materials/nodes.js} +32 -16
- package/dist/shaders/materials/stockings.d.ts +2 -0
- package/dist/shaders/materials/stockings.d.ts.map +1 -0
- package/dist/shaders/materials/stockings.js +244 -0
- package/dist/shaders/metal.d.ts +1 -1
- package/dist/shaders/metal.d.ts.map +1 -1
- package/dist/shaders/metal.js +4 -17
- package/dist/shaders/nodes.d.ts +1 -1
- package/dist/shaders/nodes.d.ts.map +1 -1
- package/dist/shaders/nodes.js +0 -9
- package/dist/shaders/passes/bloom.d.ts +4 -0
- package/dist/shaders/passes/bloom.d.ts.map +1 -0
- package/dist/shaders/passes/bloom.js +117 -0
- package/dist/shaders/passes/composite.d.ts +2 -0
- package/dist/shaders/passes/composite.d.ts.map +1 -0
- package/dist/shaders/passes/composite.js +61 -0
- package/dist/shaders/passes/ground.d.ts +2 -0
- package/dist/shaders/passes/ground.d.ts.map +1 -0
- package/dist/shaders/passes/ground.js +93 -0
- package/dist/shaders/passes/mipmap.d.ts +2 -0
- package/dist/shaders/passes/mipmap.d.ts.map +1 -0
- package/dist/shaders/passes/mipmap.js +16 -0
- package/dist/shaders/passes/outline.d.ts +2 -0
- package/dist/shaders/passes/outline.d.ts.map +1 -0
- package/dist/shaders/passes/outline.js +83 -0
- package/dist/shaders/passes/pick.d.ts +2 -0
- package/dist/shaders/passes/pick.d.ts.map +1 -0
- package/dist/shaders/passes/pick.js +39 -0
- package/dist/shaders/passes/shadow.d.ts +2 -0
- package/dist/shaders/passes/shadow.d.ts.map +1 -0
- package/dist/shaders/passes/shadow.js +16 -0
- package/dist/shaders/stockings.d.ts +1 -1
- package/dist/shaders/stockings.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/engine.ts +93 -438
- package/src/index.ts +3 -2
- package/src/shaders/{body.ts → materials/body.ts} +7 -28
- package/src/shaders/{cloth_rough.ts → materials/cloth_rough.ts} +4 -16
- package/src/shaders/{cloth_smooth.ts → materials/cloth_smooth.ts} +5 -17
- package/src/shaders/{face.ts → materials/face.ts} +21 -57
- package/src/shaders/{hair.ts → materials/hair.ts} +7 -27
- package/src/shaders/{metal.ts → materials/metal.ts} +15 -19
- package/src/shaders/materials/nodes.ts +483 -0
- package/src/shaders/passes/bloom.ts +121 -0
- package/src/shaders/passes/composite.ts +62 -0
- package/src/shaders/passes/ground.ts +94 -0
- package/src/shaders/passes/mipmap.ts +17 -0
- package/src/shaders/passes/outline.ts +84 -0
- package/src/shaders/passes/pick.ts +40 -0
- package/src/shaders/passes/shadow.ts +17 -0
- package/src/shaders/classify.ts +0 -25
- /package/src/shaders/{default.ts → materials/default.ts} +0 -0
- /package/src/shaders/{eye.ts → materials/eye.ts} +0 -0
- /package/src/shaders/{stockings.ts → materials/stockings.ts} +0 -0
package/dist/engine.js
CHANGED
|
@@ -3,18 +3,33 @@ import { Mat4, Vec3 } from "./math";
|
|
|
3
3
|
import { PmxLoader } from "./pmx-loader";
|
|
4
4
|
import { Physics } from "./physics";
|
|
5
5
|
import { createFetchAssetReader, createFileMapAssetReader, deriveBasePathFromPmxPath, fileListToMap, findFirstPmxFileInList, joinAssetPath, normalizeAssetPath, } from "./asset-reader";
|
|
6
|
-
import { DEFAULT_SHADER_WGSL } from "./shaders/default";
|
|
6
|
+
import { DEFAULT_SHADER_WGSL } from "./shaders/materials/default";
|
|
7
|
+
import { FACE_SHADER_WGSL } from "./shaders/materials/face";
|
|
8
|
+
import { HAIR_SHADER_WGSL } from "./shaders/materials/hair";
|
|
9
|
+
import { CLOTH_SMOOTH_SHADER_WGSL } from "./shaders/materials/cloth_smooth";
|
|
10
|
+
import { CLOTH_ROUGH_SHADER_WGSL } from "./shaders/materials/cloth_rough";
|
|
11
|
+
import { METAL_SHADER_WGSL } from "./shaders/materials/metal";
|
|
12
|
+
import { BODY_SHADER_WGSL } from "./shaders/materials/body";
|
|
13
|
+
import { EYE_SHADER_WGSL } from "./shaders/materials/eye";
|
|
14
|
+
import { STOCKINGS_SHADER_WGSL } from "./shaders/materials/stockings";
|
|
7
15
|
import { BRDF_LUT_SIZE, BRDF_LUT_BAKE_WGSL } from "./shaders/dfg_lut";
|
|
8
16
|
import { LTC_MAG_LUT_SIZE, LTC_MAG_LUT_DATA } from "./shaders/ltc_mag_lut";
|
|
9
|
-
import {
|
|
10
|
-
import {
|
|
11
|
-
import {
|
|
12
|
-
import {
|
|
13
|
-
import {
|
|
14
|
-
import {
|
|
15
|
-
import {
|
|
16
|
-
|
|
17
|
-
|
|
17
|
+
import { SHADOW_DEPTH_SHADER_WGSL } from "./shaders/passes/shadow";
|
|
18
|
+
import { GROUND_SHADOW_SHADER_WGSL } from "./shaders/passes/ground";
|
|
19
|
+
import { OUTLINE_SHADER_WGSL } from "./shaders/passes/outline";
|
|
20
|
+
import { BLOOM_BLIT_SHADER_WGSL, BLOOM_DOWNSAMPLE_SHADER_WGSL, BLOOM_UPSAMPLE_SHADER_WGSL, } from "./shaders/passes/bloom";
|
|
21
|
+
import { COMPOSITE_SHADER_WGSL } from "./shaders/passes/composite";
|
|
22
|
+
import { PICK_SHADER_WGSL } from "./shaders/passes/pick";
|
|
23
|
+
import { MIPMAP_BLIT_SHADER_WGSL } from "./shaders/passes/mipmap";
|
|
24
|
+
function resolvePreset(materialName, map) {
|
|
25
|
+
if (!map)
|
|
26
|
+
return "default";
|
|
27
|
+
for (const [preset, names] of Object.entries(map)) {
|
|
28
|
+
if (names && names.includes(materialName))
|
|
29
|
+
return preset;
|
|
30
|
+
}
|
|
31
|
+
return "default";
|
|
32
|
+
}
|
|
18
33
|
export const DEFAULT_BLOOM_OPTIONS = {
|
|
19
34
|
enabled: true,
|
|
20
35
|
threshold: 0.5,
|
|
@@ -50,7 +65,7 @@ export class Engine {
|
|
|
50
65
|
this.lightData = new Float32Array(64);
|
|
51
66
|
this.lightCount = 0;
|
|
52
67
|
this.resizeObserver = null;
|
|
53
|
-
// [exposure,
|
|
68
|
+
// [exposure, invGamma, _, _, bloomTint.x, bloomTint.y, bloomTint.z, bloomIntensity]
|
|
54
69
|
this.compositeUniformData = new Float32Array(8);
|
|
55
70
|
this.bloomBlitUniformData = new Float32Array(4);
|
|
56
71
|
this.bloomUpsampleUniformData = new Float32Array(4);
|
|
@@ -200,7 +215,10 @@ export class Engine {
|
|
|
200
215
|
const effIntensity = b.enabled ? b.intensity : 0.0;
|
|
201
216
|
const u = this.compositeUniformData;
|
|
202
217
|
u[0] = v.exposure;
|
|
203
|
-
|
|
218
|
+
// Store 1/gamma so the shader avoids a per-pixel divide. Safari's Metal
|
|
219
|
+
// compiler doesn't fold `pow(x, 1/g)` into identity when g=1, so also emit
|
|
220
|
+
// a uniform branch that skips the pow entirely in the common case.
|
|
221
|
+
u[1] = 1.0 / Math.max(v.gamma, 1e-4);
|
|
204
222
|
u[2] = 0.0;
|
|
205
223
|
u[3] = 0.0;
|
|
206
224
|
u[4] = b.color.x;
|
|
@@ -640,21 +658,7 @@ export class Engine {
|
|
|
640
658
|
});
|
|
641
659
|
const shadowShader = this.device.createShaderModule({
|
|
642
660
|
label: "shadow depth",
|
|
643
|
-
code:
|
|
644
|
-
struct LightVP { viewProj: mat4x4f, };
|
|
645
|
-
@group(0) @binding(0) var<uniform> lp: LightVP;
|
|
646
|
-
@group(0) @binding(1) var<storage, read> skinMats: array<mat4x4f>;
|
|
647
|
-
@vertex fn vs(@location(0) position: vec3f, @location(1) normal: vec3f, @location(2) uv: vec2f,
|
|
648
|
-
@location(3) joints0: vec4<u32>, @location(4) weights0: vec4<f32>) -> @builtin(position) vec4f {
|
|
649
|
-
let pos4 = vec4f(position, 1.0);
|
|
650
|
-
let ws = weights0.x + weights0.y + weights0.z + weights0.w;
|
|
651
|
-
let inv = select(1.0, 1.0 / ws, ws > 0.0001);
|
|
652
|
-
let nw = select(vec4f(1.0,0.0,0.0,0.0), weights0 * inv, ws > 0.0001);
|
|
653
|
-
var sp = vec4f(0.0);
|
|
654
|
-
for (var i = 0u; i < 4u; i++) { sp += (skinMats[joints0[i]] * pos4) * nw[i]; }
|
|
655
|
-
return lp.viewProj * vec4f(sp.xyz, 1.0);
|
|
656
|
-
}
|
|
657
|
-
`,
|
|
661
|
+
code: SHADOW_DEPTH_SHADER_WGSL,
|
|
658
662
|
});
|
|
659
663
|
this.shadowDepthPipeline = this.device.createRenderPipeline({
|
|
660
664
|
label: "shadow depth pipeline",
|
|
@@ -711,99 +715,7 @@ export class Engine {
|
|
|
711
715
|
});
|
|
712
716
|
const groundShadowShader = this.device.createShaderModule({
|
|
713
717
|
label: "ground shadow",
|
|
714
|
-
code:
|
|
715
|
-
struct CameraUniforms { view: mat4x4f, projection: mat4x4f, viewPos: vec3f, _p: f32, };
|
|
716
|
-
struct Light { direction: vec4f, color: vec4f, };
|
|
717
|
-
struct LightUniforms { ambientColor: vec4f, lights: array<Light, 4>, };
|
|
718
|
-
struct GroundShadowMat {
|
|
719
|
-
diffuseColor: vec3f, fadeStart: f32,
|
|
720
|
-
fadeEnd: f32, shadowStrength: f32, pcfTexel: f32, gridSpacing: f32,
|
|
721
|
-
gridLineWidth: f32, gridLineOpacity: f32, noiseStrength: f32, _pad: f32,
|
|
722
|
-
gridLineColor: vec3f, _pad2: f32,
|
|
723
|
-
};
|
|
724
|
-
struct LightVP { viewProj: mat4x4f, };
|
|
725
|
-
@group(0) @binding(0) var<uniform> camera: CameraUniforms;
|
|
726
|
-
@group(0) @binding(1) var<uniform> light: LightUniforms;
|
|
727
|
-
@group(0) @binding(2) var shadowMap: texture_depth_2d;
|
|
728
|
-
@group(0) @binding(3) var shadowSampler: sampler_comparison;
|
|
729
|
-
@group(0) @binding(4) var<uniform> material: GroundShadowMat;
|
|
730
|
-
@group(0) @binding(5) var<uniform> lightVP: LightVP;
|
|
731
|
-
|
|
732
|
-
// Hash-based noise for frosted/matte surface
|
|
733
|
-
fn hash2(p: vec2f) -> f32 {
|
|
734
|
-
var p3 = fract(vec3f(p.x, p.y, p.x) * 0.1031);
|
|
735
|
-
p3 += dot(p3, vec3f(p3.y + 33.33, p3.z + 33.33, p3.x + 33.33));
|
|
736
|
-
return fract((p3.x + p3.y) * p3.z);
|
|
737
|
-
}
|
|
738
|
-
fn valueNoise(p: vec2f) -> f32 {
|
|
739
|
-
let i = floor(p);
|
|
740
|
-
let f = fract(p);
|
|
741
|
-
let u = f * f * (3.0 - 2.0 * f);
|
|
742
|
-
return mix(mix(hash2(i), hash2(i + vec2f(1.0, 0.0)), u.x),
|
|
743
|
-
mix(hash2(i + vec2f(0.0, 1.0)), hash2(i + vec2f(1.0, 1.0)), u.x), u.y);
|
|
744
|
-
}
|
|
745
|
-
fn fbmNoise(p: vec2f) -> f32 {
|
|
746
|
-
var v = 0.0;
|
|
747
|
-
var a = 0.5;
|
|
748
|
-
var pp = p;
|
|
749
|
-
for (var i = 0; i < 4; i++) {
|
|
750
|
-
v += a * valueNoise(pp);
|
|
751
|
-
pp *= 2.0;
|
|
752
|
-
a *= 0.5;
|
|
753
|
-
}
|
|
754
|
-
return v;
|
|
755
|
-
}
|
|
756
|
-
|
|
757
|
-
struct VO { @builtin(position) position: vec4f, @location(0) worldPos: vec3f, @location(1) normal: vec3f, };
|
|
758
|
-
@vertex fn vs(@location(0) position: vec3f, @location(1) normal: vec3f, @location(2) uv: vec2f) -> VO {
|
|
759
|
-
var o: VO; o.worldPos = position; o.normal = normal;
|
|
760
|
-
o.position = camera.projection * camera.view * vec4f(position, 1.0); return o;
|
|
761
|
-
}
|
|
762
|
-
struct FSOut { @location(0) color: vec4f, @location(1) mask: f32 };
|
|
763
|
-
@fragment fn fs(i: VO) -> FSOut {
|
|
764
|
-
let n = normalize(i.normal);
|
|
765
|
-
let centerDist = length(i.worldPos.xz);
|
|
766
|
-
let edgeFade = 1.0 - smoothstep(0.0, 1.0, clamp((centerDist - material.fadeStart) / max(material.fadeEnd - material.fadeStart, 0.001), 0.0, 1.0));
|
|
767
|
-
|
|
768
|
-
// Shadow sampling
|
|
769
|
-
let lclip = lightVP.viewProj * vec4f(i.worldPos, 1.0);
|
|
770
|
-
let ndc = lclip.xyz / max(lclip.w, 1e-6);
|
|
771
|
-
let suv = vec2f(ndc.x * 0.5 + 0.5, 0.5 - ndc.y * 0.5);
|
|
772
|
-
let suv_c = clamp(suv, vec2f(0.02), vec2f(0.98));
|
|
773
|
-
let st = material.pcfTexel;
|
|
774
|
-
let compareZ = ndc.z - 0.0035;
|
|
775
|
-
var vis = 0.0;
|
|
776
|
-
for (var y = -2; y <= 2; y++) {
|
|
777
|
-
for (var x = -2; x <= 2; x++) {
|
|
778
|
-
vis += textureSampleCompare(shadowMap, shadowSampler, suv_c + vec2f(f32(x), f32(y)) * st, compareZ);
|
|
779
|
-
}
|
|
780
|
-
}
|
|
781
|
-
vis *= 0.04;
|
|
782
|
-
|
|
783
|
-
// Frosted/matte micro-texture (磨砂)
|
|
784
|
-
let noiseVal = fbmNoise(i.worldPos.xz * 3.0);
|
|
785
|
-
let noiseTint = 1.0 + (noiseVal - 0.5) * material.noiseStrength;
|
|
786
|
-
|
|
787
|
-
// Grid lines — anti-aliased via screen-space derivatives
|
|
788
|
-
let gp = i.worldPos.xz / material.gridSpacing;
|
|
789
|
-
let gridFrac = abs(fract(gp - 0.5) - 0.5);
|
|
790
|
-
let gridDeriv = fwidth(gp);
|
|
791
|
-
let halfLine = material.gridLineWidth * 0.5;
|
|
792
|
-
let gridLine = 1.0 - min(
|
|
793
|
-
smoothstep(halfLine - gridDeriv.x, halfLine + gridDeriv.x, gridFrac.x),
|
|
794
|
-
smoothstep(halfLine - gridDeriv.y, halfLine + gridDeriv.y, gridFrac.y)
|
|
795
|
-
);
|
|
796
|
-
let sun = light.ambientColor.xyz + light.lights[0].color.xyz * light.lights[0].color.w * max(dot(n, -light.lights[0].direction.xyz), 0.0);
|
|
797
|
-
let dark = (1.0 - vis) * material.shadowStrength;
|
|
798
|
-
var baseColor = material.diffuseColor * sun * (1.0 - dark * 0.65);
|
|
799
|
-
baseColor *= noiseTint;
|
|
800
|
-
let finalColor = mix(baseColor, material.gridLineColor, gridLine * material.gridLineOpacity * edgeFade);
|
|
801
|
-
var out: FSOut;
|
|
802
|
-
out.color = vec4f(finalColor * edgeFade, edgeFade);
|
|
803
|
-
out.mask = 0.0;
|
|
804
|
-
return out;
|
|
805
|
-
}
|
|
806
|
-
`,
|
|
718
|
+
code: GROUND_SHADOW_SHADER_WGSL,
|
|
807
719
|
});
|
|
808
720
|
this.groundShadowPipeline = this.createRenderPipeline({
|
|
809
721
|
label: "ground shadow pipeline",
|
|
@@ -843,90 +755,7 @@ export class Engine {
|
|
|
843
755
|
});
|
|
844
756
|
const outlineShaderModule = this.device.createShaderModule({
|
|
845
757
|
label: "outline shaders",
|
|
846
|
-
code:
|
|
847
|
-
struct CameraUniforms {
|
|
848
|
-
view: mat4x4f,
|
|
849
|
-
projection: mat4x4f,
|
|
850
|
-
viewPos: vec3f,
|
|
851
|
-
_padding: f32,
|
|
852
|
-
};
|
|
853
|
-
|
|
854
|
-
struct MaterialUniforms {
|
|
855
|
-
edgeColor: vec4f,
|
|
856
|
-
edgeSize: f32,
|
|
857
|
-
_padding1: f32,
|
|
858
|
-
_padding2: f32,
|
|
859
|
-
_padding3: f32,
|
|
860
|
-
};
|
|
861
|
-
|
|
862
|
-
// group 0: per-frame
|
|
863
|
-
@group(0) @binding(0) var<uniform> camera: CameraUniforms;
|
|
864
|
-
// group 1: per-instance
|
|
865
|
-
@group(1) @binding(0) var<storage, read> skinMats: array<mat4x4f>;
|
|
866
|
-
// group 2: per-material
|
|
867
|
-
@group(2) @binding(0) var<uniform> material: MaterialUniforms;
|
|
868
|
-
|
|
869
|
-
struct VertexOutput {
|
|
870
|
-
@builtin(position) position: vec4f,
|
|
871
|
-
};
|
|
872
|
-
|
|
873
|
-
@vertex fn vs(
|
|
874
|
-
@location(0) position: vec3f,
|
|
875
|
-
@location(1) normal: vec3f,
|
|
876
|
-
@location(3) joints0: vec4<u32>,
|
|
877
|
-
@location(4) weights0: vec4<f32>
|
|
878
|
-
) -> VertexOutput {
|
|
879
|
-
var output: VertexOutput;
|
|
880
|
-
let pos4 = vec4f(position, 1.0);
|
|
881
|
-
|
|
882
|
-
let weightSum = weights0.x + weights0.y + weights0.z + weights0.w;
|
|
883
|
-
let invWeightSum = select(1.0, 1.0 / weightSum, weightSum > 0.0001);
|
|
884
|
-
let normalizedWeights = select(vec4f(1.0, 0.0, 0.0, 0.0), weights0 * invWeightSum, weightSum > 0.0001);
|
|
885
|
-
|
|
886
|
-
var skinnedPos = vec4f(0.0, 0.0, 0.0, 0.0);
|
|
887
|
-
var skinnedNrm = vec3f(0.0, 0.0, 0.0);
|
|
888
|
-
for (var i = 0u; i < 4u; i++) {
|
|
889
|
-
let j = joints0[i];
|
|
890
|
-
let w = normalizedWeights[i];
|
|
891
|
-
let m = skinMats[j];
|
|
892
|
-
skinnedPos += (m * pos4) * w;
|
|
893
|
-
let r3 = mat3x3f(m[0].xyz, m[1].xyz, m[2].xyz);
|
|
894
|
-
skinnedNrm += (r3 * normal) * w;
|
|
895
|
-
}
|
|
896
|
-
let worldPos = skinnedPos.xyz;
|
|
897
|
-
let worldNormal = normalize(skinnedNrm);
|
|
898
|
-
|
|
899
|
-
// Screen-space outline extrusion — MMD-style pixel-stable edge line.
|
|
900
|
-
// 1. Project position and normal-as-direction to clip space.
|
|
901
|
-
// 2. Normalize the 2D clip-space normal, aspect-compensated so "one pixel horizontally"
|
|
902
|
-
// matches "one pixel vertically" (otherwise wide viewports squash the outline in X).
|
|
903
|
-
// 3. Offset clip-space xy by (normal * edgeSize * edgeScale), then multiply by w
|
|
904
|
-
// so the perspective divide cancels out → offset stays constant in NDC regardless
|
|
905
|
-
// of depth, matching how MMD / babylon-mmd style outlines look identical when zooming.
|
|
906
|
-
// 4. edgeScale is in NDC-y units per PMX edgeSize. ≈ 0.006 gives ~3px at 1080p; it's
|
|
907
|
-
// tied to viewport HEIGHT so resizing the window keeps pixel thickness stable.
|
|
908
|
-
let viewProj = camera.projection * camera.view;
|
|
909
|
-
let clipPos = viewProj * vec4f(worldPos, 1.0);
|
|
910
|
-
let clipNormal = (viewProj * vec4f(worldNormal, 0.0)).xy;
|
|
911
|
-
// projection is column-major: proj[0][0] = 1/(aspect·tan(fov/2)), proj[1][1] = 1/tan(fov/2).
|
|
912
|
-
// Ratio proj[1][1]/proj[0][0] recovers the viewport aspect (width/height).
|
|
913
|
-
let aspect = camera.projection[1][1] / camera.projection[0][0];
|
|
914
|
-
let pixelDir = normalize(vec2f(clipNormal.x * aspect, clipNormal.y));
|
|
915
|
-
let ndcDir = vec2f(pixelDir.x / aspect, pixelDir.y);
|
|
916
|
-
let edgeScale = 0.0016;
|
|
917
|
-
let offset = ndcDir * material.edgeSize * edgeScale * clipPos.w;
|
|
918
|
-
output.position = vec4f(clipPos.xy + offset, clipPos.z, clipPos.w);
|
|
919
|
-
return output;
|
|
920
|
-
}
|
|
921
|
-
|
|
922
|
-
struct FSOut { @location(0) color: vec4f, @location(1) mask: f32 };
|
|
923
|
-
@fragment fn fs() -> FSOut {
|
|
924
|
-
var out: FSOut;
|
|
925
|
-
out.color = material.edgeColor;
|
|
926
|
-
out.mask = 1.0;
|
|
927
|
-
return out;
|
|
928
|
-
}
|
|
929
|
-
`,
|
|
758
|
+
code: OUTLINE_SHADER_WGSL,
|
|
930
759
|
});
|
|
931
760
|
this.outlinePipeline = this.createRenderPipeline({
|
|
932
761
|
label: "outline pipeline",
|
|
@@ -987,127 +816,17 @@ export class Engine {
|
|
|
987
816
|
{ binding: 3, visibility: GPUShaderStage.FRAGMENT, buffer: { type: "uniform" } },
|
|
988
817
|
],
|
|
989
818
|
});
|
|
990
|
-
const bloomFullscreenVs = /* wgsl */ `
|
|
991
|
-
@vertex fn vs(@builtin(vertex_index) vi: u32) -> @builtin(position) vec4f {
|
|
992
|
-
let x = f32((vi & 1u) << 2u) - 1.0;
|
|
993
|
-
let y = f32((vi & 2u) << 1u) - 1.0;
|
|
994
|
-
return vec4f(x, y, 0.0, 1.0);
|
|
995
|
-
}
|
|
996
|
-
`;
|
|
997
|
-
// Blit: full-res HDR → half-res. Karis 4-tap firefly average + EEVEE quadratic knee threshold + clamp.
|
|
998
819
|
const bloomBlitShader = this.device.createShaderModule({
|
|
999
820
|
label: "bloom blit (Karis prefilter)",
|
|
1000
|
-
code:
|
|
1001
|
-
@group(0) @binding(0) var hdrTex: texture_2d<f32>;
|
|
1002
|
-
@group(0) @binding(1) var<uniform> prefilter: vec4<f32>; // threshold, knee, clamp, _unused
|
|
1003
|
-
@group(0) @binding(2) var maskTex: texture_2d<f32>;
|
|
1004
|
-
|
|
1005
|
-
fn luminance(c: vec3f) -> f32 {
|
|
1006
|
-
return dot(max(c, vec3f(0.0)), vec3f(0.2126, 0.7152, 0.0722));
|
|
1007
|
-
}
|
|
1008
|
-
fn fetch(c: vec2<i32>, clampV: f32) -> vec3f {
|
|
1009
|
-
let d = vec2<i32>(textureDimensions(hdrTex));
|
|
1010
|
-
let cc = clamp(c, vec2<i32>(0), d - vec2<i32>(1));
|
|
1011
|
-
let s = textureLoad(hdrTex, cc, 0);
|
|
1012
|
-
// Scene pass uses src-alpha blend with clear alpha 0 → premultiplied. Unpremultiply.
|
|
1013
|
-
let rgb = max(s.rgb / max(s.a, 1e-6), vec3f(0.0));
|
|
1014
|
-
// Bloom mask: MRT r8unorm written by material shaders (1.0 = bloom, 0.0 = skip).
|
|
1015
|
-
let mask = textureLoad(maskTex, cc, 0).r;
|
|
1016
|
-
let masked = rgb * mask;
|
|
1017
|
-
// Blender: clamp each tap BEFORE Karis average (eevee_bloom: color = min(clampIntensity, color)).
|
|
1018
|
-
return select(masked, min(masked, vec3f(clampV)), clampV > 0.0);
|
|
1019
|
-
}
|
|
1020
|
-
|
|
1021
|
-
@fragment fn fs(@builtin(position) p: vec4f) -> @location(0) vec4f {
|
|
1022
|
-
let dst = vec2<i32>(p.xy - vec2f(0.5));
|
|
1023
|
-
let base = dst * 2;
|
|
1024
|
-
let clampV = prefilter.z;
|
|
1025
|
-
let a = fetch(base + vec2<i32>(0, 0), clampV);
|
|
1026
|
-
let b = fetch(base + vec2<i32>(1, 0), clampV);
|
|
1027
|
-
let c = fetch(base + vec2<i32>(0, 1), clampV);
|
|
1028
|
-
let d = fetch(base + vec2<i32>(1, 1), clampV);
|
|
1029
|
-
// Karis partial average: weight each tap by 1/(1+luma) — suppresses fireflies.
|
|
1030
|
-
let wa = 1.0 / (1.0 + luminance(a));
|
|
1031
|
-
let wb = 1.0 / (1.0 + luminance(b));
|
|
1032
|
-
let wc = 1.0 / (1.0 + luminance(c));
|
|
1033
|
-
let wd = 1.0 / (1.0 + luminance(d));
|
|
1034
|
-
let avg = (a * wa + b * wb + c * wc + d * wd) / max(wa + wb + wc + wd, 1e-6);
|
|
1035
|
-
// EEVEE quadratic threshold (brightness = max-channel, then soft-knee curve).
|
|
1036
|
-
let bright = max(avg.r, max(avg.g, avg.b));
|
|
1037
|
-
let soft = clamp(bright - prefilter.x + prefilter.y, 0.0, 2.0 * prefilter.y);
|
|
1038
|
-
let q = (soft * soft) / (4.0 * max(prefilter.y, 1e-4) + 1e-6);
|
|
1039
|
-
let contrib = max(q, bright - prefilter.x) / max(bright, 1e-4);
|
|
1040
|
-
return vec4f(max(avg * contrib, vec3f(0.0)), 1.0);
|
|
1041
|
-
}
|
|
1042
|
-
`,
|
|
821
|
+
code: BLOOM_BLIT_SHADER_WGSL,
|
|
1043
822
|
});
|
|
1044
|
-
// Downsample: Jimenez/COD 13-tap dual-box — 5 weighted 2×2 averages, rejects nyquist ringing.
|
|
1045
823
|
const bloomDownsampleShader = this.device.createShaderModule({
|
|
1046
824
|
label: "bloom downsample 13-tap",
|
|
1047
|
-
code:
|
|
1048
|
-
@group(0) @binding(0) var srcTex: texture_2d<f32>;
|
|
1049
|
-
@group(0) @binding(1) var srcSamp: sampler;
|
|
1050
|
-
|
|
1051
|
-
fn samp(uv: vec2f, off: vec2f) -> vec3f {
|
|
1052
|
-
return textureSampleLevel(srcTex, srcSamp, uv + off, 0.0).rgb;
|
|
1053
|
-
}
|
|
1054
|
-
|
|
1055
|
-
@fragment fn fs(@builtin(position) p: vec4f) -> @location(0) vec4f {
|
|
1056
|
-
let srcDims = vec2f(textureDimensions(srcTex));
|
|
1057
|
-
let t = 1.0 / srcDims;
|
|
1058
|
-
// fragCoord.xy reports pixel centers (e.g. 0.5,0.5 for first pixel) — divide by dst dims directly.
|
|
1059
|
-
let dstDims = srcDims * 0.5;
|
|
1060
|
-
let uv = p.xy / max(dstDims, vec2f(1.0));
|
|
1061
|
-
let A = samp(uv, t * vec2f(-2.0, -2.0));
|
|
1062
|
-
let B = samp(uv, t * vec2f( 0.0, -2.0));
|
|
1063
|
-
let C = samp(uv, t * vec2f( 2.0, -2.0));
|
|
1064
|
-
let D = samp(uv, t * vec2f(-1.0, -1.0));
|
|
1065
|
-
let E = samp(uv, t * vec2f( 1.0, -1.0));
|
|
1066
|
-
let F = samp(uv, t * vec2f(-2.0, 0.0));
|
|
1067
|
-
let G = samp(uv, t * vec2f( 0.0, 0.0));
|
|
1068
|
-
let H = samp(uv, t * vec2f( 2.0, 0.0));
|
|
1069
|
-
let I = samp(uv, t * vec2f(-1.0, 1.0));
|
|
1070
|
-
let J = samp(uv, t * vec2f( 1.0, 1.0));
|
|
1071
|
-
let K = samp(uv, t * vec2f(-2.0, 2.0));
|
|
1072
|
-
let L = samp(uv, t * vec2f( 0.0, 2.0));
|
|
1073
|
-
let M = samp(uv, t * vec2f( 2.0, 2.0));
|
|
1074
|
-
var o = (D + E + I + J) * (0.5 / 4.0);
|
|
1075
|
-
o = o + (A + B + G + F) * (0.125 / 4.0);
|
|
1076
|
-
o = o + (B + C + H + G) * (0.125 / 4.0);
|
|
1077
|
-
o = o + (F + G + L + K) * (0.125 / 4.0);
|
|
1078
|
-
o = o + (G + H + M + L) * (0.125 / 4.0);
|
|
1079
|
-
return vec4f(o, 1.0);
|
|
1080
|
-
}
|
|
1081
|
-
`,
|
|
825
|
+
code: BLOOM_DOWNSAMPLE_SHADER_WGSL,
|
|
1082
826
|
});
|
|
1083
|
-
// Upsample: 9-tap tent, progressively added to matching downsample mip. Blender radius = sample scale.
|
|
1084
827
|
const bloomUpsampleShader = this.device.createShaderModule({
|
|
1085
828
|
label: "bloom upsample 9-tap tent",
|
|
1086
|
-
code:
|
|
1087
|
-
@group(0) @binding(0) var srcTex: texture_2d<f32>; // coarser accumulator
|
|
1088
|
-
@group(0) @binding(1) var baseTex: texture_2d<f32>; // matching downsample mip
|
|
1089
|
-
@group(0) @binding(2) var srcSamp: sampler;
|
|
1090
|
-
@group(0) @binding(3) var<uniform> upU: vec4<f32>; // sampleScale, _, _, _
|
|
1091
|
-
|
|
1092
|
-
@fragment fn fs(@builtin(position) p: vec4f) -> @location(0) vec4f {
|
|
1093
|
-
let srcDims = vec2f(textureDimensions(srcTex));
|
|
1094
|
-
let baseDims = vec2f(textureDimensions(baseTex));
|
|
1095
|
-
let uv = p.xy / max(baseDims, vec2f(1.0));
|
|
1096
|
-
let t = upU.x / srcDims;
|
|
1097
|
-
var o = textureSampleLevel(srcTex, srcSamp, uv + t * vec2f(-1.0, -1.0), 0.0).rgb * 1.0;
|
|
1098
|
-
o = o + textureSampleLevel(srcTex, srcSamp, uv + t * vec2f( 0.0, -1.0), 0.0).rgb * 2.0;
|
|
1099
|
-
o = o + textureSampleLevel(srcTex, srcSamp, uv + t * vec2f( 1.0, -1.0), 0.0).rgb * 1.0;
|
|
1100
|
-
o = o + textureSampleLevel(srcTex, srcSamp, uv + t * vec2f(-1.0, 0.0), 0.0).rgb * 2.0;
|
|
1101
|
-
o = o + textureSampleLevel(srcTex, srcSamp, uv + t * vec2f( 0.0, 0.0), 0.0).rgb * 4.0;
|
|
1102
|
-
o = o + textureSampleLevel(srcTex, srcSamp, uv + t * vec2f( 1.0, 0.0), 0.0).rgb * 2.0;
|
|
1103
|
-
o = o + textureSampleLevel(srcTex, srcSamp, uv + t * vec2f(-1.0, 1.0), 0.0).rgb * 1.0;
|
|
1104
|
-
o = o + textureSampleLevel(srcTex, srcSamp, uv + t * vec2f( 0.0, 1.0), 0.0).rgb * 2.0;
|
|
1105
|
-
o = o + textureSampleLevel(srcTex, srcSamp, uv + t * vec2f( 1.0, 1.0), 0.0).rgb * 1.0;
|
|
1106
|
-
o = o * (1.0 / 16.0);
|
|
1107
|
-
let base = textureSampleLevel(baseTex, srcSamp, uv, 0.0).rgb;
|
|
1108
|
-
return vec4f(o + base, 1.0);
|
|
1109
|
-
}
|
|
1110
|
-
`,
|
|
829
|
+
code: BLOOM_UPSAMPLE_SHADER_WGSL,
|
|
1111
830
|
});
|
|
1112
831
|
const bloomBlitLayout = this.device.createPipelineLayout({ bindGroupLayouts: [this.bloomBlitBindGroupLayout] });
|
|
1113
832
|
const bloomDownLayout = this.device.createPipelineLayout({
|
|
@@ -1154,67 +873,25 @@ export class Engine {
|
|
|
1154
873
|
});
|
|
1155
874
|
const compositeShader = this.device.createShaderModule({
|
|
1156
875
|
label: "composite shader",
|
|
1157
|
-
code:
|
|
1158
|
-
@group(0) @binding(0) var hdrTex: texture_2d<f32>;
|
|
1159
|
-
@group(0) @binding(1) var bloomTex: texture_2d<f32>; // bloomUpTexture mip 0 (full pyramid top)
|
|
1160
|
-
@group(0) @binding(2) var bloomSamp: sampler;
|
|
1161
|
-
@group(0) @binding(3) var<uniform> viewU: array<vec4<f32>, 2>;
|
|
1162
|
-
// viewU[0] = (exposure, gamma, _, _); viewU[1] = (tint.rgb, intensity)
|
|
1163
|
-
|
|
1164
|
-
fn filmic(x: f32) -> f32 {
|
|
1165
|
-
// Re-fit against Blender 3.6 Filmic MHC anchors (sobotka/filmic-blender
|
|
1166
|
-
// look_medium-high-contrast.spi1d). Previous curve was compressed:
|
|
1167
|
-
// midtones too bright, highlights too dim — flattened contrast, read
|
|
1168
|
-
// as "washed-out" on saturated surfaces (hair especially).
|
|
1169
|
-
// Reference checkpoints: linear 0.18 → ~0.395, linear 1.0 → ~0.83.
|
|
1170
|
-
var lut = array<f32, 14>(
|
|
1171
|
-
0.0028, 0.0068, 0.0151, 0.0313, 0.0610, 0.1120, 0.1920,
|
|
1172
|
-
0.3060, 0.4590, 0.6310, 0.8200, 0.9070, 0.9620, 0.9890
|
|
1173
|
-
);
|
|
1174
|
-
let t = clamp(log2(max(x, 1e-10)) + 10.0, 0.0, 13.0);
|
|
1175
|
-
let i = u32(t);
|
|
1176
|
-
let j = min(i + 1u, 13u);
|
|
1177
|
-
return mix(lut[i], lut[j], t - f32(i));
|
|
1178
|
-
}
|
|
1179
|
-
|
|
1180
|
-
@vertex fn vs(@builtin(vertex_index) vi: u32) -> @builtin(position) vec4f {
|
|
1181
|
-
let x = f32((vi & 1u) << 2u) - 1.0;
|
|
1182
|
-
let y = f32((vi & 2u) << 1u) - 1.0;
|
|
1183
|
-
return vec4f(x, y, 0.0, 1.0);
|
|
1184
|
-
}
|
|
1185
|
-
|
|
1186
|
-
@fragment fn fs(@builtin(position) fragCoord: vec4f) -> @location(0) vec4f {
|
|
1187
|
-
let hdr = textureLoad(hdrTex, vec2<i32>(fragCoord.xy), 0);
|
|
1188
|
-
let a = max(hdr.a, 1e-6);
|
|
1189
|
-
let straight = hdr.rgb / a;
|
|
1190
|
-
let fullSz = vec2f(textureDimensions(hdrTex));
|
|
1191
|
-
let bloomSz = vec2f(textureDimensions(bloomTex));
|
|
1192
|
-
// Bloom is at half-res (pyramid mip 0). Sampler interpolates back to full-res UVs.
|
|
1193
|
-
// fragCoord.xy is already at pixel center (e.g. 0.5, 0.5 for first pixel).
|
|
1194
|
-
let bloomUv = fragCoord.xy / max(fullSz, vec2f(1.0));
|
|
1195
|
-
let tint = viewU[1].xyz;
|
|
1196
|
-
let intensity = viewU[1].w;
|
|
1197
|
-
let bloom = textureSampleLevel(bloomTex, bloomSamp, bloomUv, 0.0).rgb * tint * intensity;
|
|
1198
|
-
let combined = straight + bloom;
|
|
1199
|
-
let exposed = combined * exp2(viewU[0].x);
|
|
1200
|
-
let tm = vec3f(filmic(exposed.r), filmic(exposed.g), filmic(exposed.b));
|
|
1201
|
-
let g = max(viewU[0].y, 1e-4);
|
|
1202
|
-
let disp = pow(max(tm, vec3f(0.0)), vec3f(1.0 / g));
|
|
1203
|
-
return vec4f(disp * hdr.a, hdr.a);
|
|
1204
|
-
}
|
|
1205
|
-
`,
|
|
876
|
+
code: COMPOSITE_SHADER_WGSL,
|
|
1206
877
|
});
|
|
1207
|
-
|
|
1208
|
-
|
|
1209
|
-
|
|
878
|
+
const compositePipelineLayout = this.device.createPipelineLayout({
|
|
879
|
+
bindGroupLayouts: [this.compositeBindGroupLayout],
|
|
880
|
+
});
|
|
881
|
+
const makeCompositePipeline = (applyGamma, label) => this.device.createRenderPipeline({
|
|
882
|
+
label,
|
|
883
|
+
layout: compositePipelineLayout,
|
|
1210
884
|
vertex: { module: compositeShader, entryPoint: "vs" },
|
|
1211
885
|
fragment: {
|
|
1212
886
|
module: compositeShader,
|
|
1213
887
|
entryPoint: "fs",
|
|
888
|
+
constants: { APPLY_GAMMA: applyGamma ? 1 : 0 },
|
|
1214
889
|
targets: [{ format: this.presentationFormat }],
|
|
1215
890
|
},
|
|
1216
891
|
primitive: { topology: "triangle-list" },
|
|
1217
892
|
});
|
|
893
|
+
this.compositePipelineIdentity = makeCompositePipeline(false, "composite pipeline (gamma=1)");
|
|
894
|
+
this.compositePipelineGamma = makeCompositePipeline(true, "composite pipeline (gamma!=1)");
|
|
1218
895
|
this.bloomPassDescriptor = {
|
|
1219
896
|
label: "bloom pass",
|
|
1220
897
|
colorAttachments: [
|
|
@@ -1226,47 +903,9 @@ export class Engine {
|
|
|
1226
903
|
},
|
|
1227
904
|
],
|
|
1228
905
|
};
|
|
1229
|
-
// GPU picking: encode (modelIndex, materialIndex) as color
|
|
1230
906
|
const pickShaderModule = this.device.createShaderModule({
|
|
1231
907
|
label: "pick shader",
|
|
1232
|
-
code:
|
|
1233
|
-
struct CameraUniforms {
|
|
1234
|
-
view: mat4x4f,
|
|
1235
|
-
projection: mat4x4f,
|
|
1236
|
-
viewPos: vec3f,
|
|
1237
|
-
_padding: f32,
|
|
1238
|
-
};
|
|
1239
|
-
struct PickId {
|
|
1240
|
-
modelId: f32,
|
|
1241
|
-
materialId: f32,
|
|
1242
|
-
_p1: f32,
|
|
1243
|
-
_p2: f32,
|
|
1244
|
-
};
|
|
1245
|
-
|
|
1246
|
-
@group(0) @binding(0) var<uniform> camera: CameraUniforms;
|
|
1247
|
-
@group(1) @binding(0) var<storage, read> skinMats: array<mat4x4f>;
|
|
1248
|
-
@group(2) @binding(0) var<uniform> pickId: PickId;
|
|
1249
|
-
|
|
1250
|
-
@vertex fn vs(
|
|
1251
|
-
@location(0) position: vec3f,
|
|
1252
|
-
@location(1) normal: vec3f,
|
|
1253
|
-
@location(2) uv: vec2f,
|
|
1254
|
-
@location(3) joints0: vec4<u32>,
|
|
1255
|
-
@location(4) weights0: vec4<f32>
|
|
1256
|
-
) -> @builtin(position) vec4f {
|
|
1257
|
-
let pos4 = vec4f(position, 1.0);
|
|
1258
|
-
let weightSum = weights0.x + weights0.y + weights0.z + weights0.w;
|
|
1259
|
-
let invWeightSum = select(1.0, 1.0 / weightSum, weightSum > 0.0001);
|
|
1260
|
-
let nw = select(vec4f(1.0, 0.0, 0.0, 0.0), weights0 * invWeightSum, weightSum > 0.0001);
|
|
1261
|
-
var sp = vec4f(0.0);
|
|
1262
|
-
for (var i = 0u; i < 4u; i++) { sp += (skinMats[joints0[i]] * pos4) * nw[i]; }
|
|
1263
|
-
return camera.projection * camera.view * vec4f(sp.xyz, 1.0);
|
|
1264
|
-
}
|
|
1265
|
-
|
|
1266
|
-
@fragment fn fs() -> @location(0) vec4f {
|
|
1267
|
-
return vec4f(pickId.modelId / 255.0, pickId.materialId / 255.0, 0.0, 1.0);
|
|
1268
|
-
}
|
|
1269
|
-
`,
|
|
908
|
+
code: PICK_SHADER_WGSL,
|
|
1270
909
|
});
|
|
1271
910
|
this.pickPerFrameBindGroupLayout = this.device.createBindGroupLayout({
|
|
1272
911
|
label: "pick per-frame layout",
|
|
@@ -1400,19 +1039,23 @@ export class Engine {
|
|
|
1400
1039
|
usage: GPUTextureUsage.RENDER_ATTACHMENT,
|
|
1401
1040
|
});
|
|
1402
1041
|
const depthTextureView = this.depthTexture.createView();
|
|
1042
|
+
// storeOp="discard" on MSAA views keeps per-sample data in Apple TBDR tile memory —
|
|
1043
|
+
// only the resolveTarget (hdrResolveTexture / maskResolveView) gets written to RAM.
|
|
1044
|
+
// With storeOp="store" Safari's Metal backend spills the full MS buffer every frame
|
|
1045
|
+
// (rgba16f × 4 samples on a 4K canvas ≈ 256 MB/frame of dead bandwidth).
|
|
1403
1046
|
const colorAttachment = {
|
|
1404
1047
|
view: this.multisampleTexture.createView(),
|
|
1405
1048
|
resolveTarget: this.hdrResolveTexture.createView(),
|
|
1406
1049
|
clearValue: { r: 0, g: 0, b: 0, a: 0 },
|
|
1407
1050
|
loadOp: "clear",
|
|
1408
|
-
storeOp: "
|
|
1051
|
+
storeOp: "discard",
|
|
1409
1052
|
};
|
|
1410
1053
|
const maskAttachment = {
|
|
1411
1054
|
view: this.multisampleMaskTexture.createView(),
|
|
1412
1055
|
resolveTarget: this.maskResolveView,
|
|
1413
1056
|
clearValue: { r: 0, g: 0, b: 0, a: 0 },
|
|
1414
1057
|
loadOp: "clear",
|
|
1415
|
-
storeOp: "
|
|
1058
|
+
storeOp: "discard",
|
|
1416
1059
|
};
|
|
1417
1060
|
this.renderPassDescriptor = {
|
|
1418
1061
|
label: "renderPass",
|
|
@@ -1421,7 +1064,8 @@ export class Engine {
|
|
|
1421
1064
|
view: depthTextureView,
|
|
1422
1065
|
depthClearValue: 1.0,
|
|
1423
1066
|
depthLoadOp: "clear",
|
|
1424
|
-
|
|
1067
|
+
// Main-pass depth is not sampled later (shadow uses its own map, composite is depthless).
|
|
1068
|
+
depthStoreOp: "discard",
|
|
1425
1069
|
stencilClearValue: 0,
|
|
1426
1070
|
stencilLoadOp: "clear",
|
|
1427
1071
|
stencilStoreOp: "discard",
|
|
@@ -2213,20 +1857,7 @@ export class Engine {
|
|
|
2213
1857
|
});
|
|
2214
1858
|
const module = this.device.createShaderModule({
|
|
2215
1859
|
label: "mipmap blit",
|
|
2216
|
-
code:
|
|
2217
|
-
@group(0) @binding(0) var src: texture_2d<f32>;
|
|
2218
|
-
@group(0) @binding(1) var samp: sampler;
|
|
2219
|
-
@vertex fn vs(@builtin(vertex_index) vi: u32) -> @builtin(position) vec4f {
|
|
2220
|
-
let x = f32((vi & 1u) << 2u) - 1.0;
|
|
2221
|
-
let y = f32((vi & 2u) << 1u) - 1.0;
|
|
2222
|
-
return vec4f(x, y, 0.0, 1.0);
|
|
2223
|
-
}
|
|
2224
|
-
@fragment fn fs(@builtin(position) p: vec4f) -> @location(0) vec4f {
|
|
2225
|
-
let dstDims = vec2f(textureDimensions(src)) * 0.5;
|
|
2226
|
-
let uv = p.xy / max(dstDims, vec2f(1.0));
|
|
2227
|
-
return textureSampleLevel(src, samp, uv, 0.0);
|
|
2228
|
-
}
|
|
2229
|
-
`,
|
|
1860
|
+
code: MIPMAP_BLIT_SHADER_WGSL,
|
|
2230
1861
|
});
|
|
2231
1862
|
this.mipBlitPipeline = this.device.createRenderPipeline({
|
|
2232
1863
|
label: "mipmap blit pipeline",
|
|
@@ -2441,7 +2072,8 @@ export class Engine {
|
|
|
2441
2072
|
const compositeAttachment = this.compositePassDescriptor.colorAttachments[0];
|
|
2442
2073
|
compositeAttachment.view = this.context.getCurrentTexture().createView();
|
|
2443
2074
|
const cpass = encoder.beginRenderPass(this.compositePassDescriptor);
|
|
2444
|
-
|
|
2075
|
+
const compositePipeline = this.viewTransform.gamma === 1.0 ? this.compositePipelineIdentity : this.compositePipelineGamma;
|
|
2076
|
+
cpass.setPipeline(compositePipeline);
|
|
2445
2077
|
cpass.setBindGroup(0, this.compositeBindGroup);
|
|
2446
2078
|
cpass.draw(3);
|
|
2447
2079
|
cpass.end();
|
package/dist/index.d.ts
CHANGED
|
@@ -1,9 +1,8 @@
|
|
|
1
|
-
export { Engine, DEFAULT_BLOOM_OPTIONS, DEFAULT_VIEW_TRANSFORM, type EngineStats, type EngineOptions, type BloomOptions, type ViewTransformOptions, type LoadModelFromFilesOptions, } from "./engine";
|
|
1
|
+
export { Engine, DEFAULT_BLOOM_OPTIONS, DEFAULT_VIEW_TRANSFORM, type EngineStats, type EngineOptions, type BloomOptions, type ViewTransformOptions, type LoadModelFromFilesOptions, type MaterialPreset, type MaterialPresetMap, } from "./engine";
|
|
2
2
|
export { parsePmxFolderInput, pmxFileAtRelativePath, type PmxFolderInputResult } from "./folder-upload";
|
|
3
3
|
export { Model } from "./model";
|
|
4
4
|
export { Vec3, Quat, Mat4 } from "./math";
|
|
5
5
|
export type { AnimationClip, AnimationPlayOptions, AnimationProgress, BoneKeyframe, MorphKeyframe, BoneInterpolation, ControlPoint, } from "./animation";
|
|
6
6
|
export { FPS } from "./animation";
|
|
7
7
|
export { Physics, type PhysicsOptions } from "./physics";
|
|
8
|
-
export type { MaterialPreset, MaterialPresetMap } from "./shaders/classify";
|
|
9
8
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,MAAM,EACN,qBAAqB,EACrB,sBAAsB,EACtB,KAAK,WAAW,EAChB,KAAK,aAAa,EAClB,KAAK,YAAY,EACjB,KAAK,oBAAoB,EACzB,KAAK,yBAAyB,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,MAAM,EACN,qBAAqB,EACrB,sBAAsB,EACtB,KAAK,WAAW,EAChB,KAAK,aAAa,EAClB,KAAK,YAAY,EACjB,KAAK,oBAAoB,EACzB,KAAK,yBAAyB,EAC9B,KAAK,cAAc,EACnB,KAAK,iBAAiB,GACvB,MAAM,UAAU,CAAA;AACjB,OAAO,EAAE,mBAAmB,EAAE,qBAAqB,EAAE,KAAK,oBAAoB,EAAE,MAAM,iBAAiB,CAAA;AACvG,OAAO,EAAE,KAAK,EAAE,MAAM,SAAS,CAAA;AAC/B,OAAO,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,MAAM,QAAQ,CAAA;AACzC,YAAY,EACV,aAAa,EACb,oBAAoB,EACpB,iBAAiB,EACjB,YAAY,EACZ,aAAa,EACb,iBAAiB,EACjB,YAAY,GACb,MAAM,aAAa,CAAA;AACpB,OAAO,EAAE,GAAG,EAAE,MAAM,aAAa,CAAA;AACjC,OAAO,EAAE,OAAO,EAAE,KAAK,cAAc,EAAE,MAAM,WAAW,CAAA"}
|