matrix-engine-wgpu 1.3.19 → 1.4.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/examples/camera-texture.js +2 -0
- package/examples/load-obj-file.js +9 -13
- package/examples/load-objs-sequence.js +8 -0
- package/examples/video-texture.js +13 -7
- package/package.json +4 -2
- package/public/examples.js +707 -543
- package/readme.md +19 -8
- package/src/engine/engine.js +1 -1
- package/src/engine/lights.js +174 -87
- package/src/engine/materials.js +129 -68
- package/src/engine/mesh-obj.js +96 -176
- package/src/physics/matrix-ammo.js +0 -42
- package/src/shaders/fragment.video.wgsl.js +3 -2
- package/src/shaders/fragment.wgsl.js +113 -51
- package/src/shaders/shaders.js +0 -1
- package/src/shaders/vertex.wgsl.js +6 -20
- package/src/world.js +207 -127
|
@@ -1,75 +1,137 @@
|
|
|
1
1
|
export let fragmentWGSL = `override shadowDepthTextureSize: f32 = 1024.0;
|
|
2
2
|
|
|
3
|
+
// Created by Nikola Lukic with chatgtp assist.
|
|
4
|
+
|
|
3
5
|
struct Scene {
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
6
|
+
lightViewProjMatrix : mat4x4f,
|
|
7
|
+
cameraViewProjMatrix : mat4x4f,
|
|
8
|
+
cameraPos : vec3f,
|
|
9
|
+
padding2 : f32, // align to 16 bytes
|
|
10
|
+
lightPos : vec3f,
|
|
11
|
+
padding : f32, // align to 16 bytes
|
|
12
|
+
};
|
|
9
13
|
|
|
10
14
|
struct SpotLight {
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
15
|
+
position : vec3f,
|
|
16
|
+
_pad1 : f32,
|
|
17
|
+
|
|
18
|
+
direction : vec3f,
|
|
19
|
+
_pad2 : f32,
|
|
20
|
+
|
|
21
|
+
innerCutoff : f32,
|
|
22
|
+
outerCutoff : f32,
|
|
23
|
+
intensity : f32,
|
|
24
|
+
_pad3 : f32,
|
|
25
|
+
|
|
26
|
+
color : vec3f,
|
|
27
|
+
_pad4 : f32,
|
|
28
|
+
|
|
29
|
+
range : f32,
|
|
30
|
+
ambientFactor : f32,
|
|
31
|
+
shadowBias : f32,
|
|
32
|
+
_pad5 : f32,
|
|
33
|
+
|
|
34
|
+
lightViewProj : mat4x4<f32>,
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
const MAX_SPOTLIGHTS = 20u;
|
|
19
38
|
|
|
20
39
|
@group(0) @binding(0) var<uniform> scene : Scene;
|
|
21
|
-
@group(0) @binding(1) var
|
|
40
|
+
@group(0) @binding(1) var shadowMapArray: texture_depth_2d_array;
|
|
22
41
|
@group(0) @binding(2) var shadowSampler: sampler_comparison;
|
|
23
42
|
@group(0) @binding(3) var meshTexture: texture_2d<f32>;
|
|
24
43
|
@group(0) @binding(4) var meshSampler: sampler;
|
|
25
|
-
@group(0) @binding(5) var<uniform>
|
|
44
|
+
@group(0) @binding(5) var<uniform> spotlights: array<SpotLight, MAX_SPOTLIGHTS>;
|
|
26
45
|
|
|
27
46
|
struct FragmentInput {
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
47
|
+
@location(0) shadowPos : vec4f,
|
|
48
|
+
@location(1) fragPos : vec3f,
|
|
49
|
+
@location(2) fragNorm : vec3f,
|
|
50
|
+
@location(3) uv : vec2f,
|
|
32
51
|
}
|
|
33
52
|
|
|
34
53
|
const albedo = vec3f(0.9);
|
|
35
|
-
const ambientFactor = 0.7;
|
|
36
54
|
|
|
37
55
|
fn calculateSpotlightFactor(light: SpotLight, fragPos: vec3f) -> f32 {
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
return intensity;
|
|
56
|
+
let L = normalize(light.position - fragPos);
|
|
57
|
+
let theta = dot(L, normalize(-light.direction));
|
|
58
|
+
let epsilon = light.innerCutoff - light.outerCutoff;
|
|
59
|
+
return clamp((theta - light.outerCutoff) / epsilon, 0.0, 1.0);
|
|
43
60
|
}
|
|
44
61
|
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
let offset = vec2f(vec2(x, y)) * oneOverSize;
|
|
53
|
-
visibility += textureSampleCompare(
|
|
54
|
-
shadowMap, shadowSampler,
|
|
55
|
-
input.shadowPos.xy + offset, input.shadowPos.z - 0.007
|
|
56
|
-
);
|
|
57
|
-
}
|
|
58
|
-
}
|
|
59
|
-
visibility /= 9.0;
|
|
62
|
+
fn computeSpotLight(light: SpotLight, normal: vec3f, fragPos: vec3f, viewDir: vec3f) -> vec3f {
|
|
63
|
+
let L = light.position - fragPos;
|
|
64
|
+
let distance = length(L);
|
|
65
|
+
let lightDir = normalize(L);
|
|
66
|
+
|
|
67
|
+
let spotFactor = calculateSpotlightFactor(light, fragPos);
|
|
68
|
+
let atten = clamp(1.0 - (distance / light.range), 0.0, 1.0);
|
|
60
69
|
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
let lambert = max(dot(norm, lightDir), 0.0);
|
|
70
|
+
let diff = max(dot(normal, lightDir), 0.0);
|
|
71
|
+
let halfwayDir = normalize(lightDir + viewDir);
|
|
72
|
+
let spec = pow(max(dot(normal, halfwayDir), 0.0), 32.0);
|
|
65
73
|
|
|
66
|
-
|
|
67
|
-
|
|
74
|
+
let diffuse = diff * light.color * light.intensity * atten;
|
|
75
|
+
let specular = spec * light.color * light.intensity * atten;
|
|
68
76
|
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
77
|
+
return (diffuse + specular) * spotFactor;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
// Corrected PCF for texture_depth_2d_array
|
|
81
|
+
fn sampleShadow(shadowUV: vec2f, layer: i32, depthRef: f32, normal: vec3f, lightDir: vec3f) -> f32 {
|
|
82
|
+
var visibility: f32 = 0.0;
|
|
83
|
+
let biasConstant: f32 = 0.001;
|
|
84
|
+
// Slope bias: avoid self-shadowing on steep angles
|
|
85
|
+
// let slopeBias: f32 = max(0.002 * (1.0 - dot(normal, lightDir)), 0.0);
|
|
86
|
+
let bias = biasConstant;// + slopeBias;
|
|
87
|
+
|
|
88
|
+
let oneOverSize = 1.0 / (shadowDepthTextureSize * 0.5);
|
|
72
89
|
|
|
73
|
-
|
|
90
|
+
// 3x3 PCF kernel
|
|
91
|
+
let offsets: array<vec2f, 9> = array<vec2f, 9>(
|
|
92
|
+
vec2(-1.0, -1.0), vec2(0.0, -1.0), vec2(1.0, -1.0),
|
|
93
|
+
vec2(-1.0, 0.0), vec2(0.0, 0.0), vec2(1.0, 0.0),
|
|
94
|
+
vec2(-1.0, 1.0), vec2(0.0, 1.0), vec2(1.0, 1.0)
|
|
95
|
+
);
|
|
96
|
+
|
|
97
|
+
for(var i: u32 = 0u; i < 9u; i = i + 1u) {
|
|
98
|
+
visibility += textureSampleCompare(
|
|
99
|
+
shadowMapArray,
|
|
100
|
+
shadowSampler,
|
|
101
|
+
shadowUV + offsets[i] * oneOverSize,
|
|
102
|
+
layer,
|
|
103
|
+
depthRef //+ bias
|
|
104
|
+
);
|
|
105
|
+
}
|
|
106
|
+
return visibility / 9.0;
|
|
74
107
|
}
|
|
75
|
-
|
|
108
|
+
|
|
109
|
+
@fragment
|
|
110
|
+
fn main(input: FragmentInput) -> @location(0) vec4f {
|
|
111
|
+
let norm = normalize(input.fragNorm);
|
|
112
|
+
|
|
113
|
+
let viewDir = normalize(scene.cameraPos - input.fragPos);
|
|
114
|
+
// let viewDir = normalize(scene.cameraViewProjMatrix[3].xyz - input.fragPos);
|
|
115
|
+
|
|
116
|
+
var lightContribution = vec3f(0.0);
|
|
117
|
+
var ambient = vec3f(0.0);
|
|
118
|
+
|
|
119
|
+
for (var i: u32 = 0u; i < MAX_SPOTLIGHTS; i = i + 1u) {
|
|
120
|
+
let sc = spotlights[i].lightViewProj * vec4<f32>(input.fragPos, 1.0);
|
|
121
|
+
let p = sc.xyz / sc.w;
|
|
122
|
+
let uv = clamp(p.xy * 0.5 + vec2<f32>(0.5), vec2<f32>(0.0), vec2<f32>(1.0));
|
|
123
|
+
let depthRef = p.z * 0.5 + 0.5;
|
|
124
|
+
let lightDir = normalize(spotlights[i].position - input.fragPos);
|
|
125
|
+
let angleFactor = 1.0 - dot(norm, lightDir);
|
|
126
|
+
let slopeBias = 0.01 * (1.0 - dot(norm, lightDir));
|
|
127
|
+
let bias = spotlights[i].shadowBias + slopeBias;
|
|
128
|
+
let visibility = sampleShadow(uv, i32(i), depthRef - bias, norm, lightDir);
|
|
129
|
+
let contrib = computeSpotLight(spotlights[i], norm, input.fragPos, viewDir);
|
|
130
|
+
lightContribution += contrib * visibility;
|
|
131
|
+
ambient += spotlights[i].ambientFactor * spotlights[i].color;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
let texColor = textureSample(meshTexture, meshSampler, input.uv);
|
|
135
|
+
let finalColor = texColor.rgb * (ambient + lightContribution); // * albedo;
|
|
136
|
+
return vec4f(finalColor, 1.0);
|
|
137
|
+
}`;
|
package/src/shaders/shaders.js
CHANGED
|
@@ -12,11 +12,10 @@ struct Model {
|
|
|
12
12
|
@group(1) @binding(0) var<uniform> model : Model;
|
|
13
13
|
|
|
14
14
|
struct VertexOutput {
|
|
15
|
-
@location(0) shadowPos:
|
|
15
|
+
@location(0) shadowPos: vec4f, // now vec4
|
|
16
16
|
@location(1) fragPos: vec3f,
|
|
17
17
|
@location(2) fragNorm: vec3f,
|
|
18
|
-
@location(3) uv
|
|
19
|
-
|
|
18
|
+
@location(3) uv: vec2f,
|
|
20
19
|
@builtin(position) Position: vec4f,
|
|
21
20
|
}
|
|
22
21
|
|
|
@@ -24,31 +23,18 @@ struct VertexOutput {
|
|
|
24
23
|
fn main(
|
|
25
24
|
@location(0) position: vec3f,
|
|
26
25
|
@location(1) normal: vec3f,
|
|
27
|
-
@location(2) uv
|
|
26
|
+
@location(2) uv: vec2f
|
|
28
27
|
) -> VertexOutput {
|
|
29
28
|
var output : VertexOutput;
|
|
30
29
|
|
|
31
|
-
// XY is in (-1, 1) space, Z is in (0, 1) space
|
|
32
30
|
let posFromLight = scene.lightViewProjMatrix * model.modelMatrix * vec4(position, 1.0);
|
|
33
|
-
|
|
34
|
-
// Convert XY to (0, 1)
|
|
35
|
-
// Y is flipped because texture coords are Y-down.
|
|
36
|
-
output.shadowPos = vec3(
|
|
37
|
-
posFromLight.xy * vec2(0.5, -0.5) + vec2(0.5),
|
|
38
|
-
posFromLight.z
|
|
39
|
-
);
|
|
40
|
-
|
|
41
|
-
// follewed camera code
|
|
42
|
-
// output.Position = scene.cameraViewProjMatrix * model.modelMatrix * vec4(position, 1.0);
|
|
43
|
-
// output.fragPos = output.Position.xyz;
|
|
44
|
-
// output.fragNorm = normal;
|
|
31
|
+
output.shadowPos = posFromLight; // pass full vec4 for perspective divide
|
|
45
32
|
|
|
46
33
|
let worldPos = model.modelMatrix * vec4(position, 1.0);
|
|
47
34
|
output.Position = scene.cameraViewProjMatrix * worldPos;
|
|
48
|
-
output.fragPos = worldPos.xyz;
|
|
35
|
+
output.fragPos = worldPos.xyz;
|
|
49
36
|
|
|
50
37
|
output.fragNorm = normalize((model.modelMatrix * vec4(normal, 0.0)).xyz);
|
|
51
38
|
output.uv = uv;
|
|
52
39
|
return output;
|
|
53
|
-
}
|
|
54
|
-
`;
|
|
40
|
+
}`;
|