Update readme, example project, fix runtime errors

This commit is contained in:
Szymon Nowakowski 2023-08-15 22:18:08 +02:00
parent 5002138070
commit e26be8ee09
23 changed files with 441 additions and 42 deletions

View File

@ -1,2 +1,9 @@
# oktaeder
3D rendering library for WebGPU
This project uses [pnpm](https://pnpm.io/). If you don't have it, you can get it
using npm by running `npm install -g pnpm`. Then, run `pnpm install` to install
all dependencies.
To run the example, run `pnpm start:example` and visit
[localhost:8000](http://localhost:8000).

11
example/index.html Normal file
View File

@ -0,0 +1,11 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>oktaeder example</title>
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="stylesheet" href="bundle.css">
<script type="module" src="bundle.js"></script>
</head>
<body></body>
</html>

102
example/script.ts Normal file
View File

@ -0,0 +1,102 @@
import { Color, Material, Mesh, Node, PerspectiveCamera, PointLight, Quaternion, Scene, Submesh, Vector3 } from "../src/data/index";
import { Renderer } from "../src/oktaeder";
import "./style.css";
new EventSource("/esbuild").addEventListener("change", () => location.reload());
const canvas = document.createElement("canvas");
window.addEventListener("resize", onResize);
onResize.call(window);
const renderer = await Renderer.init(canvas);
const camera = new PerspectiveCamera({
verticalFovRad: 50 * (Math.PI / 180),
nearPlane: 0.001,
farPlane: Infinity,
});
const vertexBuffer = renderer.createVertexBuffer({ vertexCount: 6 });
vertexBuffer.writeTypedArray(0, {
position: new Float32Array([
-1, 0, 0,
1, 0, 0,
0, -1, 0,
0, 1, 0,
0, 0, -1,
0, 0, 1,
]),
});
const indexBuffer = renderer.createIndexBuffer({ indexCount: 24, indexFormat: "uint16" });
indexBuffer.writeArray(0, [
0, 4, 3,
4, 1, 3,
1, 5, 3,
5, 0, 3,
4, 0, 2,
1, 4, 2,
5, 1, 2,
0, 5, 2,
]);
const submesh: Submesh = { start: 0, length: 24 };
const mesh = new Mesh({ vertexBuffer, indexBuffer, submeshes: [submesh] });
const material = new Material({
baseColor: Color.white(),
roughness: 0.5,
metallic: 0,
});
const node = new Node({ mesh, materials: [material] });
const cameraPitchRad = 15 * (Math.PI / 180);
const scene = new Scene({
nodes: [
node,
new Node({
translation: new Vector3(-1, 1, 0),
light: new PointLight({ color: new Color(1, 0, 0) }),
}),
new Node({
translation: new Vector3(0, 1, -1),
light: new PointLight({ color: new Color(0, 1, 0) }),
}),
new Node({
translation: new Vector3(1, 1, 0),
light: new PointLight({ color: new Color(0, 0, 1) }),
}),
new Node({
translation: new Vector3(0, 1, 1),
light: new PointLight({ color: new Color(1, 1, 0) }),
}),
new Node({
translation: new Vector3(0, 0.8, -3),
rotation: new Quaternion(Math.sin(0.5 * cameraPitchRad), 0, 0, Math.cos(0.5 * cameraPitchRad)),
camera,
}),
],
ambientLight: new Color(0.01, 0.01, 0.01),
});
function onResize(this: Window) {
canvas.width = this.innerWidth;
canvas.height = this.innerHeight;
}
const rotation = Quaternion.identity();
function draw(time: number) {
rotation.y = Math.cos(0.001 * time);
rotation.w = Math.sin(0.001 * time);
node.setRotation(rotation);
renderer.render(scene, camera);
requestAnimationFrame(draw);
}
requestAnimationFrame(draw);
document.body.appendChild(canvas);

10
example/style.css Normal file
View File

@ -0,0 +1,10 @@
html, body {
margin: 0;
padding: 0;
}
html, body, canvas {
width: 100%;
height: 100%;
overflow: hidden;
}

View File

@ -18,6 +18,7 @@
"url": "https://github.com/iszn11/oktaeder.git"
},
"scripts": {
"start:example": "esbuild example/script.ts --bundle --outfile=example/bundle.js --watch --servedir=example --format=esm --sourcemap",
"build": "tsc --build"
},
"dependencies": {
@ -25,6 +26,7 @@
},
"devDependencies": {
"@webgpu/types": "^0.1.34",
"esbuild": "^0.19.2",
"typescript": "5.1.6"
},
"exports": {

View File

@ -13,16 +13,247 @@ devDependencies:
'@webgpu/types':
specifier: ^0.1.34
version: 0.1.34
esbuild:
specifier: ^0.19.2
version: 0.19.2
typescript:
specifier: 5.1.6
version: 5.1.6
packages:
/@esbuild/android-arm64@0.19.2:
resolution: {integrity: sha512-lsB65vAbe90I/Qe10OjkmrdxSX4UJDjosDgb8sZUKcg3oefEuW2OT2Vozz8ef7wrJbMcmhvCC+hciF8jY/uAkw==}
engines: {node: '>=12'}
cpu: [arm64]
os: [android]
requiresBuild: true
dev: true
optional: true
/@esbuild/android-arm@0.19.2:
resolution: {integrity: sha512-tM8yLeYVe7pRyAu9VMi/Q7aunpLwD139EY1S99xbQkT4/q2qa6eA4ige/WJQYdJ8GBL1K33pPFhPfPdJ/WzT8Q==}
engines: {node: '>=12'}
cpu: [arm]
os: [android]
requiresBuild: true
dev: true
optional: true
/@esbuild/android-x64@0.19.2:
resolution: {integrity: sha512-qK/TpmHt2M/Hg82WXHRc/W/2SGo/l1thtDHZWqFq7oi24AjZ4O/CpPSu6ZuYKFkEgmZlFoa7CooAyYmuvnaG8w==}
engines: {node: '>=12'}
cpu: [x64]
os: [android]
requiresBuild: true
dev: true
optional: true
/@esbuild/darwin-arm64@0.19.2:
resolution: {integrity: sha512-Ora8JokrvrzEPEpZO18ZYXkH4asCdc1DLdcVy8TGf5eWtPO1Ie4WroEJzwI52ZGtpODy3+m0a2yEX9l+KUn0tA==}
engines: {node: '>=12'}
cpu: [arm64]
os: [darwin]
requiresBuild: true
dev: true
optional: true
/@esbuild/darwin-x64@0.19.2:
resolution: {integrity: sha512-tP+B5UuIbbFMj2hQaUr6EALlHOIOmlLM2FK7jeFBobPy2ERdohI4Ka6ZFjZ1ZYsrHE/hZimGuU90jusRE0pwDw==}
engines: {node: '>=12'}
cpu: [x64]
os: [darwin]
requiresBuild: true
dev: true
optional: true
/@esbuild/freebsd-arm64@0.19.2:
resolution: {integrity: sha512-YbPY2kc0acfzL1VPVK6EnAlig4f+l8xmq36OZkU0jzBVHcOTyQDhnKQaLzZudNJQyymd9OqQezeaBgkTGdTGeQ==}
engines: {node: '>=12'}
cpu: [arm64]
os: [freebsd]
requiresBuild: true
dev: true
optional: true
/@esbuild/freebsd-x64@0.19.2:
resolution: {integrity: sha512-nSO5uZT2clM6hosjWHAsS15hLrwCvIWx+b2e3lZ3MwbYSaXwvfO528OF+dLjas1g3bZonciivI8qKR/Hm7IWGw==}
engines: {node: '>=12'}
cpu: [x64]
os: [freebsd]
requiresBuild: true
dev: true
optional: true
/@esbuild/linux-arm64@0.19.2:
resolution: {integrity: sha512-ig2P7GeG//zWlU0AggA3pV1h5gdix0MA3wgB+NsnBXViwiGgY77fuN9Wr5uoCrs2YzaYfogXgsWZbm+HGr09xg==}
engines: {node: '>=12'}
cpu: [arm64]
os: [linux]
requiresBuild: true
dev: true
optional: true
/@esbuild/linux-arm@0.19.2:
resolution: {integrity: sha512-Odalh8hICg7SOD7XCj0YLpYCEc+6mkoq63UnExDCiRA2wXEmGlK5JVrW50vZR9Qz4qkvqnHcpH+OFEggO3PgTg==}
engines: {node: '>=12'}
cpu: [arm]
os: [linux]
requiresBuild: true
dev: true
optional: true
/@esbuild/linux-ia32@0.19.2:
resolution: {integrity: sha512-mLfp0ziRPOLSTek0Gd9T5B8AtzKAkoZE70fneiiyPlSnUKKI4lp+mGEnQXcQEHLJAcIYDPSyBvsUbKUG2ri/XQ==}
engines: {node: '>=12'}
cpu: [ia32]
os: [linux]
requiresBuild: true
dev: true
optional: true
/@esbuild/linux-loong64@0.19.2:
resolution: {integrity: sha512-hn28+JNDTxxCpnYjdDYVMNTR3SKavyLlCHHkufHV91fkewpIyQchS1d8wSbmXhs1fiYDpNww8KTFlJ1dHsxeSw==}
engines: {node: '>=12'}
cpu: [loong64]
os: [linux]
requiresBuild: true
dev: true
optional: true
/@esbuild/linux-mips64el@0.19.2:
resolution: {integrity: sha512-KbXaC0Sejt7vD2fEgPoIKb6nxkfYW9OmFUK9XQE4//PvGIxNIfPk1NmlHmMg6f25x57rpmEFrn1OotASYIAaTg==}
engines: {node: '>=12'}
cpu: [mips64el]
os: [linux]
requiresBuild: true
dev: true
optional: true
/@esbuild/linux-ppc64@0.19.2:
resolution: {integrity: sha512-dJ0kE8KTqbiHtA3Fc/zn7lCd7pqVr4JcT0JqOnbj4LLzYnp+7h8Qi4yjfq42ZlHfhOCM42rBh0EwHYLL6LEzcw==}
engines: {node: '>=12'}
cpu: [ppc64]
os: [linux]
requiresBuild: true
dev: true
optional: true
/@esbuild/linux-riscv64@0.19.2:
resolution: {integrity: sha512-7Z/jKNFufZ/bbu4INqqCN6DDlrmOTmdw6D0gH+6Y7auok2r02Ur661qPuXidPOJ+FSgbEeQnnAGgsVynfLuOEw==}
engines: {node: '>=12'}
cpu: [riscv64]
os: [linux]
requiresBuild: true
dev: true
optional: true
/@esbuild/linux-s390x@0.19.2:
resolution: {integrity: sha512-U+RinR6aXXABFCcAY4gSlv4CL1oOVvSSCdseQmGO66H+XyuQGZIUdhG56SZaDJQcLmrSfRmx5XZOWyCJPRqS7g==}
engines: {node: '>=12'}
cpu: [s390x]
os: [linux]
requiresBuild: true
dev: true
optional: true
/@esbuild/linux-x64@0.19.2:
resolution: {integrity: sha512-oxzHTEv6VPm3XXNaHPyUTTte+3wGv7qVQtqaZCrgstI16gCuhNOtBXLEBkBREP57YTd68P0VgDgG73jSD8bwXQ==}
engines: {node: '>=12'}
cpu: [x64]
os: [linux]
requiresBuild: true
dev: true
optional: true
/@esbuild/netbsd-x64@0.19.2:
resolution: {integrity: sha512-WNa5zZk1XpTTwMDompZmvQLHszDDDN7lYjEHCUmAGB83Bgs20EMs7ICD+oKeT6xt4phV4NDdSi/8OfjPbSbZfQ==}
engines: {node: '>=12'}
cpu: [x64]
os: [netbsd]
requiresBuild: true
dev: true
optional: true
/@esbuild/openbsd-x64@0.19.2:
resolution: {integrity: sha512-S6kI1aT3S++Dedb7vxIuUOb3oAxqxk2Rh5rOXOTYnzN8JzW1VzBd+IqPiSpgitu45042SYD3HCoEyhLKQcDFDw==}
engines: {node: '>=12'}
cpu: [x64]
os: [openbsd]
requiresBuild: true
dev: true
optional: true
/@esbuild/sunos-x64@0.19.2:
resolution: {integrity: sha512-VXSSMsmb+Z8LbsQGcBMiM+fYObDNRm8p7tkUDMPG/g4fhFX5DEFmjxIEa3N8Zr96SjsJ1woAhF0DUnS3MF3ARw==}
engines: {node: '>=12'}
cpu: [x64]
os: [sunos]
requiresBuild: true
dev: true
optional: true
/@esbuild/win32-arm64@0.19.2:
resolution: {integrity: sha512-5NayUlSAyb5PQYFAU9x3bHdsqB88RC3aM9lKDAz4X1mo/EchMIT1Q+pSeBXNgkfNmRecLXA0O8xP+x8V+g/LKg==}
engines: {node: '>=12'}
cpu: [arm64]
os: [win32]
requiresBuild: true
dev: true
optional: true
/@esbuild/win32-ia32@0.19.2:
resolution: {integrity: sha512-47gL/ek1v36iN0wL9L4Q2MFdujR0poLZMJwhO2/N3gA89jgHp4MR8DKCmwYtGNksbfJb9JoTtbkoe6sDhg2QTA==}
engines: {node: '>=12'}
cpu: [ia32]
os: [win32]
requiresBuild: true
dev: true
optional: true
/@esbuild/win32-x64@0.19.2:
resolution: {integrity: sha512-tcuhV7ncXBqbt/Ybf0IyrMcwVOAPDckMK9rXNHtF17UTK18OKLpg08glminN06pt2WCoALhXdLfSPbVvK/6fxw==}
engines: {node: '>=12'}
cpu: [x64]
os: [win32]
requiresBuild: true
dev: true
optional: true
/@webgpu/types@0.1.34:
resolution: {integrity: sha512-9mXtH+CC8q+Ku7Z+1XazNIte81FvfdXwR2lLRO7Ykzjd/hh1J1krJa0gtnkF1kvP11psUmKEPKo7iMTeEcUpNA==}
dev: true
/esbuild@0.19.2:
resolution: {integrity: sha512-G6hPax8UbFakEj3hWO0Vs52LQ8k3lnBhxZWomUJDxfz3rZTLqF5k/FCzuNdLx2RbpBiQQF9H9onlDDH1lZsnjg==}
engines: {node: '>=12'}
hasBin: true
requiresBuild: true
optionalDependencies:
'@esbuild/android-arm': 0.19.2
'@esbuild/android-arm64': 0.19.2
'@esbuild/android-x64': 0.19.2
'@esbuild/darwin-arm64': 0.19.2
'@esbuild/darwin-x64': 0.19.2
'@esbuild/freebsd-arm64': 0.19.2
'@esbuild/freebsd-x64': 0.19.2
'@esbuild/linux-arm': 0.19.2
'@esbuild/linux-arm64': 0.19.2
'@esbuild/linux-ia32': 0.19.2
'@esbuild/linux-loong64': 0.19.2
'@esbuild/linux-mips64el': 0.19.2
'@esbuild/linux-ppc64': 0.19.2
'@esbuild/linux-riscv64': 0.19.2
'@esbuild/linux-s390x': 0.19.2
'@esbuild/linux-x64': 0.19.2
'@esbuild/netbsd-x64': 0.19.2
'@esbuild/openbsd-x64': 0.19.2
'@esbuild/sunos-x64': 0.19.2
'@esbuild/win32-arm64': 0.19.2
'@esbuild/win32-ia32': 0.19.2
'@esbuild/win32-x64': 0.19.2
dev: true
/tslib@2.6.1:
resolution: {integrity: sha512-t0hLfiEKfMUoqhG+U1oid7Pva4bbDPHYfJNiB7BiIjRkj1pyC++4N3huJfqY6aRH6VTB0rvtzQwjM4K6qpfOig==}
dev: false

View File

@ -53,18 +53,20 @@ export class _BinaryWriter {
}
ensureUnusedCapacity(desiredUnusedCapacity: number): _BinaryWriter {
return this.ensureCapacity(this._buffer.byteLength + desiredUnusedCapacity);
return this.ensureCapacity(this._length + desiredUnusedCapacity);
}
writeU32(value: number): _BinaryWriter {
this.ensureUnusedCapacity(4);
this._dataView.setUint32(this._length, value, true);
this._length += 4;
return this;
}
writeF32(value: number): _BinaryWriter {
this.ensureUnusedCapacity(4);
this._dataView.setFloat32(this._length, value, true);
this._length += 4;
return this;
}
@ -116,6 +118,19 @@ export class _BinaryWriter {
return this;
}
padToAlign(alignment: number): _BinaryWriter {
const alignedLength = (this._length + alignment - 1) & ~(alignment - 1);
const padding = alignedLength - this._length;
if (padding === 0) {
return this;
}
this.ensureUnusedCapacity(padding);
this._typedArray.fill(0, this._length, alignedLength);
this._length = alignedLength;
return this;
}
alloc(byteLength: number): DataView {
this.ensureUnusedCapacity(byteLength);
const dataView = new DataView(this._buffer, this._length, byteLength);

View File

@ -26,7 +26,7 @@ export interface PerspectiveCameraProps {
export class OrthographicCamera {
readonly type!: "OrthographicCamera";
declare readonly type: "OrthographicCamera";
_name: string;
@ -101,7 +101,7 @@ export class OrthographicCamera {
export class PerspectiveCamera {
readonly type!: "PerspectiveCamera";
declare readonly type: "PerspectiveCamera";
_name: string;

View File

@ -56,7 +56,7 @@ export type ColorTuple = readonly [r: number, g: number, b: number];
export class Color {
readonly type!: "Color";
declare readonly type: "Color";
r: number;
g: number;

View File

@ -22,7 +22,7 @@ export interface PointLightProps {
export class DirectionalLight {
readonly type!: "DirectionalLight";
get type(): "DirectionalLight" {};
_name: string;
@ -81,7 +81,7 @@ export class DirectionalLight {
export class PointLight {
readonly type!: "PointLight";
declare readonly type: "PointLight";
_name: string;

View File

@ -34,7 +34,7 @@ export interface MaterialProps {
export class Material {
readonly type!: "Material";
declare readonly type: "Material";
_name: string;

View File

@ -34,7 +34,7 @@ export type Matrix4x4Tuple = readonly [
export class Matrix4x4 {
readonly type!: "Matrix4x4";
declare readonly type: "Matrix4x4";
ix: number;
iy: number;

View File

@ -23,7 +23,7 @@ export interface NodeProps {
export class Node {
readonly type!: "Node";
declare readonly type: "Node";
_name: string;

View File

@ -15,7 +15,7 @@ export type QuaternionTuple = readonly [x: number, y: number, z: number, w: numb
export class Quaternion {
readonly type!: "Quaternion";
declare readonly type: "Quaternion";
x: number;
y: number;

View File

@ -16,7 +16,7 @@ export interface SceneProps {
export class Scene {
readonly type!: "Scene";
declare readonly type: "Scene";
_name: string;

View File

@ -13,7 +13,7 @@ export type Vector2Tuple = readonly [x: number, y: number];
export class Vector2 {
readonly type!: "Vector2";
declare readonly type: "Vector2";
x: number;
y: number;

View File

@ -14,7 +14,7 @@ export type Vector3Tuple = readonly [x: number, y: number, z: number];
export class Vector3 {
readonly type!: "Vector3";
declare readonly type: "Vector3";
x: number;
y: number;

View File

@ -15,7 +15,7 @@ export type Vector4Tuple = readonly [x: number, y: number, z: number, w: number]
export class Vector4 {
readonly type!: "Vector4";
declare readonly type: "Vector4";
x: number;
y: number;

View File

@ -11,7 +11,7 @@ import { _BinaryWriter as BinaryWriter } from "./_BinaryWriter";
import { _Mapping as Mapping } from "./_Mapping";
import { Camera, Material, Matrix4x4, Node, Scene, Vector3, isDirectionalLight, isPointLight, preOrder } from "./data";
import { IndexBuffer, IndexBufferProps, Texture2D, Texture2DProps, VertexBuffer, VertexBufferProps } from "./resources";
import { ShaderFlagKey, ShaderFlags, createPipeline, shaderFlagsKey } from "./shader";
import { GLOBAL_UNIFORMS_SIZE, MATERIAL_UNIFORMS_SIZE, OBJECT_UNIFORMS_SIZE, ShaderFlagKey, ShaderFlags, _createPipeline, _shaderFlagsKey } from "./shader";
const _matrixOStoWSNormal = new Matrix4x4(
NaN, NaN, NaN, NaN,
@ -117,6 +117,7 @@ export class Renderer {
width: framebufferTexture.width,
height: framebufferTexture.height,
format: "depth",
usage: GPUTextureUsage.RENDER_ATTACHMENT,
});
this._globalBindGroupLayout = device.createBindGroupLayout({
@ -266,7 +267,7 @@ export class Renderer {
this._globalBindGroup = device.createBindGroup({
layout: this._globalBindGroupLayout,
entries: [
{ binding: 0, resource: { buffer: this._uniformBuffer } },
{ binding: 0, resource: { buffer: this._uniformBuffer, size: GLOBAL_UNIFORMS_SIZE } },
{ binding: 1, resource: { buffer: this._pointLightBuffer } },
{ binding: 2, resource: { buffer: this._directionalLightBuffer } },
],
@ -275,7 +276,7 @@ export class Renderer {
this._objectBindGroup = device.createBindGroup({
layout: this._objectBindGroupLayout,
entries: [
{ binding: 0, resource: { buffer: this._uniformBuffer } },
{ binding: 0, resource: { buffer: this._uniformBuffer, size: OBJECT_UNIFORMS_SIZE } },
],
label: "Object",
});
@ -336,14 +337,14 @@ export class Renderer {
}
_getOrCreatePipeline(flags: ShaderFlags): GPURenderPipeline {
const key = shaderFlagsKey(flags);
const key = _shaderFlagsKey(flags);
let pipeline = this._pipelineCache.get(key);
if (pipeline !== undefined) {
return pipeline;
}
pipeline = createPipeline(this, flags);
pipeline = _createPipeline(this, flags);
this._pipelineCache.set(key, pipeline);
return pipeline;
}
@ -401,11 +402,12 @@ export class Renderer {
this._uniformWriter.writeF32(material._normalScale);
this._uniformWriter.writeColorF32(material._emissive);
this._uniformWriter.writeF32(material._ior);
this._uniformWriter.padToAlign(256);
const bindGroup = this._device.createBindGroup({
layout: this._materialBindGroupLayout,
entries: [
{ binding: 0, resource: { buffer: this._uniformBuffer } },
{ binding: 0, resource: { buffer: this._uniformBuffer, size: MATERIAL_UNIFORMS_SIZE } },
{ binding: 1, resource: this._sampler },
{ binding: 2, resource: material._baseColorPartialCoverageTexture?._textureView ?? this._textureWhite._textureView },
{ binding: 3, resource: material._occlusionTexture?._textureView ?? this._textureWhite._textureView },
@ -433,6 +435,7 @@ export class Renderer {
object._updateWorldMatrix();
this._uniformWriter.writeMatrix4x4(object._worldMatrix);
this._uniformWriter.writeMatrix4x4(_matrixOStoWSNormal.setObject(object._worldMatrix).inverseTransposeAffine());
this._uniformWriter.padToAlign(256);
return offset;
});
@ -491,9 +494,7 @@ export class Renderer {
this._uniformWriter.writeColorF32(scene._ambientLight);
this._uniformWriter.writeU32(pointLightCount);
this._uniformWriter.writeU32(directionalLightCount);
this._uniformWriter.writeU32(0);
this._uniformWriter.writeU32(0);
this._uniformWriter.writeU32(0);
this._uniformWriter.padToAlign(256);
// upload uniforms
@ -520,11 +521,18 @@ export class Renderer {
pass.setPipeline(renderPipeline);
/* WORKAROUND
*
* As of writing, Chrome doesn't support passing null as the second
* argument. We could (and should) bind the buffers unconditionally
* for increased safety. For now, we only do this when they are not
* null.
*/
pass.setVertexBuffer(0, vertexBuffer._positionBuffer);
pass.setVertexBuffer(1, vertexBuffer._texCoordBuffer);
pass.setVertexBuffer(2, vertexBuffer._lightTexCoordBuffer);
pass.setVertexBuffer(3, vertexBuffer._normalBuffer);
pass.setVertexBuffer(4, vertexBuffer._tangentBuffer);
if (vertexBuffer._texCoordBuffer !== null) pass.setVertexBuffer(1, vertexBuffer._texCoordBuffer);
if (vertexBuffer._lightTexCoordBuffer !== null) pass.setVertexBuffer(2, vertexBuffer._lightTexCoordBuffer);
if (vertexBuffer._normalBuffer !== null) pass.setVertexBuffer(3, vertexBuffer._normalBuffer);
if (vertexBuffer._tangentBuffer !== null) pass.setVertexBuffer(4, vertexBuffer._tangentBuffer);
pass.setIndexBuffer(indexBuffer._buffer, indexBuffer._indexFormat);
pass.setBindGroup(2, this._objectBindGroup, [objectOffset]);

View File

@ -20,7 +20,7 @@ export interface IndexBufferResizeProps {
export class IndexBuffer {
readonly type!: "IndexBuffer";
declare readonly type: "IndexBuffer";
_renderer: Renderer;
_name: string;

View File

@ -21,12 +21,15 @@ export interface Texture2DProps {
readonly height: number;
readonly format: Texture2DFormat;
readonly usage?: GPUTextureUsageFlags;
}
export interface Texture2DResizeProps {
readonly width?: number;
readonly height?: number;
readonly format?: Texture2DFormat;
readonly usage?: GPUTextureUsageFlags;
}
export interface Texture2DAdvancedWriteProps {
@ -39,7 +42,7 @@ export interface Texture2DAdvancedWriteProps {
export class Texture2D {
readonly type!: "Texture2D";
declare readonly type: "Texture2D";
_renderer: Renderer;
_name: string;
@ -53,6 +56,7 @@ export class Texture2D {
width,
height,
format,
usage = GPUTextureUsage.TEXTURE_BINDING | GPUTextureUsage.COPY_DST,
}: Texture2DProps) {
this._renderer = renderer;
@ -62,7 +66,7 @@ export class Texture2D {
this._renderer = renderer;
this._texture = renderer._device.createTexture({
usage: GPUTextureUsage.TEXTURE_BINDING | GPUTextureUsage.COPY_DST,
usage,
size: { width, height },
format: gpuFormat,
label: name
@ -147,13 +151,14 @@ export class Texture2D {
width = this._texture.width,
height = this._texture.height,
format = this._format,
usage = this._texture.usage,
}: Texture2DResizeProps): Texture2D {
this._texture.destroy();
const gpuFormat = gpuTextureFormat(format);
this._texture = this._renderer._device.createTexture({
usage: GPUTextureUsage.TEXTURE_BINDING | GPUTextureUsage.COPY_DST,
usage,
size: { width, height },
format: gpuFormat,
label: this._name

View File

@ -51,7 +51,7 @@ export interface VertexBufferWriteTypedArrayProps {
export class VertexBuffer {
readonly type!: "VertexBuffer";
declare readonly type: "VertexBuffer";
_renderer: Renderer;
_name: string;

View File

@ -6,6 +6,13 @@
import { Renderer } from "./oktaeder";
// 152 bytes padded to 256
export const GLOBAL_UNIFORMS_SIZE = 256;
// 64 bytes padded to 256
export const MATERIAL_UNIFORMS_SIZE = 256;
// 128 bytes padded to 256
export const OBJECT_UNIFORMS_SIZE = 256;
export type ShaderFlagKey = number;
export interface ShaderFlags {
@ -15,7 +22,7 @@ export interface ShaderFlags {
readonly tangent: boolean;
}
export function shaderFlagsKey({
export function _shaderFlagsKey({
texCoord,
lightTexCoord,
normal,
@ -29,13 +36,13 @@ export function shaderFlagsKey({
return key;
}
export function createPipeline(renderer: Renderer, {
export function _createPipeline(renderer: Renderer, {
texCoord,
lightTexCoord,
normal,
tangent,
}: ShaderFlags): GPURenderPipeline {
const shaderCode = createShaderCode({ texCoord, lightTexCoord, normal, tangent });
const shaderCode = _createShaderCode({ texCoord, lightTexCoord, normal, tangent });
const shaderModule = renderer._device.createShaderModule({
code: shaderCode,
@ -130,7 +137,7 @@ export function createPipeline(renderer: Renderer, {
return pipeline;
}
export function createShaderCode({
export function _createShaderCode({
texCoord,
lightTexCoord,
normal,
@ -147,7 +154,7 @@ struct Vertex {
struct Varyings {
@builtin(position) positionCS: vec4<f32>,
@location(0) positionVS: vec4<f32>,
@location(0) positionVS: vec3<f32>,
${texCoord ? `@location(1) texCoord: vec2<f32>,` : ""}
${lightTexCoord ? `@location(2) lightTexCoord: vec2<f32>,` : ""}
${normal ? `@location(3) normalVS: vec3<f32>,` : ""}
@ -219,7 +226,7 @@ fn visibilityGGX(dotNL: f32, dotNV: f32, alpha: f32) -> f32 {
let vGGX = dotNL * sqrt(dotNV * dotNV * (1.0 - alphaSquared) + alphaSquared);
let lGGX = dotNV * sqrt(dotNL * dotNL * (1.0 - alphaSquared) + alphaSquared);
let GGX = vGGX + lGGX;
return GGX > 0.0 ? 0.5 / GGX : 0.0;
return select(0.0, 0.5 / GGX, GGX > 0.0);
}
fn distributionGGX(dotNH: f32, alpha: f32) -> f32 {
@ -270,7 +277,7 @@ fn screenSpaceMatrixTStoVS(positionVS: vec3<f32>, normalVS: vec3<f32>, texCoord:
let bitangentVS = q1perp * uv0.y + q0perp * uv1.y;
let det = max(dot(tangentVS, tangentVS), dot(bitangentVS, bitangentVS));
let scale = (det == 0.0) ? 0.0 : inserseSqrt(det);
let scale = select(0.0, inverseSqrt(det), det != 0.0);
return mat3x3(tangentVS * scale, bitangentVS * scale, normalVS);
}
@ -297,10 +304,11 @@ fn vert(vertex: Vertex) -> Varyings {
` : ""}
${texCoord ? "output.texCoord = vertex.texCoord;" : ""}
${lightTexCoord ? "output.lightTexCoord = vertex.lightTexCoord;" : ""}
return output;
}
@fragment
fn frag(fragment: Varyings) -> @location(0) vec2<f32> {
fn frag(fragment: Varyings) -> @location(0) vec4<f32> {
var baseColor = _Material.baseColor;
var partialCoverage = _Material.partialCoverage;
var occlusion = 1.0;
@ -357,7 +365,7 @@ fn frag(fragment: Varyings) -> @location(0) vec2<f32> {
var outgoingRadiance = vec3(0.0);
for (var i: u32 = 0; i < _Global.pointLightCount; ++i) {
for (var i: u32 = 0; i < _Global.pointLightCount; i++) {
let light = _PointLights[i];
let lightPositionVS = (_Global.matrixWStoVS * vec4(light.positionWS, 1.0)).xyz;
@ -373,7 +381,7 @@ fn frag(fragment: Varyings) -> @location(0) vec2<f32> {
);
}
for (var i: u32 = 0; i < _Global.directionalLightCount; ++i) {
for (var i: u32 = 0; i < _Global.directionalLightCount; i++) {
let light = _DirectionalLights[i];
let lightDirectionVS = normalize((_Global.matrixWStoVS * vec4(light.directionWS, 0.0)).xyz);