matrix-engine-wgpu 1.3.19 → 1.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/examples/load-obj-file.js +4 -8
- package/package.json +1 -1
- package/public/examples.js +10895 -10960
- package/readme.md +1 -1
- package/src/engine/lights.js +23 -36
- package/src/engine/materials.js +5 -6
- package/src/engine/mesh-obj.js +6 -26
- package/src/physics/matrix-ammo.js +0 -42
- package/src/shaders/fragment.wgsl.js +77 -45
- package/src/world.js +42 -72
package/readme.md
CHANGED
|
@@ -162,7 +162,7 @@ Features
|
|
|
162
162
|
|
|
163
163
|
✅ Supports multiple lights (attach as many as you want to the scene)
|
|
164
164
|
|
|
165
|
-
✅ Shadow-ready (
|
|
165
|
+
✅ Shadow-ready (spotlight0 shadows implemented, extendable to others)
|
|
166
166
|
|
|
167
167
|
|
|
168
168
|
### Object Interaction (Raycasting)
|
package/src/engine/lights.js
CHANGED
|
@@ -30,11 +30,7 @@ export class SpotLight {
|
|
|
30
30
|
inputHandler,
|
|
31
31
|
position = vec3.create(0, 5, -10),
|
|
32
32
|
target = vec3.create(0, 0, 0),
|
|
33
|
-
fov = 45,
|
|
34
|
-
aspect = 1.0,
|
|
35
|
-
near = 0.1,
|
|
36
|
-
far = 200
|
|
37
|
-
) {
|
|
33
|
+
fov = 45, aspect = 1.0, near = 0.1, far = 200) {
|
|
38
34
|
this.camera = camera;
|
|
39
35
|
this.inputHandler = inputHandler;
|
|
40
36
|
|
|
@@ -42,6 +38,8 @@ export class SpotLight {
|
|
|
42
38
|
this.target = target;
|
|
43
39
|
this.up = vec3.create(0, 1, 0);
|
|
44
40
|
this.direction = vec3.normalize(vec3.subtract(target, position));
|
|
41
|
+
this.intensity = 1.0;
|
|
42
|
+
this.color = vec3.create(1.0, 1.0, 1.0); // white
|
|
45
43
|
|
|
46
44
|
this.viewMatrix = mat4.lookAt(position, target, this.up);
|
|
47
45
|
this.projectionMatrix = mat4.perspective(
|
|
@@ -59,14 +57,12 @@ export class SpotLight {
|
|
|
59
57
|
|
|
60
58
|
this.innerCutoff = Math.cos((Math.PI / 180) * 12.5);
|
|
61
59
|
this.outerCutoff = Math.cos((Math.PI / 180) * 17.5);
|
|
60
|
+
|
|
61
|
+
this.ambientFactor = 0.5;
|
|
62
|
+
this.range = 200.0; // example max distance
|
|
62
63
|
}
|
|
63
64
|
|
|
64
65
|
update() {
|
|
65
|
-
// this.direction = vec3.normalize(vec3.subtract(this.target, this.position));
|
|
66
|
-
// this.viewMatrix = mat4.lookAt(this.position, this.target, this.up);
|
|
67
|
-
// this.viewProjMatrix = mat4.multiply(this.projectionMatrix, this.viewMatrix);
|
|
68
|
-
// console.log('test light update this.target : ', this.target)
|
|
69
|
-
// Use the existing direction
|
|
70
66
|
const target = vec3.add(this.position, this.direction);
|
|
71
67
|
this.viewMatrix = mat4.lookAt(this.position, target, this.up);
|
|
72
68
|
this.viewProjMatrix = mat4.multiply(this.projectionMatrix, this.viewMatrix);
|
|
@@ -92,29 +88,14 @@ export class SpotLight {
|
|
|
92
88
|
camVP.byteLength
|
|
93
89
|
);
|
|
94
90
|
}
|
|
95
|
-
// const camVP = mat4.multiply(camera.projectionMatrix, camera.view);
|
|
96
|
-
// const sceneData = new Float32Array(36); // 16 + 16 + 4
|
|
97
|
-
// sceneData.set(this.viewProjMatrix, 0);
|
|
98
|
-
// sceneData.set(camVP, 16);
|
|
99
|
-
// sceneData.set(this.position, 32);
|
|
100
|
-
// if(!this.device) {
|
|
101
|
-
// console.warn("Device not set for SpotLight");
|
|
102
|
-
// return;
|
|
103
|
-
// }
|
|
104
|
-
// this.device.queue.writeBuffer(
|
|
105
|
-
// sceneUniformBuffer,
|
|
106
|
-
// // this.spotlightUniformBuffer,
|
|
107
|
-
// 0,
|
|
108
|
-
// sceneData.buffer,
|
|
109
|
-
// sceneData.byteOffset,
|
|
110
|
-
// sceneData.byteLength
|
|
111
|
-
// );
|
|
112
91
|
}
|
|
113
92
|
|
|
93
|
+
// DEPLACED
|
|
114
94
|
prepareBuffer(device) {
|
|
115
95
|
if(!this.device) this.device = device;
|
|
116
96
|
this.spotlightUniformBuffer = this.device.createBuffer({
|
|
117
|
-
|
|
97
|
+
label: 'spotlightUniformBuffer',
|
|
98
|
+
size: 80,
|
|
118
99
|
usage: GPUBufferUsage.UNIFORM | GPUBufferUsage.COPY_DST,
|
|
119
100
|
});
|
|
120
101
|
|
|
@@ -128,6 +109,7 @@ export class SpotLight {
|
|
|
128
109
|
);
|
|
129
110
|
}
|
|
130
111
|
|
|
112
|
+
// DEPLACED
|
|
131
113
|
updateLightBuffer() {
|
|
132
114
|
if(!this.device || !this.spotlightUniformBuffer) {return;}
|
|
133
115
|
const spotlightData = this.getLightDataBuffer();
|
|
@@ -141,13 +123,18 @@ export class SpotLight {
|
|
|
141
123
|
}
|
|
142
124
|
|
|
143
125
|
getLightDataBuffer() {
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
126
|
+
return new Float32Array([
|
|
127
|
+
...this.position, 0.0,
|
|
128
|
+
...this.direction, 0.0,
|
|
129
|
+
this.innerCutoff,
|
|
130
|
+
this.outerCutoff,
|
|
131
|
+
this.intensity,
|
|
132
|
+
0.0,
|
|
133
|
+
...this.color,
|
|
134
|
+
0.0,
|
|
135
|
+
this.range,
|
|
136
|
+
this.ambientFactor, // new
|
|
137
|
+
0.0, 0.0, // padding
|
|
138
|
+
]);
|
|
152
139
|
}
|
|
153
140
|
}
|
package/src/engine/materials.js
CHANGED
|
@@ -30,10 +30,10 @@ export default class Materials {
|
|
|
30
30
|
|
|
31
31
|
// Dymmy buffer
|
|
32
32
|
this.dummySpotlightUniformBuffer = this.device.createBuffer({
|
|
33
|
-
size:
|
|
33
|
+
size: 80, // Must match size in shader
|
|
34
34
|
usage: GPUBufferUsage.UNIFORM | GPUBufferUsage.COPY_DST,
|
|
35
35
|
});
|
|
36
|
-
this.device.queue.writeBuffer(this.dummySpotlightUniformBuffer, 0, new Float32Array(16));
|
|
36
|
+
this.device.queue.writeBuffer(this.dummySpotlightUniformBuffer, 0, new Float32Array(16));
|
|
37
37
|
}
|
|
38
38
|
|
|
39
39
|
updatePostFXMode(mode) {
|
|
@@ -155,7 +155,7 @@ export default class Materials {
|
|
|
155
155
|
// ✅ Now
|
|
156
156
|
this.createLayoutForRender();
|
|
157
157
|
this.setupPipeline();
|
|
158
|
-
|
|
158
|
+
|
|
159
159
|
}
|
|
160
160
|
|
|
161
161
|
updateVideoTexture() {
|
|
@@ -172,7 +172,6 @@ export default class Materials {
|
|
|
172
172
|
console.warn("❗Missing res skipping...");
|
|
173
173
|
return;
|
|
174
174
|
}
|
|
175
|
-
// console.log('what is this.lightContainer.length ', this.lightContainer.length)
|
|
176
175
|
if(this.isVideo == true) {
|
|
177
176
|
this.sceneBindGroupForRender = this.device.createBindGroup({
|
|
178
177
|
layout: this.bglForRender,
|
|
@@ -226,8 +225,8 @@ export default class Materials {
|
|
|
226
225
|
},
|
|
227
226
|
{
|
|
228
227
|
binding: 5,
|
|
229
|
-
resource: {buffer: this.
|
|
230
|
-
}
|
|
228
|
+
resource: {buffer: !this.spotlightUniformBuffer ? this.dummySpotlightUniformBuffer : this.spotlightUniformBuffer},
|
|
229
|
+
}
|
|
231
230
|
],
|
|
232
231
|
});
|
|
233
232
|
}
|
package/src/engine/mesh-obj.js
CHANGED
|
@@ -10,16 +10,12 @@ import {fragmentVideoWGSL} from '../shaders/fragment.video.wgsl';
|
|
|
10
10
|
export default class MEMeshObj extends Materials {
|
|
11
11
|
constructor(canvas, device, context, o, sceneUniformBuffer) {
|
|
12
12
|
super(device);
|
|
13
|
-
if(typeof o.name === 'undefined') o.name = genName(
|
|
13
|
+
if(typeof o.name === 'undefined') o.name = genName(3);
|
|
14
14
|
if(typeof o.raycast === 'undefined') {
|
|
15
|
-
this.raycast = {
|
|
16
|
-
enabled: false,
|
|
17
|
-
radius: 2
|
|
18
|
-
};
|
|
15
|
+
this.raycast = {enabled: false, radius: 2};
|
|
19
16
|
} else {
|
|
20
17
|
this.raycast = o.raycast;
|
|
21
18
|
}
|
|
22
|
-
|
|
23
19
|
this.name = o.name;
|
|
24
20
|
this.done = false;
|
|
25
21
|
this.device = device;
|
|
@@ -45,7 +41,6 @@ export default class MEMeshObj extends Materials {
|
|
|
45
41
|
|
|
46
42
|
this.inputHandler = null;
|
|
47
43
|
this.cameras = o.cameras;
|
|
48
|
-
|
|
49
44
|
this.mainCameraParams = {
|
|
50
45
|
type: o.mainCameraParams.type,
|
|
51
46
|
responseCoef: o.mainCameraParams.responseCoef
|
|
@@ -65,7 +60,6 @@ export default class MEMeshObj extends Materials {
|
|
|
65
60
|
this.runProgram = () => {
|
|
66
61
|
return new Promise(async (resolve) => {
|
|
67
62
|
this.shadowDepthTextureSize = 1024;
|
|
68
|
-
// const aspect = canvas.width / canvas.height;
|
|
69
63
|
this.modelViewProjectionMatrix = mat4.create();
|
|
70
64
|
this.loadTex0(this.texturesPaths).then(() => {
|
|
71
65
|
resolve()
|
|
@@ -74,7 +68,6 @@ export default class MEMeshObj extends Materials {
|
|
|
74
68
|
}
|
|
75
69
|
|
|
76
70
|
this.runProgram().then(() => {
|
|
77
|
-
// const aspect = canvas.width / canvas.height;
|
|
78
71
|
this.context.configure({
|
|
79
72
|
device: this.device,
|
|
80
73
|
format: this.presentationFormat,
|
|
@@ -395,20 +388,21 @@ export default class MEMeshObj extends Materials {
|
|
|
395
388
|
}
|
|
396
389
|
|
|
397
390
|
setupPipeline = () => {
|
|
398
|
-
console.log('
|
|
391
|
+
console.log('Set Pipeline✅');
|
|
399
392
|
this.pipeline = this.device.createRenderPipeline({
|
|
393
|
+
label: 'Mesh Pipeline ✅',
|
|
400
394
|
layout: this.device.createPipelineLayout({
|
|
401
395
|
bindGroupLayouts: [this.bglForRender, this.uniformBufferBindGroupLayout],
|
|
402
396
|
}),
|
|
403
397
|
vertex: {
|
|
404
|
-
entryPoint: 'main',
|
|
398
|
+
entryPoint: 'main',
|
|
405
399
|
module: this.device.createShaderModule({
|
|
406
400
|
code: vertexWGSL,
|
|
407
401
|
}),
|
|
408
402
|
buffers: this.vertexBuffers,
|
|
409
403
|
},
|
|
410
404
|
fragment: {
|
|
411
|
-
entryPoint: 'main',
|
|
405
|
+
entryPoint: 'main',
|
|
412
406
|
module: this.device.createShaderModule({
|
|
413
407
|
code: (this.isVideo == true ? fragmentVideoWGSL : fragmentWGSL),
|
|
414
408
|
}),
|
|
@@ -424,7 +418,6 @@ export default class MEMeshObj extends Materials {
|
|
|
424
418
|
depthStencil: {
|
|
425
419
|
depthWriteEnabled: true,
|
|
426
420
|
depthCompare: 'less',
|
|
427
|
-
// format: 'depth24plus-stencil8',
|
|
428
421
|
format: 'depth24plus',
|
|
429
422
|
},
|
|
430
423
|
primitive: this.primitive,
|
|
@@ -432,17 +425,7 @@ export default class MEMeshObj extends Materials {
|
|
|
432
425
|
}
|
|
433
426
|
|
|
434
427
|
draw = () => {
|
|
435
|
-
// This code -> light follow camera. can be used like options later!
|
|
436
|
-
// if(this.done == false) return;
|
|
437
|
-
// const transformationMatrix = this.getTransformationMatrix(this.position);
|
|
438
|
-
// this.device.queue.writeBuffer(this.sceneUniformBuffer, 64, transformationMatrix.buffer, transformationMatrix.byteOffset, transformationMatrix.byteLength);
|
|
439
|
-
// this.renderPassDescriptor.colorAttachments[0].view = this.context
|
|
440
|
-
// .getCurrentTexture()
|
|
441
|
-
// .createView();
|
|
442
|
-
|
|
443
|
-
// test
|
|
444
428
|
if(this.done == false) return;
|
|
445
|
-
|
|
446
429
|
// Per-object model matrix only
|
|
447
430
|
const modelMatrix = this.getModelMatrix(this.position);
|
|
448
431
|
this.device.queue.writeBuffer(
|
|
@@ -452,7 +435,6 @@ export default class MEMeshObj extends Materials {
|
|
|
452
435
|
modelMatrix.byteOffset,
|
|
453
436
|
modelMatrix.byteLength
|
|
454
437
|
);
|
|
455
|
-
|
|
456
438
|
// Acquire swapchain view for the pass
|
|
457
439
|
this.renderPassDescriptor.colorAttachments[0].view =
|
|
458
440
|
this.context.getCurrentTexture().createView();
|
|
@@ -516,7 +498,6 @@ export default class MEMeshObj extends Materials {
|
|
|
516
498
|
});
|
|
517
499
|
new Float32Array(mesh.vertexNormalsBuffer.getMappedRange()).set(mesh.vertexNormals);
|
|
518
500
|
mesh.vertexNormalsBuffer.unmap();
|
|
519
|
-
|
|
520
501
|
// UVs
|
|
521
502
|
mesh.vertexTexCoordsBuffer = this.device.createBuffer({
|
|
522
503
|
size: mesh.textures.length * Float32Array.BYTES_PER_ELEMENT,
|
|
@@ -525,7 +506,6 @@ export default class MEMeshObj extends Materials {
|
|
|
525
506
|
});
|
|
526
507
|
new Float32Array(mesh.vertexTexCoordsBuffer.getMappedRange()).set(mesh.textures);
|
|
527
508
|
mesh.vertexTexCoordsBuffer.unmap();
|
|
528
|
-
|
|
529
509
|
// Indices
|
|
530
510
|
const indexCount = mesh.indices.length;
|
|
531
511
|
const indexSize = Math.ceil(indexCount * Uint16Array.BYTES_PER_ELEMENT / 4) * 4;
|
|
@@ -76,7 +76,6 @@ export default class MatrixAmmo {
|
|
|
76
76
|
}
|
|
77
77
|
|
|
78
78
|
let Ammo = this.Ammo;
|
|
79
|
-
console.log(pOptions.radius + "<<pOptions.radius")
|
|
80
79
|
var colShape = new Ammo.btSphereShape(Array.isArray(pOptions.radius) ? pOptions.radius[0] : pOptions.radius),
|
|
81
80
|
startTransform = new Ammo.btTransform();
|
|
82
81
|
startTransform.setIdentity();
|
|
@@ -234,10 +233,8 @@ export default class MatrixAmmo {
|
|
|
234
233
|
return;
|
|
235
234
|
this.lastRoll = '';
|
|
236
235
|
this.presentScore = '';
|
|
237
|
-
|
|
238
236
|
let dispatcher = this.dynamicsWorld.getDispatcher();
|
|
239
237
|
let numManifolds = dispatcher.getNumManifolds();
|
|
240
|
-
|
|
241
238
|
for(let i = 0;i < numManifolds;i++) {
|
|
242
239
|
let contactManifold = dispatcher.getManifoldByIndexInternal(i);
|
|
243
240
|
// let numContacts = contactManifold.getNumContacts();
|
|
@@ -245,51 +242,12 @@ export default class MatrixAmmo {
|
|
|
245
242
|
// if(item.kB == contactManifold.getBody0().kB) {
|
|
246
243
|
// // console.log('Detected body0 =', item.name)
|
|
247
244
|
// }
|
|
248
|
-
// if(item.kB == contactManifold.getBody1().kB) {
|
|
249
|
-
// // console.log('Detected body1 =', item.name)
|
|
250
|
-
// }
|
|
251
|
-
// })
|
|
252
|
-
|
|
253
245
|
if(this.ground.kB == contactManifold.getBody0().kB &&
|
|
254
246
|
this.getNameByBody(contactManifold.getBody1()) == 'CubePhysics1') {
|
|
255
247
|
// console.log(this.ground ,'GROUND IS IN CONTACT WHO IS BODY1 ', contactManifold.getBody1())
|
|
256
248
|
// console.log('GROUND IS IN CONTACT WHO IS BODY1 getNameByBody ', this.getNameByBody(contactManifold.getBody1()))
|
|
257
249
|
// CHECK ROTATION
|
|
258
250
|
var testR = contactManifold.getBody1().getWorldTransform().getRotation();
|
|
259
|
-
if(Math.abs(testR.y()) < 0.00001) {
|
|
260
|
-
this.lastRoll += " 4 +";
|
|
261
|
-
this.presentScore += 4;
|
|
262
|
-
dispatchEvent(new CustomEvent('dice-1', {}));
|
|
263
|
-
}
|
|
264
|
-
if(Math.abs(testR.x()) < 0.00001) {
|
|
265
|
-
this.lastRoll += " 3 +";
|
|
266
|
-
this.presentScore += 3;
|
|
267
|
-
dispatchEvent(new CustomEvent('dice-4', {}));
|
|
268
|
-
}
|
|
269
|
-
if(testR.x().toString().substring(0, 5) == testR.y().toString().substring(1, 6)) {
|
|
270
|
-
this.lastRoll += " 2 +";
|
|
271
|
-
this.presentScore += 2;
|
|
272
|
-
dispatchEvent(new CustomEvent('dice-6', {}));
|
|
273
|
-
}
|
|
274
|
-
|
|
275
|
-
if(testR.x().toString().substring(0, 5) == testR.y().toString().substring(0, 5)) {
|
|
276
|
-
this.lastRoll += " 1 +";
|
|
277
|
-
this.presentScore += 1;
|
|
278
|
-
dispatchEvent(new CustomEvent('dice-2', {}));
|
|
279
|
-
}
|
|
280
|
-
|
|
281
|
-
if(testR.z().toString().substring(0, 5) == testR.y().toString().substring(1, 6)) {
|
|
282
|
-
this.lastRoll += " 6 +";
|
|
283
|
-
this.presentScore += 6;
|
|
284
|
-
dispatchEvent(new CustomEvent('dice-5', {}));
|
|
285
|
-
}
|
|
286
|
-
|
|
287
|
-
if(testR.z().toString().substring(0, 5) == testR.y().toString().substring(0, 5)) {
|
|
288
|
-
this.lastRoll += " 5 +";
|
|
289
|
-
this.presentScore += 5;
|
|
290
|
-
dispatchEvent(new CustomEvent('dice-3', {}));
|
|
291
|
-
}
|
|
292
|
-
|
|
293
251
|
console.log('this.lastRoll = ', this.lastRoll, ' presentScore = ', this.presentScore)
|
|
294
252
|
}
|
|
295
253
|
}
|
|
@@ -1,75 +1,107 @@
|
|
|
1
1
|
export let fragmentWGSL = `override shadowDepthTextureSize: f32 = 1024.0;
|
|
2
2
|
|
|
3
3
|
struct Scene {
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
4
|
+
lightViewProjMatrix : mat4x4f,
|
|
5
|
+
cameraViewProjMatrix : mat4x4f,
|
|
6
|
+
lightPos : vec3f,
|
|
7
|
+
padding : f32, // Required for alignment
|
|
8
8
|
}
|
|
9
9
|
|
|
10
10
|
struct SpotLight {
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
11
|
+
position : vec3f,
|
|
12
|
+
_pad1 : f32,
|
|
13
|
+
|
|
14
|
+
direction : vec3f,
|
|
15
|
+
_pad2 : f32,
|
|
16
|
+
|
|
17
|
+
innerCutoff : f32,
|
|
18
|
+
outerCutoff : f32,
|
|
19
|
+
intensity : f32,
|
|
20
|
+
_pad3 : f32,
|
|
21
|
+
|
|
22
|
+
color : vec3f,
|
|
23
|
+
_pad4 : f32,
|
|
24
|
+
|
|
25
|
+
range : f32,
|
|
26
|
+
ambientFactor: f32, // new
|
|
27
|
+
_pad5 : vec2f, // padding to align to 16 bytes
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
const MAX_SPOTLIGHTS = 20u; // adjust as needed
|
|
19
31
|
|
|
20
32
|
@group(0) @binding(0) var<uniform> scene : Scene;
|
|
21
33
|
@group(0) @binding(1) var shadowMap: texture_depth_2d;
|
|
22
34
|
@group(0) @binding(2) var shadowSampler: sampler_comparison;
|
|
23
35
|
@group(0) @binding(3) var meshTexture: texture_2d<f32>;
|
|
24
36
|
@group(0) @binding(4) var meshSampler: sampler;
|
|
25
|
-
@group(0) @binding(5) var<uniform>
|
|
37
|
+
@group(0) @binding(5) var<uniform> spotlights: array<SpotLight, MAX_SPOTLIGHTS>;
|
|
38
|
+
// @group(0) @binding(6) var<uniform> spotlight1: SpotLight;
|
|
26
39
|
|
|
27
40
|
struct FragmentInput {
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
41
|
+
@location(0) shadowPos : vec3f,
|
|
42
|
+
@location(1) fragPos : vec3f,
|
|
43
|
+
@location(2) fragNorm : vec3f,
|
|
44
|
+
@location(3) uv : vec2f,
|
|
32
45
|
}
|
|
33
46
|
|
|
34
47
|
const albedo = vec3f(0.9);
|
|
35
|
-
const ambientFactor = 0.7;
|
|
48
|
+
// const ambientFactor = 0.7;
|
|
36
49
|
|
|
37
50
|
fn calculateSpotlightFactor(light: SpotLight, fragPos: vec3f) -> f32 {
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
51
|
+
let L = normalize(light.position - fragPos);
|
|
52
|
+
let theta = dot(L, normalize(-light.direction));
|
|
53
|
+
let epsilon = light.innerCutoff - light.outerCutoff;
|
|
54
|
+
return clamp((theta - light.outerCutoff) / epsilon, 0.0, 1.0);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
fn computeSpotLight(light: SpotLight, normal: vec3f, fragPos: vec3f, viewDir: vec3f) -> vec3f {
|
|
58
|
+
let L = light.position - fragPos;
|
|
59
|
+
let distance = length(L);
|
|
60
|
+
let lightDir = normalize(L);
|
|
61
|
+
|
|
62
|
+
let spotFactor = calculateSpotlightFactor(light, fragPos);
|
|
63
|
+
let atten = clamp(1.0 - (distance / light.range), 0.0, 1.0);
|
|
64
|
+
|
|
65
|
+
let diff = max(dot(normal, lightDir), 0.0);
|
|
66
|
+
let halfwayDir = normalize(lightDir + viewDir);
|
|
67
|
+
let spec = pow(max(dot(normal, halfwayDir), 0.0), 32.0);
|
|
68
|
+
|
|
69
|
+
let diffuse = diff * light.color * light.intensity * atten;
|
|
70
|
+
let specular = spec * light.color * light.intensity * atten;
|
|
71
|
+
|
|
72
|
+
return (diffuse + specular) * spotFactor;
|
|
43
73
|
}
|
|
44
74
|
|
|
45
75
|
@fragment
|
|
46
76
|
fn main(input : FragmentInput) -> @location(0) vec4f {
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
77
|
+
// Shadow PCF
|
|
78
|
+
var visibility = 0.0;
|
|
79
|
+
let oneOverSize = 1.0 / shadowDepthTextureSize;
|
|
80
|
+
for (var y = -1; y <= 1; y++) {
|
|
81
|
+
for (var x = -1; x <= 1; x++) {
|
|
82
|
+
let offset = vec2f(vec2(x, y)) * oneOverSize;
|
|
83
|
+
visibility += textureSampleCompare(
|
|
84
|
+
shadowMap, shadowSampler,
|
|
85
|
+
input.shadowPos.xy + offset, input.shadowPos.z - 0.007
|
|
86
|
+
);
|
|
87
|
+
}
|
|
57
88
|
}
|
|
58
|
-
|
|
59
|
-
|
|
89
|
+
visibility /= 9.0;
|
|
90
|
+
let norm = normalize(input.fragNorm);
|
|
91
|
+
let viewDir = normalize(scene.cameraViewProjMatrix[3].xyz - input.fragPos);
|
|
60
92
|
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
let lambert = max(dot(norm, lightDir), 0.0);
|
|
93
|
+
// Spotlight contribution (diffuse + specular + cone + distance)
|
|
94
|
+
var lightContribution = vec3f(0.0);
|
|
95
|
+
var ambient = vec3f(0.0);
|
|
65
96
|
|
|
66
|
-
|
|
67
|
-
|
|
97
|
+
for (var i = 0u; i < MAX_SPOTLIGHTS; i++) {
|
|
98
|
+
lightContribution += computeSpotLight(spotlights[i], norm, input.fragPos, viewDir);
|
|
99
|
+
ambient += spotlights[i].ambientFactor * spotlights[i].color;
|
|
100
|
+
}
|
|
68
101
|
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
let texColor = textureSample(meshTexture, meshSampler, input.uv);
|
|
102
|
+
let texColor = textureSample(meshTexture, meshSampler, input.uv);
|
|
103
|
+
let finalColor = texColor.rgb * (ambient + lightContribution * visibility) * albedo;
|
|
72
104
|
|
|
73
|
-
|
|
105
|
+
return vec4f(finalColor, 1.0);
|
|
74
106
|
}
|
|
75
107
|
`
|