├── README.md
├── WebGPU.png
├── compute-crash
└── index.html
├── cube.html
├── d3d12-bug
├── gl-matrix
│ ├── LICENSE.md
│ └── src
│ │ ├── gl-matrix.js
│ │ └── gl-matrix
│ │ ├── common.js
│ │ ├── mat2.js
│ │ ├── mat2d.js
│ │ ├── mat3.js
│ │ ├── mat4.js
│ │ ├── quat.js
│ │ ├── quat2.js
│ │ ├── vec2.js
│ │ ├── vec3.js
│ │ └── vec4.js
└── index.html
├── device-loss.html
├── favicon-16.png
├── favicon-32.png
├── favicon-48.png
├── flashing-bug
└── index.html
├── iframe-raf
├── iframe.html
└── index.html
├── iframe.html
├── index.html
├── js
├── camera.js
├── mini-gltf2.js
├── pbr-shader.js
├── renderer.js
├── third-party
│ ├── gl-matrix
│ │ ├── LICENSE.md
│ │ └── src
│ │ │ ├── gl-matrix.js
│ │ │ └── gl-matrix
│ │ │ ├── common.js
│ │ │ ├── mat2.js
│ │ │ ├── mat2d.js
│ │ │ ├── mat3.js
│ │ │ ├── mat4.js
│ │ │ ├── quat.js
│ │ │ ├── quat2.js
│ │ │ ├── vec2.js
│ │ │ ├── vec3.js
│ │ │ └── vec4.js
│ └── stats.module.js
├── webgl-renderer
│ ├── shader-program.js
│ └── webgl-renderer.js
├── webgl2-renderer
│ └── webgl2-renderer.js
└── webgpu-renderer
│ ├── pbr-shader-wgsl.js
│ ├── webgpu-renderer.js
│ ├── webgpu-texture-helper.js
│ └── wgsl-debug-helper.js
├── matrix-shader-crash.html
├── media
├── models
│ └── sponza
│ │ ├── 10381718147657362067.jpg
│ │ ├── 10388182081421875623.jpg
│ │ ├── 11474523244911310074.jpg
│ │ ├── 11490520546946913238.jpg
│ │ ├── 11872827283454512094.jpg
│ │ ├── 11968150294050148237.jpg
│ │ ├── 1219024358953944284.jpg
│ │ ├── 12501374198249454378.jpg
│ │ ├── 13196865903111448057.jpg
│ │ ├── 13824894030729245199.jpg
│ │ ├── 13982482287905699490.jpg
│ │ ├── 14118779221266351425.jpg
│ │ ├── 14170708867020035030.jpg
│ │ ├── 14267839433702832875.jpg
│ │ ├── 14650633544276105767.jpg
│ │ ├── 15295713303328085182.jpg
│ │ ├── 15722799267630235092.jpg
│ │ ├── 16275776544635328252.png
│ │ ├── 16299174074766089871.jpg
│ │ ├── 16885566240357350108.jpg
│ │ ├── 17556969131407844942.jpg
│ │ ├── 17876391417123941155.jpg
│ │ ├── 2051777328469649772.jpg
│ │ ├── 2185409758123873465.jpg
│ │ ├── 2299742237651021498.jpg
│ │ ├── 2374361008830720677.jpg
│ │ ├── 2411100444841994089.jpg
│ │ ├── 2775690330959970771.jpg
│ │ ├── 2969916736137545357.jpg
│ │ ├── 332936164838540657.jpg
│ │ ├── 3371964815757888145.jpg
│ │ ├── 3455394979645218238.jpg
│ │ ├── 3628158980083700836.jpg
│ │ ├── 3827035219084910048.jpg
│ │ ├── 4477655471536070370.jpg
│ │ ├── 4601176305987539675.jpg
│ │ ├── 466164707995436622.jpg
│ │ ├── 4675343432951571524.jpg
│ │ ├── 4871783166746854860.jpg
│ │ ├── 4910669866631290573.jpg
│ │ ├── 4975155472559461469.jpg
│ │ ├── 5061699253647017043.png
│ │ ├── 5792855332885324923.jpg
│ │ ├── 5823059166183034438.jpg
│ │ ├── 6047387724914829168.jpg
│ │ ├── 6151467286084645207.jpg
│ │ ├── 6593109234861095314.jpg
│ │ ├── 6667038893015345571.jpg
│ │ ├── 6772804448157695701.jpg
│ │ ├── 7056944414013900257.jpg
│ │ ├── 715093869573992647.jpg
│ │ ├── 7268504077753552595.jpg
│ │ ├── 7441062115984513793.jpg
│ │ ├── 755318871556304029.jpg
│ │ ├── 759203620573749278.jpg
│ │ ├── 7645212358685992005.jpg
│ │ ├── 7815564343179553343.jpg
│ │ ├── 8006627369776289000.png
│ │ ├── 8051790464816141987.jpg
│ │ ├── 8114461559286000061.jpg
│ │ ├── 8481240838833932244.jpg
│ │ ├── 8503262930880235456.jpg
│ │ ├── 8747919177698443163.jpg
│ │ ├── 8750083169368950601.jpg
│ │ ├── 8773302468495022225.jpg
│ │ ├── 8783994986360286082.jpg
│ │ ├── 9288698199695299068.jpg
│ │ ├── 9916269861720640319.jpg
│ │ ├── README.md
│ │ ├── Sponza.bin
│ │ ├── Sponza.gltf
│ │ └── white.png
└── webgpu-logo.svg
├── pipeline-fails
├── index.html
├── pipeline-test.js
├── simple.html
└── spookyball-pbr.html
├── playcanvas-crash
├── crash1.html
├── crash2.html
└── index.html
├── resize-crash
├── index.html
└── query-args.js
├── sample-mask-error
└── index.html
├── shader-of-doom
└── index.html
├── skybox-bug
├── README.md
├── index.html
└── skybox.js
├── thumb.png
├── webgpu-css-logo.html
└── webgpu-tilemap
├── index.html
├── spelunky-tiles.png
├── spelunky0.png
├── spelunky1.png
└── webgpu-tilemap.js
/README.md:
--------------------------------------------------------------------------------
1 | # Web Graphics API Test
2 |
3 | Live page at [https://toji.github.io/webgpu-test/](https://toji.github.io/webgpu-test/).
4 |
5 | This project renders a scene using WebGL, WebGL 2.0, and WebGPU as implemented in Chrome Canary circa Feb 2020. The purpose was mostly educational for me, I wanted to learn about the current state of WebGPU, but I also wanted to create a page that would allow for simple comparisons and profiling between the APIs.
6 |
7 | The renderer for each API loads resources from a glTF file and renders them using best practices for each API (without extensions). The scene was selected to be interesting and reasonably real-world in terms of geometry and materials, but it isn't particularly challenging for many modern GPUs, so don't expect to see framerate differences between the renderers. Additionally since the scene is the same and shaders are roughly equivalent across renderers the GPU time spent will probably be about the same.
8 |
9 | The more interesting thing to look at is how much time each API spends submitting commands on the JavaScript main thread, which is what the stats counter in the upper left corner is configured to show by default. Even then, though, it's worth noting that this is a relatively simple usage of each API rendering a largely static scene without much of the overhead that would come from more realistic app logic, animation, audio, etc. Also WebGPU is still a work in progress and is expected to undergo both API changes and implementation optimizations prior to shipping. As such any performance observations made with this project should be taken with a grain of salt.
10 |
11 | ## WebGL
12 | The most verbose API, so it's easily the slowest. In real world apps you'd definitely want to use at least the OES_vertex_array_object and ANGLE_instanced_arrays if applicable to reduce the number of API calls needed. Here it mostly serves as a baseline to compare the performance of the other renderers to.
13 |
14 | ## WebGL 2.0
15 | The biggest gains with this renderer vs. the WebGL renderer come from using Vertex Array Objects and Uniform Buffer Objects to drastically reduce the number of calls in the render loop. Instancing is also used to reduce the number of calls needed to render the light orbs. (The main scene does not use instancing because the source asset was not configured to use it.)
16 |
17 | ## WebGPU
18 | The nature of the API means that most of the work is done at initialization time, so the number of calls needed to dispatch draw commands is lower than WebGL 2.0 to begin with, but the render loop is reduced down to almost nothing by using the GPURenderBundles to record the draw commands used at load time and replay them with a single call during the frame callback.
19 |
20 | It should be noted that the WebGPU code path uses [WGSL shaders](https://gpuweb.github.io/gpuweb/wgsl.html), which is WebGPU's native shading language but is not yet finalized. Some breakage is expected in the future, but I'll generally keep it up-to-date with Chrome Canary's implementation.
21 |
22 | To test the WebGPU renderer use Chrome Canary on Windows or MacOS, navigate to about:flags, and turn on the "Unsafe WebGPU" flag.
23 |
--------------------------------------------------------------------------------
/WebGPU.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/toji/webgpu-test/148e8fe87465777f4994f7998c7f2822e1b9b50a/WebGPU.png
--------------------------------------------------------------------------------
/d3d12-bug/gl-matrix/LICENSE.md:
--------------------------------------------------------------------------------
1 | Copyright (c) 2015-2018, Brandon Jones, Colin MacKenzie IV.
2 |
3 | Permission is hereby granted, free of charge, to any person obtaining a copy
4 | of this software and associated documentation files (the "Software"), to deal
5 | in the Software without restriction, including without limitation the rights
6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7 | copies of the Software, and to permit persons to whom the Software is
8 | furnished to do so, subject to the following conditions:
9 |
10 | The above copyright notice and this permission notice shall be included in
11 | all copies or substantial portions of the Software.
12 |
13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19 | THE SOFTWARE.
--------------------------------------------------------------------------------
/d3d12-bug/gl-matrix/src/gl-matrix.js:
--------------------------------------------------------------------------------
1 | import * as glMatrix from "./gl-matrix/common.js";
2 | import * as mat2 from "./gl-matrix/mat2.js";
3 | import * as mat2d from "./gl-matrix/mat2d.js";
4 | import * as mat3 from "./gl-matrix/mat3.js";
5 | import * as mat4 from "./gl-matrix/mat4.js";
6 | import * as quat from "./gl-matrix/quat.js";
7 | import * as quat2 from "./gl-matrix/quat2.js";
8 | import * as vec2 from "./gl-matrix/vec2.js";
9 | import * as vec3 from "./gl-matrix/vec3.js";
10 | import * as vec4 from "./gl-matrix/vec4.js";
11 |
12 | export {
13 | glMatrix,
14 | mat2, mat2d, mat3, mat4,
15 | quat, quat2,
16 | vec2, vec3, vec4,
17 | };
18 |
--------------------------------------------------------------------------------
/d3d12-bug/gl-matrix/src/gl-matrix/common.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Common utilities
3 | * @module glMatrix
4 | */
5 |
6 | // Configuration Constants
7 | export const EPSILON = 0.000001;
8 | export let ARRAY_TYPE = (typeof Float32Array !== 'undefined') ? Float32Array : Array;
9 | export const RANDOM = Math.random;
10 |
11 | /**
12 | * Sets the type of array used when creating new vectors and matrices
13 | *
14 | * @param {Type} type Array type, such as Float32Array or Array
15 | */
16 | export function setMatrixArrayType(type) {
17 | ARRAY_TYPE = type;
18 | }
19 |
20 | const degree = Math.PI / 180;
21 |
22 | /**
23 | * Convert Degree To Radian
24 | *
25 | * @param {Number} a Angle in Degrees
26 | */
27 | export function toRadian(a) {
28 | return a * degree;
29 | }
30 |
31 | /**
32 | * Tests whether or not the arguments have approximately the same value, within an absolute
33 | * or relative tolerance of glMatrix.EPSILON (an absolute tolerance is used for values less
34 | * than or equal to 1.0, and a relative tolerance is used for larger values)
35 | *
36 | * @param {Number} a The first number to test.
37 | * @param {Number} b The second number to test.
38 | * @returns {Boolean} True if the numbers are approximately equal, false otherwise.
39 | */
40 | export function equals(a, b) {
41 | return Math.abs(a - b) <= EPSILON*Math.max(1.0, Math.abs(a), Math.abs(b));
42 | }
43 |
--------------------------------------------------------------------------------
/d3d12-bug/gl-matrix/src/gl-matrix/mat2.js:
--------------------------------------------------------------------------------
1 | import * as glMatrix from "./common.js"
2 |
3 | /**
4 | * 2x2 Matrix
5 | * @module mat2
6 | */
7 |
8 | /**
9 | * Creates a new identity mat2
10 | *
11 | * @returns {mat2} a new 2x2 matrix
12 | */
13 | export function create() {
14 | let out = new glMatrix.ARRAY_TYPE(4);
15 | if(glMatrix.ARRAY_TYPE != Float32Array) {
16 | out[1] = 0;
17 | out[2] = 0;
18 | }
19 | out[0] = 1;
20 | out[3] = 1;
21 | return out;
22 | }
23 |
24 | /**
25 | * Creates a new mat2 initialized with values from an existing matrix
26 | *
27 | * @param {mat2} a matrix to clone
28 | * @returns {mat2} a new 2x2 matrix
29 | */
30 | export function clone(a) {
31 | let out = new glMatrix.ARRAY_TYPE(4);
32 | out[0] = a[0];
33 | out[1] = a[1];
34 | out[2] = a[2];
35 | out[3] = a[3];
36 | return out;
37 | }
38 |
39 | /**
40 | * Copy the values from one mat2 to another
41 | *
42 | * @param {mat2} out the receiving matrix
43 | * @param {mat2} a the source matrix
44 | * @returns {mat2} out
45 | */
46 | export function copy(out, a) {
47 | out[0] = a[0];
48 | out[1] = a[1];
49 | out[2] = a[2];
50 | out[3] = a[3];
51 | return out;
52 | }
53 |
54 | /**
55 | * Set a mat2 to the identity matrix
56 | *
57 | * @param {mat2} out the receiving matrix
58 | * @returns {mat2} out
59 | */
60 | export function identity(out) {
61 | out[0] = 1;
62 | out[1] = 0;
63 | out[2] = 0;
64 | out[3] = 1;
65 | return out;
66 | }
67 |
68 | /**
69 | * Create a new mat2 with the given values
70 | *
71 | * @param {Number} m00 Component in column 0, row 0 position (index 0)
72 | * @param {Number} m01 Component in column 0, row 1 position (index 1)
73 | * @param {Number} m10 Component in column 1, row 0 position (index 2)
74 | * @param {Number} m11 Component in column 1, row 1 position (index 3)
75 | * @returns {mat2} out A new 2x2 matrix
76 | */
77 | export function fromValues(m00, m01, m10, m11) {
78 | let out = new glMatrix.ARRAY_TYPE(4);
79 | out[0] = m00;
80 | out[1] = m01;
81 | out[2] = m10;
82 | out[3] = m11;
83 | return out;
84 | }
85 |
86 | /**
87 | * Set the components of a mat2 to the given values
88 | *
89 | * @param {mat2} out the receiving matrix
90 | * @param {Number} m00 Component in column 0, row 0 position (index 0)
91 | * @param {Number} m01 Component in column 0, row 1 position (index 1)
92 | * @param {Number} m10 Component in column 1, row 0 position (index 2)
93 | * @param {Number} m11 Component in column 1, row 1 position (index 3)
94 | * @returns {mat2} out
95 | */
96 | export function set(out, m00, m01, m10, m11) {
97 | out[0] = m00;
98 | out[1] = m01;
99 | out[2] = m10;
100 | out[3] = m11;
101 | return out;
102 | }
103 |
104 | /**
105 | * Transpose the values of a mat2
106 | *
107 | * @param {mat2} out the receiving matrix
108 | * @param {mat2} a the source matrix
109 | * @returns {mat2} out
110 | */
111 | export function transpose(out, a) {
112 | // If we are transposing ourselves we can skip a few steps but have to cache
113 | // some values
114 | if (out === a) {
115 | let a1 = a[1];
116 | out[1] = a[2];
117 | out[2] = a1;
118 | } else {
119 | out[0] = a[0];
120 | out[1] = a[2];
121 | out[2] = a[1];
122 | out[3] = a[3];
123 | }
124 |
125 | return out;
126 | }
127 |
128 | /**
129 | * Inverts a mat2
130 | *
131 | * @param {mat2} out the receiving matrix
132 | * @param {mat2} a the source matrix
133 | * @returns {mat2} out
134 | */
135 | export function invert(out, a) {
136 | let a0 = a[0], a1 = a[1], a2 = a[2], a3 = a[3];
137 |
138 | // Calculate the determinant
139 | let det = a0 * a3 - a2 * a1;
140 |
141 | if (!det) {
142 | return null;
143 | }
144 | det = 1.0 / det;
145 |
146 | out[0] = a3 * det;
147 | out[1] = -a1 * det;
148 | out[2] = -a2 * det;
149 | out[3] = a0 * det;
150 |
151 | return out;
152 | }
153 |
154 | /**
155 | * Calculates the adjugate of a mat2
156 | *
157 | * @param {mat2} out the receiving matrix
158 | * @param {mat2} a the source matrix
159 | * @returns {mat2} out
160 | */
161 | export function adjoint(out, a) {
162 | // Caching this value is nessecary if out == a
163 | let a0 = a[0];
164 | out[0] = a[3];
165 | out[1] = -a[1];
166 | out[2] = -a[2];
167 | out[3] = a0;
168 |
169 | return out;
170 | }
171 |
172 | /**
173 | * Calculates the determinant of a mat2
174 | *
175 | * @param {mat2} a the source matrix
176 | * @returns {Number} determinant of a
177 | */
178 | export function determinant(a) {
179 | return a[0] * a[3] - a[2] * a[1];
180 | }
181 |
182 | /**
183 | * Multiplies two mat2's
184 | *
185 | * @param {mat2} out the receiving matrix
186 | * @param {mat2} a the first operand
187 | * @param {mat2} b the second operand
188 | * @returns {mat2} out
189 | */
190 | export function multiply(out, a, b) {
191 | let a0 = a[0], a1 = a[1], a2 = a[2], a3 = a[3];
192 | let b0 = b[0], b1 = b[1], b2 = b[2], b3 = b[3];
193 | out[0] = a0 * b0 + a2 * b1;
194 | out[1] = a1 * b0 + a3 * b1;
195 | out[2] = a0 * b2 + a2 * b3;
196 | out[3] = a1 * b2 + a3 * b3;
197 | return out;
198 | }
199 |
200 | /**
201 | * Rotates a mat2 by the given angle
202 | *
203 | * @param {mat2} out the receiving matrix
204 | * @param {mat2} a the matrix to rotate
205 | * @param {Number} rad the angle to rotate the matrix by
206 | * @returns {mat2} out
207 | */
208 | export function rotate(out, a, rad) {
209 | let a0 = a[0], a1 = a[1], a2 = a[2], a3 = a[3];
210 | let s = Math.sin(rad);
211 | let c = Math.cos(rad);
212 | out[0] = a0 * c + a2 * s;
213 | out[1] = a1 * c + a3 * s;
214 | out[2] = a0 * -s + a2 * c;
215 | out[3] = a1 * -s + a3 * c;
216 | return out;
217 | }
218 |
219 | /**
220 | * Scales the mat2 by the dimensions in the given vec2
221 | *
222 | * @param {mat2} out the receiving matrix
223 | * @param {mat2} a the matrix to rotate
224 | * @param {vec2} v the vec2 to scale the matrix by
225 | * @returns {mat2} out
226 | **/
227 | export function scale(out, a, v) {
228 | let a0 = a[0], a1 = a[1], a2 = a[2], a3 = a[3];
229 | let v0 = v[0], v1 = v[1];
230 | out[0] = a0 * v0;
231 | out[1] = a1 * v0;
232 | out[2] = a2 * v1;
233 | out[3] = a3 * v1;
234 | return out;
235 | }
236 |
237 | /**
238 | * Creates a matrix from a given angle
239 | * This is equivalent to (but much faster than):
240 | *
241 | * mat2.identity(dest);
242 | * mat2.rotate(dest, dest, rad);
243 | *
244 | * @param {mat2} out mat2 receiving operation result
245 | * @param {Number} rad the angle to rotate the matrix by
246 | * @returns {mat2} out
247 | */
248 | export function fromRotation(out, rad) {
249 | let s = Math.sin(rad);
250 | let c = Math.cos(rad);
251 | out[0] = c;
252 | out[1] = s;
253 | out[2] = -s;
254 | out[3] = c;
255 | return out;
256 | }
257 |
258 | /**
259 | * Creates a matrix from a vector scaling
260 | * This is equivalent to (but much faster than):
261 | *
262 | * mat2.identity(dest);
263 | * mat2.scale(dest, dest, vec);
264 | *
265 | * @param {mat2} out mat2 receiving operation result
266 | * @param {vec2} v Scaling vector
267 | * @returns {mat2} out
268 | */
269 | export function fromScaling(out, v) {
270 | out[0] = v[0];
271 | out[1] = 0;
272 | out[2] = 0;
273 | out[3] = v[1];
274 | return out;
275 | }
276 |
277 | /**
278 | * Returns a string representation of a mat2
279 | *
280 | * @param {mat2} a matrix to represent as a string
281 | * @returns {String} string representation of the matrix
282 | */
283 | export function str(a) {
284 | return 'mat2(' + a[0] + ', ' + a[1] + ', ' + a[2] + ', ' + a[3] + ')';
285 | }
286 |
287 | /**
288 | * Returns Frobenius norm of a mat2
289 | *
290 | * @param {mat2} a the matrix to calculate Frobenius norm of
291 | * @returns {Number} Frobenius norm
292 | */
293 | export function frob(a) {
294 | return(Math.sqrt(Math.pow(a[0], 2) + Math.pow(a[1], 2) + Math.pow(a[2], 2) + Math.pow(a[3], 2)))
295 | }
296 |
297 | /**
298 | * Returns L, D and U matrices (Lower triangular, Diagonal and Upper triangular) by factorizing the input matrix
299 | * @param {mat2} L the lower triangular matrix
300 | * @param {mat2} D the diagonal matrix
301 | * @param {mat2} U the upper triangular matrix
302 | * @param {mat2} a the input matrix to factorize
303 | */
304 |
305 | export function LDU(L, D, U, a) {
306 | L[2] = a[2]/a[0];
307 | U[0] = a[0];
308 | U[1] = a[1];
309 | U[3] = a[3] - L[2] * U[1];
310 | return [L, D, U];
311 | }
312 |
313 | /**
314 | * Adds two mat2's
315 | *
316 | * @param {mat2} out the receiving matrix
317 | * @param {mat2} a the first operand
318 | * @param {mat2} b the second operand
319 | * @returns {mat2} out
320 | */
321 | export function add(out, a, b) {
322 | out[0] = a[0] + b[0];
323 | out[1] = a[1] + b[1];
324 | out[2] = a[2] + b[2];
325 | out[3] = a[3] + b[3];
326 | return out;
327 | }
328 |
329 | /**
330 | * Subtracts matrix b from matrix a
331 | *
332 | * @param {mat2} out the receiving matrix
333 | * @param {mat2} a the first operand
334 | * @param {mat2} b the second operand
335 | * @returns {mat2} out
336 | */
337 | export function subtract(out, a, b) {
338 | out[0] = a[0] - b[0];
339 | out[1] = a[1] - b[1];
340 | out[2] = a[2] - b[2];
341 | out[3] = a[3] - b[3];
342 | return out;
343 | }
344 |
345 | /**
346 | * Returns whether or not the matrices have exactly the same elements in the same position (when compared with ===)
347 | *
348 | * @param {mat2} a The first matrix.
349 | * @param {mat2} b The second matrix.
350 | * @returns {Boolean} True if the matrices are equal, false otherwise.
351 | */
352 | export function exactEquals(a, b) {
353 | return a[0] === b[0] && a[1] === b[1] && a[2] === b[2] && a[3] === b[3];
354 | }
355 |
356 | /**
357 | * Returns whether or not the matrices have approximately the same elements in the same position.
358 | *
359 | * @param {mat2} a The first matrix.
360 | * @param {mat2} b The second matrix.
361 | * @returns {Boolean} True if the matrices are equal, false otherwise.
362 | */
363 | export function equals(a, b) {
364 | let a0 = a[0], a1 = a[1], a2 = a[2], a3 = a[3];
365 | let b0 = b[0], b1 = b[1], b2 = b[2], b3 = b[3];
366 | return (Math.abs(a0 - b0) <= glMatrix.EPSILON*Math.max(1.0, Math.abs(a0), Math.abs(b0)) &&
367 | Math.abs(a1 - b1) <= glMatrix.EPSILON*Math.max(1.0, Math.abs(a1), Math.abs(b1)) &&
368 | Math.abs(a2 - b2) <= glMatrix.EPSILON*Math.max(1.0, Math.abs(a2), Math.abs(b2)) &&
369 | Math.abs(a3 - b3) <= glMatrix.EPSILON*Math.max(1.0, Math.abs(a3), Math.abs(b3)));
370 | }
371 |
372 | /**
373 | * Multiply each element of the matrix by a scalar.
374 | *
375 | * @param {mat2} out the receiving matrix
376 | * @param {mat2} a the matrix to scale
377 | * @param {Number} b amount to scale the matrix's elements by
378 | * @returns {mat2} out
379 | */
380 | export function multiplyScalar(out, a, b) {
381 | out[0] = a[0] * b;
382 | out[1] = a[1] * b;
383 | out[2] = a[2] * b;
384 | out[3] = a[3] * b;
385 | return out;
386 | }
387 |
388 | /**
389 | * Adds two mat2's after multiplying each element of the second operand by a scalar value.
390 | *
391 | * @param {mat2} out the receiving vector
392 | * @param {mat2} a the first operand
393 | * @param {mat2} b the second operand
394 | * @param {Number} scale the amount to scale b's elements by before adding
395 | * @returns {mat2} out
396 | */
397 | export function multiplyScalarAndAdd(out, a, b, scale) {
398 | out[0] = a[0] + (b[0] * scale);
399 | out[1] = a[1] + (b[1] * scale);
400 | out[2] = a[2] + (b[2] * scale);
401 | out[3] = a[3] + (b[3] * scale);
402 | return out;
403 | }
404 |
405 | /**
406 | * Alias for {@link mat2.multiply}
407 | * @function
408 | */
409 | export const mul = multiply;
410 |
411 | /**
412 | * Alias for {@link mat2.subtract}
413 | * @function
414 | */
415 | export const sub = subtract;
416 |
--------------------------------------------------------------------------------
/d3d12-bug/gl-matrix/src/gl-matrix/mat2d.js:
--------------------------------------------------------------------------------
1 | import * as glMatrix from "./common.js";
2 |
3 | /**
4 | * 2x3 Matrix
5 | * @module mat2d
6 | *
7 | * @description
8 | * A mat2d contains six elements defined as:
9 | *
10 | * [a, c, tx,
11 | * b, d, ty]
12 | *
13 | * This is a short form for the 3x3 matrix:
14 | *
15 | * [a, c, tx,
16 | * b, d, ty,
17 | * 0, 0, 1]
18 | *
19 | * The last row is ignored so the array is shorter and operations are faster.
20 | */
21 |
22 | /**
23 | * Creates a new identity mat2d
24 | *
25 | * @returns {mat2d} a new 2x3 matrix
26 | */
27 | export function create() {
28 | let out = new glMatrix.ARRAY_TYPE(6);
29 | if(glMatrix.ARRAY_TYPE != Float32Array) {
30 | out[1] = 0;
31 | out[2] = 0;
32 | out[4] = 0;
33 | out[5] = 0;
34 | }
35 | out[0] = 1;
36 | out[3] = 1;
37 | return out;
38 | }
39 |
40 | /**
41 | * Creates a new mat2d initialized with values from an existing matrix
42 | *
43 | * @param {mat2d} a matrix to clone
44 | * @returns {mat2d} a new 2x3 matrix
45 | */
46 | export function clone(a) {
47 | let out = new glMatrix.ARRAY_TYPE(6);
48 | out[0] = a[0];
49 | out[1] = a[1];
50 | out[2] = a[2];
51 | out[3] = a[3];
52 | out[4] = a[4];
53 | out[5] = a[5];
54 | return out;
55 | }
56 |
57 | /**
58 | * Copy the values from one mat2d to another
59 | *
60 | * @param {mat2d} out the receiving matrix
61 | * @param {mat2d} a the source matrix
62 | * @returns {mat2d} out
63 | */
64 | export function copy(out, a) {
65 | out[0] = a[0];
66 | out[1] = a[1];
67 | out[2] = a[2];
68 | out[3] = a[3];
69 | out[4] = a[4];
70 | out[5] = a[5];
71 | return out;
72 | }
73 |
74 | /**
75 | * Set a mat2d to the identity matrix
76 | *
77 | * @param {mat2d} out the receiving matrix
78 | * @returns {mat2d} out
79 | */
80 | export function identity(out) {
81 | out[0] = 1;
82 | out[1] = 0;
83 | out[2] = 0;
84 | out[3] = 1;
85 | out[4] = 0;
86 | out[5] = 0;
87 | return out;
88 | }
89 |
90 | /**
91 | * Create a new mat2d with the given values
92 | *
93 | * @param {Number} a Component A (index 0)
94 | * @param {Number} b Component B (index 1)
95 | * @param {Number} c Component C (index 2)
96 | * @param {Number} d Component D (index 3)
97 | * @param {Number} tx Component TX (index 4)
98 | * @param {Number} ty Component TY (index 5)
99 | * @returns {mat2d} A new mat2d
100 | */
101 | export function fromValues(a, b, c, d, tx, ty) {
102 | let out = new glMatrix.ARRAY_TYPE(6);
103 | out[0] = a;
104 | out[1] = b;
105 | out[2] = c;
106 | out[3] = d;
107 | out[4] = tx;
108 | out[5] = ty;
109 | return out;
110 | }
111 |
112 | /**
113 | * Set the components of a mat2d to the given values
114 | *
115 | * @param {mat2d} out the receiving matrix
116 | * @param {Number} a Component A (index 0)
117 | * @param {Number} b Component B (index 1)
118 | * @param {Number} c Component C (index 2)
119 | * @param {Number} d Component D (index 3)
120 | * @param {Number} tx Component TX (index 4)
121 | * @param {Number} ty Component TY (index 5)
122 | * @returns {mat2d} out
123 | */
124 | export function set(out, a, b, c, d, tx, ty) {
125 | out[0] = a;
126 | out[1] = b;
127 | out[2] = c;
128 | out[3] = d;
129 | out[4] = tx;
130 | out[5] = ty;
131 | return out;
132 | }
133 |
134 | /**
135 | * Inverts a mat2d
136 | *
137 | * @param {mat2d} out the receiving matrix
138 | * @param {mat2d} a the source matrix
139 | * @returns {mat2d} out
140 | */
141 | export function invert(out, a) {
142 | let aa = a[0], ab = a[1], ac = a[2], ad = a[3];
143 | let atx = a[4], aty = a[5];
144 |
145 | let det = aa * ad - ab * ac;
146 | if(!det){
147 | return null;
148 | }
149 | det = 1.0 / det;
150 |
151 | out[0] = ad * det;
152 | out[1] = -ab * det;
153 | out[2] = -ac * det;
154 | out[3] = aa * det;
155 | out[4] = (ac * aty - ad * atx) * det;
156 | out[5] = (ab * atx - aa * aty) * det;
157 | return out;
158 | }
159 |
160 | /**
161 | * Calculates the determinant of a mat2d
162 | *
163 | * @param {mat2d} a the source matrix
164 | * @returns {Number} determinant of a
165 | */
166 | export function determinant(a) {
167 | return a[0] * a[3] - a[1] * a[2];
168 | }
169 |
170 | /**
171 | * Multiplies two mat2d's
172 | *
173 | * @param {mat2d} out the receiving matrix
174 | * @param {mat2d} a the first operand
175 | * @param {mat2d} b the second operand
176 | * @returns {mat2d} out
177 | */
178 | export function multiply(out, a, b) {
179 | let a0 = a[0], a1 = a[1], a2 = a[2], a3 = a[3], a4 = a[4], a5 = a[5];
180 | let b0 = b[0], b1 = b[1], b2 = b[2], b3 = b[3], b4 = b[4], b5 = b[5];
181 | out[0] = a0 * b0 + a2 * b1;
182 | out[1] = a1 * b0 + a3 * b1;
183 | out[2] = a0 * b2 + a2 * b3;
184 | out[3] = a1 * b2 + a3 * b3;
185 | out[4] = a0 * b4 + a2 * b5 + a4;
186 | out[5] = a1 * b4 + a3 * b5 + a5;
187 | return out;
188 | }
189 |
190 | /**
191 | * Rotates a mat2d by the given angle
192 | *
193 | * @param {mat2d} out the receiving matrix
194 | * @param {mat2d} a the matrix to rotate
195 | * @param {Number} rad the angle to rotate the matrix by
196 | * @returns {mat2d} out
197 | */
198 | export function rotate(out, a, rad) {
199 | let a0 = a[0], a1 = a[1], a2 = a[2], a3 = a[3], a4 = a[4], a5 = a[5];
200 | let s = Math.sin(rad);
201 | let c = Math.cos(rad);
202 | out[0] = a0 * c + a2 * s;
203 | out[1] = a1 * c + a3 * s;
204 | out[2] = a0 * -s + a2 * c;
205 | out[3] = a1 * -s + a3 * c;
206 | out[4] = a4;
207 | out[5] = a5;
208 | return out;
209 | }
210 |
211 | /**
212 | * Scales the mat2d by the dimensions in the given vec2
213 | *
214 | * @param {mat2d} out the receiving matrix
215 | * @param {mat2d} a the matrix to translate
216 | * @param {vec2} v the vec2 to scale the matrix by
217 | * @returns {mat2d} out
218 | **/
219 | export function scale(out, a, v) {
220 | let a0 = a[0], a1 = a[1], a2 = a[2], a3 = a[3], a4 = a[4], a5 = a[5];
221 | let v0 = v[0], v1 = v[1];
222 | out[0] = a0 * v0;
223 | out[1] = a1 * v0;
224 | out[2] = a2 * v1;
225 | out[3] = a3 * v1;
226 | out[4] = a4;
227 | out[5] = a5;
228 | return out;
229 | }
230 |
231 | /**
232 | * Translates the mat2d by the dimensions in the given vec2
233 | *
234 | * @param {mat2d} out the receiving matrix
235 | * @param {mat2d} a the matrix to translate
236 | * @param {vec2} v the vec2 to translate the matrix by
237 | * @returns {mat2d} out
238 | **/
239 | export function translate(out, a, v) {
240 | let a0 = a[0], a1 = a[1], a2 = a[2], a3 = a[3], a4 = a[4], a5 = a[5];
241 | let v0 = v[0], v1 = v[1];
242 | out[0] = a0;
243 | out[1] = a1;
244 | out[2] = a2;
245 | out[3] = a3;
246 | out[4] = a0 * v0 + a2 * v1 + a4;
247 | out[5] = a1 * v0 + a3 * v1 + a5;
248 | return out;
249 | }
250 |
251 | /**
252 | * Creates a matrix from a given angle
253 | * This is equivalent to (but much faster than):
254 | *
255 | * mat2d.identity(dest);
256 | * mat2d.rotate(dest, dest, rad);
257 | *
258 | * @param {mat2d} out mat2d receiving operation result
259 | * @param {Number} rad the angle to rotate the matrix by
260 | * @returns {mat2d} out
261 | */
262 | export function fromRotation(out, rad) {
263 | let s = Math.sin(rad), c = Math.cos(rad);
264 | out[0] = c;
265 | out[1] = s;
266 | out[2] = -s;
267 | out[3] = c;
268 | out[4] = 0;
269 | out[5] = 0;
270 | return out;
271 | }
272 |
273 | /**
274 | * Creates a matrix from a vector scaling
275 | * This is equivalent to (but much faster than):
276 | *
277 | * mat2d.identity(dest);
278 | * mat2d.scale(dest, dest, vec);
279 | *
280 | * @param {mat2d} out mat2d receiving operation result
281 | * @param {vec2} v Scaling vector
282 | * @returns {mat2d} out
283 | */
284 | export function fromScaling(out, v) {
285 | out[0] = v[0];
286 | out[1] = 0;
287 | out[2] = 0;
288 | out[3] = v[1];
289 | out[4] = 0;
290 | out[5] = 0;
291 | return out;
292 | }
293 |
294 | /**
295 | * Creates a matrix from a vector translation
296 | * This is equivalent to (but much faster than):
297 | *
298 | * mat2d.identity(dest);
299 | * mat2d.translate(dest, dest, vec);
300 | *
301 | * @param {mat2d} out mat2d receiving operation result
302 | * @param {vec2} v Translation vector
303 | * @returns {mat2d} out
304 | */
305 | export function fromTranslation(out, v) {
306 | out[0] = 1;
307 | out[1] = 0;
308 | out[2] = 0;
309 | out[3] = 1;
310 | out[4] = v[0];
311 | out[5] = v[1];
312 | return out;
313 | }
314 |
315 | /**
316 | * Returns a string representation of a mat2d
317 | *
318 | * @param {mat2d} a matrix to represent as a string
319 | * @returns {String} string representation of the matrix
320 | */
321 | export function str(a) {
322 | return 'mat2d(' + a[0] + ', ' + a[1] + ', ' + a[2] + ', ' +
323 | a[3] + ', ' + a[4] + ', ' + a[5] + ')';
324 | }
325 |
326 | /**
327 | * Returns Frobenius norm of a mat2d
328 | *
329 | * @param {mat2d} a the matrix to calculate Frobenius norm of
330 | * @returns {Number} Frobenius norm
331 | */
332 | export function frob(a) {
333 | return(Math.sqrt(Math.pow(a[0], 2) + Math.pow(a[1], 2) + Math.pow(a[2], 2) + Math.pow(a[3], 2) + Math.pow(a[4], 2) + Math.pow(a[5], 2) + 1))
334 | }
335 |
336 | /**
337 | * Adds two mat2d's
338 | *
339 | * @param {mat2d} out the receiving matrix
340 | * @param {mat2d} a the first operand
341 | * @param {mat2d} b the second operand
342 | * @returns {mat2d} out
343 | */
344 | export function add(out, a, b) {
345 | out[0] = a[0] + b[0];
346 | out[1] = a[1] + b[1];
347 | out[2] = a[2] + b[2];
348 | out[3] = a[3] + b[3];
349 | out[4] = a[4] + b[4];
350 | out[5] = a[5] + b[5];
351 | return out;
352 | }
353 |
354 | /**
355 | * Subtracts matrix b from matrix a
356 | *
357 | * @param {mat2d} out the receiving matrix
358 | * @param {mat2d} a the first operand
359 | * @param {mat2d} b the second operand
360 | * @returns {mat2d} out
361 | */
362 | export function subtract(out, a, b) {
363 | out[0] = a[0] - b[0];
364 | out[1] = a[1] - b[1];
365 | out[2] = a[2] - b[2];
366 | out[3] = a[3] - b[3];
367 | out[4] = a[4] - b[4];
368 | out[5] = a[5] - b[5];
369 | return out;
370 | }
371 |
372 | /**
373 | * Multiply each element of the matrix by a scalar.
374 | *
375 | * @param {mat2d} out the receiving matrix
376 | * @param {mat2d} a the matrix to scale
377 | * @param {Number} b amount to scale the matrix's elements by
378 | * @returns {mat2d} out
379 | */
380 | export function multiplyScalar(out, a, b) {
381 | out[0] = a[0] * b;
382 | out[1] = a[1] * b;
383 | out[2] = a[2] * b;
384 | out[3] = a[3] * b;
385 | out[4] = a[4] * b;
386 | out[5] = a[5] * b;
387 | return out;
388 | }
389 |
390 | /**
391 | * Adds two mat2d's after multiplying each element of the second operand by a scalar value.
392 | *
393 | * @param {mat2d} out the receiving vector
394 | * @param {mat2d} a the first operand
395 | * @param {mat2d} b the second operand
396 | * @param {Number} scale the amount to scale b's elements by before adding
397 | * @returns {mat2d} out
398 | */
399 | export function multiplyScalarAndAdd(out, a, b, scale) {
400 | out[0] = a[0] + (b[0] * scale);
401 | out[1] = a[1] + (b[1] * scale);
402 | out[2] = a[2] + (b[2] * scale);
403 | out[3] = a[3] + (b[3] * scale);
404 | out[4] = a[4] + (b[4] * scale);
405 | out[5] = a[5] + (b[5] * scale);
406 | return out;
407 | }
408 |
409 | /**
410 | * Returns whether or not the matrices have exactly the same elements in the same position (when compared with ===)
411 | *
412 | * @param {mat2d} a The first matrix.
413 | * @param {mat2d} b The second matrix.
414 | * @returns {Boolean} True if the matrices are equal, false otherwise.
415 | */
416 | export function exactEquals(a, b) {
417 | return a[0] === b[0] && a[1] === b[1] && a[2] === b[2] && a[3] === b[3] && a[4] === b[4] && a[5] === b[5];
418 | }
419 |
420 | /**
421 | * Returns whether or not the matrices have approximately the same elements in the same position.
422 | *
423 | * @param {mat2d} a The first matrix.
424 | * @param {mat2d} b The second matrix.
425 | * @returns {Boolean} True if the matrices are equal, false otherwise.
426 | */
427 | export function equals(a, b) {
428 | let a0 = a[0], a1 = a[1], a2 = a[2], a3 = a[3], a4 = a[4], a5 = a[5];
429 | let b0 = b[0], b1 = b[1], b2 = b[2], b3 = b[3], b4 = b[4], b5 = b[5];
430 | return (Math.abs(a0 - b0) <= glMatrix.EPSILON*Math.max(1.0, Math.abs(a0), Math.abs(b0)) &&
431 | Math.abs(a1 - b1) <= glMatrix.EPSILON*Math.max(1.0, Math.abs(a1), Math.abs(b1)) &&
432 | Math.abs(a2 - b2) <= glMatrix.EPSILON*Math.max(1.0, Math.abs(a2), Math.abs(b2)) &&
433 | Math.abs(a3 - b3) <= glMatrix.EPSILON*Math.max(1.0, Math.abs(a3), Math.abs(b3)) &&
434 | Math.abs(a4 - b4) <= glMatrix.EPSILON*Math.max(1.0, Math.abs(a4), Math.abs(b4)) &&
435 | Math.abs(a5 - b5) <= glMatrix.EPSILON*Math.max(1.0, Math.abs(a5), Math.abs(b5)));
436 | }
437 |
438 | /**
439 | * Alias for {@link mat2d.multiply}
440 | * @function
441 | */
442 | export const mul = multiply;
443 |
444 | /**
445 | * Alias for {@link mat2d.subtract}
446 | * @function
447 | */
448 | export const sub = subtract;
449 |
--------------------------------------------------------------------------------
/d3d12-bug/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 | WebGPU Dissapearing Geometry Bug Repro
11 |
12 |
33 |
34 |
35 |
36 |
325 |
326 |
327 |
--------------------------------------------------------------------------------
/device-loss.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 | WebGPU test
14 |
15 |
29 |
30 |
31 |
32 |
33 |
304 |
305 |
306 |
--------------------------------------------------------------------------------
/favicon-16.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/toji/webgpu-test/148e8fe87465777f4994f7998c7f2822e1b9b50a/favicon-16.png
--------------------------------------------------------------------------------
/favicon-32.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/toji/webgpu-test/148e8fe87465777f4994f7998c7f2822e1b9b50a/favicon-32.png
--------------------------------------------------------------------------------
/favicon-48.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/toji/webgpu-test/148e8fe87465777f4994f7998c7f2822e1b9b50a/favicon-48.png
--------------------------------------------------------------------------------
/flashing-bug/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 | WebGPU test
11 |
12 |
13 |
14 |
15 |
16 | WARNING: Clicking on the toggle button may cause either minor flickering or intense flashing.
17 |
311 |
312 |
--------------------------------------------------------------------------------
/iframe-raf/iframe.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | iframe rAF test page
6 |
7 |
8 | same-origin iframe
9 |
30 |
31 |
--------------------------------------------------------------------------------
/iframe-raf/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 | Particle System - WebGPU
11 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
56 |
57 |
--------------------------------------------------------------------------------
/iframe.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 | WebGPU in an iframe test
14 |
15 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
--------------------------------------------------------------------------------
/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 | Web Graphics API Tester
14 |
15 |
42 |
43 |
44 |
50 |
132 |
133 |
134 |
--------------------------------------------------------------------------------
/js/camera.js:
--------------------------------------------------------------------------------
1 | import { vec3, mat4 } from './third-party/gl-matrix/src/gl-matrix.js';
2 |
3 | const DIR = vec3.create();
4 |
5 | export class FlyingCamera {
6 | constructor() {
7 | this._element = null;
8 | this._angles = vec3.create();
9 | this._position = vec3.create();
10 | this._viewMat = mat4.create();
11 | this._rotMat = mat4.create();
12 | this._dirty = true;
13 |
14 | this.speed = 3;
15 |
16 | this._pressedKeys = new Array(128);
17 | window.addEventListener('keydown', (event) => {
18 | this._pressedKeys[event.keyCode] = true;
19 | });
20 | window.addEventListener('keyup', (event) => {
21 | this._pressedKeys[event.keyCode] = false;
22 | });
23 |
24 | let moving = false;
25 | let lastX, lastY;
26 | this.mousedownCallback = (event) => {
27 | if (event.isPrimary) {
28 | moving = true;
29 | }
30 | lastX = event.pageX;
31 | lastY = event.pageY;
32 | };
33 | this.mousemoveCallback = (event) => {
34 | let xDelta, yDelta;
35 |
36 | if(document.pointerLockEnabled) {
37 | xDelta = event.movementX;
38 | yDelta = event.movementY;
39 | this.rotateView(xDelta * 0.025, yDelta * 0.025);
40 | } else if (moving) {
41 | xDelta = event.pageX - lastX;
42 | yDelta = event.pageY - lastY;
43 | lastX = event.pageX;
44 | lastY = event.pageY;
45 | this.rotateView(xDelta * 0.025, yDelta * 0.025);
46 | }
47 | };
48 | this.mouseupCallback = (event) => {
49 | if (event.isPrimary) {
50 | moving = false;
51 | }
52 | };
53 |
54 | let lastFrameTime = -1;
55 | this.frameCallback = (timestamp) => {
56 | if (lastFrameTime == -1) {
57 | lastFrameTime = timestamp;
58 | } else {
59 | this.update(timestamp - lastFrameTime);
60 | lastFrameTime = timestamp;
61 | }
62 | requestAnimationFrame(this.frameCallback);
63 | }
64 | requestAnimationFrame(this.frameCallback);
65 | }
66 |
67 | set element(value) {
68 | if (this._element && this._element != value) {
69 | this._element.removeEventListener('pointerdown', this.mousedownCallback);
70 | this._element.removeEventListener('pointermove', this.mousemoveCallback);
71 | this._element.removeEventListener('pointerup', this.mouseupCallback);
72 | }
73 |
74 | this._element = value;
75 | if (this._element) {
76 | this._element.addEventListener('pointerdown', this.mousedownCallback);
77 | this._element.addEventListener('pointermove', this.mousemoveCallback);
78 | this._element.addEventListener('pointerup', this.mouseupCallback);
79 | }
80 | }
81 |
82 | get element() {
83 | return this._element;
84 | }
85 |
86 | rotateView(xDelta, yDelta) {
87 | let rot = this._rotMat;
88 |
89 | if(xDelta || yDelta) {
90 | this._angles[1] += xDelta;
91 | // Keep our rotation in the range of [0, 2*PI]
92 | // (Prevents numeric instability if you spin around a LOT.)
93 | while (this._angles[1] < 0) {
94 | this._angles[1] += Math.PI * 2.0;
95 | }
96 | while (this._angles[1] >= Math.PI * 2.0) {
97 | this._angles[1] -= Math.PI * 2.0;
98 | }
99 |
100 | this._angles[0] += yDelta;
101 | // Clamp the up/down rotation to prevent us from flipping upside-down
102 | if (this._angles[0] < -Math.PI * 0.5) {
103 | this._angles[0] = -Math.PI * 0.5;
104 | }
105 | if (this._angles[0] > Math.PI * 0.5) {
106 | this._angles[0] = Math.PI * 0.5;
107 | }
108 |
109 | // Update the directional matrix
110 | mat4.identity(rot);
111 |
112 | mat4.rotateY(rot, rot, -this._angles[1]);
113 | mat4.rotateX(rot, rot, -this._angles[0]);
114 |
115 | this._dirty = true;
116 | }
117 | }
118 |
119 | set position(value) {
120 | vec3.copy(this._position, value);
121 | this._dirty = true;
122 | }
123 |
124 | get position() {
125 | return this._position;
126 | }
127 |
128 | get viewMatrix() {
129 | if (this._dirty) {
130 | let mv = this._viewMat;
131 | mat4.identity(mv);
132 |
133 | //mat4.rotateX(mv, mv, -Math.PI * 0.5);
134 | mat4.rotateX(mv, mv, this._angles[0]);
135 | mat4.rotateY(mv, mv, this._angles[1]);
136 | mat4.translate(mv, mv, [-this._position[0], -this._position[1], -this._position[2]]);
137 | this._dirty = false;
138 | }
139 |
140 | return this._viewMat;
141 | }
142 |
143 | update(frameTime) {
144 | if (!this._element) return;
145 |
146 | const speed = (this.speed / 1000) * frameTime;
147 |
148 | vec3.set(DIR, 0, 0, 0);
149 |
150 | // This is our first person movement code. It's not really pretty, but it works
151 | if (this._pressedKeys['W'.charCodeAt(0)]) {
152 | DIR[2] -= speed;
153 | }
154 | if (this._pressedKeys['S'.charCodeAt(0)]) {
155 | DIR[2] += speed;
156 | }
157 | if (this._pressedKeys['A'.charCodeAt(0)]) {
158 | DIR[0] -= speed;
159 | }
160 | if (this._pressedKeys['D'.charCodeAt(0)]) {
161 | DIR[0] += speed;
162 | }
163 | if (this._pressedKeys[32]) { // Space, moves up
164 | DIR[1] += speed;
165 | }
166 | if (this._pressedKeys[17]) { // Ctrl, moves down
167 | DIR[1] -= speed;
168 | }
169 |
170 | if (DIR[0] !== 0 || DIR[1] !== 0 || DIR[2] !== 0) {
171 | // Move the camera in the direction we are facing
172 | vec3.transformMat4(DIR, DIR, this._rotMat);
173 | vec3.add(this._position, this._position, DIR);
174 |
175 | this._dirty = true;
176 | }
177 | }
178 | }
179 |
--------------------------------------------------------------------------------
/js/pbr-shader.js:
--------------------------------------------------------------------------------
1 | // Copyright 2020 Brandon Jones
2 | //
3 | // Permission is hereby granted, free of charge, to any person obtaining a copy
4 | // of this software and associated documentation files (the "Software"), to deal
5 | // in the Software without restriction, including without limitation the rights
6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7 | // copies of the Software, and to permit persons to whom the Software is
8 | // furnished to do so, subject to the following conditions:
9 |
10 | // The above copyright notice and this permission notice shall be included in
11 | // all copies or substantial portions of the Software.
12 |
13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
19 | // SOFTWARE.
20 |
21 | export const ATTRIB_MAP = {
22 | POSITION: 1,
23 | NORMAL: 2,
24 | TANGENT: 3,
25 | TEXCOORD_0: 4,
26 | COLOR_0: 5,
27 | };
28 |
29 | export const SAMPLER_MAP = {
30 | baseColorTexture: 0,
31 | normalTexture: 1,
32 | metallicRoughnessTexture: 2,
33 | emissiveTexture: 3,
34 | occlusionTexture: 4
35 | };
36 |
37 | export const UNIFORM_BLOCKS = {
38 | FrameUniforms: 0,
39 | MaterialUniforms: 1,
40 | PrimitiveUniforms: 2,
41 | LightUniforms: 3
42 | };
43 |
44 | const WEBGL_ATTRIBUTES = `
45 | attribute vec3 POSITION;
46 | attribute vec3 NORMAL;
47 | attribute vec4 TANGENT;
48 | attribute vec2 TEXCOORD_0;
49 | attribute vec4 COLOR_0;
50 | `;
51 |
52 | const ATTRIBUTES_WITH_LAYOUT = `
53 | layout(location = ${ATTRIB_MAP.POSITION}) in vec3 POSITION;
54 | layout(location = ${ATTRIB_MAP.NORMAL}) in vec3 NORMAL;
55 | #ifdef USE_NORMAL_MAP
56 | layout(location = ${ATTRIB_MAP.TANGENT}) in vec4 TANGENT;
57 | #endif
58 | layout(location = ${ATTRIB_MAP.TEXCOORD_0}) in vec2 TEXCOORD_0;
59 | #ifdef USE_VERTEX_COLOR
60 | layout(location = ${ATTRIB_MAP.COLOR_0}) in vec4 COLOR_0;
61 | #endif
62 | `;
63 |
64 | function WEBGL_VARYINGS(type = 'varying') {
65 | return `
66 | ${type} vec3 vWorldPos;
67 | ${type} vec3 vView; // Vector from vertex to camera.
68 | ${type} vec2 vTex;
69 | ${type} vec4 vCol;
70 |
71 | #ifdef USE_NORMAL_MAP
72 | ${type} mat3 vTBN;
73 | #else
74 | ${type} vec3 vNorm;
75 | #endif
76 | `;
77 | }
78 |
79 | const WEBGL_VERTEX_UNIFORMS = `
80 | uniform mat4 projectionMatrix;
81 | uniform mat4 viewMatrix;
82 | uniform vec3 cameraPosition;
83 |
84 | uniform mat4 modelMatrix;
85 | `;
86 |
87 | const WEBGL2_VERTEX_UNIFORMS = `
88 | layout(std140) uniform FrameUniforms
89 | {
90 | mat4 projectionMatrix;
91 | mat4 viewMatrix;
92 | vec3 cameraPosition;
93 | };
94 |
95 | uniform mat4 modelMatrix;
96 | `;
97 |
98 | const WEBGL_FRAGMENT_UNIFORMS = `
99 | uniform vec4 baseColorFactor;
100 | uniform vec2 metallicRoughnessFactor;
101 | uniform vec3 emissiveFactor;
102 | uniform float occlusionStrength;
103 |
104 | uniform sampler2D baseColorTexture;
105 | uniform sampler2D normalTexture;
106 | uniform sampler2D metallicRoughnessTexture;
107 | uniform sampler2D occlusionTexture;
108 | uniform sampler2D emissiveTexture;
109 |
110 | struct Light {
111 | vec4 position;
112 | vec4 color;
113 | };
114 |
115 | uniform Light lights[LIGHT_COUNT];
116 | uniform float lightAmbient;
117 | `;
118 |
119 | const WEBGL2_FRAGMENT_UNIFORMS = `
120 | layout(std140) uniform MaterialUniforms {
121 | vec4 baseColorFactor;
122 | vec2 metallicRoughnessFactor;
123 | vec3 emissiveFactor;
124 | float occlusionStrength;
125 | };
126 |
127 | uniform sampler2D baseColorTexture;
128 | uniform sampler2D normalTexture;
129 | uniform sampler2D metallicRoughnessTexture;
130 | uniform sampler2D occlusionTexture;
131 | uniform sampler2D emissiveTexture;
132 |
133 | struct Light {
134 | vec4 position;
135 | vec4 color;
136 | };
137 |
138 | layout(std140) uniform LightUniforms {
139 | Light lights[LIGHT_COUNT];
140 | float lightAmbient;
141 | };
142 | `;
143 |
144 | function WEBGL_TEXTURE(texture, texcoord) {
145 | return `texture2D(${texture}, ${texcoord})`;
146 | }
147 |
148 | function WEBGL2_TEXTURE(texture, texcoord) {
149 | return `texture(${texture}, ${texcoord})`;
150 | }
151 |
152 | // Much of the shader used here was pulled from https://learnopengl.com/PBR/Lighting
153 | // Thanks!
154 | const PBR_FUNCTIONS = `
155 | const float PI = 3.14159265359;
156 |
157 | vec3 FresnelSchlick(float cosTheta, vec3 F0) {
158 | return F0 + (1.0 - F0) * pow(1.0 - cosTheta, 5.0);
159 | }
160 |
161 | float DistributionGGX(vec3 N, vec3 H, float roughness) {
162 | float a = roughness*roughness;
163 | float a2 = a*a;
164 | float NdotH = max(dot(N, H), 0.0);
165 | float NdotH2 = NdotH*NdotH;
166 |
167 | float num = a2;
168 | float denom = (NdotH2 * (a2 - 1.0) + 1.0);
169 | denom = PI * denom * denom;
170 |
171 | return num / denom;
172 | }
173 |
174 | float GeometrySchlickGGX(float NdotV, float roughness) {
175 | float r = (roughness + 1.0);
176 | float k = (r*r) / 8.0;
177 |
178 | float num = NdotV;
179 | float denom = NdotV * (1.0 - k) + k;
180 |
181 | return num / denom;
182 | }
183 |
184 | float GeometrySmith(vec3 N, vec3 V, vec3 L, float roughness) {
185 | float NdotV = max(dot(N, V), 0.0);
186 | float NdotL = max(dot(N, L), 0.0);
187 | float ggx2 = GeometrySchlickGGX(NdotV, roughness);
188 | float ggx1 = GeometrySchlickGGX(NdotL, roughness);
189 |
190 | return ggx1 * ggx2;
191 | }`;
192 |
193 | const PBR_VERTEX_MAIN = `
194 | void main() {
195 | vec3 n = normalize(vec3(modelMatrix * vec4(NORMAL, 0.0)));
196 | #ifdef USE_NORMAL_MAP
197 | vec3 t = normalize(vec3(modelMatrix * vec4(TANGENT.xyz, 0.0)));
198 | vec3 b = cross(n, t) * TANGENT.w;
199 | vTBN = mat3(t, b, n);
200 | #else
201 | vNorm = n;
202 | #endif
203 |
204 | #ifdef USE_VERTEX_COLOR
205 | vCol = COLOR_0;
206 | #endif
207 |
208 | vTex = TEXCOORD_0;
209 | vec4 mPos = modelMatrix * vec4(POSITION, 1.0);
210 | vWorldPos = mPos.xyz;
211 | vView = cameraPosition - mPos.xyz;
212 | gl_Position = projectionMatrix * viewMatrix * mPos;
213 | }`;
214 |
215 | function PBR_FRAGMENT_MAIN(textureFunc) {
216 | return `
217 | ${PBR_FUNCTIONS}
218 |
219 | const vec3 dielectricSpec = vec3(0.04);
220 | const vec3 black = vec3(0.0);
221 |
222 | vec4 computeColor() {
223 | vec4 baseColor = baseColorFactor;
224 | #ifdef USE_BASE_COLOR_MAP
225 | vec4 baseColorMap = ${textureFunc('baseColorTexture', 'vTex')};
226 | if (baseColorMap.a < 0.05) {
227 | discard;
228 | }
229 | baseColor *= baseColorMap;
230 | #endif
231 | #ifdef USE_VERTEX_COLOR
232 | baseColor *= vCol;
233 | #endif
234 |
235 | vec3 albedo = baseColor.rgb; //pow(baseColor.rgb, 2.2);
236 |
237 | float metallic = metallicRoughnessFactor.x;
238 | float roughness = metallicRoughnessFactor.y;
239 |
240 | #ifdef USE_METAL_ROUGH_MAP
241 | vec4 metallicRoughness = ${textureFunc('metallicRoughnessTexture', 'vTex')};
242 | metallic *= metallicRoughness.b;
243 | roughness *= metallicRoughness.g;
244 | #endif
245 |
246 | #ifdef USE_NORMAL_MAP
247 | vec3 N = ${textureFunc('normalTexture', 'vTex')}.rgb;
248 | N = normalize(vTBN * (2.0 * N - 1.0));
249 | #else
250 | vec3 N = normalize(vNorm);
251 | #endif
252 |
253 | vec3 V = normalize(vView);
254 |
255 | vec3 F0 = vec3(0.04);
256 | F0 = mix(F0, albedo, metallic);
257 |
258 | // reflectance equation
259 | vec3 Lo = vec3(0.0);
260 |
261 | for (int i = 0; i < LIGHT_COUNT; ++i) {
262 | // calculate per-light radiance
263 | vec3 L = normalize(lights[i].position.xyz - vWorldPos);
264 | vec3 H = normalize(V + L);
265 | float distance = length(lights[i].position.xyz - vWorldPos);
266 | float attenuation = 1.0 / (1.0 + distance * distance);
267 | vec3 radiance = lights[i].color.rgb * attenuation;
268 |
269 | // cook-torrance brdf
270 | float NDF = DistributionGGX(N, H, roughness);
271 | float G = GeometrySmith(N, V, L, roughness);
272 | vec3 F = FresnelSchlick(max(dot(H, V), 0.0), F0);
273 |
274 | vec3 kS = F;
275 | vec3 kD = vec3(1.0) - kS;
276 | kD *= 1.0 - metallic;
277 |
278 | vec3 numerator = NDF * G * F;
279 | float denominator = 4.0 * max(dot(N, V), 0.0) * max(dot(N, L), 0.0);
280 | vec3 specular = numerator / max(denominator, 0.001);
281 |
282 | // add to outgoing radiance Lo
283 | float NdotL = max(dot(N, L), 0.0);
284 | Lo += (kD * albedo / PI + specular) * radiance * NdotL;
285 | }
286 |
287 | #ifdef USE_OCCLUSION
288 | float ao = ${textureFunc('occlusionTexture', 'vTex')}.r * occlusionStrength;
289 | #else
290 | float ao = 1.0;
291 | #endif
292 |
293 | vec3 ambient = vec3(lightAmbient) * albedo * ao;
294 | vec3 color = ambient + Lo;
295 |
296 | vec3 emissive = emissiveFactor;
297 | #ifdef USE_EMISSIVE_TEXTURE
298 | emissive *= ${textureFunc('emissiveTexture', 'vTex')}.rgb;
299 | #endif
300 | color += emissive;
301 |
302 | color = color / (color + vec3(1.0));
303 | color = pow(color, vec3(1.0/2.2));
304 |
305 | return vec4(color, baseColor.a);
306 | }`;
307 | };
308 |
309 | function DEFINES(defines = {}) {
310 | let definesString = '';
311 | for (let define in defines) {
312 | definesString += `#define ${define} ${defines[define]}\n`;
313 | }
314 | return definesString;
315 | }
316 |
317 | export function WEBGL_VERTEX_SOURCE(defines) {
318 | return `
319 | ${DEFINES(defines)}
320 | ${WEBGL_ATTRIBUTES}
321 | ${WEBGL_VARYINGS()}
322 | ${WEBGL_VERTEX_UNIFORMS}
323 | ${PBR_VERTEX_MAIN}
324 | `;
325 | }
326 |
327 | export function WEBGL_FRAGMENT_SOURCE(defines) {
328 | return `precision highp float;
329 | ${DEFINES(defines)}
330 | ${WEBGL_VARYINGS()}
331 | ${WEBGL_FRAGMENT_UNIFORMS}
332 | ${PBR_FRAGMENT_MAIN(WEBGL_TEXTURE)}
333 |
334 | void main() {
335 | gl_FragColor = computeColor();
336 | }
337 | `;
338 | }
339 |
340 | export function WEBGL2_VERTEX_SOURCE(defines) {
341 | return `#version 300 es
342 | ${DEFINES(defines)}
343 | ${ATTRIBUTES_WITH_LAYOUT}
344 | ${WEBGL_VARYINGS('out')}
345 | ${WEBGL2_VERTEX_UNIFORMS}
346 | ${PBR_VERTEX_MAIN}
347 | `;
348 | }
349 |
350 | export function WEBGL2_FRAGMENT_SOURCE(defines) {
351 | return `#version 300 es
352 | precision highp float;
353 | ${DEFINES(defines)}
354 | ${WEBGL_VARYINGS('in')}
355 | ${WEBGL2_FRAGMENT_UNIFORMS}
356 | ${PBR_FRAGMENT_MAIN(WEBGL2_TEXTURE)}
357 |
358 | out vec4 outputColor;
359 | void main() {
360 | outputColor = computeColor();;
361 | }
362 | `;
363 | }
364 |
365 | export function GetDefinesForPrimitive(primitive) {
366 | const attributes = primitive.enabledAttributes;
367 | const material = primitive.material;
368 | const programDefines = {};
369 |
370 | if (attributes.has('COLOR_0')) {
371 | programDefines['USE_VERTEX_COLOR'] = 1;
372 | }
373 |
374 | if (attributes.has('TEXCOORD_0')) {
375 | if (material.baseColorTexture) {
376 | programDefines['USE_BASE_COLOR_MAP'] = 1;
377 | }
378 |
379 | if (material.normalTexture && (attributes.has('TANGENT'))) {
380 | programDefines['USE_NORMAL_MAP'] = 1;
381 | }
382 |
383 | if (material.metallicRoughnessTexture) {
384 | programDefines['USE_METAL_ROUGH_MAP'] = 1;
385 | }
386 |
387 | if (material.occlusionTexture) {
388 | programDefines['USE_OCCLUSION'] = 1;
389 | }
390 |
391 | if (material.emissiveTexture) {
392 | programDefines['USE_EMISSIVE_TEXTURE'] = 1;
393 | }
394 | }
395 |
396 | if ((!material.metallicRoughnessTexture ||
397 | !(attributes.has('TEXCOORD_0'))) &&
398 | material.metallicRoughnessFactor[1] == 1.0) {
399 | programDefines['FULLY_ROUGH'] = 1;
400 | }
401 |
402 | return programDefines;
403 | }
404 |
--------------------------------------------------------------------------------
/js/renderer.js:
--------------------------------------------------------------------------------
1 | // Copyright 2020 Brandon Jones
2 | //
3 | // Permission is hereby granted, free of charge, to any person obtaining a copy
4 | // of this software and associated documentation files (the "Software"), to deal
5 | // in the Software without restriction, including without limitation the rights
6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7 | // copies of the Software, and to permit persons to whom the Software is
8 | // furnished to do so, subject to the following conditions:
9 |
10 | // The above copyright notice and this permission notice shall be included in
11 | // all copies or substantial portions of the Software.
12 |
13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
19 | // SOFTWARE.
20 |
21 | import { vec3, mat4 } from './third-party/gl-matrix/src/gl-matrix.js';
22 |
23 | const lightFloatCount = 8;
24 | const lightByteSize = lightFloatCount * 4;
25 |
26 | class Light {
27 | constructor(buffer, offset) {
28 | this.position = new Float32Array(buffer, offset, 4);
29 | this.color = new Float32Array(buffer, offset + 4 * 4, 4);
30 | }
31 | }
32 |
33 | export class Renderer {
34 | constructor() {
35 | this.canvas = document.createElement('canvas');
36 | this.camera = null;
37 | this.rafId = 0;
38 | this.frameCount = -1;
39 |
40 | // Storage for global uniforms.
41 | // These can either be used individually or as a uniform buffer.
42 | this.frameUniforms = new Float32Array(16 + 16 + 4);
43 |
44 | this.projectionMatrix = new Float32Array(this.frameUniforms.buffer, 0, 16);
45 | this.viewMatrix = new Float32Array(this.frameUniforms.buffer, 16 * 4, 16);
46 | this.cameraPosition = new Float32Array(this.frameUniforms.buffer, 32 * 4, 3);
47 |
48 | this.lightCount = 5;
49 |
50 | this.lightUniforms = new Float32Array(lightFloatCount * this.lightCount + 4);
51 |
52 | this.lights = new Array(this.lightCount);
53 | for (let i = 0; i < this.lightCount; ++i) {
54 | this.lights[i] = new Light(this.lightUniforms.buffer, lightByteSize * i);
55 | }
56 |
57 | this.lightAmbient = new Float32Array(this.lightUniforms.buffer, (lightByteSize * this.lightCount), 4);
58 |
59 | // Central wandering light
60 | vec3.set(this.lights[0].position, 0, 1.5, 0);
61 | vec3.set(this.lights[0].color, 10, 10, 10);
62 |
63 | // Lights in each corner over the birdbath things.
64 | vec3.set(this.lights[1].position, 8.95, 1, -3.55);
65 | vec3.set(this.lights[1].color, 5, 1, 1);
66 |
67 | vec3.set(this.lights[2].position, 8.95, 1, 3.2);
68 | vec3.set(this.lights[2].color, 5, 1, 1);
69 |
70 | vec3.set(this.lights[3].position, -9.65, 1, -3.55);
71 | vec3.set(this.lights[3].color, 1, 1, 5);
72 |
73 | vec3.set(this.lights[4].position, -9.65, 1, 3.2);
74 | vec3.set(this.lights[4].color, 1, 1, 5);
75 |
76 | this.lightAmbient[0] = 0.05;
77 |
78 | this.frameCallback = (timestamp) => {
79 | this.rafId = requestAnimationFrame(this.frameCallback);
80 | this.frameCount++;
81 | if (this.frameCount % 200 == 0) { return; }
82 |
83 | if (this.stats) {
84 | this.stats.begin();
85 | }
86 |
87 | this.beforeFrame(timestamp);
88 |
89 | this.onFrame(timestamp);
90 |
91 | if (this.stats) {
92 | this.stats.end();
93 | }
94 | };
95 |
96 | this.resizeCallback = () => {
97 | this.canvas.width = this.canvas.clientWidth * devicePixelRatio;
98 | this.canvas.height = this.canvas.clientHeight * devicePixelRatio;
99 |
100 | const aspect = this.canvas.width / this.canvas.height;
101 | mat4.perspective(this.projectionMatrix, Math.PI * 0.5, aspect, 0.1, 1000.0);
102 |
103 | this.onResize(this.canvas.width, this.canvas.height);
104 | };
105 | }
106 |
107 | async init() {
108 | // Override with renderer-specific initialization logic.
109 | }
110 |
111 | setStats(stats) {
112 | this.stats = stats;
113 | }
114 |
115 | setGltf(gltf) {
116 | // Override with renderer-specific mesh loading logic.
117 | }
118 |
119 | setViewMatrix(viewMatrix) {
120 | mat4.copy(this.viewMatrix, viewMatrix);
121 | }
122 |
123 | start() {
124 | window.addEventListener('resize', this.resizeCallback);
125 | this.resizeCallback();
126 | this.rafId = requestAnimationFrame(this.frameCallback);
127 | }
128 |
129 | stop() {
130 | if (this.rafId) {
131 | cancelAnimationFrame(this.rafId);
132 | this.rafId = 0;
133 | }
134 | window.removeEventListener('resize', this.resizeCallback);
135 | }
136 |
137 | // Handles frame logic that's common to all renderers.
138 | beforeFrame(timestamp) {
139 | // Copy values from the camera into our frame uniform buffers
140 | mat4.copy(this.viewMatrix, this.camera.viewMatrix);
141 | vec3.copy(this.cameraPosition, this.camera.position);
142 |
143 | // Update the lights
144 | vec3.set(this.lights[0].position,
145 | Math.sin(timestamp / 1500) * 4,
146 | Math.cos(timestamp / 600) * 0.25 + 1.5,
147 | Math.cos(timestamp / 500) * 0.75);
148 |
149 | for (let i = 1; i < 5; ++i) {
150 | this.lights[i].position[1] = 1.25 + Math.sin((timestamp + i * 250) / 800) * 0.1;
151 | }
152 | }
153 |
154 | onResize(width, height) {
155 | // Override with renderer-specific resize logic.
156 | }
157 |
158 | onFrame(timestamp) {
159 | // Override with renderer-specific frame logic.
160 | }
161 |
162 |
163 | }
--------------------------------------------------------------------------------
/js/third-party/gl-matrix/LICENSE.md:
--------------------------------------------------------------------------------
1 | Copyright (c) 2015-2018, Brandon Jones, Colin MacKenzie IV.
2 |
3 | Permission is hereby granted, free of charge, to any person obtaining a copy
4 | of this software and associated documentation files (the "Software"), to deal
5 | in the Software without restriction, including without limitation the rights
6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7 | copies of the Software, and to permit persons to whom the Software is
8 | furnished to do so, subject to the following conditions:
9 |
10 | The above copyright notice and this permission notice shall be included in
11 | all copies or substantial portions of the Software.
12 |
13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19 | THE SOFTWARE.
--------------------------------------------------------------------------------
/js/third-party/gl-matrix/src/gl-matrix.js:
--------------------------------------------------------------------------------
1 | import * as glMatrix from "./gl-matrix/common.js";
2 | import * as mat2 from "./gl-matrix/mat2.js";
3 | import * as mat2d from "./gl-matrix/mat2d.js";
4 | import * as mat3 from "./gl-matrix/mat3.js";
5 | import * as mat4 from "./gl-matrix/mat4.js";
6 | import * as quat from "./gl-matrix/quat.js";
7 | import * as quat2 from "./gl-matrix/quat2.js";
8 | import * as vec2 from "./gl-matrix/vec2.js";
9 | import * as vec3 from "./gl-matrix/vec3.js";
10 | import * as vec4 from "./gl-matrix/vec4.js";
11 |
12 | export {
13 | glMatrix,
14 | mat2, mat2d, mat3, mat4,
15 | quat, quat2,
16 | vec2, vec3, vec4,
17 | };
18 |
--------------------------------------------------------------------------------
/js/third-party/gl-matrix/src/gl-matrix/common.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Common utilities
3 | * @module glMatrix
4 | */
5 |
6 | // Configuration Constants
7 | export const EPSILON = 0.000001;
8 | export let ARRAY_TYPE = (typeof Float32Array !== 'undefined') ? Float32Array : Array;
9 | export const RANDOM = Math.random;
10 |
11 | /**
12 | * Sets the type of array used when creating new vectors and matrices
13 | *
14 | * @param {Type} type Array type, such as Float32Array or Array
15 | */
16 | export function setMatrixArrayType(type) {
17 | ARRAY_TYPE = type;
18 | }
19 |
20 | const degree = Math.PI / 180;
21 |
22 | /**
23 | * Convert Degree To Radian
24 | *
25 | * @param {Number} a Angle in Degrees
26 | */
27 | export function toRadian(a) {
28 | return a * degree;
29 | }
30 |
31 | /**
32 | * Tests whether or not the arguments have approximately the same value, within an absolute
33 | * or relative tolerance of glMatrix.EPSILON (an absolute tolerance is used for values less
34 | * than or equal to 1.0, and a relative tolerance is used for larger values)
35 | *
36 | * @param {Number} a The first number to test.
37 | * @param {Number} b The second number to test.
38 | * @returns {Boolean} True if the numbers are approximately equal, false otherwise.
39 | */
40 | export function equals(a, b) {
41 | return Math.abs(a - b) <= EPSILON*Math.max(1.0, Math.abs(a), Math.abs(b));
42 | }
43 |
--------------------------------------------------------------------------------
/js/third-party/gl-matrix/src/gl-matrix/mat2.js:
--------------------------------------------------------------------------------
1 | import * as glMatrix from "./common.js"
2 |
3 | /**
4 | * 2x2 Matrix
5 | * @module mat2
6 | */
7 |
8 | /**
9 | * Creates a new identity mat2
10 | *
11 | * @returns {mat2} a new 2x2 matrix
12 | */
13 | export function create() {
14 | let out = new glMatrix.ARRAY_TYPE(4);
15 | if(glMatrix.ARRAY_TYPE != Float32Array) {
16 | out[1] = 0;
17 | out[2] = 0;
18 | }
19 | out[0] = 1;
20 | out[3] = 1;
21 | return out;
22 | }
23 |
24 | /**
25 | * Creates a new mat2 initialized with values from an existing matrix
26 | *
27 | * @param {mat2} a matrix to clone
28 | * @returns {mat2} a new 2x2 matrix
29 | */
30 | export function clone(a) {
31 | let out = new glMatrix.ARRAY_TYPE(4);
32 | out[0] = a[0];
33 | out[1] = a[1];
34 | out[2] = a[2];
35 | out[3] = a[3];
36 | return out;
37 | }
38 |
39 | /**
40 | * Copy the values from one mat2 to another
41 | *
42 | * @param {mat2} out the receiving matrix
43 | * @param {mat2} a the source matrix
44 | * @returns {mat2} out
45 | */
46 | export function copy(out, a) {
47 | out[0] = a[0];
48 | out[1] = a[1];
49 | out[2] = a[2];
50 | out[3] = a[3];
51 | return out;
52 | }
53 |
54 | /**
55 | * Set a mat2 to the identity matrix
56 | *
57 | * @param {mat2} out the receiving matrix
58 | * @returns {mat2} out
59 | */
60 | export function identity(out) {
61 | out[0] = 1;
62 | out[1] = 0;
63 | out[2] = 0;
64 | out[3] = 1;
65 | return out;
66 | }
67 |
68 | /**
69 | * Create a new mat2 with the given values
70 | *
71 | * @param {Number} m00 Component in column 0, row 0 position (index 0)
72 | * @param {Number} m01 Component in column 0, row 1 position (index 1)
73 | * @param {Number} m10 Component in column 1, row 0 position (index 2)
74 | * @param {Number} m11 Component in column 1, row 1 position (index 3)
75 | * @returns {mat2} out A new 2x2 matrix
76 | */
77 | export function fromValues(m00, m01, m10, m11) {
78 | let out = new glMatrix.ARRAY_TYPE(4);
79 | out[0] = m00;
80 | out[1] = m01;
81 | out[2] = m10;
82 | out[3] = m11;
83 | return out;
84 | }
85 |
86 | /**
87 | * Set the components of a mat2 to the given values
88 | *
89 | * @param {mat2} out the receiving matrix
90 | * @param {Number} m00 Component in column 0, row 0 position (index 0)
91 | * @param {Number} m01 Component in column 0, row 1 position (index 1)
92 | * @param {Number} m10 Component in column 1, row 0 position (index 2)
93 | * @param {Number} m11 Component in column 1, row 1 position (index 3)
94 | * @returns {mat2} out
95 | */
96 | export function set(out, m00, m01, m10, m11) {
97 | out[0] = m00;
98 | out[1] = m01;
99 | out[2] = m10;
100 | out[3] = m11;
101 | return out;
102 | }
103 |
104 | /**
105 | * Transpose the values of a mat2
106 | *
107 | * @param {mat2} out the receiving matrix
108 | * @param {mat2} a the source matrix
109 | * @returns {mat2} out
110 | */
111 | export function transpose(out, a) {
112 | // If we are transposing ourselves we can skip a few steps but have to cache
113 | // some values
114 | if (out === a) {
115 | let a1 = a[1];
116 | out[1] = a[2];
117 | out[2] = a1;
118 | } else {
119 | out[0] = a[0];
120 | out[1] = a[2];
121 | out[2] = a[1];
122 | out[3] = a[3];
123 | }
124 |
125 | return out;
126 | }
127 |
128 | /**
129 | * Inverts a mat2
130 | *
131 | * @param {mat2} out the receiving matrix
132 | * @param {mat2} a the source matrix
133 | * @returns {mat2} out
134 | */
135 | export function invert(out, a) {
136 | let a0 = a[0], a1 = a[1], a2 = a[2], a3 = a[3];
137 |
138 | // Calculate the determinant
139 | let det = a0 * a3 - a2 * a1;
140 |
141 | if (!det) {
142 | return null;
143 | }
144 | det = 1.0 / det;
145 |
146 | out[0] = a3 * det;
147 | out[1] = -a1 * det;
148 | out[2] = -a2 * det;
149 | out[3] = a0 * det;
150 |
151 | return out;
152 | }
153 |
154 | /**
155 | * Calculates the adjugate of a mat2
156 | *
157 | * @param {mat2} out the receiving matrix
158 | * @param {mat2} a the source matrix
159 | * @returns {mat2} out
160 | */
161 | export function adjoint(out, a) {
162 | // Caching this value is nessecary if out == a
163 | let a0 = a[0];
164 | out[0] = a[3];
165 | out[1] = -a[1];
166 | out[2] = -a[2];
167 | out[3] = a0;
168 |
169 | return out;
170 | }
171 |
172 | /**
173 | * Calculates the determinant of a mat2
174 | *
175 | * @param {mat2} a the source matrix
176 | * @returns {Number} determinant of a
177 | */
178 | export function determinant(a) {
179 | return a[0] * a[3] - a[2] * a[1];
180 | }
181 |
182 | /**
183 | * Multiplies two mat2's
184 | *
185 | * @param {mat2} out the receiving matrix
186 | * @param {mat2} a the first operand
187 | * @param {mat2} b the second operand
188 | * @returns {mat2} out
189 | */
190 | export function multiply(out, a, b) {
191 | let a0 = a[0], a1 = a[1], a2 = a[2], a3 = a[3];
192 | let b0 = b[0], b1 = b[1], b2 = b[2], b3 = b[3];
193 | out[0] = a0 * b0 + a2 * b1;
194 | out[1] = a1 * b0 + a3 * b1;
195 | out[2] = a0 * b2 + a2 * b3;
196 | out[3] = a1 * b2 + a3 * b3;
197 | return out;
198 | }
199 |
200 | /**
201 | * Rotates a mat2 by the given angle
202 | *
203 | * @param {mat2} out the receiving matrix
204 | * @param {mat2} a the matrix to rotate
205 | * @param {Number} rad the angle to rotate the matrix by
206 | * @returns {mat2} out
207 | */
208 | export function rotate(out, a, rad) {
209 | let a0 = a[0], a1 = a[1], a2 = a[2], a3 = a[3];
210 | let s = Math.sin(rad);
211 | let c = Math.cos(rad);
212 | out[0] = a0 * c + a2 * s;
213 | out[1] = a1 * c + a3 * s;
214 | out[2] = a0 * -s + a2 * c;
215 | out[3] = a1 * -s + a3 * c;
216 | return out;
217 | }
218 |
219 | /**
220 | * Scales the mat2 by the dimensions in the given vec2
221 | *
222 | * @param {mat2} out the receiving matrix
223 | * @param {mat2} a the matrix to rotate
224 | * @param {vec2} v the vec2 to scale the matrix by
225 | * @returns {mat2} out
226 | **/
227 | export function scale(out, a, v) {
228 | let a0 = a[0], a1 = a[1], a2 = a[2], a3 = a[3];
229 | let v0 = v[0], v1 = v[1];
230 | out[0] = a0 * v0;
231 | out[1] = a1 * v0;
232 | out[2] = a2 * v1;
233 | out[3] = a3 * v1;
234 | return out;
235 | }
236 |
237 | /**
238 | * Creates a matrix from a given angle
239 | * This is equivalent to (but much faster than):
240 | *
241 | * mat2.identity(dest);
242 | * mat2.rotate(dest, dest, rad);
243 | *
244 | * @param {mat2} out mat2 receiving operation result
245 | * @param {Number} rad the angle to rotate the matrix by
246 | * @returns {mat2} out
247 | */
248 | export function fromRotation(out, rad) {
249 | let s = Math.sin(rad);
250 | let c = Math.cos(rad);
251 | out[0] = c;
252 | out[1] = s;
253 | out[2] = -s;
254 | out[3] = c;
255 | return out;
256 | }
257 |
258 | /**
259 | * Creates a matrix from a vector scaling
260 | * This is equivalent to (but much faster than):
261 | *
262 | * mat2.identity(dest);
263 | * mat2.scale(dest, dest, vec);
264 | *
265 | * @param {mat2} out mat2 receiving operation result
266 | * @param {vec2} v Scaling vector
267 | * @returns {mat2} out
268 | */
269 | export function fromScaling(out, v) {
270 | out[0] = v[0];
271 | out[1] = 0;
272 | out[2] = 0;
273 | out[3] = v[1];
274 | return out;
275 | }
276 |
277 | /**
278 | * Returns a string representation of a mat2
279 | *
280 | * @param {mat2} a matrix to represent as a string
281 | * @returns {String} string representation of the matrix
282 | */
283 | export function str(a) {
284 | return 'mat2(' + a[0] + ', ' + a[1] + ', ' + a[2] + ', ' + a[3] + ')';
285 | }
286 |
287 | /**
288 | * Returns Frobenius norm of a mat2
289 | *
290 | * @param {mat2} a the matrix to calculate Frobenius norm of
291 | * @returns {Number} Frobenius norm
292 | */
293 | export function frob(a) {
294 | return(Math.sqrt(Math.pow(a[0], 2) + Math.pow(a[1], 2) + Math.pow(a[2], 2) + Math.pow(a[3], 2)))
295 | }
296 |
297 | /**
298 | * Returns L, D and U matrices (Lower triangular, Diagonal and Upper triangular) by factorizing the input matrix
299 | * @param {mat2} L the lower triangular matrix
300 | * @param {mat2} D the diagonal matrix
301 | * @param {mat2} U the upper triangular matrix
302 | * @param {mat2} a the input matrix to factorize
303 | */
304 |
305 | export function LDU(L, D, U, a) {
306 | L[2] = a[2]/a[0];
307 | U[0] = a[0];
308 | U[1] = a[1];
309 | U[3] = a[3] - L[2] * U[1];
310 | return [L, D, U];
311 | }
312 |
313 | /**
314 | * Adds two mat2's
315 | *
316 | * @param {mat2} out the receiving matrix
317 | * @param {mat2} a the first operand
318 | * @param {mat2} b the second operand
319 | * @returns {mat2} out
320 | */
321 | export function add(out, a, b) {
322 | out[0] = a[0] + b[0];
323 | out[1] = a[1] + b[1];
324 | out[2] = a[2] + b[2];
325 | out[3] = a[3] + b[3];
326 | return out;
327 | }
328 |
329 | /**
330 | * Subtracts matrix b from matrix a
331 | *
332 | * @param {mat2} out the receiving matrix
333 | * @param {mat2} a the first operand
334 | * @param {mat2} b the second operand
335 | * @returns {mat2} out
336 | */
337 | export function subtract(out, a, b) {
338 | out[0] = a[0] - b[0];
339 | out[1] = a[1] - b[1];
340 | out[2] = a[2] - b[2];
341 | out[3] = a[3] - b[3];
342 | return out;
343 | }
344 |
345 | /**
346 | * Returns whether or not the matrices have exactly the same elements in the same position (when compared with ===)
347 | *
348 | * @param {mat2} a The first matrix.
349 | * @param {mat2} b The second matrix.
350 | * @returns {Boolean} True if the matrices are equal, false otherwise.
351 | */
352 | export function exactEquals(a, b) {
353 | return a[0] === b[0] && a[1] === b[1] && a[2] === b[2] && a[3] === b[3];
354 | }
355 |
356 | /**
357 | * Returns whether or not the matrices have approximately the same elements in the same position.
358 | *
359 | * @param {mat2} a The first matrix.
360 | * @param {mat2} b The second matrix.
361 | * @returns {Boolean} True if the matrices are equal, false otherwise.
362 | */
363 | export function equals(a, b) {
364 | let a0 = a[0], a1 = a[1], a2 = a[2], a3 = a[3];
365 | let b0 = b[0], b1 = b[1], b2 = b[2], b3 = b[3];
366 | return (Math.abs(a0 - b0) <= glMatrix.EPSILON*Math.max(1.0, Math.abs(a0), Math.abs(b0)) &&
367 | Math.abs(a1 - b1) <= glMatrix.EPSILON*Math.max(1.0, Math.abs(a1), Math.abs(b1)) &&
368 | Math.abs(a2 - b2) <= glMatrix.EPSILON*Math.max(1.0, Math.abs(a2), Math.abs(b2)) &&
369 | Math.abs(a3 - b3) <= glMatrix.EPSILON*Math.max(1.0, Math.abs(a3), Math.abs(b3)));
370 | }
371 |
372 | /**
373 | * Multiply each element of the matrix by a scalar.
374 | *
375 | * @param {mat2} out the receiving matrix
376 | * @param {mat2} a the matrix to scale
377 | * @param {Number} b amount to scale the matrix's elements by
378 | * @returns {mat2} out
379 | */
380 | export function multiplyScalar(out, a, b) {
381 | out[0] = a[0] * b;
382 | out[1] = a[1] * b;
383 | out[2] = a[2] * b;
384 | out[3] = a[3] * b;
385 | return out;
386 | }
387 |
388 | /**
389 | * Adds two mat2's after multiplying each element of the second operand by a scalar value.
390 | *
391 | * @param {mat2} out the receiving vector
392 | * @param {mat2} a the first operand
393 | * @param {mat2} b the second operand
394 | * @param {Number} scale the amount to scale b's elements by before adding
395 | * @returns {mat2} out
396 | */
397 | export function multiplyScalarAndAdd(out, a, b, scale) {
398 | out[0] = a[0] + (b[0] * scale);
399 | out[1] = a[1] + (b[1] * scale);
400 | out[2] = a[2] + (b[2] * scale);
401 | out[3] = a[3] + (b[3] * scale);
402 | return out;
403 | }
404 |
405 | /**
406 | * Alias for {@link mat2.multiply}
407 | * @function
408 | */
409 | export const mul = multiply;
410 |
411 | /**
412 | * Alias for {@link mat2.subtract}
413 | * @function
414 | */
415 | export const sub = subtract;
416 |
--------------------------------------------------------------------------------
/js/third-party/stats.module.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @author mrdoob / http://mrdoob.com/
3 | */
4 |
5 | var Stats = function () {
6 |
7 | var mode = 0;
8 |
9 | var container = document.createElement( 'div' );
10 | container.style.cssText = 'position:fixed;top:0;left:0;cursor:pointer;opacity:0.9;z-index:10000';
11 | container.addEventListener( 'click', function ( event ) {
12 |
13 | event.preventDefault();
14 | showPanel( ++ mode % container.children.length );
15 |
16 | }, false );
17 |
18 | //
19 |
20 | function addPanel( panel ) {
21 |
22 | container.appendChild( panel.dom );
23 | return panel;
24 |
25 | }
26 |
27 | function showPanel( id ) {
28 |
29 | for ( var i = 0; i < container.children.length; i ++ ) {
30 |
31 | container.children[ i ].style.display = i === id ? 'block' : 'none';
32 |
33 | }
34 |
35 | mode = id;
36 |
37 | }
38 |
39 | //
40 |
41 | var beginTime = ( performance || Date ).now(), prevTime = beginTime, frames = 0;
42 | var frameTime = 0;
43 | var fpsPanel = addPanel( new Stats.Panel( 'FPS', '#0ff', '#002' ) );
44 | var msPanel = addPanel( new Stats.Panel( 'MS', '#0f0', '#020' ) );
45 |
46 | if ( self.performance && self.performance.memory ) {
47 |
48 | var memPanel = addPanel( new Stats.Panel( 'MB', '#f08', '#201' ) );
49 |
50 | }
51 |
52 | showPanel( 1 );
53 |
54 | return {
55 |
56 | REVISION: 16,
57 |
58 | dom: container,
59 |
60 | addPanel: addPanel,
61 | showPanel: showPanel,
62 |
63 | begin: function () {
64 |
65 | beginTime = ( performance || Date ).now();
66 |
67 | },
68 |
69 | end: function () {
70 |
71 | frames ++;
72 |
73 | var time = ( performance || Date ).now();
74 |
75 | frameTime += time - beginTime;
76 |
77 | if ( time >= prevTime + 1000 ) {
78 |
79 | msPanel.update( frameTime / frames, 3 );
80 | fpsPanel.update( ( frames * 1000 ) / ( time - prevTime ), 100 );
81 |
82 | frameTime = 0;
83 | prevTime = time;
84 | frames = 0;
85 |
86 | if ( memPanel ) {
87 |
88 | var memory = performance.memory;
89 | memPanel.update( memory.usedJSHeapSize / 1048576, memory.jsHeapSizeLimit / 1048576 );
90 |
91 | }
92 |
93 | }
94 |
95 | return time;
96 |
97 | },
98 |
99 | update: function () {
100 |
101 | beginTime = this.end();
102 |
103 | },
104 |
105 | // Backwards Compatibility
106 |
107 | domElement: container,
108 | setMode: showPanel
109 |
110 | };
111 |
112 | };
113 |
114 | Stats.Panel = function ( name, fg, bg ) {
115 |
116 | var min = Infinity, max = 0, round = Math.round;
117 | var PR = round( window.devicePixelRatio || 1 );
118 |
119 | var WIDTH = 80 * PR, HEIGHT = 48 * PR,
120 | TEXT_X = 3 * PR, TEXT_Y = 2 * PR,
121 | GRAPH_X = 3 * PR, GRAPH_Y = 15 * PR,
122 | GRAPH_WIDTH = 74 * PR, GRAPH_HEIGHT = 30 * PR;
123 |
124 | var canvas = document.createElement( 'canvas' );
125 | canvas.width = WIDTH;
126 | canvas.height = HEIGHT;
127 | canvas.style.cssText = 'width:80px;height:48px';
128 |
129 | var context = canvas.getContext( '2d' );
130 | context.font = 'bold ' + ( 9 * PR ) + 'px Helvetica,Arial,sans-serif';
131 | context.textBaseline = 'top';
132 |
133 | context.fillStyle = bg;
134 | context.fillRect( 0, 0, WIDTH, HEIGHT );
135 |
136 | context.fillStyle = fg;
137 | context.fillText( name, TEXT_X, TEXT_Y );
138 | context.fillRect( GRAPH_X, GRAPH_Y, GRAPH_WIDTH, GRAPH_HEIGHT );
139 |
140 | context.fillStyle = bg;
141 | context.globalAlpha = 0.9;
142 | context.fillRect( GRAPH_X, GRAPH_Y, GRAPH_WIDTH, GRAPH_HEIGHT );
143 |
144 | return {
145 |
146 | dom: canvas,
147 |
148 | update: function ( value, maxValue ) {
149 |
150 | min = Math.min( min, value );
151 | max = Math.max( max, value );
152 |
153 | context.fillStyle = bg;
154 | context.globalAlpha = 1;
155 | context.fillRect( 0, 0, WIDTH, GRAPH_Y );
156 | context.fillStyle = fg;
157 | context.fillText( value.toFixed(2) + ' ' + name + ' (' + round( min ) + '-' + round( max ) + ')', TEXT_X, TEXT_Y );
158 |
159 | context.drawImage( canvas, GRAPH_X + PR, GRAPH_Y, GRAPH_WIDTH - PR, GRAPH_HEIGHT, GRAPH_X, GRAPH_Y, GRAPH_WIDTH - PR, GRAPH_HEIGHT );
160 |
161 | context.fillRect( GRAPH_X + GRAPH_WIDTH - PR, GRAPH_Y, PR, GRAPH_HEIGHT );
162 |
163 | context.fillStyle = bg;
164 | context.globalAlpha = 0.9;
165 | context.fillRect( GRAPH_X + GRAPH_WIDTH - PR, GRAPH_Y, PR, round( ( 1 - ( value / maxValue ) ) * GRAPH_HEIGHT ) );
166 |
167 | }
168 |
169 | };
170 |
171 | };
172 |
173 | export default Stats;
174 |
--------------------------------------------------------------------------------
/js/webgl-renderer/shader-program.js:
--------------------------------------------------------------------------------
1 | // Copyright 2018 The Immersive Web Community Group
2 | //
3 | // Permission is hereby granted, free of charge, to any person obtaining a copy
4 | // of this software and associated documentation files (the "Software"), to deal
5 | // in the Software without restriction, including without limitation the rights
6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7 | // copies of the Software, and to permit persons to whom the Software is
8 | // furnished to do so, subject to the following conditions:
9 |
10 | // The above copyright notice and this permission notice shall be included in
11 | // all copies or substantial portions of the Software.
12 |
13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
19 | // SOFTWARE.
20 |
21 | // Just a helper class to easily compile and link a shader program, then query
22 | // some helpful values such as attribute and uniform locations.
23 | export class ShaderProgram {
24 | constructor(gl, config) {
25 | if (!config || !config.vertexSource || !config.fragmentSource) {
26 | throw new Error('Must provide a vertexSource and fragmentSource');
27 | }
28 |
29 | this.gl = gl;
30 | this.program = gl.createProgram();
31 | this.attribute = {};
32 | this.uniform = {};
33 | this.uniformBlock = {};
34 |
35 | const vertShader = gl.createShader(gl.VERTEX_SHADER);
36 | gl.attachShader(this.program, vertShader);
37 | gl.shaderSource(vertShader, config.vertexSource);
38 | gl.compileShader(vertShader);
39 |
40 | const fragShader = gl.createShader(gl.FRAGMENT_SHADER);
41 | gl.attachShader(this.program, fragShader);
42 | gl.shaderSource(fragShader, config.fragmentSource);
43 | gl.compileShader(fragShader);
44 |
45 | if (config.attributeLocations) {
46 | for (let attribName in config.attributeLocations) {
47 | gl.bindAttribLocation(this.program, config.attributeLocations[attribName], attribName);
48 | this.attribute[attribName] = config.attributeLocations[attribName];
49 | }
50 | }
51 |
52 | gl.linkProgram(this.program);
53 |
54 | if (!gl.getProgramParameter(this.program, gl.LINK_STATUS)) {
55 | if (!gl.getShaderParameter(vertShader, gl.COMPILE_STATUS)) {
56 | console.error('Vertex shader compile error: ' + gl.getShaderInfoLog(vertShader));
57 | } else if (!gl.getShaderParameter(fragShader, gl.COMPILE_STATUS)) {
58 | console.error('Fragment shader compile error: ' + gl.getShaderInfoLog(fragShader));
59 | } else {
60 | console.error('Program link error: ' + gl.getProgramInfoLog(this.program));
61 | }
62 | gl.deleteProgram(this.program);
63 | this.program = null;
64 | return;
65 | }
66 |
67 | gl.deleteShader(vertShader);
68 | gl.deleteShader(fragShader);
69 |
70 | if (!config.attributeLocations) {
71 | let attribCount = gl.getProgramParameter(this.program, gl.ACTIVE_ATTRIBUTES);
72 | for (let i = 0; i < attribCount; i++) {
73 | let attribInfo = gl.getActiveAttrib(this.program, i);
74 | this.attribute[attribInfo.name] = gl.getAttribLocation(this.program, attribInfo.name);
75 | }
76 | }
77 |
78 | let uniformCount = gl.getProgramParameter(this.program, gl.ACTIVE_UNIFORMS);
79 | let uniformName = '';
80 | for (let i = 0; i < uniformCount; i++) {
81 | let uniformInfo = gl.getActiveUniform(this.program, i);
82 | uniformName = uniformInfo.name;
83 | this.uniform[uniformName] = gl.getUniformLocation(this.program, uniformName);
84 | }
85 |
86 | // Are we using WebGL 2?
87 | if (gl.ACTIVE_UNIFORM_BLOCKS) {
88 | let uniformBlockCount = gl.getProgramParameter(this.program, gl.ACTIVE_UNIFORM_BLOCKS);
89 | for (let i = 0; i < uniformBlockCount; i++) {
90 | let uniformBlockName = gl.getActiveUniformBlockName(this.program, i);
91 | this.uniformBlock[uniformBlockName] = i;
92 | }
93 | }
94 | }
95 |
96 | use() {
97 | this.gl.useProgram(this.program);
98 | }
99 | }
100 |
--------------------------------------------------------------------------------
/js/webgpu-renderer/pbr-shader-wgsl.js:
--------------------------------------------------------------------------------
1 | // Copyright 2020 Brandon Jones
2 | //
3 | // Permission is hereby granted, free of charge, to any person obtaining a copy
4 | // of this software and associated documentation files (the "Software"), to deal
5 | // in the Software without restriction, including without limitation the rights
6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7 | // copies of the Software, and to permit persons to whom the Software is
8 | // furnished to do so, subject to the following conditions:
9 |
10 | // The above copyright notice and this permission notice shall be included in
11 | // all copies or substantial portions of the Software.
12 |
13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
19 | // SOFTWARE.
20 |
21 | export const ATTRIB_MAP = {
22 | POSITION: 1,
23 | NORMAL: 2,
24 | TANGENT: 3,
25 | TEXCOORD_0: 4,
26 | COLOR_0: 5,
27 | };
28 |
29 | export const UNIFORM_BLOCKS = {
30 | FrameUniforms: 0,
31 | MaterialUniforms: 1,
32 | PrimitiveUniforms: 2,
33 | LightUniforms: 3
34 | };
35 |
36 | function PBR_VARYINGS(defines) { return `
37 | @location(0) vWorldPos : vec3f,
38 | @location(1) vView : vec3f, // Vector from vertex to camera.
39 | @location(2) vTex : vec2f,
40 | @location(3) vCol : vec4f,
41 | @location(4) vNorm : vec3f,
42 |
43 | ${defines.USE_NORMAL_MAP ? `
44 | @location(5) vTangent : vec3f,
45 | @location(6) vBitangent : vec3f,
46 | ` : ``}
47 | `;
48 | }
49 |
50 | export function WEBGPU_VERTEX_SOURCE(defines) { return `
51 | struct VertexInput {
52 | @location(${ATTRIB_MAP.POSITION}) POSITION : vec3f,
53 | @location(${ATTRIB_MAP.NORMAL}) NORMAL : vec3f,
54 | ${defines.USE_NORMAL_MAP ? `
55 | @location(${ATTRIB_MAP.TANGENT}) TANGENT : vec4f,
56 | ` : ``}
57 | @location(${ATTRIB_MAP.TEXCOORD_0}) TEXCOORD_0 : vec2f,
58 | ${defines.USE_VERTEX_COLOR ? `
59 | @location(${ATTRIB_MAP.COLOR_0}) COLOR_0 : vec4f,
60 | ` : ``}
61 | };
62 |
63 | struct FrameUniforms {
64 | projectionMatrix : mat4x4f,
65 | viewMatrix : mat4x4f,
66 | cameraPosition : vec3f,
67 | };
68 | @binding(0) @group(${UNIFORM_BLOCKS.FrameUniforms}) var frame : FrameUniforms;
69 |
70 | struct PrimitiveUniforms {
71 | modelMatrix : mat4x4f
72 | };
73 | @binding(0) @group(${UNIFORM_BLOCKS.PrimitiveUniforms}) var primitive : PrimitiveUniforms;
74 |
75 | struct VertexOutput {
76 | @builtin(position) position : vec4f,
77 | ${PBR_VARYINGS(defines)}
78 | };
79 |
80 | @vertex
81 | fn main(input : VertexInput) -> VertexOutput {
82 | var output : VertexOutput;
83 | output.vNorm = normalize((primitive.modelMatrix * vec4(input.NORMAL, 0.0)).xyz);
84 | ${defines.USE_NORMAL_MAP ? `
85 | output.vTangent = normalize((primitive.modelMatrix * vec4(input.TANGENT.xyz, 0.0)).xyz);
86 | output.vBitangent = cross(output.vNorm, output.vTangent) * input.TANGENT.w;
87 | ` : ``}
88 |
89 | ${defines.USE_VERTEX_COLOR ? `
90 | output.vCol = input.COLOR_0;
91 | ` : `` }
92 |
93 | output.vTex = input.TEXCOORD_0;
94 | var mPos = primitive.modelMatrix * vec4(input.POSITION, 1.0);
95 | output.vWorldPos = mPos.xyz;
96 | output.vView = frame.cameraPosition - mPos.xyz;
97 | output.position = frame.projectionMatrix * frame.viewMatrix * mPos;
98 | return output;
99 | }`;
100 | }
101 |
102 | // Much of the shader used here was pulled from https://learnopengl.com/PBR/Lighting
103 | // Thanks!
104 | const PBR_FUNCTIONS = `
105 | const PI : f32 = ${Math.PI};
106 |
107 | fn FresnelSchlick(cosTheta : f32, F0 : vec3f) -> vec3f {
108 | return F0 + (vec3(1.0) - F0) * pow(1.0 - cosTheta, 5.0);
109 | }
110 |
111 | fn DistributionGGX(N : vec3f, H : vec3f, roughness : f32) -> f32 {
112 | var a : f32 = roughness*roughness;
113 | var a2 : f32 = a*a;
114 | var NdotH : f32 = max(dot(N, H), 0.0);
115 | var NdotH2 : f32 = NdotH*NdotH;
116 |
117 | var num : f32 = a2;
118 | var denom : f32 = (NdotH2 * (a2 - 1.0) + 1.0);
119 | denom = PI * denom * denom;
120 |
121 | return num / denom;
122 | }
123 |
124 | fn GeometrySchlickGGX(NdotV : f32, roughness : f32) -> f32 {
125 | var r : f32 = (roughness + 1.0);
126 | var k : f32 = (r*r) / 8.0;
127 |
128 | var num : f32 = NdotV;
129 | var denom : f32 = NdotV * (1.0 - k) + k;
130 |
131 | return num / denom;
132 | }
133 |
134 | fn GeometrySmith(N : vec3f, V : vec3f, L : vec3f, roughness : f32) -> f32 {
135 | var NdotV : f32 = max(dot(N, V), 0.0);
136 | var NdotL : f32 = max(dot(N, L), 0.0);
137 | var ggx2 : f32 = GeometrySchlickGGX(NdotV, roughness);
138 | var ggx1 : f32 = GeometrySchlickGGX(NdotL, roughness);
139 |
140 | return ggx1 * ggx2;
141 | }`;
142 |
143 | export function WEBGPU_FRAGMENT_SOURCE(defines) { return `
144 | ${PBR_FUNCTIONS}
145 |
146 | struct MaterialUniforms {
147 | baseColorFactor : vec4f,
148 | metallicRoughnessFactor : vec2f,
149 | emissiveFactor : vec3f,
150 | occlusionStrength : f32,
151 | };
152 | @binding(0) @group(${UNIFORM_BLOCKS.MaterialUniforms}) var material : MaterialUniforms;
153 |
154 | @group(1) @binding(1) var defaultSampler : sampler;
155 | @group(1) @binding(2) var baseColorTexture : texture_2d;
156 | @group(1) @binding(3) var normalTexture : texture_2d;
157 | @group(1) @binding(4) var metallicRoughnessTexture : texture_2d;
158 | @group(1) @binding(5) var occlusionTexture : texture_2d;
159 | @group(1) @binding(6) var emissiveTexture : texture_2d;
160 |
161 | struct Light {
162 | position : vec3f,
163 | color : vec3f,
164 | };
165 |
166 | struct LightUniforms {
167 | lights : array,
168 | lightAmbient : f32,
169 | };
170 | @binding(0) @group(${UNIFORM_BLOCKS.LightUniforms}) var light : LightUniforms;
171 |
172 | struct VertexOutput {
173 | ${PBR_VARYINGS(defines)}
174 | };
175 |
176 | const dielectricSpec = vec3(0.04);
177 | const black = vec3(0.0);
178 |
179 | @fragment
180 | fn main(input : VertexOutput) -> @location(0) vec4f {
181 | var baseColor = material.baseColorFactor;
182 | ${defines.USE_BASE_COLOR_MAP ? `
183 | var baseColorMap = textureSample(baseColorTexture, defaultSampler, input.vTex);
184 | baseColor = baseColor * baseColorMap;
185 | ` : ``}
186 | ${defines.USE_VERTEX_COLOR ? `
187 | baseColor = baseColor * vCol;
188 | ` : ``}
189 |
190 | var albedo = baseColor.rgb;
191 |
192 | var metallic : f32 = material.metallicRoughnessFactor.x;
193 | var roughness : f32 = material.metallicRoughnessFactor.y;
194 |
195 | ${defines.USE_METAL_ROUGH_MAP ? `
196 | var metallicRoughness = textureSample(metallicRoughnessTexture, defaultSampler, input.vTex);
197 | metallic = metallic * metallicRoughness.b;
198 | roughness = roughness * metallicRoughness.g;
199 | ` : ``}
200 |
201 | ${defines.USE_NORMAL_MAP ? `
202 | let tbn = mat3x3(input.vTangent, input.vBitangent, input.vNorm);
203 | var N = textureSample(normalTexture, defaultSampler, input.vTex).rgb;
204 | N = normalize(tbn * (2.0 * N - vec3(1.0)));
205 | ` : `
206 | var N = normalize(input.vNorm);
207 | `}
208 |
209 | ${defines.USE_OCCLUSION ? `
210 | var ao : f32 = textureSample(occlusionTexture, defaultSampler, input.vTex).r * material.occlusionStrength;
211 | ` : `
212 | var ao : f32 = 1.0;
213 | `}
214 |
215 | var emissive = material.emissiveFactor;
216 | ${defines.USE_EMISSIVE_TEXTURE ? `
217 | emissive = emissive * textureSample(emissiveTexture, defaultSampler, input.vTex).rgb;
218 | ` : ``}
219 |
220 | if (baseColorMap.a < 0.05) {
221 | discard;
222 | }
223 |
224 | var V = normalize(input.vView);
225 |
226 | var F0 = mix(dielectricSpec, albedo, vec3(metallic));
227 |
228 | // reflectance equation
229 | var Lo = vec3(0.0);
230 |
231 | for (var i : i32 = 0; i < ${defines.LIGHT_COUNT}; i = i + 1) {
232 | // calculate per-light radiance
233 | var L = normalize(light.lights[i].position.xyz - input.vWorldPos);
234 | var H = normalize(V + L);
235 | var distance = length(light.lights[i].position.xyz - input.vWorldPos);
236 | var attenuation = 1.0 / (1.0 + distance * distance);
237 | var radiance = light.lights[i].color.rgb * attenuation;
238 |
239 | // cook-torrance brdf
240 | var NDF = DistributionGGX(N, H, roughness);
241 | var G = GeometrySmith(N, V, L, roughness);
242 | var F = FresnelSchlick(max(dot(H, V), 0.0), F0);
243 |
244 | var kD = vec3(1.0) - F;
245 | kD = kD * (1.0 - metallic);
246 |
247 | var numerator = NDF * G * F;
248 | var denominator = 4.0 * max(dot(N, V), 0.0) * max(dot(N, L), 0.0);
249 | denominator = max(denominator, 0.001);
250 | var specular = numerator / vec3(denominator);
251 |
252 | // add to outgoing radiance Lo
253 | var NdotL = max(dot(N, L), 0.0);
254 | Lo = Lo + (kD * albedo / vec3(PI) + specular) * radiance * NdotL;
255 | }
256 |
257 | var ambient = light.lightAmbient * albedo * ao;
258 | var color = ambient + Lo;
259 |
260 | color = color + emissive;
261 |
262 | color = color / (color + vec3(1.0));
263 | color = pow(color, vec3(1.0/2.2));
264 |
265 | return vec4(color, baseColor.a);
266 | }
267 | `;
268 | }
269 |
270 | export function GetDefinesForPrimitive(primitive) {
271 | const attributes = primitive.enabledAttributes;
272 | const material = primitive.material;
273 | const programDefines = {};
274 |
275 | if (attributes.has('COLOR_0')) {
276 | programDefines['USE_VERTEX_COLOR'] = 1;
277 | }
278 |
279 | if (attributes.has('TEXCOORD_0')) {
280 | if (material.baseColorTexture) {
281 | programDefines['USE_BASE_COLOR_MAP'] = 1;
282 | }
283 |
284 | if (material.normalTexture && (attributes.has('TANGENT'))) {
285 | programDefines['USE_NORMAL_MAP'] = 1;
286 | }
287 |
288 | if (material.metallicRoughnessTexture) {
289 | programDefines['USE_METAL_ROUGH_MAP'] = 1;
290 | }
291 |
292 | if (material.occlusionTexture) {
293 | programDefines['USE_OCCLUSION'] = 1;
294 | }
295 |
296 | if (material.emissiveTexture) {
297 | programDefines['USE_EMISSIVE_TEXTURE'] = 1;
298 | }
299 | }
300 |
301 | if ((!material.metallicRoughnessTexture ||
302 | !(attributes.has('TEXCOORD_0'))) &&
303 | material.metallicRoughnessFactor[1] == 1.0) {
304 | programDefines['FULLY_ROUGH'] = 1;
305 | }
306 |
307 | return programDefines;
308 | }
309 |
--------------------------------------------------------------------------------
/js/webgpu-renderer/webgpu-texture-helper.js:
--------------------------------------------------------------------------------
1 | // Copyright 2020 Brandon Jones
2 | //
3 | // Permission is hereby granted, free of charge, to any person obtaining a copy
4 | // of this software and associated documentation files (the "Software"), to deal
5 | // in the Software without restriction, including without limitation the rights
6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7 | // copies of the Software, and to permit persons to whom the Software is
8 | // furnished to do so, subject to the following conditions:
9 |
10 | // The above copyright notice and this permission notice shall be included in
11 | // all copies or substantial portions of the Software.
12 |
13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
19 | // SOFTWARE.
20 |
21 | export class GPUTextureHelper {
22 | constructor(device) {
23 | this.device = device;
24 |
25 | const mipmapShaderModule = this.device.createShaderModule({
26 | code: `
27 | var pos : array = array(
28 | vec2(-1.0, -1.0), vec2(-1.0, 3.0), vec2(3.0, -1.0));
29 |
30 | struct VertexOutput {
31 | @builtin(position) position : vec4f,
32 | @location(0) texCoord : vec2f,
33 | };
34 |
35 | @vertex
36 | fn vertexMain(@builtin(vertex_index) vertexIndex : u32) -> VertexOutput {
37 | var output : VertexOutput;
38 | output.texCoord = pos[vertexIndex] * vec2(0.5, -0.5) + vec2(0.5);
39 | output.position = vec4(pos[vertexIndex], 0.0, 1.0);
40 | return output;
41 | }
42 |
43 | @binding(0) @group(0) var imgSampler : sampler;
44 | @binding(1) @group(0) var img : texture_2d;
45 |
46 | @fragment
47 | fn fragmentMain(@location(0) texCoord : vec2f) -> @location(0) vec4f {
48 | return textureSample(img, imgSampler, texCoord);
49 | }
50 | `,
51 | });
52 |
53 | this.mipmapSampler = device.createSampler({ minFilter: 'linear' });
54 |
55 | this.mipmapPipeline = device.createRenderPipeline({
56 | layout: 'auto',
57 | vertex: {
58 | module: mipmapShaderModule,
59 | entryPoint: 'vertexMain',
60 | },
61 | fragment: {
62 | module: mipmapShaderModule,
63 | entryPoint: 'fragmentMain',
64 | targets: [{
65 | format: 'rgba8unorm',
66 | }],
67 | }
68 | });
69 | }
70 |
71 | // TODO: Everything about this is awful.
72 | generateMipmappedTexture(imageBitmap) {
73 | let textureSize = {
74 | width: imageBitmap.width,
75 | height: imageBitmap.height,
76 | }
77 | const mipLevelCount = Math.floor(Math.log2(Math.max(imageBitmap.width, imageBitmap.height))) + 1;
78 |
79 | // Populate the top level of the srcTexture with the imageBitmap.
80 | const srcTexture = this.device.createTexture({
81 | size: textureSize,
82 | format: 'rgba8unorm',
83 | // TO COMPLAIN ABOUT: Kind of worrying that this style of mipmap generation implies that almost every texture
84 | // generated will be an output attachment. There's gotta be a performance penalty for that.
85 | usage: GPUTextureUsage.COPY_DST | GPUTextureUsage.TEXTURE_BINDING | GPUTextureUsage.RENDER_ATTACHMENT,
86 | mipLevelCount
87 | });
88 | this.device.queue.copyExternalImageToTexture({ source: imageBitmap }, { texture: srcTexture }, textureSize);
89 |
90 | const commandEncoder = this.device.createCommandEncoder({});
91 |
92 | const bindGroupLayout = this.mipmapPipeline.getBindGroupLayout(0);
93 |
94 | for (let i = 1; i < mipLevelCount; ++i) {
95 | const bindGroup = this.device.createBindGroup({
96 | layout: bindGroupLayout,
97 | entries: [{
98 | binding: 0,
99 | resource: this.mipmapSampler,
100 | }, {
101 | binding: 1,
102 | resource: srcTexture.createView({
103 | baseMipLevel: i-1,
104 | mipLevelCount: 1
105 | }),
106 | }],
107 | });
108 |
109 | const passEncoder = commandEncoder.beginRenderPass({
110 | colorAttachments: [{
111 | view: srcTexture.createView({
112 | baseMipLevel: i,
113 | mipLevelCount: 1
114 | }),
115 | loadOp: 'load',
116 | storeOp: 'store',
117 | }],
118 | });
119 | passEncoder.setPipeline(this.mipmapPipeline);
120 | passEncoder.setBindGroup(0, bindGroup);
121 | passEncoder.draw(3);
122 | passEncoder.end();
123 |
124 | textureSize.width = Math.ceil(textureSize.width / 2);
125 | textureSize.height = Math.ceil(textureSize.height / 2);
126 | }
127 | this.device.queue.submit([commandEncoder.finish()]);
128 |
129 | return srcTexture;
130 | }
131 |
132 | generateTexture(imageBitmap) {
133 | const textureSize = {
134 | width: imageBitmap.width,
135 | height: imageBitmap.height,
136 | };
137 |
138 | const texture = this.device.createTexture({
139 | size: textureSize,
140 | format: 'rgba8unorm',
141 | usage: GPUTextureUsage.COPY_DST | GPUTextureUsage.TEXTURE_BINDING,
142 | });
143 | this.device.queue.copyImageBitmapToTexture({ imageBitmap }, { texture }, textureSize);
144 |
145 | return texture;
146 | }
147 |
148 | generateColorTexture(r, g, b, a) {
149 | const imageData = new Uint8Array([r * 255, g * 255, b * 255, a * 255]);
150 |
151 | const imageSize = { width: 1, height: 1 };
152 | const texture = this.device.createTexture({
153 | size: imageSize,
154 | format: 'rgba8unorm',
155 | usage: GPUTextureUsage.COPY_DST | GPUTextureUsage.TEXTURE_BINDING,
156 | });
157 |
158 | const textureDataBuffer = this.device.createBuffer({
159 | // BUG? WTF is up with this?!? bytesPerRow has to be a multiple of 256?
160 | size: 256,
161 | usage: GPUBufferUsage.COPY_SRC,
162 | mappedAtCreation: true,
163 | });
164 | const textureDataArray = new Uint8Array(textureDataBuffer.getMappedRange());
165 | textureDataArray.set(imageData);
166 | textureDataBuffer.unmap();
167 |
168 | const commandEncoder = this.device.createCommandEncoder({});
169 | commandEncoder.copyBufferToTexture({
170 | buffer: textureDataBuffer,
171 | bytesPerRow: 256,
172 | rowsPerImage: 0, // What is this for?
173 | }, { texture: texture }, imageSize);
174 | this.device.queue.submit([commandEncoder.finish()]);
175 |
176 | return texture;
177 | }
178 | }
--------------------------------------------------------------------------------
/js/webgpu-renderer/wgsl-debug-helper.js:
--------------------------------------------------------------------------------
1 | // Copyright 2020 Brandon Jones
2 | //
3 | // Permission is hereby granted, free of charge, to any person obtaining a copy
4 | // of this software and associated documentation files (the "Software"), to deal
5 | // in the Software without restriction, including without limitation the rights
6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7 | // copies of the Software, and to permit persons to whom the Software is
8 | // furnished to do so, subject to the following conditions:
9 |
10 | // The above copyright notice and this permission notice shall be included in
11 | // all copies or substantial portions of the Software.
12 |
13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
19 | // SOFTWARE.
20 |
21 | const LOG_FULL_SHADER_TEXT = true;
22 |
23 | const MESSAGE_STYLE = {
24 | 'info': {
25 | icon: 'ℹ️',
26 | logFn: console.info,
27 | },
28 | 'warning': {
29 | icon: '⚠️',
30 | logFn: console.warn,
31 | },
32 | 'error': {
33 | icon: '⛔',
34 | logFn: console.error,
35 | }
36 | }
37 |
38 | /**
39 | * A method that captures errors returned by compiling a WebGPU shader module
40 | * and annotates them with additional information before echoing to the console
41 | * to aid with debugging.
42 | */
43 | if ('GPUDevice' in window) {
44 | const origCreateShaderModule = GPUDevice.prototype.createShaderModule;
45 | GPUDevice.prototype.createShaderModule = function(descriptor) {
46 | if (!this.pushErrorScope) {
47 | return origCreateShaderModule.call(this, descriptor);
48 | }
49 |
50 | this.pushErrorScope('validation');
51 |
52 | const shaderModule = origCreateShaderModule.call(this, descriptor);
53 |
54 | const validationPromise = this.popErrorScope().then((error) => {
55 | // If compilationInfo is not available in this browser just echo any error
56 | // messages we get.
57 | if (!shaderModule.compilationInfo && error) {
58 | console.error(error.message);
59 | } else {
60 | return error;
61 | }
62 | });
63 |
64 | if (shaderModule.compilationInfo) {
65 | shaderModule.compilationInfo().then(async (info) => {
66 | const validationError = await validationPromise;
67 |
68 | if (!info.messages.length && !validationError) {
69 | return;
70 | }
71 |
72 | const messageCount = {
73 | error: 0,
74 | warning: 0,
75 | info: 0,
76 | };
77 |
78 | for (const message of info.messages) {
79 | messageCount[message.type] += 1;
80 | }
81 |
82 | if (messageCount.error == 0 && validationError) {
83 | messageCount.error = 1;
84 | }
85 |
86 | const label = shaderModule.label;
87 | let groupLabel = (label ? `"${label}"` : 'Shader') +
88 | ' returned compilation messages:';
89 | for (const type in messageCount) {
90 | if (messageCount[type] > 0) {
91 | groupLabel += ` ${messageCount[type]}${MESSAGE_STYLE[type].icon}`;
92 | }
93 | }
94 |
95 | if (messageCount.error == 0) {
96 | console.groupCollapsed(groupLabel);
97 | } else {
98 | console.group(groupLabel);
99 | }
100 |
101 | const code = descriptor.code;
102 | for (const message of info.messages) {
103 | const type = message.type;
104 |
105 | // If the message doesn't have an associated position in the code just
106 | // repeat the message verbatim
107 | if (message.lineNum == 0 && message.linePos == 0) {
108 | MESSAGE_STYLE[type].logFn(message.message);
109 | continue;
110 | }
111 |
112 | const length = Math.max(message.length, 1);
113 | const lineStartIndex = code.lastIndexOf('\n', message.offset);
114 | const lineStart = code.substring(lineStartIndex+1, message.offset);
115 | const highlightText = code.substr(message.offset, length);
116 | const lineEndIndex = code.indexOf('\n', message.offset+length);
117 | const lineEnd = code.substring(message.offset+length, lineEndIndex == -1 ? undefined : lineEndIndex);
118 |
119 | MESSAGE_STYLE[type].logFn(
120 | `%c${message.lineNum}:${message.linePos} - %c${message.message}\n%c${lineStart}%c${highlightText}%c${lineEnd}`,
121 | 'font-weight: bold;',
122 | 'font-weight: default;',
123 | 'color: green;',
124 | 'color: lightgrey; background-color: darkred; font-weight: bold;',
125 | 'color: green;');
126 | }
127 |
128 | if (validationError) {
129 | console.groupCollapsed("Validation Error Message");
130 | console.error(validationError.message);
131 | console.groupEnd();
132 | }
133 |
134 | if (LOG_FULL_SHADER_TEXT) {
135 | // Output the full shader text with numbered lines for easier reference.
136 | let numberedCodeLines = '';
137 | const codeLines = code.split('\n');
138 | const padLength = codeLines.length.toString().length;
139 | for (let i = 0; i < codeLines.length; ++i) {
140 | const lineNum = (i+1).toString().padStart(padLength, ' ');
141 | numberedCodeLines += `${lineNum}: ${codeLines[i]}\n`;
142 | }
143 |
144 | console.groupCollapsed("Full shader text");
145 | console.log(numberedCodeLines);
146 | console.groupEnd();
147 | }
148 |
149 | console.groupCollapsed("Stack Trace");
150 | console.trace();
151 | console.groupEnd();
152 |
153 | console.groupEnd();
154 | });
155 | }
156 |
157 | return shaderModule;
158 | }
159 | }
160 |
161 | // Template literal tag that offers several preprocessor improvements to WGSL
162 | // shaders. For now it's just preprocessor #if/elif/else/endif statements.
163 | const preprocessorSymbols = /#([a-z]*)\s*/gm
164 | export function wgsl(strings, ...values) {
165 | let stateStack = [];
166 | let state = { string: '', elseIsValid: false, expression: true };
167 | let depth = 1;
168 |
169 | for (let i = 0; i < strings.length; ++i) {
170 | let string = strings[i];
171 | let lastIndex = 0;
172 | let valueConsumed = false;
173 | let matchedSymbols = string.matchAll(preprocessorSymbols);
174 |
175 | for (const match of matchedSymbols) {
176 | state.string += string.substring(lastIndex, match.index);
177 | switch (match[1]) {
178 | case 'if':
179 | if (match.index + match[0].length != string.length) {
180 | console.error('WGSL preprocessor error: #if must be immediately followed by a template expression (ie: ${value})');
181 | break;
182 | }
183 | valueConsumed = true;
184 | stateStack.push(state);
185 | depth++;
186 | state = { string: '', elseIsValid: true, expression: !!values[i] };
187 | break;
188 | case 'elif':
189 | if (match.index + match[0].length != string.length) {
190 | console.error('WGSL preprocessor error: #elif must be immediately followed by a template expression (ie: ${value})');
191 | break;
192 | } else if (!state.elseIsValid) {
193 | console.error('WGSL preprocessor error: #elif not preceeded by an #if or #elif');
194 | break;
195 | }
196 | valueConsumed = true;
197 | if (state.expression && stateStack.length != depth) {
198 | stateStack.push(state);
199 | }
200 | state = { string: '', elseIsValid: true, expression: !!values[i] };
201 | break;
202 | case 'else':
203 | if (!state.elseIsValid) {
204 | console.error('WGSL preprocessor error: #else not preceeded by an #if or #elif');
205 | break;
206 | }
207 | if (state.expression && stateStack.length != depth) {
208 | stateStack.push(state);
209 | }
210 | state = { string: '', elseIsValid: false, expression: true };
211 | break;
212 | case 'endif':
213 | const branchState = stateStack.length == depth ? stateStack.pop() : state;
214 | state = stateStack.pop();
215 | depth--;
216 | if (branchState.expression) {
217 | state.string += branchState.string;
218 | }
219 | break;
220 | default:
221 | // Unknown preprocessor symbol. Emit it back into the output string unchanged.
222 | state.string += match[0];
223 | break;
224 | }
225 |
226 | lastIndex = match.index + match[0].length;
227 | }
228 |
229 | // If the string didn't end on one of the preprocessor symbols append the rest of it here.
230 | if (lastIndex != string.length) {
231 | state.string += string.substring(lastIndex, string.length);
232 | }
233 |
234 | // If the next value wasn't consumed by the preprocessor symbol, append it here.
235 | if (!valueConsumed && values.length > i) {
236 | state.string += values[i];
237 | }
238 | }
239 |
240 | if (stateStack.length) {
241 | console.error('WGSL preprocessor error: Mismatch #if/#endif count');
242 | }
243 |
244 | return state.string;
245 | }
--------------------------------------------------------------------------------
/matrix-shader-crash.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | WebGPU Shader Matrix crash
6 |
7 |
8 |
9 |
10 |
11 |
12 |
105 |
106 |
107 |
--------------------------------------------------------------------------------
/media/models/sponza/10381718147657362067.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/toji/webgpu-test/148e8fe87465777f4994f7998c7f2822e1b9b50a/media/models/sponza/10381718147657362067.jpg
--------------------------------------------------------------------------------
/media/models/sponza/10388182081421875623.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/toji/webgpu-test/148e8fe87465777f4994f7998c7f2822e1b9b50a/media/models/sponza/10388182081421875623.jpg
--------------------------------------------------------------------------------
/media/models/sponza/11474523244911310074.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/toji/webgpu-test/148e8fe87465777f4994f7998c7f2822e1b9b50a/media/models/sponza/11474523244911310074.jpg
--------------------------------------------------------------------------------
/media/models/sponza/11490520546946913238.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/toji/webgpu-test/148e8fe87465777f4994f7998c7f2822e1b9b50a/media/models/sponza/11490520546946913238.jpg
--------------------------------------------------------------------------------
/media/models/sponza/11872827283454512094.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/toji/webgpu-test/148e8fe87465777f4994f7998c7f2822e1b9b50a/media/models/sponza/11872827283454512094.jpg
--------------------------------------------------------------------------------
/media/models/sponza/11968150294050148237.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/toji/webgpu-test/148e8fe87465777f4994f7998c7f2822e1b9b50a/media/models/sponza/11968150294050148237.jpg
--------------------------------------------------------------------------------
/media/models/sponza/1219024358953944284.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/toji/webgpu-test/148e8fe87465777f4994f7998c7f2822e1b9b50a/media/models/sponza/1219024358953944284.jpg
--------------------------------------------------------------------------------
/media/models/sponza/12501374198249454378.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/toji/webgpu-test/148e8fe87465777f4994f7998c7f2822e1b9b50a/media/models/sponza/12501374198249454378.jpg
--------------------------------------------------------------------------------
/media/models/sponza/13196865903111448057.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/toji/webgpu-test/148e8fe87465777f4994f7998c7f2822e1b9b50a/media/models/sponza/13196865903111448057.jpg
--------------------------------------------------------------------------------
/media/models/sponza/13824894030729245199.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/toji/webgpu-test/148e8fe87465777f4994f7998c7f2822e1b9b50a/media/models/sponza/13824894030729245199.jpg
--------------------------------------------------------------------------------
/media/models/sponza/13982482287905699490.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/toji/webgpu-test/148e8fe87465777f4994f7998c7f2822e1b9b50a/media/models/sponza/13982482287905699490.jpg
--------------------------------------------------------------------------------
/media/models/sponza/14118779221266351425.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/toji/webgpu-test/148e8fe87465777f4994f7998c7f2822e1b9b50a/media/models/sponza/14118779221266351425.jpg
--------------------------------------------------------------------------------
/media/models/sponza/14170708867020035030.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/toji/webgpu-test/148e8fe87465777f4994f7998c7f2822e1b9b50a/media/models/sponza/14170708867020035030.jpg
--------------------------------------------------------------------------------
/media/models/sponza/14267839433702832875.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/toji/webgpu-test/148e8fe87465777f4994f7998c7f2822e1b9b50a/media/models/sponza/14267839433702832875.jpg
--------------------------------------------------------------------------------
/media/models/sponza/14650633544276105767.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/toji/webgpu-test/148e8fe87465777f4994f7998c7f2822e1b9b50a/media/models/sponza/14650633544276105767.jpg
--------------------------------------------------------------------------------
/media/models/sponza/15295713303328085182.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/toji/webgpu-test/148e8fe87465777f4994f7998c7f2822e1b9b50a/media/models/sponza/15295713303328085182.jpg
--------------------------------------------------------------------------------
/media/models/sponza/15722799267630235092.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/toji/webgpu-test/148e8fe87465777f4994f7998c7f2822e1b9b50a/media/models/sponza/15722799267630235092.jpg
--------------------------------------------------------------------------------
/media/models/sponza/16275776544635328252.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/toji/webgpu-test/148e8fe87465777f4994f7998c7f2822e1b9b50a/media/models/sponza/16275776544635328252.png
--------------------------------------------------------------------------------
/media/models/sponza/16299174074766089871.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/toji/webgpu-test/148e8fe87465777f4994f7998c7f2822e1b9b50a/media/models/sponza/16299174074766089871.jpg
--------------------------------------------------------------------------------
/media/models/sponza/16885566240357350108.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/toji/webgpu-test/148e8fe87465777f4994f7998c7f2822e1b9b50a/media/models/sponza/16885566240357350108.jpg
--------------------------------------------------------------------------------
/media/models/sponza/17556969131407844942.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/toji/webgpu-test/148e8fe87465777f4994f7998c7f2822e1b9b50a/media/models/sponza/17556969131407844942.jpg
--------------------------------------------------------------------------------
/media/models/sponza/17876391417123941155.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/toji/webgpu-test/148e8fe87465777f4994f7998c7f2822e1b9b50a/media/models/sponza/17876391417123941155.jpg
--------------------------------------------------------------------------------
/media/models/sponza/2051777328469649772.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/toji/webgpu-test/148e8fe87465777f4994f7998c7f2822e1b9b50a/media/models/sponza/2051777328469649772.jpg
--------------------------------------------------------------------------------
/media/models/sponza/2185409758123873465.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/toji/webgpu-test/148e8fe87465777f4994f7998c7f2822e1b9b50a/media/models/sponza/2185409758123873465.jpg
--------------------------------------------------------------------------------
/media/models/sponza/2299742237651021498.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/toji/webgpu-test/148e8fe87465777f4994f7998c7f2822e1b9b50a/media/models/sponza/2299742237651021498.jpg
--------------------------------------------------------------------------------
/media/models/sponza/2374361008830720677.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/toji/webgpu-test/148e8fe87465777f4994f7998c7f2822e1b9b50a/media/models/sponza/2374361008830720677.jpg
--------------------------------------------------------------------------------
/media/models/sponza/2411100444841994089.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/toji/webgpu-test/148e8fe87465777f4994f7998c7f2822e1b9b50a/media/models/sponza/2411100444841994089.jpg
--------------------------------------------------------------------------------
/media/models/sponza/2775690330959970771.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/toji/webgpu-test/148e8fe87465777f4994f7998c7f2822e1b9b50a/media/models/sponza/2775690330959970771.jpg
--------------------------------------------------------------------------------
/media/models/sponza/2969916736137545357.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/toji/webgpu-test/148e8fe87465777f4994f7998c7f2822e1b9b50a/media/models/sponza/2969916736137545357.jpg
--------------------------------------------------------------------------------
/media/models/sponza/332936164838540657.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/toji/webgpu-test/148e8fe87465777f4994f7998c7f2822e1b9b50a/media/models/sponza/332936164838540657.jpg
--------------------------------------------------------------------------------
/media/models/sponza/3371964815757888145.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/toji/webgpu-test/148e8fe87465777f4994f7998c7f2822e1b9b50a/media/models/sponza/3371964815757888145.jpg
--------------------------------------------------------------------------------
/media/models/sponza/3455394979645218238.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/toji/webgpu-test/148e8fe87465777f4994f7998c7f2822e1b9b50a/media/models/sponza/3455394979645218238.jpg
--------------------------------------------------------------------------------
/media/models/sponza/3628158980083700836.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/toji/webgpu-test/148e8fe87465777f4994f7998c7f2822e1b9b50a/media/models/sponza/3628158980083700836.jpg
--------------------------------------------------------------------------------
/media/models/sponza/3827035219084910048.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/toji/webgpu-test/148e8fe87465777f4994f7998c7f2822e1b9b50a/media/models/sponza/3827035219084910048.jpg
--------------------------------------------------------------------------------
/media/models/sponza/4477655471536070370.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/toji/webgpu-test/148e8fe87465777f4994f7998c7f2822e1b9b50a/media/models/sponza/4477655471536070370.jpg
--------------------------------------------------------------------------------
/media/models/sponza/4601176305987539675.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/toji/webgpu-test/148e8fe87465777f4994f7998c7f2822e1b9b50a/media/models/sponza/4601176305987539675.jpg
--------------------------------------------------------------------------------
/media/models/sponza/466164707995436622.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/toji/webgpu-test/148e8fe87465777f4994f7998c7f2822e1b9b50a/media/models/sponza/466164707995436622.jpg
--------------------------------------------------------------------------------
/media/models/sponza/4675343432951571524.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/toji/webgpu-test/148e8fe87465777f4994f7998c7f2822e1b9b50a/media/models/sponza/4675343432951571524.jpg
--------------------------------------------------------------------------------
/media/models/sponza/4871783166746854860.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/toji/webgpu-test/148e8fe87465777f4994f7998c7f2822e1b9b50a/media/models/sponza/4871783166746854860.jpg
--------------------------------------------------------------------------------
/media/models/sponza/4910669866631290573.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/toji/webgpu-test/148e8fe87465777f4994f7998c7f2822e1b9b50a/media/models/sponza/4910669866631290573.jpg
--------------------------------------------------------------------------------
/media/models/sponza/4975155472559461469.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/toji/webgpu-test/148e8fe87465777f4994f7998c7f2822e1b9b50a/media/models/sponza/4975155472559461469.jpg
--------------------------------------------------------------------------------
/media/models/sponza/5061699253647017043.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/toji/webgpu-test/148e8fe87465777f4994f7998c7f2822e1b9b50a/media/models/sponza/5061699253647017043.png
--------------------------------------------------------------------------------
/media/models/sponza/5792855332885324923.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/toji/webgpu-test/148e8fe87465777f4994f7998c7f2822e1b9b50a/media/models/sponza/5792855332885324923.jpg
--------------------------------------------------------------------------------
/media/models/sponza/5823059166183034438.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/toji/webgpu-test/148e8fe87465777f4994f7998c7f2822e1b9b50a/media/models/sponza/5823059166183034438.jpg
--------------------------------------------------------------------------------
/media/models/sponza/6047387724914829168.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/toji/webgpu-test/148e8fe87465777f4994f7998c7f2822e1b9b50a/media/models/sponza/6047387724914829168.jpg
--------------------------------------------------------------------------------
/media/models/sponza/6151467286084645207.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/toji/webgpu-test/148e8fe87465777f4994f7998c7f2822e1b9b50a/media/models/sponza/6151467286084645207.jpg
--------------------------------------------------------------------------------
/media/models/sponza/6593109234861095314.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/toji/webgpu-test/148e8fe87465777f4994f7998c7f2822e1b9b50a/media/models/sponza/6593109234861095314.jpg
--------------------------------------------------------------------------------
/media/models/sponza/6667038893015345571.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/toji/webgpu-test/148e8fe87465777f4994f7998c7f2822e1b9b50a/media/models/sponza/6667038893015345571.jpg
--------------------------------------------------------------------------------
/media/models/sponza/6772804448157695701.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/toji/webgpu-test/148e8fe87465777f4994f7998c7f2822e1b9b50a/media/models/sponza/6772804448157695701.jpg
--------------------------------------------------------------------------------
/media/models/sponza/7056944414013900257.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/toji/webgpu-test/148e8fe87465777f4994f7998c7f2822e1b9b50a/media/models/sponza/7056944414013900257.jpg
--------------------------------------------------------------------------------
/media/models/sponza/715093869573992647.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/toji/webgpu-test/148e8fe87465777f4994f7998c7f2822e1b9b50a/media/models/sponza/715093869573992647.jpg
--------------------------------------------------------------------------------
/media/models/sponza/7268504077753552595.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/toji/webgpu-test/148e8fe87465777f4994f7998c7f2822e1b9b50a/media/models/sponza/7268504077753552595.jpg
--------------------------------------------------------------------------------
/media/models/sponza/7441062115984513793.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/toji/webgpu-test/148e8fe87465777f4994f7998c7f2822e1b9b50a/media/models/sponza/7441062115984513793.jpg
--------------------------------------------------------------------------------
/media/models/sponza/755318871556304029.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/toji/webgpu-test/148e8fe87465777f4994f7998c7f2822e1b9b50a/media/models/sponza/755318871556304029.jpg
--------------------------------------------------------------------------------
/media/models/sponza/759203620573749278.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/toji/webgpu-test/148e8fe87465777f4994f7998c7f2822e1b9b50a/media/models/sponza/759203620573749278.jpg
--------------------------------------------------------------------------------
/media/models/sponza/7645212358685992005.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/toji/webgpu-test/148e8fe87465777f4994f7998c7f2822e1b9b50a/media/models/sponza/7645212358685992005.jpg
--------------------------------------------------------------------------------
/media/models/sponza/7815564343179553343.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/toji/webgpu-test/148e8fe87465777f4994f7998c7f2822e1b9b50a/media/models/sponza/7815564343179553343.jpg
--------------------------------------------------------------------------------
/media/models/sponza/8006627369776289000.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/toji/webgpu-test/148e8fe87465777f4994f7998c7f2822e1b9b50a/media/models/sponza/8006627369776289000.png
--------------------------------------------------------------------------------
/media/models/sponza/8051790464816141987.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/toji/webgpu-test/148e8fe87465777f4994f7998c7f2822e1b9b50a/media/models/sponza/8051790464816141987.jpg
--------------------------------------------------------------------------------
/media/models/sponza/8114461559286000061.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/toji/webgpu-test/148e8fe87465777f4994f7998c7f2822e1b9b50a/media/models/sponza/8114461559286000061.jpg
--------------------------------------------------------------------------------
/media/models/sponza/8481240838833932244.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/toji/webgpu-test/148e8fe87465777f4994f7998c7f2822e1b9b50a/media/models/sponza/8481240838833932244.jpg
--------------------------------------------------------------------------------
/media/models/sponza/8503262930880235456.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/toji/webgpu-test/148e8fe87465777f4994f7998c7f2822e1b9b50a/media/models/sponza/8503262930880235456.jpg
--------------------------------------------------------------------------------
/media/models/sponza/8747919177698443163.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/toji/webgpu-test/148e8fe87465777f4994f7998c7f2822e1b9b50a/media/models/sponza/8747919177698443163.jpg
--------------------------------------------------------------------------------
/media/models/sponza/8750083169368950601.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/toji/webgpu-test/148e8fe87465777f4994f7998c7f2822e1b9b50a/media/models/sponza/8750083169368950601.jpg
--------------------------------------------------------------------------------
/media/models/sponza/8773302468495022225.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/toji/webgpu-test/148e8fe87465777f4994f7998c7f2822e1b9b50a/media/models/sponza/8773302468495022225.jpg
--------------------------------------------------------------------------------
/media/models/sponza/8783994986360286082.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/toji/webgpu-test/148e8fe87465777f4994f7998c7f2822e1b9b50a/media/models/sponza/8783994986360286082.jpg
--------------------------------------------------------------------------------
/media/models/sponza/9288698199695299068.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/toji/webgpu-test/148e8fe87465777f4994f7998c7f2822e1b9b50a/media/models/sponza/9288698199695299068.jpg
--------------------------------------------------------------------------------
/media/models/sponza/9916269861720640319.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/toji/webgpu-test/148e8fe87465777f4994f7998c7f2822e1b9b50a/media/models/sponza/9916269861720640319.jpg
--------------------------------------------------------------------------------
/media/models/sponza/README.md:
--------------------------------------------------------------------------------
1 | # Sponza
2 |
3 | ## Screenshot
4 |
5 | Lights are shown here, they are not part of the model.
6 |
7 | 
8 |
9 | ## Model notes
10 |
11 | Tangents have been computed using MikkTSpace, as the original OBJ model did not have them.
12 | I have manually inspected the normals, and it looks correct to me.
13 | Be aware that W is -1.0 for most of the tangent signs, you will need to handle tangent W for correct results.
14 |
15 |
16 | ## Sources
17 |
18 | ### http://www.crytek.com/cryengine/cryengine3/downloads
19 | - http://www.crytek.com/download/sponza\_obj.rar
20 | - http://www.crytek.com/download/sponza\_textures.rar
21 |
22 | ### http://www.alexandre-pestana.com/pbr-textures-sponza/
23 | - http://www.alexandre-pestana.com/downloads/SponzaPBR\_Textures.rar
24 |
25 | I needed to resize some of the alpha mask textures to the 1024x1024 resolution used by the new texture pack,
26 | and merge in diffuse with alpha.
27 | I also repacked the separate metallic/roughness textures into the glTF layout (G - roughness, B - metallic).
28 | The images are also re-encoded as PNG instead of TGA.
29 | All the materials also had a constant diffuse factor of about 0.58. I assume it was supposed to be there, so I kept it.
30 | I also ran the vertices and indices through a mesh optimizer.
31 |
32 | ## Licensing notes
33 |
34 | Taken from copyright.txt in SponzaPBR\_Textures.rar
35 |
36 | ```
37 | PBR textures for the Sponza model.
38 | For more informations: www.alexandre-pestana.com
39 |
40 |
41 | Original copyright:
42 |
43 | July 14, 2011 Morgan McGuire modified the model from Crytek's OBJ
44 | export to correct some small errors. He computed bump maps from the
45 | normal maps using normal2bump.cpp (since
47 | MTL files expect height bumps, not normals), put the "mask" textures
48 | into the alpha channel of the associated diffuse texture, cleaned up
49 | noise in the masks, created the missing gi_flag.tga texture, and
50 | removed the long untextured banner floating in the middle of the
51 | atrium that appears in the file but in none of the published images of
52 | the model. The banner is in banner.obj.
53 |
54 |
55 |
56 | http://www.crytek.com/cryengine/cryengine3/downloads
57 |
58 |
59 | Sponza Model
60 | August 19, 2010
61 | The Atrium Sponza Palace, Dubrovnik, is an elegant and improved model created by Frank Meinl. The original Sponza model was created by Marko Dabrovic in early 2002. Over the years, the Sponza Atrium scene has become one of the most popular 3D scenes for testing global illumination and radiosity due to it's specific architectural structure which is particularly complex for global illumination light.
62 |
63 | However, nowadays it is considered as a simple model, thus it was decided to crate a new model with highly improved appearance and scene complexity. It is donated to the public for radiosity and is represented in several different formats (3ds, Obj) for use with various commercial 3D applications and renderers.
64 |
65 |
66 | Screenshot from the I3D paper
67 | http://crytek.com/sites/default/files/20100301_lpv.pdf
68 | ```
69 |
--------------------------------------------------------------------------------
/media/models/sponza/Sponza.bin:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/toji/webgpu-test/148e8fe87465777f4994f7998c7f2822e1b9b50a/media/models/sponza/Sponza.bin
--------------------------------------------------------------------------------
/media/models/sponza/white.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/toji/webgpu-test/148e8fe87465777f4994f7998c7f2822e1b9b50a/media/models/sponza/white.png
--------------------------------------------------------------------------------
/media/webgpu-logo.svg:
--------------------------------------------------------------------------------
1 |
36 |
--------------------------------------------------------------------------------
/pipeline-fails/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 | Pipeline failure tests
11 |
12 |
13 | Pipeline failure tests
14 |
18 |
19 |
--------------------------------------------------------------------------------
/pipeline-fails/pipeline-test.js:
--------------------------------------------------------------------------------
1 | const logDiv = document.createElement('div');
2 | logDiv.classList.add('log');
3 | logDiv.innerHTML = 'Log:
';
4 | document.body.appendChild(logDiv);
5 |
6 | function LogMessage(message, style = 'info') {
7 | const messageP = document.createElement('p');
8 | messageP.classList.add(style);
9 | messageP.innerText = message;
10 | logDiv.appendChild(messageP);
11 | }
12 |
13 | function LogInfo(message) {
14 | LogMessage(message, 'info');
15 | }
16 |
17 | function LogWarning(message) {
18 | LogMessage(message, 'warn');
19 | }
20 |
21 | function LogError(message) {
22 | LogMessage(message, 'error');
23 | document.body.style.backgroundColor = 'red';
24 | }
25 |
26 | export async function RunPipelineTest(pipelineType, shaderCode, pipelineDesc) {
27 | try {
28 | const adapter = await navigator.gpu?.requestAdapter();
29 | if (!adapter) {
30 | LogError('Unable to request adapter. WebGPU may not be supported.');
31 | return;
32 | }
33 |
34 | const device = await adapter.requestDevice();
35 | device.lost.then((lost) => {
36 | LogError(`WebGPU device lost (Reason - ${lost.reason}): ${lost.message}`);
37 | });
38 |
39 | const shaderModule = device.createShaderModule({
40 | code: shaderCode,
41 | });
42 | shaderModule.getCompilationInfo().then((info) => {
43 | if (!info.messages.length) { return; }
44 | LogMessage('Shader compilation produced messages: ');
45 | for (message of info.messages) {
46 | LogMessage(`${message.lineNum}:${message.linePos} - ${message.message}`, message.type);
47 | }
48 | });
49 |
50 | const shaderDiv = document.createElement('div');
51 | shaderDiv.classList.add('shader');
52 | shaderDiv.innerHTML = `Shader Code:
${shaderCode}
`;
53 | document.body.appendChild(shaderDiv);
54 |
55 | let pipelinePromise;
56 | switch (pipelineType) {
57 | case 'render':
58 | if (!pipelineDesc) {
59 | pipelineDesc = {
60 | layout: 'auto',
61 | vertex: {},
62 | fragment: {
63 | targets: [{format: 'rgba8unorm'}]
64 | }
65 | };
66 | }
67 |
68 | pipelineDesc.vertex.module = shaderModule;
69 | if (pipelineDesc.fragment) { pipelineDesc.fragment.module = shaderModule; }
70 | pipelinePromise = device.createRenderPipelineAsync(pipelineDesc);
71 | break;
72 |
73 | case 'compute':
74 | if (!pipelineDesc) {
75 | pipelineDesc = {
76 | layout: 'auto',
77 | compute: {},
78 | };
79 | }
80 |
81 | pipelineDesc.compute.module = shaderModule;
82 | pipelinePromise = device.createRenderPipelineAsync(pipelineDesc);
83 | break;
84 |
85 | default: LogError(`Unknown pipeline type "${pipelineType}"`); return;
86 | }
87 |
88 | const pipelineDiv = document.createElement('div');
89 | pipelineDiv.classList.add('shader');
90 | pipelineDiv.innerHTML = `Pipeline Desc:
${JSON.stringify(pipelineDesc, null, 2)}
`;
91 | document.body.appendChild(pipelineDiv);
92 |
93 | pipelinePromise.then((pipeline) => {
94 | LogInfo(`Pipeline creation succeeded!`);
95 | document.body.style.backgroundColor = 'green';
96 | }).catch((error) => {
97 | LogError(`Pipeline creation failed: ${error.message}`);
98 | });
99 | } catch(error) {
100 | LogError(`An error occurred: ${error.message}`);
101 | }
102 | }
--------------------------------------------------------------------------------
/pipeline-fails/simple.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 | PBR
11 |
12 |
13 |
30 |
31 |
--------------------------------------------------------------------------------
/pipeline-fails/spookyball-pbr.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 | Spookyball PBR
11 |
12 |
13 | A reduced version of the main PBR shader from https://spookyball.com
14 |
147 |
148 |
--------------------------------------------------------------------------------
/playcanvas-crash/crash1.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
43 |
44 |
--------------------------------------------------------------------------------
/playcanvas-crash/crash2.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
50 |
51 |
--------------------------------------------------------------------------------
/playcanvas-crash/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 | PlayCanvas Android crashes, reduced shaders
4 |
8 |
9 |
--------------------------------------------------------------------------------
/resize-crash/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 | WebGPU Resize Crash Bug Repro
13 |
14 |
25 |
26 |
27 |
28 |
197 |
198 |
199 |
--------------------------------------------------------------------------------
/resize-crash/query-args.js:
--------------------------------------------------------------------------------
1 | // Copyright 2018 The Immersive Web Community Group
2 | //
3 | // Permission is hereby granted, free of charge, to any person obtaining a copy
4 | // of this software and associated documentation files (the "Software"), to deal
5 | // in the Software without restriction, including without limitation the rights
6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7 | // copies of the Software, and to permit persons to whom the Software is
8 | // furnished to do so, subject to the following conditions:
9 |
10 | // The above copyright notice and this permission notice shall be included in
11 | // all copies or substantial portions of the Software.
12 |
13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
19 | // SOFTWARE.
20 |
21 | /*
22 | Provides a simple way to get values from the query string if they're present
23 | and use a default value if not. Not strictly a "WebGL" utility, but I use it
24 | frequently enough for debugging that I wanted to include it here.
25 |
26 | Example:
27 | For the URL http://example.com/index.html?particleCount=1000
28 |
29 | QueryArgs.getInt("particleCount", 100); // URL overrides, returns 1000
30 | QueryArgs.getInt("particleSize", 10); // Not in URL, returns default of 10
31 | */
32 |
33 | let urlArgs = null;
34 | window.onhashchange = function() {
35 | // Force re-parsing on next access
36 | urlArgs = null;
37 | };
38 |
39 | function ensureArgsCached() {
40 | if (!urlArgs) {
41 | urlArgs = {};
42 | let query = window.location.search.substring(1) || window.location.hash.substring(1);
43 | let vars = query.split('&');
44 | for (let i = 0; i < vars.length; i++) {
45 | let pair = vars[i].split('=');
46 | urlArgs[pair[0].toLowerCase()] = decodeURIComponent(pair[1]);
47 | }
48 | }
49 | }
50 |
51 | export class QueryArgs {
52 | static getString(name, defaultValue) {
53 | ensureArgsCached();
54 | let lcaseName = name.toLowerCase();
55 | if (lcaseName in urlArgs) {
56 | return urlArgs[lcaseName];
57 | }
58 | return defaultValue;
59 | }
60 |
61 | static getInt(name, defaultValue) {
62 | ensureArgsCached();
63 | let lcaseName = name.toLowerCase();
64 | if (lcaseName in urlArgs) {
65 | return parseInt(urlArgs[lcaseName], 10);
66 | }
67 | return defaultValue;
68 | }
69 |
70 | static getFloat(name, defaultValue) {
71 | ensureArgsCached();
72 | let lcaseName = name.toLowerCase();
73 | if (lcaseName in urlArgs) {
74 | return parseFloat(urlArgs[lcaseName]);
75 | }
76 | return defaultValue;
77 | }
78 |
79 | static getBool(name, defaultValue) {
80 | ensureArgsCached();
81 | let lcaseName = name.toLowerCase();
82 | if (lcaseName in urlArgs) {
83 | return parseInt(urlArgs[lcaseName], 10) != 0;
84 | }
85 | return defaultValue;
86 | }
87 | }
88 |
--------------------------------------------------------------------------------
/sample-mask-error/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
70 |
71 |
--------------------------------------------------------------------------------
/skybox-bug/README.md:
--------------------------------------------------------------------------------
1 | This repro renders a "skybox" by drawing a cube at the far plane by returning the transformed W component for both the Z and W components of the output position and doing a 'less-equal' depth compare. (This is a common trick in game dev to ensure the skybox renders behind everything else.)
2 |
3 | On most devices this renders correctly, but on Pixel 6 and Pixel 7 (ARM Mali GPUs) there's a lot of apparent Z fighting.
--------------------------------------------------------------------------------
/skybox-bug/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 | WebGPU Skybox bug
9 |
10 |
31 |
32 |
33 |
34 |
132 |
133 |
--------------------------------------------------------------------------------
/skybox-bug/skybox.js:
--------------------------------------------------------------------------------
1 | const SKYBOX_SHADER = /*wgsl*/`
2 | struct Camera {
3 | projection: mat4x4f,
4 | view: mat4x4f,
5 | };
6 | @group(0) @binding(0) var camera : Camera;
7 |
8 | struct VertexOutput {
9 | @builtin(position) position : vec4f,
10 | @location(0) texcoord : vec3f,
11 | };
12 |
13 | @vertex
14 | fn vertexMain(@location(0) position : vec4f) -> VertexOutput {
15 | var output : VertexOutput;
16 |
17 | var modelView = camera.view;
18 | // Drop the translation portion of the modelView matrix
19 | modelView[3] = vec4(0, 0, 0, modelView[3].w);
20 | output.position = camera.projection * modelView * position;
21 | // Returning the W component for both Z and W forces the geometry depth to
22 | // the far plane. When combined with a depth func of "less-equal" this makes
23 | // the sky write to any depth fragment that has not been written to yet.
24 | output.position = output.position.xyww;
25 | output.texcoord = position.xyz;
26 |
27 | return output;
28 | }
29 |
30 | @group(0) @binding(2) var environmentSampler : sampler;
31 | @group(0) @binding(3) var environmentTexture : texture_cube;
32 |
33 | @fragment
34 | fn fragmentMain(@location(0) texcoord : vec3f) -> @location(0) vec4f {
35 | return vec4f(texcoord + 1 * 0.5, 1);
36 | }
37 | `;
38 |
39 | const SKYBOX_VERTS = new Float32Array([
40 | 1.0, 1.0, 1.0, // 0
41 | -1.0, 1.0, 1.0, // 1
42 | 1.0, -1.0, 1.0, // 2
43 | -1.0, -1.0, 1.0, // 3
44 | 1.0, 1.0, -1.0, // 4
45 | -1.0, 1.0, -1.0, // 5
46 | 1.0, -1.0, -1.0, // 6
47 | -1.0, -1.0, -1.0, // 7
48 | ]);
49 |
50 | const SKYBOX_INDICES = new Uint16Array([
51 | // PosX (Right)
52 | 0, 2, 4,
53 | 6, 4, 2,
54 |
55 | // NegX (Left)
56 | 5, 3, 1,
57 | 3, 5, 7,
58 |
59 | // PosY (Top)
60 | 4, 1, 0,
61 | 1, 4, 5,
62 |
63 | // NegY (Bottom)
64 | 2, 3, 6,
65 | 7, 6, 3,
66 |
67 | // PosZ (Front)
68 | 0, 1, 2,
69 | 3, 2, 1,
70 |
71 | // NegZ (Back)
72 | 6, 5, 4,
73 | 5, 6, 7,
74 | ]);
75 |
76 | export class SkyboxRenderer {
77 | device;
78 | pipeline;
79 |
80 | skyboxVertexBuffer;
81 | skyboxIndexBuffer;
82 |
83 | constructor(device, frameBindGroupLayout, colorFormat, depthFormat) {
84 | this.device = device;
85 |
86 | const shaderModule = device.createShaderModule({
87 | label: 'skybox shader',
88 | code: SKYBOX_SHADER,
89 | });
90 |
91 | // Setup a render pipeline for drawing the skybox
92 | this.pipeline = device.createRenderPipeline({
93 | label: `skybox pipeline`,
94 | layout: device.createPipelineLayout({
95 | bindGroupLayouts: [
96 | frameBindGroupLayout,
97 | ]
98 | }),
99 | vertex: {
100 | module: shaderModule,
101 | entryPoint: 'vertexMain',
102 | buffers: [{
103 | arrayStride: 3 * Float32Array.BYTES_PER_ELEMENT,
104 | attributes: [{
105 | shaderLocation: 0,
106 | format: 'float32x3',
107 | offset: 0,
108 | }]
109 | }]
110 | },
111 | fragment: {
112 | module: shaderModule,
113 | entryPoint: 'fragmentMain',
114 | targets: [{
115 | format: colorFormat,
116 | }],
117 | },
118 | depthStencil: {
119 | depthWriteEnabled: false,
120 | depthCompare: 'less-equal',
121 | format: depthFormat,
122 | }
123 | });
124 |
125 | this.skyboxVertexBuffer = device.createBuffer({
126 | label: 'skybox vertex buffer',
127 | size: SKYBOX_VERTS.byteLength,
128 | usage: GPUBufferUsage.VERTEX | GPUBufferUsage.COPY_DST,
129 | });
130 | device.queue.writeBuffer(this.skyboxVertexBuffer, 0, SKYBOX_VERTS);
131 |
132 | this.skyboxIndexBuffer = device.createBuffer({
133 | label: 'skybox index buffer',
134 | size: SKYBOX_INDICES.byteLength,
135 | usage: GPUBufferUsage.INDEX | GPUBufferUsage.COPY_DST,
136 | });
137 | device.queue.writeBuffer(this.skyboxIndexBuffer, 0, SKYBOX_INDICES);
138 | }
139 |
140 | render(renderPass) {
141 | // Skybox is part of the frame bind group, which should already be bound prior to calling this method.
142 | renderPass.setPipeline(this.pipeline);
143 | renderPass.setVertexBuffer(0, this.skyboxVertexBuffer);
144 | renderPass.setIndexBuffer(this.skyboxIndexBuffer, 'uint16');
145 | renderPass.drawIndexed(SKYBOX_INDICES.length);
146 | }
147 | }
148 |
--------------------------------------------------------------------------------
/thumb.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/toji/webgpu-test/148e8fe87465777f4994f7998c7f2822e1b9b50a/thumb.png
--------------------------------------------------------------------------------
/webgpu-css-logo.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
80 |
81 |
82 | SVG:
83 | 
84 |
85 | CSS-only:
86 |
87 |
88 |
89 |
--------------------------------------------------------------------------------
/webgpu-tilemap/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 | WebGPU Tilemap
11 |
12 |
30 |
31 |
32 |
33 |
86 |
87 |
--------------------------------------------------------------------------------
/webgpu-tilemap/spelunky-tiles.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/toji/webgpu-test/148e8fe87465777f4994f7998c7f2822e1b9b50a/webgpu-tilemap/spelunky-tiles.png
--------------------------------------------------------------------------------
/webgpu-tilemap/spelunky0.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/toji/webgpu-test/148e8fe87465777f4994f7998c7f2822e1b9b50a/webgpu-tilemap/spelunky0.png
--------------------------------------------------------------------------------
/webgpu-tilemap/spelunky1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/toji/webgpu-test/148e8fe87465777f4994f7998c7f2822e1b9b50a/webgpu-tilemap/spelunky1.png
--------------------------------------------------------------------------------