matrix-engine-wgpu 1.2.1 → 1.2.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/index.js +1 -1
- package/package.json +2 -1
- package/.codesandbox/tasks.json +0 -46
- package/.devcontainer/devcontainer.json +0 -22
- package/.github/dependabot.yml +0 -12
- package/REFERENCE.md +0 -59
- package/app-worker.js +0 -45
- package/dev.md +0 -460
- package/empty.js +0 -13
- package/examples/games/jamb/html-content.js +0 -128
- package/examples/games/jamb/jamb.js +0 -1340
- package/examples/games/jamb/readme.md +0 -3
- package/examples/load-obj-file.js +0 -78
- package/examples/unlit-textures.js +0 -31
- package/examples.js +0 -39
- package/main.js +0 -635
- package/non-project-files/cubebuffer-example.js +0 -51
- package/non-project-files/dev.txt +0 -21
- package/non-project-files/image1.png +0 -0
- package/non-project-files/image6.png +0 -0
- package/public/ammojs/ammo.js +0 -957
- package/public/ammojs/ammo.wasm.js +0 -921
- package/public/ammojs/ammo.wasm.wasm +0 -0
- package/public/app-worker.js +0 -47
- package/public/app.js +0 -12502
- package/public/css/style.css +0 -710
- package/public/empty.html +0 -25
- package/public/empty.js +0 -10387
- package/public/examples.html +0 -27
- package/public/examples.js +0 -11946
- package/public/index.html +0 -20
- package/public/manifest copy.web +0 -35
- package/public/manifest.web +0 -25
- package/public/res/audios/block.mp3 +0 -0
- package/public/res/audios/dice-roll.mp3 +0 -0
- package/public/res/audios/dice1.mp3 +0 -0
- package/public/res/audios/dice2.mp3 +0 -0
- package/public/res/audios/kenney/Kenney.url +0 -2
- package/public/res/audios/kenney/License.txt +0 -22
- package/public/res/audios/kenney/Patreon.url +0 -2
- package/public/res/audios/kenney/audios/back_001.ogg +0 -0
- package/public/res/audios/kenney/audios/back_002.ogg +0 -0
- package/public/res/audios/kenney/audios/back_003.ogg +0 -0
- package/public/res/audios/kenney/audios/back_004.ogg +0 -0
- package/public/res/audios/kenney/audios/bong_001.ogg +0 -0
- package/public/res/audios/kenney/audios/click_001.ogg +0 -0
- package/public/res/audios/kenney/audios/click_002.ogg +0 -0
- package/public/res/audios/kenney/audios/click_003.ogg +0 -0
- package/public/res/audios/kenney/audios/click_004.ogg +0 -0
- package/public/res/audios/kenney/audios/click_005.ogg +0 -0
- package/public/res/audios/kenney/audios/close_001.ogg +0 -0
- package/public/res/audios/kenney/audios/close_002.ogg +0 -0
- package/public/res/audios/kenney/audios/close_003.ogg +0 -0
- package/public/res/audios/kenney/audios/close_004.ogg +0 -0
- package/public/res/audios/kenney/audios/confirmation_001.ogg +0 -0
- package/public/res/audios/kenney/audios/confirmation_002.ogg +0 -0
- package/public/res/audios/kenney/audios/confirmation_003.ogg +0 -0
- package/public/res/audios/kenney/audios/confirmation_004.ogg +0 -0
- package/public/res/audios/kenney/audios/drop_001.ogg +0 -0
- package/public/res/audios/kenney/audios/drop_002.ogg +0 -0
- package/public/res/audios/kenney/audios/drop_003.ogg +0 -0
- package/public/res/audios/kenney/audios/drop_004.ogg +0 -0
- package/public/res/audios/kenney/audios/error_001.ogg +0 -0
- package/public/res/audios/kenney/audios/error_002.ogg +0 -0
- package/public/res/audios/kenney/audios/error_003.ogg +0 -0
- package/public/res/audios/kenney/audios/error_004.ogg +0 -0
- package/public/res/audios/kenney/audios/error_005.ogg +0 -0
- package/public/res/audios/kenney/audios/error_006.ogg +0 -0
- package/public/res/audios/kenney/audios/error_007.ogg +0 -0
- package/public/res/audios/kenney/audios/error_008.ogg +0 -0
- package/public/res/audios/kenney/audios/glass_001.ogg +0 -0
- package/public/res/audios/kenney/audios/glass_002.ogg +0 -0
- package/public/res/audios/kenney/audios/glass_003.ogg +0 -0
- package/public/res/audios/kenney/audios/glass_004.ogg +0 -0
- package/public/res/audios/kenney/audios/glass_005.ogg +0 -0
- package/public/res/audios/kenney/audios/glass_006.ogg +0 -0
- package/public/res/audios/kenney/audios/glitch_001.ogg +0 -0
- package/public/res/audios/kenney/audios/glitch_002.ogg +0 -0
- package/public/res/audios/kenney/audios/glitch_003.ogg +0 -0
- package/public/res/audios/kenney/audios/glitch_004.ogg +0 -0
- package/public/res/audios/kenney/audios/maximize_001.ogg +0 -0
- package/public/res/audios/kenney/audios/maximize_002.ogg +0 -0
- package/public/res/audios/kenney/audios/maximize_003.ogg +0 -0
- package/public/res/audios/kenney/audios/maximize_004.ogg +0 -0
- package/public/res/audios/kenney/audios/maximize_005.ogg +0 -0
- package/public/res/audios/kenney/audios/maximize_006.ogg +0 -0
- package/public/res/audios/kenney/audios/maximize_007.ogg +0 -0
- package/public/res/audios/kenney/audios/maximize_008.ogg +0 -0
- package/public/res/audios/kenney/audios/maximize_009.ogg +0 -0
- package/public/res/audios/kenney/audios/minimize_001.ogg +0 -0
- package/public/res/audios/kenney/audios/minimize_002.ogg +0 -0
- package/public/res/audios/kenney/audios/minimize_003.ogg +0 -0
- package/public/res/audios/kenney/audios/minimize_004.ogg +0 -0
- package/public/res/audios/kenney/audios/minimize_005.ogg +0 -0
- package/public/res/audios/kenney/audios/minimize_006.ogg +0 -0
- package/public/res/audios/kenney/audios/minimize_007.ogg +0 -0
- package/public/res/audios/kenney/audios/minimize_008.ogg +0 -0
- package/public/res/audios/kenney/audios/minimize_009.ogg +0 -0
- package/public/res/audios/kenney/audios/open_001.ogg +0 -0
- package/public/res/audios/kenney/audios/open_002.ogg +0 -0
- package/public/res/audios/kenney/audios/open_003.ogg +0 -0
- package/public/res/audios/kenney/audios/open_004.ogg +0 -0
- package/public/res/audios/kenney/audios/pluck_001.ogg +0 -0
- package/public/res/audios/kenney/audios/pluck_002.ogg +0 -0
- package/public/res/audios/kenney/audios/question_001.ogg +0 -0
- package/public/res/audios/kenney/audios/question_002.ogg +0 -0
- package/public/res/audios/kenney/audios/question_003.ogg +0 -0
- package/public/res/audios/kenney/audios/question_004.ogg +0 -0
- package/public/res/audios/kenney/audios/scratch_001.ogg +0 -0
- package/public/res/audios/kenney/audios/scratch_002.ogg +0 -0
- package/public/res/audios/kenney/audios/scratch_003.ogg +0 -0
- package/public/res/audios/kenney/audios/scratch_004.ogg +0 -0
- package/public/res/audios/kenney/audios/scratch_005.ogg +0 -0
- package/public/res/audios/kenney/audios/scroll_001.ogg +0 -0
- package/public/res/audios/kenney/audios/scroll_002.ogg +0 -0
- package/public/res/audios/kenney/audios/scroll_003.ogg +0 -0
- package/public/res/audios/kenney/audios/scroll_004.ogg +0 -0
- package/public/res/audios/kenney/audios/scroll_005.ogg +0 -0
- package/public/res/audios/kenney/audios/select_001.ogg +0 -0
- package/public/res/audios/kenney/audios/select_002.ogg +0 -0
- package/public/res/audios/kenney/audios/select_003.ogg +0 -0
- package/public/res/audios/kenney/audios/select_004.ogg +0 -0
- package/public/res/audios/kenney/audios/select_005.ogg +0 -0
- package/public/res/audios/kenney/audios/select_006.ogg +0 -0
- package/public/res/audios/kenney/audios/select_007.ogg +0 -0
- package/public/res/audios/kenney/audios/select_008.ogg +0 -0
- package/public/res/audios/kenney/audios/switch_001.ogg +0 -0
- package/public/res/audios/kenney/audios/switch_002.ogg +0 -0
- package/public/res/audios/kenney/audios/switch_003.ogg +0 -0
- package/public/res/audios/kenney/audios/switch_004.ogg +0 -0
- package/public/res/audios/kenney/audios/switch_005.ogg +0 -0
- package/public/res/audios/kenney/audios/switch_006.ogg +0 -0
- package/public/res/audios/kenney/audios/switch_007.ogg +0 -0
- package/public/res/audios/kenney/audios/tick_001.ogg +0 -0
- package/public/res/audios/kenney/audios/tick_002.ogg +0 -0
- package/public/res/audios/kenney/audios/tick_004.ogg +0 -0
- package/public/res/audios/kenney/audios/toggle_001.ogg +0 -0
- package/public/res/audios/kenney/audios/toggle_002.ogg +0 -0
- package/public/res/audios/kenney/audios/toggle_003.ogg +0 -0
- package/public/res/audios/kenney/audios/toggle_004.ogg +0 -0
- package/public/res/audios/start.mp3 +0 -0
- package/public/res/audios/toggle_002.mp3 +0 -0
- package/public/res/fonts/Accuratist.ttf +0 -0
- package/public/res/fonts/Closeness.ttf +0 -0
- package/public/res/fonts/WARGAMES.TTF +0 -0
- package/public/res/fonts/readme.txt +0 -5
- package/public/res/fonts/stormfaze.ttf +0 -0
- package/public/res/icons/512.png +0 -0
- package/public/res/icons/webgpu-horizontal.svg +0 -45
- package/public/res/meshes/blender/cube.blend +0 -0
- package/public/res/meshes/blender/cube.blend1 +0 -0
- package/public/res/meshes/blender/cube.mtl +0 -12
- package/public/res/meshes/blender/cube.obj +0 -46
- package/public/res/meshes/blender/cube.png +0 -0
- package/public/res/meshes/blender/cubeSmartUV.blend +0 -0
- package/public/res/meshes/blender/cubeSmartUV.mtl +0 -12
- package/public/res/meshes/blender/cubeSmartUV.obj +0 -46
- package/public/res/meshes/blender/lopta.mtl +0 -10
- package/public/res/meshes/blender/lopta.obj +0 -3402
- package/public/res/meshes/blender/piramyd.blend +0 -0
- package/public/res/meshes/blender/piramyd.blend1 +0 -0
- package/public/res/meshes/blender/piramyd.js +0 -42
- package/public/res/meshes/blender/piramyd.mtl +0 -10
- package/public/res/meshes/blender/piramyd.obj +0 -18696
- package/public/res/meshes/blender/piramyd1.js +0 -42
- package/public/res/meshes/blender/sphepe.blend +0 -0
- package/public/res/meshes/blender/sphepe.blend1 +0 -0
- package/public/res/meshes/blender/sphere.mtl +0 -10
- package/public/res/meshes/blender/sphere.obj +0 -3402
- package/public/res/meshes/blender/welcomeTextblend.blend +0 -0
- package/public/res/meshes/dragon/stanfordDragonData.js +0 -5
- package/public/res/meshes/jamb/bg.blend +0 -0
- package/public/res/meshes/jamb/bg.blend1 +0 -0
- package/public/res/meshes/jamb/bg.mtl +0 -12
- package/public/res/meshes/jamb/bg.obj +0 -17
- package/public/res/meshes/jamb/bg.png +0 -0
- package/public/res/meshes/jamb/dice-default.png +0 -0
- package/public/res/meshes/jamb/dice-mark.png +0 -0
- package/public/res/meshes/jamb/dice.mtl +0 -12
- package/public/res/meshes/jamb/dice.obj +0 -40
- package/public/res/meshes/jamb/dice.png +0 -0
- package/public/res/meshes/jamb/jamb-title.mtl +0 -12
- package/public/res/meshes/jamb/jamb-title.obj +0 -26008
- package/public/res/meshes/jamb/jamb.blend +0 -0
- package/public/res/meshes/jamb/jamb.blend1 +0 -0
- package/public/res/meshes/jamb/logo.png +0 -0
- package/public/res/meshes/jamb/nidzaDice.blend +0 -0
- package/public/res/meshes/jamb/nidzaDice.blend1 +0 -0
- package/public/res/meshes/jamb/pile.blend +0 -0
- package/public/res/meshes/jamb/simpleCube.blend +0 -0
- package/public/res/meshes/jamb/simpleCube.blend1 +0 -0
- package/public/res/meshes/jamb/sounds/roll1.wav +0 -0
- package/public/res/meshes/jamb/text.png +0 -0
- package/public/res/meshes/obj/armor.obj +0 -319
- package/public/res/meshes/obj/armor.png +0 -0
- package/public/res/meshes/shapes/star1.obj +0 -60
- package/public/res/multilang/en.json +0 -39
- package/public/res/multilang/sr.json +0 -39
- package/public/res/textures/default.png +0 -0
- package/public/res/textures/rust.jpg +0 -0
- package/public/res/textures/tex1.jpg +0 -0
- package/public/test.html +0 -636
- package/public/three-test.js +0 -165
- package/public/worker.html +0 -25
package/index.js
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
* Nikola Lukic zlatnaspirala@gmail.com mart 2024
|
|
4
4
|
* npm import/export
|
|
5
5
|
*/
|
|
6
|
-
import {degToRad} from "wgpu-matrix
|
|
6
|
+
import { degToRad } from "wgpu-matrix";
|
|
7
7
|
import {downloadMeshes} from "./src/engine/loader-obj.js";
|
|
8
8
|
import MatrixEngineWGPU from "./src/meWGPU.js";
|
|
9
9
|
import {addRaycastListener, getRayFromMouse, rayIntersectsSphere} from "./src/engine/raycast.js";
|
package/package.json
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "matrix-engine-wgpu",
|
|
3
|
-
"version": "1.2.
|
|
3
|
+
"version": "1.2.3",
|
|
4
4
|
"description": "+HOTFIX raycast, webGPU powered pwa application. Crazy fast rendering with AmmoJS physics support. Simple raycaster hit object added.",
|
|
5
5
|
"main": "index.js",
|
|
6
|
+
"files": ["./src"],
|
|
6
7
|
"scripts": {
|
|
7
8
|
"main-worker": "watchify app-worker.js -p [esmify --noImplicitAny] -o public/app-worker.js",
|
|
8
9
|
"examples": "watchify examples.js -p [esmify --noImplicitAny] -o public/examples.js",
|
package/.codesandbox/tasks.json
DELETED
|
@@ -1,46 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
// These tasks will run in order when initializing your CodeSandbox project.
|
|
3
|
-
"setupTasks": [
|
|
4
|
-
{
|
|
5
|
-
"name": "Install Dependencies",
|
|
6
|
-
"command": "npm install"
|
|
7
|
-
},
|
|
8
|
-
{
|
|
9
|
-
"command": "npm run main",
|
|
10
|
-
"name": "n"
|
|
11
|
-
},
|
|
12
|
-
{
|
|
13
|
-
"command": "npm run hosts",
|
|
14
|
-
"name": "n"
|
|
15
|
-
}
|
|
16
|
-
],
|
|
17
|
-
|
|
18
|
-
// These tasks can be run from CodeSandbox. Running one will open a log in the app.
|
|
19
|
-
"tasks": {
|
|
20
|
-
"main-worker": {
|
|
21
|
-
"name": "main-worker",
|
|
22
|
-
"command": "npm run main-worker",
|
|
23
|
-
"runAtStart": false
|
|
24
|
-
},
|
|
25
|
-
"examples": {
|
|
26
|
-
"name": "examples",
|
|
27
|
-
"command": "npm run examples",
|
|
28
|
-
"runAtStart": false
|
|
29
|
-
},
|
|
30
|
-
"main": {
|
|
31
|
-
"name": "main",
|
|
32
|
-
"command": "npm run main",
|
|
33
|
-
"runAtStart": false
|
|
34
|
-
},
|
|
35
|
-
"build-empty": {
|
|
36
|
-
"name": "build-empty",
|
|
37
|
-
"command": "npm run build-empty",
|
|
38
|
-
"runAtStart": false
|
|
39
|
-
},
|
|
40
|
-
"build-all": {
|
|
41
|
-
"name": "build-all",
|
|
42
|
-
"command": "npm run build-all",
|
|
43
|
-
"runAtStart": false
|
|
44
|
-
}
|
|
45
|
-
}
|
|
46
|
-
}
|
|
@@ -1,22 +0,0 @@
|
|
|
1
|
-
// For format details, see https://aka.ms/devcontainer.json. For config options, see the
|
|
2
|
-
// README at: https://github.com/devcontainers/templates/tree/main/src/javascript-node
|
|
3
|
-
{
|
|
4
|
-
"name": "Node.js",
|
|
5
|
-
// Or use a Dockerfile or Docker Compose file. More info: https://containers.dev/guide/dockerfile
|
|
6
|
-
"image": "mcr.microsoft.com/devcontainers/javascript-node:1-22-bookworm"
|
|
7
|
-
|
|
8
|
-
// Features to add to the dev container. More info: https://containers.dev/features.
|
|
9
|
-
// "features": {},
|
|
10
|
-
|
|
11
|
-
// Use 'forwardPorts' to make a list of ports inside the container available locally.
|
|
12
|
-
// "forwardPorts": [],
|
|
13
|
-
|
|
14
|
-
// Use 'postCreateCommand' to run commands after the container is created.
|
|
15
|
-
// "postCreateCommand": "yarn install",
|
|
16
|
-
|
|
17
|
-
// Configure tool-specific properties.
|
|
18
|
-
// "customizations": {},
|
|
19
|
-
|
|
20
|
-
// Uncomment to connect as root instead. More info: https://aka.ms/dev-containers-non-root.
|
|
21
|
-
// "remoteUser": "root"
|
|
22
|
-
}
|
package/.github/dependabot.yml
DELETED
|
@@ -1,12 +0,0 @@
|
|
|
1
|
-
# To get started with Dependabot version updates, you'll need to specify which
|
|
2
|
-
# package ecosystems to update and where the package manifests are located.
|
|
3
|
-
# Please see the documentation for more information:
|
|
4
|
-
# https://docs.github.com/github/administering-a-repository/configuration-options-for-dependency-updates
|
|
5
|
-
# https://containers.dev/guide/dependabot
|
|
6
|
-
|
|
7
|
-
version: 2
|
|
8
|
-
updates:
|
|
9
|
-
- package-ecosystem: "devcontainers"
|
|
10
|
-
directory: "/"
|
|
11
|
-
schedule:
|
|
12
|
-
interval: weekly
|
package/REFERENCE.md
DELETED
|
@@ -1,59 +0,0 @@
|
|
|
1
|
-
|
|
2
|
-
### Best solution for learning:
|
|
3
|
-
|
|
4
|
-
https://webgpufundamentals.org/webgpu/lessons/webgpu-from-webgl.html
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
### I got answer from gman at stackoverflow.com :
|
|
8
|
-
|
|
9
|
-
https://stackoverflow.com/questions/78093302/webgpu-i-cant-draw-two-object-in-same-scene/78098135#78098135
|
|
10
|
-
|
|
11
|
-
Interest facts about webGPU tech:
|
|
12
|
-
|
|
13
|
-
```
|
|
14
|
-
You can have different pipelines if the things you want to draw actually need different pipelines but if possible you could try to have less pipelines than more. In the example above, if you cube had a different pipeline than the sphere you'd call setPipeline with the sphere's pipeline after drawing the cube and before drawing the sphere.
|
|
15
|
-
```
|
|
16
|
-
|
|
17
|
-
```
|
|
18
|
-
Let's assume you only have one pipeline and 2 things you want to draw with that pipeline, a cube, and a sphere. In pseudo code you might do something like this
|
|
19
|
-
|
|
20
|
-
at init time
|
|
21
|
-
create pipeline
|
|
22
|
-
create vertex buffers for cube
|
|
23
|
-
create uniform buffers for cube
|
|
24
|
-
create bindGroup for cube
|
|
25
|
-
create vertex buffers for sphere
|
|
26
|
-
create uniform buffers for sphere
|
|
27
|
-
create bindGroup for cube
|
|
28
|
-
at render time
|
|
29
|
-
create command buffer
|
|
30
|
-
begin render pass
|
|
31
|
-
set pipeline
|
|
32
|
-
set vertex buffers for cube
|
|
33
|
-
set bindGroups for cube
|
|
34
|
-
draw cube
|
|
35
|
-
set vertex buffers for sphere
|
|
36
|
-
set bindgroups for sphere
|
|
37
|
-
draw sphere
|
|
38
|
-
end render pass
|
|
39
|
-
finish command buffer
|
|
40
|
-
submit command buffer
|
|
41
|
-
|
|
42
|
-
Things to notice, there is only one command buffer. There is only 1 render pass.
|
|
43
|
-
```
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
```
|
|
47
|
-
For each render pass, you need to set loadOp and storeOp correctly.
|
|
48
|
-
Most examples that have one render pass set loadOp: 'clear' but in the example above, if render pass 2 had loadOp: 'clear' it would erase the results from render pass 1. Instead it would need to be loadOp: 'load'.
|
|
49
|
-
```
|
|
50
|
-
|
|
51
|
-
YEs this is happening " would erase the results from render pass 1 " FIXED !
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
# https://www.euclideanspace.com/maths/geometry/rotations/conversions/quaternionToAngle/index.htm
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
LOOK HERE:
|
|
58
|
-
|
|
59
|
-
https://glm.g-truc.net/0.9.0/api/a00184.html
|
package/app-worker.js
DELETED
|
@@ -1,45 +0,0 @@
|
|
|
1
|
-
|
|
2
|
-
var canvas = document.createElement('canvas')
|
|
3
|
-
canvas.width = window.innerWidth;
|
|
4
|
-
canvas.height = window.innerHeight;
|
|
5
|
-
document.body.append(canvas)
|
|
6
|
-
|
|
7
|
-
// The web worker is created by passing a path to the worker's source file, which will then be
|
|
8
|
-
// executed on a separate thread.
|
|
9
|
-
const worker = new Worker('app.js');
|
|
10
|
-
|
|
11
|
-
// The primary way to communicate with the worker is to send and receive messages.
|
|
12
|
-
worker.addEventListener('message', (ev) => {
|
|
13
|
-
// The format of the message can be whatever you'd like, but it's helpful to decide on a
|
|
14
|
-
// consistent convention so that you can tell the message types apart as your apps grow in
|
|
15
|
-
// complexity. Here we establish a convention that all messages to and from the worker will
|
|
16
|
-
// have a `type` field that we can use to determine the content of the message.
|
|
17
|
-
switch (ev.data.type) {
|
|
18
|
-
default: {
|
|
19
|
-
console.error(`Unknown Message Type: ${ev.data.type}`);
|
|
20
|
-
}
|
|
21
|
-
}
|
|
22
|
-
});
|
|
23
|
-
|
|
24
|
-
try {
|
|
25
|
-
// In order for the worker to display anything on the page, an OffscreenCanvas must be used.
|
|
26
|
-
// Here we can create one from our normal canvas by calling transferControlToOffscreen().
|
|
27
|
-
// Anything drawn to the OffscreenCanvas that call returns will automatically be displayed on
|
|
28
|
-
// the source canvas on the page.
|
|
29
|
-
const offscreenCanvas = canvas.transferControlToOffscreen();
|
|
30
|
-
const devicePixelRatio = window.devicePixelRatio;
|
|
31
|
-
offscreenCanvas.width = canvas.clientWidth * devicePixelRatio;
|
|
32
|
-
offscreenCanvas.height = canvas.clientHeight * devicePixelRatio;
|
|
33
|
-
|
|
34
|
-
// Send a message to the worker telling it to initialize WebGPU with the OffscreenCanvas. The
|
|
35
|
-
// array passed as the second argument here indicates that the OffscreenCanvas is to be
|
|
36
|
-
// transferred to the worker, meaning this main thread will lose access to it and it will be
|
|
37
|
-
// fully owned by the worker.
|
|
38
|
-
worker.postMessage({ type: 'init', offscreenCanvas, devicePixelRatio: devicePixelRatio }, [offscreenCanvas]);
|
|
39
|
-
} catch (err) {
|
|
40
|
-
// TODO: This catch is added here because React will call init twice with the same canvas, and
|
|
41
|
-
// the second time will fail the transferControlToOffscreen() because it's already been
|
|
42
|
-
// transferred. I'd love to know how to get around that.
|
|
43
|
-
console.warn(err.message);
|
|
44
|
-
worker.terminate();
|
|
45
|
-
}
|
package/dev.md
DELETED
|
@@ -1,460 +0,0 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
https://maierfelix.github.io/2020-01-13-webgpu-ray-tracing/?utm_source=chatgpt.com
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
Felix Maier
|
|
7
|
-
About Me
|
|
8
|
-
Real-Time Ray-Tracing in WebGPU
|
|
9
|
-
Posted on January 13, 2020
|
|
10
|
-
Intro
|
|
11
|
-
By the end of 2018, NVIDIA released the new GPU series Turing, best known for its ability of accelerated ray tracing.
|
|
12
|
-
|
|
13
|
-
Ray tracing is the process of simulating light paths from reality. In reality, billions of rays get shot around you and at some point, hit your eyes. Up to today, simulating this process is one of the most expensive tasks in computer science and an ongoing research area.
|
|
14
|
-
|
|
15
|
-
Previously, if you were interested in modern ray tracing, then you had a giant chunk of learning material in front of you. Modern graphics APIs became a lot more complicated to work with and ray tracing was only available for these APIs. You had to spend a lot time learning about them, before you could even start about the ray tracing topic itself.
|
|
16
|
-
|
|
17
|
-
Note: If you’re not the owner of a RTX card, but have a GTX 1060+ around, then you are one of the lucky guys who can test ray tracing without the need to buy one of those more expensive cards.
|
|
18
|
-
|
|
19
|
-
Luckily, there is WebGPU
|
|
20
|
-
WebGPU is the successor to WebGL and combines multiple graphics APIs into one, standardized API. It is said, that WebGPU’s API is a mixture of Apple’s Metal API and parts of the Vulkan API, but a lot more straightforward to work with.
|
|
21
|
-
|
|
22
|
-
Some WebGPU implementations come with multiple rendering backends, such as D3D12, Vulkan, Metal and OpenGL. Depending on the user’s setup, one of these backends get used, preferably the fastest one with the most reliability for the platform. The commands sent to WebGPU then get translated into one of these backends.
|
|
23
|
-
|
|
24
|
-
Preview
|
|
25
|
-
Here is an example project using WebGPU Ray tracing:
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
Source code: Link
|
|
29
|
-
|
|
30
|
-
There is also a custom Chromium build with ray tracing capabilities and an online demo, which you can find here.
|
|
31
|
-
|
|
32
|
-
Upfront
|
|
33
|
-
Note that ray tracing is not available officially for WebGPU (yet?) and is only available for the Node bindings for WebGPU. Recently I began adapting an unofficial ray tracing extension for Dawn, which is the WebGPU implementation for Chromium. The ray tracing extension is implemented into the Vulkan backend (using VK_KHR_ray_tracing) and the D3D12 backend (using DXR). You can find my Dawn fork with ray tracing capabilities here.
|
|
34
|
-
|
|
35
|
-
The specification of the ray tracing extension can be found here.
|
|
36
|
-
|
|
37
|
-
Now let me introduce you to the ideas and concepts of this new extension. Note that from now on, I will use RT when referring to ray tracing or RTX.
|
|
38
|
-
|
|
39
|
-
Bounding Volume Hierarchies
|
|
40
|
-
When dealing with RT, you often end up having to work with Bounding Volume Hierarchies (short: “BVH”). BVHs are used to encapsulate arbitrary geometry for faster ray-triangle intersection. BVHs are very important in RT, since without them, for each ray, you’d have to check all triangles in a scene for intersection with the ray and this process quickly becomes expensive.
|
|
41
|
-
|
|
42
|
-
Previously, for RT projects, you had to implement your own BVH system. Today you no longer have to do that, since the driver is generating the BVHs for you (either on CPU or GPU).
|
|
43
|
-
|
|
44
|
-
Ray tracing Shaders
|
|
45
|
-
Previously, you only had vertex, fragment and compute shaders. The RT extension exposes 5 new shader stages:
|
|
46
|
-
|
|
47
|
-
Ray-Generation (.rgen)
|
|
48
|
-
Ray-Closest-Hit (.rchit)
|
|
49
|
-
Ray-Any-Hit (.rahit)
|
|
50
|
-
Ray-Miss (.rmiss)
|
|
51
|
-
Ray-Intersection (.rint)
|
|
52
|
-
Ray-Generation:
|
|
53
|
-
For each pixel on the screen, we want to shoot rays into a scene. This shader allows to generate and trace rays into an acceleration container.
|
|
54
|
-
|
|
55
|
-
Ray-Closest-Hit:
|
|
56
|
-
A ray can hit multiple surfaces (e.g. a triangle), but often, we’re only interested in the surface that is the closest to the ray’s origin. This shader gets invoked for the “closest surface” that got intersected with and also contains arbitary hit information.
|
|
57
|
-
|
|
58
|
-
Ray-Any-Hit:
|
|
59
|
-
This shader is identical to the closest-hit shader, but can get invoked multiple times.
|
|
60
|
-
|
|
61
|
-
Ray-Miss:
|
|
62
|
-
This shader gets invoked, whenever a ray didn’t hit anything (i.e. “hit the sky”).
|
|
63
|
-
|
|
64
|
-
Ray-Intersection:
|
|
65
|
-
When dealing with procedural geometry, a custom intersection shader can be defined to determine what happens when a ray hits a bounding box. The default ray-intersection shader is for triangles only, but a ray-intersection shader allows to add any kind of new geometry (e.g. voxels, spheres etc.).
|
|
66
|
-
|
|
67
|
-
Shader Binding Table
|
|
68
|
-
The most common approach about shaders is to bind them, depending on how you want an object to look like on the screen. In RT however, it’s often impossible to known upfront which shaders to bind, and which shaders should get invoked in which order. To fix this, a new concept was introduced called the shader binding table (or short “SBT”).
|
|
69
|
-
|
|
70
|
-
The SBT’s purpose is to batch shaders together into groups, and later, dynamically invoke them from the RT shaders, based on a ray’s tracing result (i.e. hit or miss a surface).
|
|
71
|
-
|
|
72
|
-
Acceleration Containers
|
|
73
|
-
Acceleration containers probably seem to be the most complicated thing at first, but they are actually quite simple in their concept.
|
|
74
|
-
|
|
75
|
-
There are two different kinds of acceleration containers:
|
|
76
|
-
|
|
77
|
-
Bottom-level: The container stores references to geometry (short “BLAC”)
|
|
78
|
-
Top-level: The container stores instances with references to a bottom-level container, each with an arbitrary transform “TLAC”
|
|
79
|
-
Generally said, a bottom-level container contains just the meshes, while a top-level containers describes, where to place these meshes in a virtual world. In fact, this process is similar to Geometry Instancing, a common approach in graphics programming, which is about effectively reusing geometry across a scene to reduce memory usage and improve performance.
|
|
80
|
-
|
|
81
|
-
Coding Time
|
|
82
|
-
You can find a code reference here.
|
|
83
|
-
|
|
84
|
-
After this tutorial, you will be able to render this beautiful triangle, fully ray traced with hardware acceleration:
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
Create Geometry
|
|
89
|
-
At first, you need some kind of geometry that you want to ray trace and draw to the screen.
|
|
90
|
-
|
|
91
|
-
We’ll just use a simple triangle at first. Notice that for all buffers which will be used in acceleration containers later, the GPUBufferUsage.RAY_TRACING must be set.
|
|
92
|
-
|
|
93
|
-
let triangleVertices = new Float32Array([
|
|
94
|
-
1.0, 1.0, 0.0,
|
|
95
|
-
-1.0, 1.0, 0.0,
|
|
96
|
-
0.0, -1.0, 0.0
|
|
97
|
-
]);
|
|
98
|
-
// create a GPU local buffer containing the vertices
|
|
99
|
-
let triangleVertexBuffer = device.createBuffer({
|
|
100
|
-
size: triangleVertices.byteLength,
|
|
101
|
-
usage: GPUBufferUsage.COPY_DST | GPUBufferUsage.RAY_TRACING
|
|
102
|
-
});
|
|
103
|
-
// upload the vertices to the GPU buffer
|
|
104
|
-
triangleVertexBuffer.setSubData(0, triangleVertices);
|
|
105
|
-
Since you probably end up using an index buffer later anyway, let’s create one:
|
|
106
|
-
|
|
107
|
-
let triangleIndices = new Uint32Array([
|
|
108
|
-
0, 1, 2
|
|
109
|
-
]);
|
|
110
|
-
// create a GPU local buffer containing the indices
|
|
111
|
-
let triangleIndexBuffer = device.createBuffer({
|
|
112
|
-
size: triangleIndices.byteLength,
|
|
113
|
-
usage: GPUBufferUsage.COPY_DST | GPUBufferUsage.RAY_TRACING
|
|
114
|
-
});
|
|
115
|
-
// upload the indices to the GPU buffer
|
|
116
|
-
triangleIndexBuffer.setSubData(0, triangleIndices);
|
|
117
|
-
Create Bottom-Level Acceleration Container
|
|
118
|
-
Now we will create our first acceleration container, which stores a reference to the geometry we have just created:
|
|
119
|
-
|
|
120
|
-
let geometryContainer = device.createRayTracingAccelerationContainer({
|
|
121
|
-
level: "bottom",
|
|
122
|
-
flags: GPURayTracingAccelerationContainerFlag.PREFER_FAST_TRACE,
|
|
123
|
-
geometries: [
|
|
124
|
-
{
|
|
125
|
-
type: "triangles", // the geometry kind of the vertices (triangles or aabbs)
|
|
126
|
-
vertex: {
|
|
127
|
-
buffer: triangleVertexBuffer, // our GPU buffer containing the vertices
|
|
128
|
-
format: "float3", // one vertex is made up of 3 floats
|
|
129
|
-
stride: 3 * Float32Array.BYTES_PER_ELEMENT, // the byte stride between each vertex
|
|
130
|
-
count: triangleVertices.length, // the total amount of vertices
|
|
131
|
-
},
|
|
132
|
-
index: {
|
|
133
|
-
buffer: triangleIndexBuffer, // (optional) the index buffer to use
|
|
134
|
-
format: "uint32", // (optional) the format of the index buffer (Uint32Array)
|
|
135
|
-
count: triangleIndices.length // the total amount of indices
|
|
136
|
-
}
|
|
137
|
-
}
|
|
138
|
-
]
|
|
139
|
-
});
|
|
140
|
-
Create Top-Level Acceleration Container
|
|
141
|
-
This container will hold an instance with a reference to our triangle geometry. This instance defines how the geometry gets positioned in our world using the transform property. The property geometryContainer is used to assign geometry to the instance:
|
|
142
|
-
|
|
143
|
-
let instanceContainer = device.createRayTracingAccelerationContainer({
|
|
144
|
-
level: "top",
|
|
145
|
-
flags: GPURayTracingAccelerationContainerFlag.PREFER_FAST_TRACE,
|
|
146
|
-
instances: [
|
|
147
|
-
{
|
|
148
|
-
flags: GPURayTracingAccelerationInstanceFlag.TRIANGLE_CULL_DISABLE, // disable back-face culling
|
|
149
|
-
mask: 0xFF, // in the shader, you can cull objects based on their mask
|
|
150
|
-
instanceId: 0, // a custom Id which you can use to identify an object in the shaders
|
|
151
|
-
instanceOffset: 0x0, // unused
|
|
152
|
-
transform: { // defines how to position the instance in the world
|
|
153
|
-
translation: { x: 0, y: 0, z: 0 },
|
|
154
|
-
rotation: { x: 0, y: 0, z: 0 },
|
|
155
|
-
scale: { x: 1, y: 1, z: 1 }
|
|
156
|
-
},
|
|
157
|
-
geometryContainer: geometryContainer // reference to a geometry container
|
|
158
|
-
}
|
|
159
|
-
]
|
|
160
|
-
});
|
|
161
|
-
Building Acceleration Containers
|
|
162
|
-
To let the driver build the BVHs and everything else for our acceleration containers, we use the command buildRayTracingAccelerationContainer. There are 2 important things to keep in mind when building acceleration containers. First, BLAC which get referenced in a TLAC must be built before the TLAC. Second, BLAC and TLAC must not be built in the same pass, use different passes instead.
|
|
163
|
-
|
|
164
|
-
In terms of code this means:
|
|
165
|
-
|
|
166
|
-
let commandEncoder = device.createCommandEncoder({});
|
|
167
|
-
commandEncoder.buildRayTracingAccelerationContainer(geometryContainer);
|
|
168
|
-
queue.submit([ commandEncoder.finish() ]);
|
|
169
|
-
let commandEncoder = device.createCommandEncoder({});
|
|
170
|
-
// instanceContainer can now be built, since geometryContainer was built before
|
|
171
|
-
commandEncoder.buildRayTracingAccelerationContainer(instanceContainer);
|
|
172
|
-
queue.submit([ commandEncoder.finish() ]);
|
|
173
|
-
Pixel Buffer
|
|
174
|
-
RT shaders run similar as a compute shader and we somehow have to get our ray traced pixels to the screen. To do so, we create a pixel buffer, which we will write our pixels into, and then copy these pixels to the screen.
|
|
175
|
-
|
|
176
|
-
let pixelBufferSize = window.width * window.height * 4 * Float32Array.BYTES_PER_ELEMENT;
|
|
177
|
-
let pixelBuffer = device.createBuffer({
|
|
178
|
-
size: pixelBufferSize,
|
|
179
|
-
usage: GPUBufferUsage.STORAGE
|
|
180
|
-
});
|
|
181
|
-
Bind Group Layout
|
|
182
|
-
There is a new type for bind group layouts, which allows us to assign a TLAC:
|
|
183
|
-
|
|
184
|
-
let rtBindGroupLayout = device.createBindGroupLayout({
|
|
185
|
-
bindings: [
|
|
186
|
-
// the first binding will be the acceleration container
|
|
187
|
-
{
|
|
188
|
-
binding: 0,
|
|
189
|
-
visibility: GPUShaderStage.RAY_GENERATION,
|
|
190
|
-
type: "acceleration-container"
|
|
191
|
-
},
|
|
192
|
-
// the second binding will be the pixel buffer
|
|
193
|
-
{
|
|
194
|
-
binding: 1,
|
|
195
|
-
visibility: GPUShaderStage.RAY_GENERATION,
|
|
196
|
-
type: "storage-buffer"
|
|
197
|
-
}
|
|
198
|
-
]
|
|
199
|
-
});
|
|
200
|
-
Bind Group
|
|
201
|
-
When creating our bind group, we simply set the acceleration container and the pixel buffer.
|
|
202
|
-
|
|
203
|
-
let rtBindGroup = device.createBindGroup({
|
|
204
|
-
layout: rtBindGroupLayout,
|
|
205
|
-
bindings: [
|
|
206
|
-
{
|
|
207
|
-
binding: 0,
|
|
208
|
-
accelerationContainer: instanceContainer,
|
|
209
|
-
offset: 0,
|
|
210
|
-
size: 0
|
|
211
|
-
},
|
|
212
|
-
{
|
|
213
|
-
binding: 1,
|
|
214
|
-
buffer: pixelBuffer,
|
|
215
|
-
offset: 0,
|
|
216
|
-
size: pixelBufferSize
|
|
217
|
-
}
|
|
218
|
-
]
|
|
219
|
-
});
|
|
220
|
-
Ray-Generation Shader
|
|
221
|
-
RT shaders are similar to compute shaders, but when requiring the GL_EXT_ray_tracing extension, things change quite a lot. I’ll only describe the most important bits:
|
|
222
|
-
|
|
223
|
-
rayPayloadEXT: This payload is used to communicate between shader stages. For example, when the hit shader is called, we can write a result into the payload, and read it back in the ray generation shader. Note that in other shaders, the payload is called rayPayloadInEXT.
|
|
224
|
-
uniform accelerationStructureEXT: The TLAC that we’ve binded in our bind group.
|
|
225
|
-
gl_LaunchIDEXT: Is the relative pixel position, based on our traceRays call dimension.
|
|
226
|
-
gl_LaunchSizeEXT: Is the dimension, specified in our traceRays call.
|
|
227
|
-
traceRayEXT: Traces rays into a TLAC
|
|
228
|
-
#version 460
|
|
229
|
-
#extension GL_EXT_ray_tracing : enable
|
|
230
|
-
#pragma shader_stage(raygen)
|
|
231
|
-
|
|
232
|
-
struct RayPayload { vec3 color; };
|
|
233
|
-
layout(location = 0) rayPayloadEXT RayPayload payload;
|
|
234
|
-
|
|
235
|
-
layout(set = 0, binding = 0) uniform accelerationStructureEXT acc;
|
|
236
|
-
layout(set = 0, binding = 1, std140) buffer PixelBuffer {
|
|
237
|
-
vec4 pixels[];
|
|
238
|
-
} pixelBuffer;
|
|
239
|
-
|
|
240
|
-
void main() {
|
|
241
|
-
ivec2 ipos = ivec2(gl_LaunchIDEXT.xy);
|
|
242
|
-
const ivec2 resolution = ivec2(gl_LaunchSizeEXT.xy);
|
|
243
|
-
|
|
244
|
-
vec2 pixelCenter = vec2(gl_LaunchIDEXT.xy) + vec2(0.5);
|
|
245
|
-
|
|
246
|
-
vec2 d = (pixelCenter / vec2(gl_LaunchSizeEXT.xy)) * 2.0 - 1.0;
|
|
247
|
-
float aspectRatio = float(gl_LaunchSizeEXT.x) / float(gl_LaunchSizeEXT.y);
|
|
248
|
-
|
|
249
|
-
vec3 rayOrigin = vec3(0, 0, -1.5);
|
|
250
|
-
vec3 rayDir = normalize(vec3(d.x * aspectRatio, -d.y, 1));
|
|
251
|
-
|
|
252
|
-
uint sbtOffset = 0;
|
|
253
|
-
uint sbtStride = 0;
|
|
254
|
-
uint missIndex = 0;
|
|
255
|
-
payload.color = vec3(0);
|
|
256
|
-
traceRayEXT(
|
|
257
|
-
acc, gl_RayFlagsOpaqueEXT, 0xff,
|
|
258
|
-
sbtOffset, sbtStride, missIndex,
|
|
259
|
-
rayOrigin, 0.001, rayDir, 100.0,
|
|
260
|
-
0
|
|
261
|
-
);
|
|
262
|
-
|
|
263
|
-
const uint pixelIndex = ipos.y * resolution.x + ipos.x;
|
|
264
|
-
pixelBuffer.pixels[pixelIndex] = vec4(payload.color, 1.0);
|
|
265
|
-
}
|
|
266
|
-
Ray-Closest-Hit Shader
|
|
267
|
-
Gets executed for the closest intersection, relative to the ray.
|
|
268
|
-
|
|
269
|
-
rayPayloadInEXT: The payload shared between this shader and the ray generation shader.
|
|
270
|
-
hitAttributeEXT: Intersection point defined in the barycentric coordinate space.
|
|
271
|
-
Not used in this example, but important properties are also:
|
|
272
|
-
|
|
273
|
-
gl_InstanceCustomIndexEXT: Returns us the Id of the instance we have intersected with - We can define the instance id, when creating a TLAC.
|
|
274
|
-
gl_WorldRayDirectionEXT: Returns the ray’s direction in world-space and is normalized.
|
|
275
|
-
gl_WorldToObjectEXT and gl_ObjectToWorldEXT: Can be used, to convert between world-space and object-space. Note that both are 3x4 matrices.
|
|
276
|
-
gl_HitTEXT: The traveled distance of the ray.
|
|
277
|
-
Note that hit and miss shaders mostly have the same properties available (most importantly miss shaders have no hit information). You can find a list of all available properties here.
|
|
278
|
-
|
|
279
|
-
#version 460
|
|
280
|
-
#extension GL_EXT_ray_tracing : enable
|
|
281
|
-
#pragma shader_stage(closest)
|
|
282
|
-
|
|
283
|
-
struct RayPayload { vec3 color; };
|
|
284
|
-
layout(location = 0) rayPayloadInEXT RayPayload payload;
|
|
285
|
-
|
|
286
|
-
struct HitAttributeData { vec2 bary; };
|
|
287
|
-
hitAttributeEXT HitAttributeData attribs;
|
|
288
|
-
|
|
289
|
-
void main() {
|
|
290
|
-
vec3 bary = vec3(1.0 - attribs.bary.x - attribs.bary.y, attribs.bary.x, attribs.bary.y);
|
|
291
|
-
payload.color = bary;
|
|
292
|
-
}
|
|
293
|
-
Ray-Miss Shader
|
|
294
|
-
Gets executed whenever a ray hit nothing at all. We simply return a gray color here.
|
|
295
|
-
|
|
296
|
-
#version 460
|
|
297
|
-
#extension GL_EXT_ray_tracing : enable
|
|
298
|
-
#pragma shader_stage(miss)
|
|
299
|
-
|
|
300
|
-
struct RayPayload { vec3 color; };
|
|
301
|
-
layout(location = 0) rayPayloadInEXT RayPayload payload;
|
|
302
|
-
|
|
303
|
-
void main() {
|
|
304
|
-
payload.color = vec3(0.3);
|
|
305
|
-
}
|
|
306
|
-
Shader Binding Table
|
|
307
|
-
The SBT allows to group shaders together into groups, to dynamically invoke them later.
|
|
308
|
-
|
|
309
|
-
The following shaders are supported:
|
|
310
|
-
|
|
311
|
-
GPUShaderStage.RAY_GENERATION
|
|
312
|
-
GPUShaderStage.RAY_ANY_HIT
|
|
313
|
-
GPUShaderStage.RAY_CLOSEST_HIT
|
|
314
|
-
GPUShaderStage.RAY_MISS
|
|
315
|
-
GPUShaderStage.RAY_INTERSECTION
|
|
316
|
-
In stages, you add all the shaders you want to use. In groups, you then index the shaders defined in stages and also define how each shader will be used then.
|
|
317
|
-
|
|
318
|
-
If the index in a group is -1, it means that there is no associated stage. You can also combine multiple stages into one group together, which is done e.g. for procedural hit shaders, where a hit shader is used along an intersection shader.
|
|
319
|
-
|
|
320
|
-
Note that the generalIndex is used for ray generation and ray miss shaders.
|
|
321
|
-
|
|
322
|
-
let shaderBindingTable = device.createRayTracingShaderBindingTable({
|
|
323
|
-
stages: [
|
|
324
|
-
{
|
|
325
|
-
module: rayGenShaderModule,
|
|
326
|
-
stage: GPUShaderStage.RAY_GENERATION
|
|
327
|
-
},
|
|
328
|
-
{
|
|
329
|
-
module: rayCHitShaderModule,
|
|
330
|
-
stage: GPUShaderStage.RAY_CLOSEST_HIT
|
|
331
|
-
},
|
|
332
|
-
{
|
|
333
|
-
module: rayMissShaderModule,
|
|
334
|
-
stage: GPUShaderStage.RAY_MISS
|
|
335
|
-
}
|
|
336
|
-
],
|
|
337
|
-
groups: [
|
|
338
|
-
{
|
|
339
|
-
type: "general",
|
|
340
|
-
generalIndex: 0,
|
|
341
|
-
anyHitIndex: -1,
|
|
342
|
-
closestHitIndex: -1,
|
|
343
|
-
intersectionIndex: -1
|
|
344
|
-
},
|
|
345
|
-
{
|
|
346
|
-
type: "triangles-hit-group",
|
|
347
|
-
generalIndex: -1,
|
|
348
|
-
anyHitIndex: -1,
|
|
349
|
-
closestHitIndex: 1,
|
|
350
|
-
intersectionIndex: -1
|
|
351
|
-
},
|
|
352
|
-
{
|
|
353
|
-
type: "general",
|
|
354
|
-
generalIndex: 2,
|
|
355
|
-
anyHitIndex: -1,
|
|
356
|
-
closestHitIndex: -1,
|
|
357
|
-
intersectionIndex: -1
|
|
358
|
-
}
|
|
359
|
-
]
|
|
360
|
-
});
|
|
361
|
-
Ray tracing Pipeline
|
|
362
|
-
Creating the RT pipeline is straightforward. The only important property here is maxRecursionDepth, which we can use to specify upfront how many shader recursions we want to allow. Note that GPUs are bad at performing recursion, and when possible, recursive calls should be flattened into a loop. We will leave this value to 1, so we can shoot our rays in the ray generation shader.
|
|
363
|
-
|
|
364
|
-
let rtPipeline = device.createRayTracingPipeline({
|
|
365
|
-
layout: device.createPipelineLayout({
|
|
366
|
-
bindGroupLayouts: [rtBindGroupLayout]
|
|
367
|
-
}),
|
|
368
|
-
rayTracingState: {
|
|
369
|
-
shaderBindingTable,
|
|
370
|
-
maxRecursionDepth: 1
|
|
371
|
-
}
|
|
372
|
-
});
|
|
373
|
-
Tracing Rays
|
|
374
|
-
The RT Pass’s only difference to other passes is, that we have the command traceRays, which allows us to shoot rays with a given dimension. You might find it useful to kind of think of traceRays as the dispatch command in a compute pass.
|
|
375
|
-
|
|
376
|
-
let commandEncoder = device.createCommandEncoder({});
|
|
377
|
-
let passEncoder = commandEncoder.beginRayTracingPass({});
|
|
378
|
-
passEncoder.setPipeline(rtPipeline);
|
|
379
|
-
passEncoder.setBindGroup(0, rtBindGroup);
|
|
380
|
-
passEncoder.traceRays(
|
|
381
|
-
0, // sbt ray-generation index
|
|
382
|
-
1, // sbt ray-hit index
|
|
383
|
-
2, // sbt ray-miss index
|
|
384
|
-
window.width, // query width
|
|
385
|
-
window.height, // query height
|
|
386
|
-
1 // query depth
|
|
387
|
-
);
|
|
388
|
-
passEncoder.endPass();
|
|
389
|
-
queue.submit([ commandEncoder.finish() ]);
|
|
390
|
-
Blit to Screen
|
|
391
|
-
I left the part of setting up the rasterization pipeline, as it should be straightforward and only the blit shaders should be interesting.
|
|
392
|
-
|
|
393
|
-
The blit vertex shader is created like this and defines a fullscreen quad:
|
|
394
|
-
|
|
395
|
-
#version 450
|
|
396
|
-
#pragma shader_stage(vertex)
|
|
397
|
-
|
|
398
|
-
layout (location = 0) out vec2 uv;
|
|
399
|
-
|
|
400
|
-
void main() {
|
|
401
|
-
vec2 pos = vec2((gl_VertexIndex << 1) & 2, gl_VertexIndex & 2);
|
|
402
|
-
gl_Position = vec4(pos * 2.0 - 1.0, 0.0, 1.0);
|
|
403
|
-
uv = pos;
|
|
404
|
-
}
|
|
405
|
-
Note that you have to draw with a vertex count 3 to successfully run this shader. Also, make sure that your blit pass has a color attachment with the swapchain image as input.
|
|
406
|
-
|
|
407
|
-
#version 450
|
|
408
|
-
#pragma shader_stage(fragment)
|
|
409
|
-
|
|
410
|
-
layout (location = 0) in vec2 uv;
|
|
411
|
-
layout (location = 0) out vec4 outColor;
|
|
412
|
-
|
|
413
|
-
layout(std140, set = 0, binding = 0) buffer PixelBuffer {
|
|
414
|
-
vec4 pixels[];
|
|
415
|
-
} pixelBuffer;
|
|
416
|
-
|
|
417
|
-
layout(set = 0, binding = 1) uniform ScreenDimension {
|
|
418
|
-
vec2 resolution;
|
|
419
|
-
};
|
|
420
|
-
|
|
421
|
-
void main() {
|
|
422
|
-
const ivec2 bufferCoord = ivec2(floor(uv * resolution));
|
|
423
|
-
const vec2 fragCoord = (uv * resolution);
|
|
424
|
-
const uint pixelIndex = bufferCoord.y * uint(resolution.x) + bufferCoord.x;
|
|
425
|
-
|
|
426
|
-
vec4 pixelColor = pixelBuffer.pixels[pixelIndex];
|
|
427
|
-
outColor = pixelColor;
|
|
428
|
-
}
|
|
429
|
-
The fragment shader is just copying the pixels of the pixel buffer into the color attachment.
|
|
430
|
-
|
|
431
|
-
Tada
|
|
432
|
-
Even though it’s just a simple triangle, you can do quite a lot things with them. In this tutorial, I didn’t cover the entire extension, but let me show you 3 further features, which can be quite handy:
|
|
433
|
-
|
|
434
|
-
Procedural geometry
|
|
435
|
-
Instead of triangles, using ray intersection shaders and AABB geometry, it’s possible to efficiently render millions of objects.
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
Procedural geometry (Voxels) with ray tracing
|
|
439
|
-
|
|
440
|
-
On a GTX 1080, I could smoothly render about 25.000.000 Voxels.
|
|
441
|
-
|
|
442
|
-
updateRayTracingAccelerationContainer
|
|
443
|
-
This method updates an acceleration container and can be used for BLAC and TLAC. If it’s a BLAC, then you might have updated the vertex buffer before (e.g. skeletal animation). Or in case of a TLAC, you might want to update its instances. Note that to efficiently update the instances of a TLAC, GPURayTracingAccelerationContainerDescriptor.instanceBuffer should be used.
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
GLTF skeletal animation with RT
|
|
447
|
-
|
|
448
|
-
This image is showing a quick implementation for skeletal animation, where the acceleration containers get updated each frame, and the vertex skinning is done in a compute shader. The model and the animation is taken from Unreal Engine 4.
|
|
449
|
-
|
|
450
|
-
copyRayTracingAccelerationContainer
|
|
451
|
-
This method allows to copy the state of an acceleration container into another container and works for both BLAC and TLAC.
|
|
452
|
-
|
|
453
|
-
Tags: WebGPU Ray Tracing
|
|
454
|
-
RSSEmail meGitHubTwitter
|
|
455
|
-
Felix Maier • 2022 • maierfelix.github.io
|
|
456
|
-
|
|
457
|
-
Theme by beautiful-jekyll
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
https://maierfelix.github.io/2020-01-13-webgpu-ray-tracing/?utm_source=chatgpt.com
|
package/empty.js
DELETED
|
@@ -1,13 +0,0 @@
|
|
|
1
|
-
import MatrixEngineWGPU from "./src/world.js";
|
|
2
|
-
import {downloadMeshes} from './src/engine/loader-obj.js';
|
|
3
|
-
import {LOG_MATRIX} from "./src/engine/utils.js";
|
|
4
|
-
|
|
5
|
-
export let application = new MatrixEngineWGPU({
|
|
6
|
-
useSingleRenderPass: false,
|
|
7
|
-
canvasSize: 'fullscreen'
|
|
8
|
-
}, () => {
|
|
9
|
-
window.app = application
|
|
10
|
-
// for now
|
|
11
|
-
window.downloadMeshes = downloadMeshes;
|
|
12
|
-
console.info(`%c matrix-engine-wgpu [ready]`, LOG_MATRIX);
|
|
13
|
-
})
|