Compare commits
No commits in common. "4bf3f3cd73388cf3270a54dc110a8e63b026f7a1" and "4a72b71a8832bbda8b4c0a12e6dda8306fcb0b55" have entirely different histories.
4bf3f3cd73
...
4a72b71a88
@ -1,11 +1,2 @@
|
|||||||
# oktaeder
|
# oktaeder
|
||||||
3D rendering library for WebGPU
|
3D rendering library for WebGPU
|
||||||
|
|
||||||
[oktaeder.webm](https://github.com/iszn11/oktaeder/assets/7891270/5dbcb03a-608f-41b8-860e-8e9c8e09e242)
|
|
||||||
|
|
||||||
This project ships with [bun.lockb](https://bun.sh/docs/install/lockfile)
|
|
||||||
lockfile for the [Bun](https://bun.sh/) JavaScript runtime. You should be able
|
|
||||||
to install the dependencies with any JavaScript package manager, though.
|
|
||||||
|
|
||||||
To run the example, run `start:example` script with your JavaScript package
|
|
||||||
manager and visit [localhost:8000](http://localhost:8000).
|
|
||||||
|
@ -1,11 +0,0 @@
|
|||||||
<!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>
|
|
@ -1,104 +0,0 @@
|
|||||||
import { Color, DirectionalLight, Mesh, Node, PerspectiveCamera, PointLight, Quaternion, Scene, Submesh, Vector3 } from "../src/data/index";
|
|
||||||
import { Renderer, degToRad } 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: degToRad(50),
|
|
||||||
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 = renderer.createMaterial({
|
|
||||||
baseColor: Color.white(),
|
|
||||||
roughness: 0.5,
|
|
||||||
metallic: 1,
|
|
||||||
});
|
|
||||||
|
|
||||||
const node = new Node({ mesh, materials: [material] });
|
|
||||||
|
|
||||||
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({
|
|
||||||
rotation: Quaternion.fromRotationYZ(degToRad(-90)),
|
|
||||||
light: new DirectionalLight({ color: new Color(0.5, 0.5, 0.5) }),
|
|
||||||
}),
|
|
||||||
new Node({
|
|
||||||
translation: new Vector3(0, 0.8, -3),
|
|
||||||
rotation: Quaternion.fromRotationYZ(degToRad(15)),
|
|
||||||
camera,
|
|
||||||
}),
|
|
||||||
],
|
|
||||||
ambientLight: new Color(0.01, 0.01, 0.01),
|
|
||||||
});
|
|
||||||
|
|
||||||
function onResize(this: Window) {
|
|
||||||
canvas.width = this.innerWidth;
|
|
||||||
canvas.height = this.innerHeight;
|
|
||||||
}
|
|
||||||
|
|
||||||
const _quaternion = Quaternion.identity();
|
|
||||||
|
|
||||||
function draw(timeMs: number) {
|
|
||||||
const time = 0.001 * timeMs;
|
|
||||||
node.setRotation(_quaternion.setRotationZX(-0.5 * time));
|
|
||||||
|
|
||||||
renderer.render(scene, camera);
|
|
||||||
requestAnimationFrame(draw);
|
|
||||||
}
|
|
||||||
|
|
||||||
requestAnimationFrame(draw);
|
|
||||||
|
|
||||||
document.body.appendChild(canvas);
|
|
@ -1,10 +0,0 @@
|
|||||||
html, body {
|
|
||||||
margin: 0;
|
|
||||||
padding: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
html, body, canvas {
|
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
overflow: hidden;
|
|
||||||
}
|
|
@ -18,7 +18,6 @@
|
|||||||
"url": "https://github.com/iszn11/oktaeder.git"
|
"url": "https://github.com/iszn11/oktaeder.git"
|
||||||
},
|
},
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"start:example": "esbuild example/script.ts --bundle --outfile=example/bundle.js --watch --servedir=example --format=esm --sourcemap",
|
|
||||||
"build": "tsc --build"
|
"build": "tsc --build"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
@ -26,7 +25,6 @@
|
|||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@webgpu/types": "^0.1.34",
|
"@webgpu/types": "^0.1.34",
|
||||||
"esbuild": "^0.19.2",
|
|
||||||
"typescript": "5.1.6"
|
"typescript": "5.1.6"
|
||||||
},
|
},
|
||||||
"exports": {
|
"exports": {
|
||||||
|
34
pnpm-lock.yaml
generated
Normal file
34
pnpm-lock.yaml
generated
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
lockfileVersion: '6.0'
|
||||||
|
|
||||||
|
settings:
|
||||||
|
autoInstallPeers: true
|
||||||
|
excludeLinksFromLockfile: false
|
||||||
|
|
||||||
|
dependencies:
|
||||||
|
tslib:
|
||||||
|
specifier: ^2.6.1
|
||||||
|
version: 2.6.1
|
||||||
|
|
||||||
|
devDependencies:
|
||||||
|
'@webgpu/types':
|
||||||
|
specifier: ^0.1.34
|
||||||
|
version: 0.1.34
|
||||||
|
typescript:
|
||||||
|
specifier: 5.1.6
|
||||||
|
version: 5.1.6
|
||||||
|
|
||||||
|
packages:
|
||||||
|
|
||||||
|
/@webgpu/types@0.1.34:
|
||||||
|
resolution: {integrity: sha512-9mXtH+CC8q+Ku7Z+1XazNIte81FvfdXwR2lLRO7Ykzjd/hh1J1krJa0gtnkF1kvP11psUmKEPKo7iMTeEcUpNA==}
|
||||||
|
dev: true
|
||||||
|
|
||||||
|
/tslib@2.6.1:
|
||||||
|
resolution: {integrity: sha512-t0hLfiEKfMUoqhG+U1oid7Pva4bbDPHYfJNiB7BiIjRkj1pyC++4N3huJfqY6aRH6VTB0rvtzQwjM4K6qpfOig==}
|
||||||
|
dev: false
|
||||||
|
|
||||||
|
/typescript@5.1.6:
|
||||||
|
resolution: {integrity: sha512-zaWCozRZ6DLEWAWFrVDz1H6FVXzUSfTy5FUMWsQlU8Ym5JP9eO4xkTIROFCQvhQf61z6O/G6ugw3SgAnvvm+HA==}
|
||||||
|
engines: {node: '>=14.17'}
|
||||||
|
hasBin: true
|
||||||
|
dev: true
|
@ -53,20 +53,18 @@ export class _BinaryWriter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
ensureUnusedCapacity(desiredUnusedCapacity: number): _BinaryWriter {
|
ensureUnusedCapacity(desiredUnusedCapacity: number): _BinaryWriter {
|
||||||
return this.ensureCapacity(this._length + desiredUnusedCapacity);
|
return this.ensureCapacity(this._buffer.byteLength + desiredUnusedCapacity);
|
||||||
}
|
}
|
||||||
|
|
||||||
writeU32(value: number): _BinaryWriter {
|
writeU32(value: number): _BinaryWriter {
|
||||||
this.ensureUnusedCapacity(4);
|
this.ensureUnusedCapacity(4);
|
||||||
this._dataView.setUint32(this._length, value, true);
|
this._dataView.setUint32(this._length, value, true);
|
||||||
this._length += 4;
|
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
writeF32(value: number): _BinaryWriter {
|
writeF32(value: number): _BinaryWriter {
|
||||||
this.ensureUnusedCapacity(4);
|
this.ensureUnusedCapacity(4);
|
||||||
this._dataView.setFloat32(this._length, value, true);
|
this._dataView.setFloat32(this._length, value, true);
|
||||||
this._length += 4;
|
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -118,19 +116,6 @@ export class _BinaryWriter {
|
|||||||
return this;
|
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 {
|
alloc(byteLength: number): DataView {
|
||||||
this.ensureUnusedCapacity(byteLength);
|
this.ensureUnusedCapacity(byteLength);
|
||||||
const dataView = new DataView(this._buffer, this._length, byteLength);
|
const dataView = new DataView(this._buffer, this._length, byteLength);
|
||||||
|
@ -11,7 +11,7 @@ export type Camera = OrthographicCamera | PerspectiveCamera;
|
|||||||
export interface OrthographicCameraProps {
|
export interface OrthographicCameraProps {
|
||||||
readonly name?: string;
|
readonly name?: string;
|
||||||
|
|
||||||
readonly halfVerticalSize: number;
|
readonly verticalSize: number;
|
||||||
readonly nearPlane: number;
|
readonly nearPlane: number;
|
||||||
readonly farPlane: number;
|
readonly farPlane: number;
|
||||||
}
|
}
|
||||||
@ -26,7 +26,7 @@ export interface PerspectiveCameraProps {
|
|||||||
|
|
||||||
export class OrthographicCamera {
|
export class OrthographicCamera {
|
||||||
|
|
||||||
declare readonly type: "OrthographicCamera";
|
readonly type!: "OrthographicCamera";
|
||||||
|
|
||||||
_name: string;
|
_name: string;
|
||||||
|
|
||||||
@ -39,13 +39,13 @@ export class OrthographicCamera {
|
|||||||
|
|
||||||
constructor({
|
constructor({
|
||||||
name = "",
|
name = "",
|
||||||
halfVerticalSize,
|
verticalSize,
|
||||||
nearPlane,
|
nearPlane,
|
||||||
farPlane,
|
farPlane,
|
||||||
}: OrthographicCameraProps) {
|
}: OrthographicCameraProps) {
|
||||||
this._name = name;
|
this._name = name;
|
||||||
|
|
||||||
this._halfVerticalSize = halfVerticalSize;
|
this._halfVerticalSize = verticalSize;
|
||||||
this._nearPlane = nearPlane;
|
this._nearPlane = nearPlane;
|
||||||
this._farPlane = farPlane;
|
this._farPlane = farPlane;
|
||||||
|
|
||||||
@ -101,7 +101,7 @@ export class OrthographicCamera {
|
|||||||
|
|
||||||
export class PerspectiveCamera {
|
export class PerspectiveCamera {
|
||||||
|
|
||||||
declare readonly type: "PerspectiveCamera";
|
readonly type!: "PerspectiveCamera";
|
||||||
|
|
||||||
_name: string;
|
_name: string;
|
||||||
|
|
||||||
|
@ -56,7 +56,7 @@ export type ColorTuple = readonly [r: number, g: number, b: number];
|
|||||||
|
|
||||||
export class Color {
|
export class Color {
|
||||||
|
|
||||||
declare readonly type: "Color";
|
readonly type!: "Color";
|
||||||
|
|
||||||
r: number;
|
r: number;
|
||||||
g: number;
|
g: number;
|
||||||
|
@ -22,7 +22,7 @@ export interface PointLightProps {
|
|||||||
|
|
||||||
export class DirectionalLight {
|
export class DirectionalLight {
|
||||||
|
|
||||||
declare readonly type: "DirectionalLight";
|
readonly type!: "DirectionalLight";
|
||||||
|
|
||||||
_name: string;
|
_name: string;
|
||||||
|
|
||||||
@ -81,7 +81,7 @@ export class DirectionalLight {
|
|||||||
|
|
||||||
export class PointLight {
|
export class PointLight {
|
||||||
|
|
||||||
declare readonly type: "PointLight";
|
readonly type!: "PointLight";
|
||||||
|
|
||||||
_name: string;
|
_name: string;
|
||||||
|
|
||||||
|
@ -6,11 +6,35 @@
|
|||||||
|
|
||||||
import { Color, ColorObject } from ".";
|
import { Color, ColorObject } from ".";
|
||||||
import { Texture2D } from "../resources";
|
import { Texture2D } from "../resources";
|
||||||
import { MaterialProps } from "./MaterialProps";
|
|
||||||
|
|
||||||
export class DynamicMaterial {
|
export interface MaterialProps {
|
||||||
|
name?: string;
|
||||||
|
|
||||||
declare readonly type: "DynamicMaterial";
|
baseColor?: ColorObject;
|
||||||
|
partialCoverage?: number;
|
||||||
|
transmission?: ColorObject;
|
||||||
|
collimation?: number;
|
||||||
|
occlusionTextureStrength?: number;
|
||||||
|
roughness?: number;
|
||||||
|
metallic?: number;
|
||||||
|
normalScale?: number;
|
||||||
|
emissive?: ColorObject;
|
||||||
|
ior?: number;
|
||||||
|
|
||||||
|
baseColorPartialCoverageTexture?: Texture2D | null;
|
||||||
|
occlusionTexture?: Texture2D | null;
|
||||||
|
roughnessMetallicTexture?: Texture2D | null;
|
||||||
|
normalTexture?: Texture2D | null;
|
||||||
|
emissiveTexture?: Texture2D | null;
|
||||||
|
transmissionCollimationTexture?: Texture2D | null;
|
||||||
|
|
||||||
|
transparent?: boolean;
|
||||||
|
doubleSided?: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
export class Material {
|
||||||
|
|
||||||
|
readonly type!: "Material";
|
||||||
|
|
||||||
_name: string;
|
_name: string;
|
||||||
|
|
||||||
@ -83,7 +107,7 @@ export class DynamicMaterial {
|
|||||||
set name(value: string) { this._name = value; }
|
set name(value: string) { this._name = value; }
|
||||||
get name(): string { return this._name; }
|
get name(): string { return this._name; }
|
||||||
|
|
||||||
setBaseColor(value: ColorObject): DynamicMaterial {
|
setBaseColor(value: ColorObject): Material {
|
||||||
this._baseColor.setObject(value);
|
this._baseColor.setObject(value);
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
@ -106,7 +130,7 @@ export class DynamicMaterial {
|
|||||||
set normalScale(value: number) { this._normalScale = value; }
|
set normalScale(value: number) { this._normalScale = value; }
|
||||||
get normalScale(): number { return this._normalScale; }
|
get normalScale(): number { return this._normalScale; }
|
||||||
|
|
||||||
setEmissive(value: ColorObject): DynamicMaterial {
|
setEmissive(value: ColorObject): Material {
|
||||||
this._emissive.setObject(value);
|
this._emissive.setObject(value);
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
@ -114,7 +138,7 @@ export class DynamicMaterial {
|
|||||||
return res.setObject(this._emissive);
|
return res.setObject(this._emissive);
|
||||||
}
|
}
|
||||||
|
|
||||||
setTransmission(value: ColorObject): DynamicMaterial {
|
setTransmission(value: ColorObject): Material {
|
||||||
this._transmission.setObject(value);
|
this._transmission.setObject(value);
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
@ -128,22 +152,22 @@ export class DynamicMaterial {
|
|||||||
set ior(value: number) { this._ior = value; }
|
set ior(value: number) { this._ior = value; }
|
||||||
get ior(): number { return this._ior; }
|
get ior(): number { return this._ior; }
|
||||||
|
|
||||||
set baseColorPartialCoverageTexture(value: Texture2D | null) { this._baseColorPartialCoverageTexture = value; }
|
set baseColorPartialCoverageTexture(value: Texture2D | null) { this._baseColorPartialCoverageTexture = value;}
|
||||||
get baseColorPartialCoverageTexture(): Texture2D | null { return this._baseColorPartialCoverageTexture; }
|
get baseColorPartialCoverageTexture(): Texture2D | null { return this._baseColorPartialCoverageTexture; }
|
||||||
|
|
||||||
set occlusionTexture(value: Texture2D | null) { this._occlusionTexture = value; }
|
set occlusionTexture(value: Texture2D | null) { this._occlusionTexture = value;}
|
||||||
get occlusionTexture(): Texture2D | null { return this._occlusionTexture; }
|
get occlusionTexture(): Texture2D | null { return this._occlusionTexture; }
|
||||||
|
|
||||||
set roughnessMetallicTexture(value: Texture2D | null) { this._roughnessMetallicTexture = value; }
|
set roughnessMetallicTexture(value: Texture2D | null) { this._roughnessMetallicTexture = value;}
|
||||||
get roughnessMetallicTexture(): Texture2D | null { return this._roughnessMetallicTexture; }
|
get roughnessMetallicTexture(): Texture2D | null { return this._roughnessMetallicTexture; }
|
||||||
|
|
||||||
set normalTexture(value: Texture2D | null) { this._normalTexture = value; }
|
set normalTexture(value: Texture2D | null) { this._normalTexture = value;}
|
||||||
get normalTexture(): Texture2D | null { return this._normalTexture; }
|
get normalTexture(): Texture2D | null { return this._normalTexture; }
|
||||||
|
|
||||||
set emissiveTexture(value: Texture2D | null) { this._emissiveTexture = value; }
|
set emissiveTexture(value: Texture2D | null) { this._emissiveTexture = value;}
|
||||||
get emissiveTexture(): Texture2D | null { return this._emissiveTexture; }
|
get emissiveTexture(): Texture2D | null { return this._emissiveTexture; }
|
||||||
|
|
||||||
set transmissionCollimationTexture(value: Texture2D | null) { this._transmissionCollimationTexture = value; }
|
set transmissionCollimationTexture(value: Texture2D | null) { this._transmissionCollimationTexture = value;}
|
||||||
get transmissionCollimationTexture(): Texture2D | null { return this._transmissionCollimationTexture; }
|
get transmissionCollimationTexture(): Texture2D | null { return this._transmissionCollimationTexture; }
|
||||||
|
|
||||||
set transparent(value: boolean) { this._transparent = value; }
|
set transparent(value: boolean) { this._transparent = value; }
|
||||||
@ -153,8 +177,8 @@ export class DynamicMaterial {
|
|||||||
get doubleSided(): boolean { return this._doubleSided; }
|
get doubleSided(): boolean { return this._doubleSided; }
|
||||||
}
|
}
|
||||||
|
|
||||||
Object.defineProperty(DynamicMaterial.prototype, "type", { value: "DynamicMaterial" });
|
Object.defineProperty(Material.prototype, "type", { value: "Material" });
|
||||||
|
|
||||||
export function isDynamicMaterial(value: unknown): value is DynamicMaterial {
|
export function isMaterial(value: unknown): value is Material {
|
||||||
return Boolean(value) && (value as DynamicMaterial).type === "DynamicMaterial";
|
return Boolean(value) && (value as Material).type === "Material";
|
||||||
}
|
}
|
@ -1,33 +0,0 @@
|
|||||||
/*!
|
|
||||||
* This Source Code Form is subject to the terms of the Mozilla Public License,
|
|
||||||
* v. 2.0. If a copy of the MPL was not distributed with this file, You can
|
|
||||||
* obtain one at http://mozilla.org/MPL/2.0/.
|
|
||||||
*/
|
|
||||||
|
|
||||||
import { ColorObject } from ".";
|
|
||||||
import { Texture2D } from "../resources";
|
|
||||||
|
|
||||||
export interface MaterialProps {
|
|
||||||
name?: string;
|
|
||||||
|
|
||||||
baseColor?: ColorObject;
|
|
||||||
partialCoverage?: number;
|
|
||||||
transmission?: ColorObject;
|
|
||||||
collimation?: number;
|
|
||||||
occlusionTextureStrength?: number;
|
|
||||||
roughness?: number;
|
|
||||||
metallic?: number;
|
|
||||||
normalScale?: number;
|
|
||||||
emissive?: ColorObject;
|
|
||||||
ior?: number;
|
|
||||||
|
|
||||||
baseColorPartialCoverageTexture?: Texture2D | null;
|
|
||||||
occlusionTexture?: Texture2D | null;
|
|
||||||
roughnessMetallicTexture?: Texture2D | null;
|
|
||||||
normalTexture?: Texture2D | null;
|
|
||||||
emissiveTexture?: Texture2D | null;
|
|
||||||
transmissionCollimationTexture?: Texture2D | null;
|
|
||||||
|
|
||||||
transparent?: boolean;
|
|
||||||
doubleSided?: boolean;
|
|
||||||
}
|
|
@ -34,7 +34,7 @@ export type Matrix4x4Tuple = readonly [
|
|||||||
|
|
||||||
export class Matrix4x4 {
|
export class Matrix4x4 {
|
||||||
|
|
||||||
declare readonly type: "Matrix4x4";
|
readonly type!: "Matrix4x4";
|
||||||
|
|
||||||
ix: number;
|
ix: number;
|
||||||
iy: number;
|
iy: number;
|
||||||
|
@ -4,8 +4,7 @@
|
|||||||
* obtain one at http://mozilla.org/MPL/2.0/.
|
* obtain one at http://mozilla.org/MPL/2.0/.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { Camera, DynamicMaterial, Light, Matrix4x4, Mesh, Quaternion, QuaternionObject, Vector3, Vector3Object } from ".";
|
import { Camera, Light, Material, Matrix4x4, Mesh, Quaternion, QuaternionObject, Vector3, Vector3Object } from ".";
|
||||||
import { Material } from "../resources";
|
|
||||||
|
|
||||||
export interface NodeProps {
|
export interface NodeProps {
|
||||||
readonly name?: string;
|
readonly name?: string;
|
||||||
@ -17,14 +16,14 @@ export interface NodeProps {
|
|||||||
readonly camera?: Camera | null;
|
readonly camera?: Camera | null;
|
||||||
readonly light?: Light | null;
|
readonly light?: Light | null;
|
||||||
readonly mesh?: Mesh | null;
|
readonly mesh?: Mesh | null;
|
||||||
readonly materials?: (Material | DynamicMaterial)[];
|
readonly materials?: Material[];
|
||||||
|
|
||||||
readonly children?: Node[];
|
readonly children?: Node[];
|
||||||
}
|
}
|
||||||
|
|
||||||
export class Node {
|
export class Node {
|
||||||
|
|
||||||
declare readonly type: "Node";
|
readonly type!: "Node";
|
||||||
|
|
||||||
_name: string;
|
_name: string;
|
||||||
|
|
||||||
@ -39,7 +38,7 @@ export class Node {
|
|||||||
/** shared */
|
/** shared */
|
||||||
_mesh: Mesh | null;
|
_mesh: Mesh | null;
|
||||||
/** shared */
|
/** shared */
|
||||||
_materials: (Material | DynamicMaterial)[];
|
_materials: Material[];
|
||||||
|
|
||||||
/** unique */
|
/** unique */
|
||||||
_children: Node[];
|
_children: Node[];
|
||||||
@ -219,13 +218,13 @@ export class Node {
|
|||||||
set mesh(value: Mesh | null) { this._mesh = value; }
|
set mesh(value: Mesh | null) { this._mesh = value; }
|
||||||
get mesh(): Mesh | null { return this._mesh; }
|
get mesh(): Mesh | null { return this._mesh; }
|
||||||
|
|
||||||
setMaterials(value: readonly (Material | DynamicMaterial)[]): Node {
|
setMaterials(value: readonly Material[]): Node {
|
||||||
this._materials.length = 0;
|
this._materials.length = 0;
|
||||||
this._materials.push(...value);
|
this._materials.push(...value);
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
getMaterials(res: (Material | DynamicMaterial)[]): (Material | DynamicMaterial)[] {
|
getMaterials(res: Material[]): Material[] {
|
||||||
res.length = 0;
|
res.length = 0;
|
||||||
res.push(...this._materials);
|
res.push(...this._materials);
|
||||||
return res;
|
return res;
|
||||||
|
@ -15,7 +15,7 @@ export type QuaternionTuple = readonly [x: number, y: number, z: number, w: numb
|
|||||||
|
|
||||||
export class Quaternion {
|
export class Quaternion {
|
||||||
|
|
||||||
declare readonly type: "Quaternion";
|
readonly type!: "Quaternion";
|
||||||
|
|
||||||
x: number;
|
x: number;
|
||||||
y: number;
|
y: number;
|
||||||
@ -41,27 +41,6 @@ export class Quaternion {
|
|||||||
return new Quaternion(0, 0, 0, 1);
|
return new Quaternion(0, 0, 0, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
static fromRotationXY(angleRad: number): Quaternion {
|
|
||||||
const halfAngleRad = 0.5 * angleRad;
|
|
||||||
const c = Math.cos(halfAngleRad);
|
|
||||||
const s = Math.sin(halfAngleRad);
|
|
||||||
return new Quaternion(0, 0, s, c);
|
|
||||||
}
|
|
||||||
|
|
||||||
static fromRotationYZ(angleRad: number): Quaternion {
|
|
||||||
const halfAngleRad = 0.5 * angleRad;
|
|
||||||
const c = Math.cos(halfAngleRad);
|
|
||||||
const s = Math.sin(halfAngleRad);
|
|
||||||
return new Quaternion(s, 0, 0, c);
|
|
||||||
}
|
|
||||||
|
|
||||||
static fromRotationZX(angleRad: number): Quaternion {
|
|
||||||
const halfAngleRad = 0.5 * angleRad;
|
|
||||||
const c = Math.cos(halfAngleRad);
|
|
||||||
const s = Math.sin(halfAngleRad);
|
|
||||||
return new Quaternion(0, s, 0, c);
|
|
||||||
}
|
|
||||||
|
|
||||||
setObject(object: QuaternionObject): Quaternion {
|
setObject(object: QuaternionObject): Quaternion {
|
||||||
this.x = object.x;
|
this.x = object.x;
|
||||||
this.y = object.y;
|
this.y = object.y;
|
||||||
@ -85,33 +64,6 @@ export class Quaternion {
|
|||||||
this.w = 1;
|
this.w = 1;
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
setRotationXY(angleRad: number): Quaternion {
|
|
||||||
const halfAngleRad = 0.5 * angleRad;
|
|
||||||
this.x = 0;
|
|
||||||
this.y = 0;
|
|
||||||
this.z = Math.sin(halfAngleRad);
|
|
||||||
this.w = Math.cos(halfAngleRad);
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
setRotationYZ(angleRad: number): Quaternion {
|
|
||||||
const halfAngleRad = 0.5 * angleRad;
|
|
||||||
this.x = Math.sin(halfAngleRad);
|
|
||||||
this.y = 0;
|
|
||||||
this.z = 0;
|
|
||||||
this.w = Math.cos(halfAngleRad);
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
setRotationZX(angleRad: number): Quaternion {
|
|
||||||
const halfAngleRad = 0.5 * angleRad;
|
|
||||||
this.x = 0;
|
|
||||||
this.y = Math.sin(halfAngleRad);
|
|
||||||
this.z = 0;
|
|
||||||
this.w = Math.cos(halfAngleRad);
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Object.defineProperty(Quaternion.prototype, "type", { value: "Quaternion" });
|
Object.defineProperty(Quaternion.prototype, "type", { value: "Quaternion" });
|
||||||
|
@ -16,7 +16,7 @@ export interface SceneProps {
|
|||||||
|
|
||||||
export class Scene {
|
export class Scene {
|
||||||
|
|
||||||
declare readonly type: "Scene";
|
readonly type!: "Scene";
|
||||||
|
|
||||||
_name: string;
|
_name: string;
|
||||||
|
|
||||||
|
@ -13,7 +13,7 @@ export type Vector2Tuple = readonly [x: number, y: number];
|
|||||||
|
|
||||||
export class Vector2 {
|
export class Vector2 {
|
||||||
|
|
||||||
declare readonly type: "Vector2";
|
readonly type!: "Vector2";
|
||||||
|
|
||||||
x: number;
|
x: number;
|
||||||
y: number;
|
y: number;
|
||||||
|
@ -14,7 +14,7 @@ export type Vector3Tuple = readonly [x: number, y: number, z: number];
|
|||||||
|
|
||||||
export class Vector3 {
|
export class Vector3 {
|
||||||
|
|
||||||
declare readonly type: "Vector3";
|
readonly type!: "Vector3";
|
||||||
|
|
||||||
x: number;
|
x: number;
|
||||||
y: number;
|
y: number;
|
||||||
|
@ -15,7 +15,7 @@ export type Vector4Tuple = readonly [x: number, y: number, z: number, w: number]
|
|||||||
|
|
||||||
export class Vector4 {
|
export class Vector4 {
|
||||||
|
|
||||||
declare readonly type: "Vector4";
|
readonly type!: "Vector4";
|
||||||
|
|
||||||
x: number;
|
x: number;
|
||||||
y: number;
|
y: number;
|
||||||
|
@ -6,9 +6,8 @@
|
|||||||
|
|
||||||
export * from "./Camera";
|
export * from "./Camera";
|
||||||
export * from "./Color";
|
export * from "./Color";
|
||||||
export * from "./DynamicMaterial";
|
|
||||||
export * from "./Light";
|
export * from "./Light";
|
||||||
export * from "./MaterialProps";
|
export * from "./Material";
|
||||||
export * from "./Matrix4x4";
|
export * from "./Matrix4x4";
|
||||||
export * from "./Mesh";
|
export * from "./Mesh";
|
||||||
export * from "./Node";
|
export * from "./Node";
|
||||||
|
@ -1,13 +0,0 @@
|
|||||||
/*!
|
|
||||||
* This Source Code Form is subject to the terms of the Mozilla Public License,
|
|
||||||
* v. 2.0. If a copy of the MPL was not distributed with this file, You can
|
|
||||||
* obtain one at http://mozilla.org/MPL/2.0/.
|
|
||||||
*/
|
|
||||||
|
|
||||||
export function degToRad(angleDeg: number): number {
|
|
||||||
return angleDeg * Math.PI / 180;
|
|
||||||
}
|
|
||||||
|
|
||||||
export function radToDeg(angleRad: number): number {
|
|
||||||
return angleRad * 180 / Math.PI;
|
|
||||||
}
|
|
1170
src/gltf.ts
1170
src/gltf.ts
File diff suppressed because it is too large
Load Diff
@ -5,14 +5,13 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
export * from "./_BinaryWriter";
|
export * from "./_BinaryWriter";
|
||||||
export * from "./geometry";
|
|
||||||
export * from "./shader";
|
export * from "./shader";
|
||||||
|
|
||||||
import { _BinaryWriter as BinaryWriter } from "./_BinaryWriter";
|
import { _BinaryWriter as BinaryWriter } from "./_BinaryWriter";
|
||||||
import { _Mapping as Mapping } from "./_Mapping";
|
import { _Mapping as Mapping } from "./_Mapping";
|
||||||
import { Camera, DynamicMaterial, MaterialProps, Matrix4x4, Node, Scene, Vector3, isDirectionalLight, isDynamicMaterial, isPointLight, preOrder } from "./data";
|
import { Camera, Material, Matrix4x4, Node, Scene, Vector3, isDirectionalLight, isPointLight, preOrder } from "./data";
|
||||||
import { IndexBuffer, IndexBufferProps, Material, Texture2D, Texture2DProps, VertexBuffer, VertexBufferProps, isMaterial } from "./resources";
|
import { IndexBuffer, IndexBufferProps, Texture2D, Texture2DProps, VertexBuffer, VertexBufferProps } from "./resources";
|
||||||
import { GLOBAL_UNIFORMS_SIZE, MATERIAL_UNIFORMS_SIZE, OBJECT_UNIFORMS_SIZE, ShaderFlagKey, ShaderFlags, _createPipeline, _shaderFlagsKey } from "./shader";
|
import { ShaderFlagKey, ShaderFlags, createPipeline, shaderFlagsKey } from "./shader";
|
||||||
|
|
||||||
const _matrixOStoWSNormal = new Matrix4x4(
|
const _matrixOStoWSNormal = new Matrix4x4(
|
||||||
NaN, NaN, NaN, NaN,
|
NaN, NaN, NaN, NaN,
|
||||||
@ -118,7 +117,6 @@ export class Renderer {
|
|||||||
width: framebufferTexture.width,
|
width: framebufferTexture.width,
|
||||||
height: framebufferTexture.height,
|
height: framebufferTexture.height,
|
||||||
format: "depth",
|
format: "depth",
|
||||||
usage: GPUTextureUsage.RENDER_ATTACHMENT,
|
|
||||||
});
|
});
|
||||||
|
|
||||||
this._globalBindGroupLayout = device.createBindGroupLayout({
|
this._globalBindGroupLayout = device.createBindGroupLayout({
|
||||||
@ -268,7 +266,7 @@ export class Renderer {
|
|||||||
this._globalBindGroup = device.createBindGroup({
|
this._globalBindGroup = device.createBindGroup({
|
||||||
layout: this._globalBindGroupLayout,
|
layout: this._globalBindGroupLayout,
|
||||||
entries: [
|
entries: [
|
||||||
{ binding: 0, resource: { buffer: this._uniformBuffer, size: GLOBAL_UNIFORMS_SIZE } },
|
{ binding: 0, resource: { buffer: this._uniformBuffer } },
|
||||||
{ binding: 1, resource: { buffer: this._pointLightBuffer } },
|
{ binding: 1, resource: { buffer: this._pointLightBuffer } },
|
||||||
{ binding: 2, resource: { buffer: this._directionalLightBuffer } },
|
{ binding: 2, resource: { buffer: this._directionalLightBuffer } },
|
||||||
],
|
],
|
||||||
@ -277,7 +275,7 @@ export class Renderer {
|
|||||||
this._objectBindGroup = device.createBindGroup({
|
this._objectBindGroup = device.createBindGroup({
|
||||||
layout: this._objectBindGroupLayout,
|
layout: this._objectBindGroupLayout,
|
||||||
entries: [
|
entries: [
|
||||||
{ binding: 0, resource: { buffer: this._uniformBuffer, size: OBJECT_UNIFORMS_SIZE } },
|
{ binding: 0, resource: { buffer: this._uniformBuffer } },
|
||||||
],
|
],
|
||||||
label: "Object",
|
label: "Object",
|
||||||
});
|
});
|
||||||
@ -329,10 +327,6 @@ export class Renderer {
|
|||||||
return new IndexBuffer(this, props);
|
return new IndexBuffer(this, props);
|
||||||
}
|
}
|
||||||
|
|
||||||
createMaterial(props: MaterialProps): Material {
|
|
||||||
return new Material(this, props);
|
|
||||||
}
|
|
||||||
|
|
||||||
createTexture(props: Texture2DProps): Texture2D {
|
createTexture(props: Texture2DProps): Texture2D {
|
||||||
return new Texture2D(this, props);
|
return new Texture2D(this, props);
|
||||||
}
|
}
|
||||||
@ -342,14 +336,14 @@ export class Renderer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
_getOrCreatePipeline(flags: ShaderFlags): GPURenderPipeline {
|
_getOrCreatePipeline(flags: ShaderFlags): GPURenderPipeline {
|
||||||
const key = _shaderFlagsKey(flags);
|
const key = shaderFlagsKey(flags);
|
||||||
|
|
||||||
let pipeline = this._pipelineCache.get(key);
|
let pipeline = this._pipelineCache.get(key);
|
||||||
if (pipeline !== undefined) {
|
if (pipeline !== undefined) {
|
||||||
return pipeline;
|
return pipeline;
|
||||||
}
|
}
|
||||||
|
|
||||||
pipeline = _createPipeline(this, flags);
|
pipeline = createPipeline(this, flags);
|
||||||
this._pipelineCache.set(key, pipeline);
|
this._pipelineCache.set(key, pipeline);
|
||||||
return pipeline;
|
return pipeline;
|
||||||
}
|
}
|
||||||
@ -386,18 +380,16 @@ export class Renderer {
|
|||||||
|
|
||||||
this._uniformWriter.clear();
|
this._uniformWriter.clear();
|
||||||
|
|
||||||
// gather dynamic materials
|
// gather materials
|
||||||
|
|
||||||
const dynamicMaterialMapping = new Mapping<DynamicMaterial>();
|
const materialMapping = new Mapping<Material>();
|
||||||
for (const node of preOrder(scene._nodes)) {
|
for (const node of preOrder(scene._nodes)) {
|
||||||
for (const material of node._materials) {
|
for (const material of node._materials) {
|
||||||
if (isDynamicMaterial(material)) {
|
materialMapping.add(material);
|
||||||
dynamicMaterialMapping.add(material);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const dynamicMaterialBindGroups = dynamicMaterialMapping.table.map((material) => {
|
const materialBindGroups = materialMapping.table.map((material) => {
|
||||||
const offset = this._uniformWriter._length;
|
const offset = this._uniformWriter._length;
|
||||||
this._uniformWriter.writeColorF32(material._baseColor);
|
this._uniformWriter.writeColorF32(material._baseColor);
|
||||||
this._uniformWriter.writeF32(material._partialCoverage);
|
this._uniformWriter.writeF32(material._partialCoverage);
|
||||||
@ -409,12 +401,11 @@ export class Renderer {
|
|||||||
this._uniformWriter.writeF32(material._normalScale);
|
this._uniformWriter.writeF32(material._normalScale);
|
||||||
this._uniformWriter.writeColorF32(material._emissive);
|
this._uniformWriter.writeColorF32(material._emissive);
|
||||||
this._uniformWriter.writeF32(material._ior);
|
this._uniformWriter.writeF32(material._ior);
|
||||||
this._uniformWriter.padToAlign(256);
|
|
||||||
|
|
||||||
const bindGroup = this._device.createBindGroup({
|
const bindGroup = this._device.createBindGroup({
|
||||||
layout: this._materialBindGroupLayout,
|
layout: this._materialBindGroupLayout,
|
||||||
entries: [
|
entries: [
|
||||||
{ binding: 0, resource: { buffer: this._uniformBuffer, size: MATERIAL_UNIFORMS_SIZE } },
|
{ binding: 0, resource: { buffer: this._uniformBuffer } },
|
||||||
{ binding: 1, resource: this._sampler },
|
{ binding: 1, resource: this._sampler },
|
||||||
{ binding: 2, resource: material._baseColorPartialCoverageTexture?._textureView ?? this._textureWhite._textureView },
|
{ binding: 2, resource: material._baseColorPartialCoverageTexture?._textureView ?? this._textureWhite._textureView },
|
||||||
{ binding: 3, resource: material._occlusionTexture?._textureView ?? this._textureWhite._textureView },
|
{ binding: 3, resource: material._occlusionTexture?._textureView ?? this._textureWhite._textureView },
|
||||||
@ -442,7 +433,6 @@ export class Renderer {
|
|||||||
object._updateWorldMatrix();
|
object._updateWorldMatrix();
|
||||||
this._uniformWriter.writeMatrix4x4(object._worldMatrix);
|
this._uniformWriter.writeMatrix4x4(object._worldMatrix);
|
||||||
this._uniformWriter.writeMatrix4x4(_matrixOStoWSNormal.setObject(object._worldMatrix).inverseTransposeAffine());
|
this._uniformWriter.writeMatrix4x4(_matrixOStoWSNormal.setObject(object._worldMatrix).inverseTransposeAffine());
|
||||||
this._uniformWriter.padToAlign(256);
|
|
||||||
return offset;
|
return offset;
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -501,7 +491,9 @@ export class Renderer {
|
|||||||
this._uniformWriter.writeColorF32(scene._ambientLight);
|
this._uniformWriter.writeColorF32(scene._ambientLight);
|
||||||
this._uniformWriter.writeU32(pointLightCount);
|
this._uniformWriter.writeU32(pointLightCount);
|
||||||
this._uniformWriter.writeU32(directionalLightCount);
|
this._uniformWriter.writeU32(directionalLightCount);
|
||||||
this._uniformWriter.padToAlign(256);
|
this._uniformWriter.writeU32(0);
|
||||||
|
this._uniformWriter.writeU32(0);
|
||||||
|
this._uniformWriter.writeU32(0);
|
||||||
|
|
||||||
// upload uniforms
|
// upload uniforms
|
||||||
|
|
||||||
@ -528,18 +520,11 @@ export class Renderer {
|
|||||||
|
|
||||||
pass.setPipeline(renderPipeline);
|
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(0, vertexBuffer._positionBuffer);
|
||||||
if (vertexBuffer._texCoordBuffer !== null) pass.setVertexBuffer(1, vertexBuffer._texCoordBuffer);
|
pass.setVertexBuffer(1, vertexBuffer._texCoordBuffer);
|
||||||
if (vertexBuffer._lightTexCoordBuffer !== null) pass.setVertexBuffer(2, vertexBuffer._lightTexCoordBuffer);
|
pass.setVertexBuffer(2, vertexBuffer._lightTexCoordBuffer);
|
||||||
if (vertexBuffer._normalBuffer !== null) pass.setVertexBuffer(3, vertexBuffer._normalBuffer);
|
pass.setVertexBuffer(3, vertexBuffer._normalBuffer);
|
||||||
if (vertexBuffer._tangentBuffer !== null) pass.setVertexBuffer(4, vertexBuffer._tangentBuffer);
|
pass.setVertexBuffer(4, vertexBuffer._tangentBuffer);
|
||||||
pass.setIndexBuffer(indexBuffer._buffer, indexBuffer._indexFormat);
|
pass.setIndexBuffer(indexBuffer._buffer, indexBuffer._indexFormat);
|
||||||
|
|
||||||
pass.setBindGroup(2, this._objectBindGroup, [objectOffset]);
|
pass.setBindGroup(2, this._objectBindGroup, [objectOffset]);
|
||||||
@ -547,17 +532,9 @@ export class Renderer {
|
|||||||
for (let si = 0; si < mesh._submeshes.length; ++si) {
|
for (let si = 0; si < mesh._submeshes.length; ++si) {
|
||||||
const submesh = mesh._submeshes[si]!;
|
const submesh = mesh._submeshes[si]!;
|
||||||
const material = object._materials[si]!;
|
const material = object._materials[si]!;
|
||||||
|
const { bindGroup: materialBindGroup, offset: materialOffset } = materialBindGroups[materialMapping.get(material)!]!;
|
||||||
|
|
||||||
if (isMaterial(material)) {
|
pass.setBindGroup(1, materialBindGroup, [materialOffset]);
|
||||||
pass.setBindGroup(1, material._bindGroup, [0]);
|
|
||||||
} else if (isDynamicMaterial(material)) {
|
|
||||||
const {
|
|
||||||
bindGroup: materialBindGroup,
|
|
||||||
offset: materialOffset
|
|
||||||
} = dynamicMaterialBindGroups[dynamicMaterialMapping.get(material)!]!;
|
|
||||||
pass.setBindGroup(1, materialBindGroup, [materialOffset]);
|
|
||||||
}
|
|
||||||
|
|
||||||
pass.drawIndexed(submesh.length, 1, submesh.start, 0, 0);
|
pass.drawIndexed(submesh.length, 1, submesh.start, 0, 0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -20,7 +20,7 @@ export interface IndexBufferResizeProps {
|
|||||||
|
|
||||||
export class IndexBuffer {
|
export class IndexBuffer {
|
||||||
|
|
||||||
declare readonly type: "IndexBuffer";
|
readonly type!: "IndexBuffer";
|
||||||
_renderer: Renderer;
|
_renderer: Renderer;
|
||||||
|
|
||||||
_name: string;
|
_name: string;
|
||||||
|
@ -1,169 +0,0 @@
|
|||||||
/*!
|
|
||||||
* This Source Code Form is subject to the terms of the Mozilla Public License,
|
|
||||||
* v. 2.0. If a copy of the MPL was not distributed with this file, You can
|
|
||||||
* obtain one at http://mozilla.org/MPL/2.0/.
|
|
||||||
*/
|
|
||||||
|
|
||||||
import { Texture2D } from ".";
|
|
||||||
import { Color, MaterialProps } from "../data";
|
|
||||||
import { Renderer, _BinaryWriter } from "../oktaeder";
|
|
||||||
|
|
||||||
export class Material {
|
|
||||||
|
|
||||||
declare readonly type: "Material";
|
|
||||||
_renderer: Renderer;
|
|
||||||
|
|
||||||
_uniformBuffer: GPUBuffer;
|
|
||||||
_bindGroup: GPUBindGroup;
|
|
||||||
|
|
||||||
_name: string;
|
|
||||||
|
|
||||||
readonly _baseColor: Color;
|
|
||||||
readonly _partialCoverage: number;
|
|
||||||
readonly _occlusionTextureStrength: number;
|
|
||||||
readonly _metallic: number;
|
|
||||||
readonly _roughness: number;
|
|
||||||
readonly _normalScale: number;
|
|
||||||
readonly _emissive: Color;
|
|
||||||
readonly _transmission: Color;
|
|
||||||
readonly _collimation: number;
|
|
||||||
readonly _ior: number;
|
|
||||||
|
|
||||||
readonly _baseColorPartialCoverageTexture: Texture2D | null;
|
|
||||||
readonly _occlusionTexture: Texture2D | null;
|
|
||||||
readonly _roughnessMetallicTexture: Texture2D | null;
|
|
||||||
readonly _normalTexture: Texture2D | null;
|
|
||||||
readonly _emissiveTexture: Texture2D | null;
|
|
||||||
readonly _transmissionCollimationTexture: Texture2D | null;
|
|
||||||
|
|
||||||
readonly _transparent: boolean;
|
|
||||||
readonly _doubleSided: boolean;
|
|
||||||
|
|
||||||
constructor(renderer: Renderer, {
|
|
||||||
name = "",
|
|
||||||
baseColor,
|
|
||||||
partialCoverage = 1,
|
|
||||||
occlusionTextureStrength = 1,
|
|
||||||
metallic = 1,
|
|
||||||
roughness = 1,
|
|
||||||
normalScale = 1,
|
|
||||||
emissive,
|
|
||||||
transmission,
|
|
||||||
collimation = 1,
|
|
||||||
ior = 1.45,
|
|
||||||
baseColorPartialCoverageTexture = null,
|
|
||||||
occlusionTexture = null,
|
|
||||||
roughnessMetallicTexture = null,
|
|
||||||
normalTexture = null,
|
|
||||||
emissiveTexture = null,
|
|
||||||
transmissionCollimationTexture = null,
|
|
||||||
transparent = false,
|
|
||||||
doubleSided = false,
|
|
||||||
}: MaterialProps) {
|
|
||||||
this._renderer = renderer;
|
|
||||||
|
|
||||||
this._name = name;
|
|
||||||
|
|
||||||
this._baseColor = baseColor !== undefined ? Color.fromObject(baseColor) : Color.white();
|
|
||||||
this._partialCoverage = partialCoverage;
|
|
||||||
this._occlusionTextureStrength = occlusionTextureStrength;
|
|
||||||
this._metallic = metallic;
|
|
||||||
this._roughness = roughness;
|
|
||||||
this._normalScale = normalScale;
|
|
||||||
this._emissive = emissive !== undefined ? Color.fromObject(emissive) : Color.black();
|
|
||||||
this._transmission = transmission !== undefined ? Color.fromObject(transmission) : Color.black();
|
|
||||||
this._collimation = collimation;
|
|
||||||
this._ior = ior;
|
|
||||||
|
|
||||||
this._baseColorPartialCoverageTexture = baseColorPartialCoverageTexture;
|
|
||||||
this._occlusionTexture = occlusionTexture;
|
|
||||||
this._roughnessMetallicTexture = roughnessMetallicTexture;
|
|
||||||
this._normalTexture = normalTexture;
|
|
||||||
this._emissiveTexture = emissiveTexture;
|
|
||||||
this._transmissionCollimationTexture = transmissionCollimationTexture;
|
|
||||||
|
|
||||||
this._transparent = transparent;
|
|
||||||
this._doubleSided = doubleSided;
|
|
||||||
|
|
||||||
this._uniformBuffer = renderer._device.createBuffer({
|
|
||||||
usage: GPUBufferUsage.COPY_DST | GPUBufferUsage.UNIFORM,
|
|
||||||
size: 64,
|
|
||||||
label: name,
|
|
||||||
});
|
|
||||||
|
|
||||||
const writer = new _BinaryWriter(64);
|
|
||||||
writer.writeColorF32(this._baseColor);
|
|
||||||
writer.writeF32(this._partialCoverage);
|
|
||||||
writer.writeColorF32(this._transmission);
|
|
||||||
writer.writeF32(this._collimation);
|
|
||||||
writer.writeF32(this._occlusionTextureStrength);
|
|
||||||
writer.writeF32(this._roughness);
|
|
||||||
writer.writeF32(this._metallic);
|
|
||||||
writer.writeF32(this._normalScale);
|
|
||||||
writer.writeColorF32(this._emissive);
|
|
||||||
writer.writeF32(this._ior);
|
|
||||||
|
|
||||||
renderer._device.queue.writeBuffer(this._uniformBuffer, 0, writer.subarray);
|
|
||||||
|
|
||||||
this._bindGroup = renderer._device.createBindGroup({
|
|
||||||
layout: renderer._materialBindGroupLayout,
|
|
||||||
entries: [
|
|
||||||
{ binding: 0, resource: { buffer: this._uniformBuffer, size: 64 } },
|
|
||||||
{ binding: 1, resource: renderer._sampler },
|
|
||||||
{ binding: 2, resource: this._baseColorPartialCoverageTexture?._textureView ?? renderer._textureWhite._textureView },
|
|
||||||
{ binding: 3, resource: this._occlusionTexture?._textureView ?? renderer._textureWhite._textureView },
|
|
||||||
{ binding: 4, resource: this._roughnessMetallicTexture?._textureView ?? renderer._textureWhite._textureView },
|
|
||||||
{ binding: 5, resource: this._normalTexture?._textureView ?? renderer._textureNormal._textureView },
|
|
||||||
{ binding: 6, resource: this._emissiveTexture?._textureView ?? renderer._textureWhite._textureView },
|
|
||||||
{ binding: 7, resource: this._transmissionCollimationTexture?._textureView ?? renderer._textureBlack._textureView },
|
|
||||||
],
|
|
||||||
label: name,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Destroys owned GPU resources. The index buffer should not be used after
|
|
||||||
* calling this method.
|
|
||||||
* @returns `this` for chaining
|
|
||||||
*/
|
|
||||||
dispose(): Material {
|
|
||||||
this._uniformBuffer.destroy();
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
getBaseColor(res: Color): Color {
|
|
||||||
return res.setObject(this._baseColor);
|
|
||||||
}
|
|
||||||
|
|
||||||
get partialCoverage(): number { return this._partialCoverage; }
|
|
||||||
get occlusionTextureStrength(): number { return this._occlusionTextureStrength; }
|
|
||||||
get metallic(): number { return this._metallic; }
|
|
||||||
get roughness(): number { return this._roughness; }
|
|
||||||
get normalScale(): number { return this._normalScale; }
|
|
||||||
|
|
||||||
getEmissive(res: Color): Color {
|
|
||||||
return res.setObject(this._emissive);
|
|
||||||
}
|
|
||||||
|
|
||||||
getTransmission(res: Color): Color {
|
|
||||||
return res.setObject(this._transmission);
|
|
||||||
}
|
|
||||||
|
|
||||||
get collimation(): number { return this._collimation; }
|
|
||||||
get ior(): number { return this._ior; }
|
|
||||||
get baseColorPartialCoverageTexture(): Texture2D | null { return this._baseColorPartialCoverageTexture; }
|
|
||||||
get occlusionTexture(): Texture2D | null { return this._occlusionTexture; }
|
|
||||||
get roughnessMetallicTexture(): Texture2D | null { return this._roughnessMetallicTexture; }
|
|
||||||
get normalTexture(): Texture2D | null { return this._normalTexture; }
|
|
||||||
get emissiveTexture(): Texture2D | null { return this._emissiveTexture; }
|
|
||||||
get transmissionCollimationTexture(): Texture2D | null { return this._transmissionCollimationTexture; }
|
|
||||||
|
|
||||||
get transparent(): boolean { return this._transparent; }
|
|
||||||
get doubleSided(): boolean { return this._doubleSided; }
|
|
||||||
}
|
|
||||||
|
|
||||||
Object.defineProperty(Material.prototype, "type", { value: "Material" });
|
|
||||||
|
|
||||||
export function isMaterial(value: unknown): value is Material {
|
|
||||||
return Boolean(value) && (value as Material).type === "Material";
|
|
||||||
}
|
|
@ -21,15 +21,12 @@ export interface Texture2DProps {
|
|||||||
readonly height: number;
|
readonly height: number;
|
||||||
|
|
||||||
readonly format: Texture2DFormat;
|
readonly format: Texture2DFormat;
|
||||||
|
|
||||||
readonly usage?: GPUTextureUsageFlags;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface Texture2DResizeProps {
|
export interface Texture2DResizeProps {
|
||||||
readonly width?: number;
|
readonly width?: number;
|
||||||
readonly height?: number;
|
readonly height?: number;
|
||||||
readonly format?: Texture2DFormat;
|
readonly format?: Texture2DFormat;
|
||||||
readonly usage?: GPUTextureUsageFlags;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface Texture2DAdvancedWriteProps {
|
export interface Texture2DAdvancedWriteProps {
|
||||||
@ -42,7 +39,7 @@ export interface Texture2DAdvancedWriteProps {
|
|||||||
|
|
||||||
export class Texture2D {
|
export class Texture2D {
|
||||||
|
|
||||||
declare readonly type: "Texture2D";
|
readonly type!: "Texture2D";
|
||||||
_renderer: Renderer;
|
_renderer: Renderer;
|
||||||
|
|
||||||
_name: string;
|
_name: string;
|
||||||
@ -56,7 +53,6 @@ export class Texture2D {
|
|||||||
width,
|
width,
|
||||||
height,
|
height,
|
||||||
format,
|
format,
|
||||||
usage = GPUTextureUsage.TEXTURE_BINDING | GPUTextureUsage.COPY_DST,
|
|
||||||
}: Texture2DProps) {
|
}: Texture2DProps) {
|
||||||
this._renderer = renderer;
|
this._renderer = renderer;
|
||||||
|
|
||||||
@ -66,7 +62,7 @@ export class Texture2D {
|
|||||||
|
|
||||||
this._renderer = renderer;
|
this._renderer = renderer;
|
||||||
this._texture = renderer._device.createTexture({
|
this._texture = renderer._device.createTexture({
|
||||||
usage,
|
usage: GPUTextureUsage.TEXTURE_BINDING | GPUTextureUsage.COPY_DST,
|
||||||
size: { width, height },
|
size: { width, height },
|
||||||
format: gpuFormat,
|
format: gpuFormat,
|
||||||
label: name
|
label: name
|
||||||
@ -151,14 +147,13 @@ export class Texture2D {
|
|||||||
width = this._texture.width,
|
width = this._texture.width,
|
||||||
height = this._texture.height,
|
height = this._texture.height,
|
||||||
format = this._format,
|
format = this._format,
|
||||||
usage = this._texture.usage,
|
|
||||||
}: Texture2DResizeProps): Texture2D {
|
}: Texture2DResizeProps): Texture2D {
|
||||||
this._texture.destroy();
|
this._texture.destroy();
|
||||||
|
|
||||||
const gpuFormat = gpuTextureFormat(format);
|
const gpuFormat = gpuTextureFormat(format);
|
||||||
|
|
||||||
this._texture = this._renderer._device.createTexture({
|
this._texture = this._renderer._device.createTexture({
|
||||||
usage,
|
usage: GPUTextureUsage.TEXTURE_BINDING | GPUTextureUsage.COPY_DST,
|
||||||
size: { width, height },
|
size: { width, height },
|
||||||
format: gpuFormat,
|
format: gpuFormat,
|
||||||
label: this._name
|
label: this._name
|
||||||
|
@ -51,7 +51,7 @@ export interface VertexBufferWriteTypedArrayProps {
|
|||||||
|
|
||||||
export class VertexBuffer {
|
export class VertexBuffer {
|
||||||
|
|
||||||
declare readonly type: "VertexBuffer";
|
readonly type!: "VertexBuffer";
|
||||||
_renderer: Renderer;
|
_renderer: Renderer;
|
||||||
|
|
||||||
_name: string;
|
_name: string;
|
||||||
|
@ -5,6 +5,5 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
export * from "./IndexBuffer";
|
export * from "./IndexBuffer";
|
||||||
export * from "./Material";
|
|
||||||
export * from "./Texture2D";
|
export * from "./Texture2D";
|
||||||
export * from "./VertexBuffer";
|
export * from "./VertexBuffer";
|
||||||
|
130
src/shader.ts
130
src/shader.ts
@ -6,13 +6,6 @@
|
|||||||
|
|
||||||
import { Renderer } from "./oktaeder";
|
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 type ShaderFlagKey = number;
|
||||||
|
|
||||||
export interface ShaderFlags {
|
export interface ShaderFlags {
|
||||||
@ -22,7 +15,7 @@ export interface ShaderFlags {
|
|||||||
readonly tangent: boolean;
|
readonly tangent: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function _shaderFlagsKey({
|
export function shaderFlagsKey({
|
||||||
texCoord,
|
texCoord,
|
||||||
lightTexCoord,
|
lightTexCoord,
|
||||||
normal,
|
normal,
|
||||||
@ -36,13 +29,13 @@ export function _shaderFlagsKey({
|
|||||||
return key;
|
return key;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function _createPipeline(renderer: Renderer, {
|
export function createPipeline(renderer: Renderer, {
|
||||||
texCoord,
|
texCoord,
|
||||||
lightTexCoord,
|
lightTexCoord,
|
||||||
normal,
|
normal,
|
||||||
tangent,
|
tangent,
|
||||||
}: ShaderFlags): GPURenderPipeline {
|
}: ShaderFlags): GPURenderPipeline {
|
||||||
const shaderCode = _createShaderCode({ texCoord, lightTexCoord, normal, tangent });
|
const shaderCode = createShaderCode({ texCoord, lightTexCoord, normal, tangent });
|
||||||
|
|
||||||
const shaderModule = renderer._device.createShaderModule({
|
const shaderModule = renderer._device.createShaderModule({
|
||||||
code: shaderCode,
|
code: shaderCode,
|
||||||
@ -137,12 +130,14 @@ export function _createPipeline(renderer: Renderer, {
|
|||||||
return pipeline;
|
return pipeline;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function _createShaderCode({
|
export function createShaderCode({
|
||||||
texCoord,
|
texCoord,
|
||||||
lightTexCoord,
|
lightTexCoord,
|
||||||
normal,
|
normal,
|
||||||
tangent,
|
tangent,
|
||||||
}: ShaderFlags): string {
|
}: ShaderFlags): string {
|
||||||
|
let varyingLocation = 0;
|
||||||
|
|
||||||
return `
|
return `
|
||||||
struct Vertex {
|
struct Vertex {
|
||||||
@location(0) positionOS: vec3<f32>,
|
@location(0) positionOS: vec3<f32>,
|
||||||
@ -154,12 +149,12 @@ struct Vertex {
|
|||||||
|
|
||||||
struct Varyings {
|
struct Varyings {
|
||||||
@builtin(position) positionCS: vec4<f32>,
|
@builtin(position) positionCS: vec4<f32>,
|
||||||
@location(0) positionVS: vec3<f32>,
|
@location(${varyingLocation++}) positionVS: vec4<f32>,
|
||||||
${texCoord ? `@location(1) texCoord: vec2<f32>,` : ""}
|
${texCoord ? `@location(${varyingLocation++}) texCoord: vec2<f32>,` : ""}
|
||||||
${lightTexCoord ? `@location(2) lightTexCoord: vec2<f32>,` : ""}
|
${lightTexCoord ? `@location(${varyingLocation++}) lightTexCoord: vec2<f32>,` : ""}
|
||||||
${normal ? `@location(3) normalVS: vec3<f32>,` : ""}
|
${normal ? `@location(${varyingLocation++}) normalVS: vec3<f32>,` : ""}
|
||||||
${normal && tangent ? `@location(4) tangentVS: vec3<f32>,` : ""}
|
${normal && tangent ? `@location(${varyingLocation++}) tangentVS: vec3<f32>,` : ""}
|
||||||
${normal && tangent ? `@location(5) bitangentVS: vec3<f32>,` : ""}
|
${normal && tangent ? `@location(${varyingLocation++}) bitangentVS: vec3<f32>,` : ""}
|
||||||
}
|
}
|
||||||
|
|
||||||
struct PointLight {
|
struct PointLight {
|
||||||
@ -213,57 +208,6 @@ struct ObjectUniforms {
|
|||||||
@group(1) @binding(6) var _EmissiveTexture: texture_2d<f32>;
|
@group(1) @binding(6) var _EmissiveTexture: texture_2d<f32>;
|
||||||
@group(1) @binding(7) var _TransmissionCollimationTexture: texture_2d<f32>;
|
@group(1) @binding(7) var _TransmissionCollimationTexture: texture_2d<f32>;
|
||||||
|
|
||||||
const INV_PI: f32 = 0.31830987;
|
|
||||||
|
|
||||||
fn fresnelSchlick(dotVH: f32, f0: vec3<f32>) -> vec3<f32> {
|
|
||||||
const f90 = vec3(1.0);
|
|
||||||
return f0 + (f90 - f0) * pow(1.0 - dotVH, 5.0);
|
|
||||||
}
|
|
||||||
|
|
||||||
fn visibilityGGX(dotNL: f32, dotNV: f32, alpha: f32) -> f32 {
|
|
||||||
let alphaSquared = alpha * alpha;
|
|
||||||
|
|
||||||
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 select(0.0, 0.5 / GGX, GGX > 0.0);
|
|
||||||
}
|
|
||||||
|
|
||||||
fn distributionGGX(dotNH: f32, alpha: f32) -> f32 {
|
|
||||||
let alphaSquared = alpha * alpha;
|
|
||||||
let tmp = dotNH * dotNH * (alphaSquared - 1.0) + 1.0;
|
|
||||||
return alphaSquared * INV_PI / (tmp * tmp);
|
|
||||||
}
|
|
||||||
|
|
||||||
fn toneMapAcesNarkowicz(color: vec3<f32>) -> vec3<f32> {
|
|
||||||
const A: f32 = 2.51;
|
|
||||||
const B: f32 = 0.03;
|
|
||||||
const C: f32 = 2.43;
|
|
||||||
const D: f32 = 0.59;
|
|
||||||
const E: f32 = 0.14;
|
|
||||||
return saturate((color * (A * color + B)) / (color * (C * color + D) + E));
|
|
||||||
}
|
|
||||||
|
|
||||||
fn lightOutgoingRadiance(
|
|
||||||
viewDirectionVS: vec3<f32>, actualNormalVS: vec3<f32>, dotNV: f32,
|
|
||||||
baseColor: vec3<f32>, alpha: f32, metallic: f32, f0: vec3<f32>,
|
|
||||||
incomingRadiance: vec3<f32>, lightDirectionVS: vec3<f32>,
|
|
||||||
) -> vec3<f32> {
|
|
||||||
let halfVectorVS = normalize(lightDirectionVS + viewDirectionVS);
|
|
||||||
let dotVH = saturate(dot(viewDirectionVS, halfVectorVS));
|
|
||||||
let dotNH = saturate(dot(actualNormalVS, halfVectorVS));
|
|
||||||
let dotNL = saturate(dot(actualNormalVS, lightDirectionVS));
|
|
||||||
|
|
||||||
let fresnel = fresnelSchlick(dotVH, f0);
|
|
||||||
let visibility = visibilityGGX(dotNL, dotNV, alpha);
|
|
||||||
let distribution = distributionGGX(dotNH, alpha);
|
|
||||||
|
|
||||||
let scatteredFactor = (1.0 - fresnel) * (1.0 - metallic) * baseColor * INV_PI;
|
|
||||||
let reflectedFactor = fresnel * visibility * distribution;
|
|
||||||
|
|
||||||
return (scatteredFactor + reflectedFactor) * incomingRadiance * dotNL;
|
|
||||||
}
|
|
||||||
|
|
||||||
fn screenSpaceMatrixTStoVS(positionVS: vec3<f32>, normalVS: vec3<f32>, texCoord: vec2<f32>) -> mat3x3<f32> {
|
fn screenSpaceMatrixTStoVS(positionVS: vec3<f32>, normalVS: vec3<f32>, texCoord: vec2<f32>) -> mat3x3<f32> {
|
||||||
let q0 = dpdx(positionVS);
|
let q0 = dpdx(positionVS);
|
||||||
let q1 = dpdy(positionVS);
|
let q1 = dpdy(positionVS);
|
||||||
@ -277,7 +221,7 @@ fn screenSpaceMatrixTStoVS(positionVS: vec3<f32>, normalVS: vec3<f32>, texCoord:
|
|||||||
let bitangentVS = q1perp * uv0.y + q0perp * uv1.y;
|
let bitangentVS = q1perp * uv0.y + q0perp * uv1.y;
|
||||||
|
|
||||||
let det = max(dot(tangentVS, tangentVS), dot(bitangentVS, bitangentVS));
|
let det = max(dot(tangentVS, tangentVS), dot(bitangentVS, bitangentVS));
|
||||||
let scale = select(0.0, inverseSqrt(det), det != 0.0);
|
let scale = (det == 0.0) ? 0.0 : inserseSqrt(det);
|
||||||
|
|
||||||
return mat3x3(tangentVS * scale, bitangentVS * scale, normalVS);
|
return mat3x3(tangentVS * scale, bitangentVS * scale, normalVS);
|
||||||
}
|
}
|
||||||
@ -304,11 +248,10 @@ fn vert(vertex: Vertex) -> Varyings {
|
|||||||
` : ""}
|
` : ""}
|
||||||
${texCoord ? "output.texCoord = vertex.texCoord;" : ""}
|
${texCoord ? "output.texCoord = vertex.texCoord;" : ""}
|
||||||
${lightTexCoord ? "output.lightTexCoord = vertex.lightTexCoord;" : ""}
|
${lightTexCoord ? "output.lightTexCoord = vertex.lightTexCoord;" : ""}
|
||||||
return output;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@fragment
|
@fragment
|
||||||
fn frag(fragment: Varyings) -> @location(0) vec4<f32> {
|
fn frag(fragment: Varyings) -> @location(0) vec2<f32> {
|
||||||
var baseColor = _Material.baseColor;
|
var baseColor = _Material.baseColor;
|
||||||
var partialCoverage = _Material.partialCoverage;
|
var partialCoverage = _Material.partialCoverage;
|
||||||
var occlusion = 1.0;
|
var occlusion = 1.0;
|
||||||
@ -355,50 +298,5 @@ fn frag(fragment: Varyings) -> @location(0) vec4<f32> {
|
|||||||
` : `
|
` : `
|
||||||
let actualNormalVS = geometricNormalVS;
|
let actualNormalVS = geometricNormalVS;
|
||||||
`}
|
`}
|
||||||
|
|
||||||
let viewDirectionVS = normalize(-positionVS);
|
|
||||||
let dotNV = saturate(dot(actualNormalVS, viewDirectionVS));
|
|
||||||
let alpha = roughness * roughness;
|
|
||||||
|
|
||||||
var f0 = vec3(pow((ior - 1.0) / (ior + 1.0), 2.0));
|
|
||||||
f0 = mix(f0, baseColor, metallic);
|
|
||||||
|
|
||||||
var outgoingRadiance = vec3(0.0);
|
|
||||||
|
|
||||||
for (var i: u32 = 0; i < _Global.pointLightCount; i++) {
|
|
||||||
let light = _PointLights[i];
|
|
||||||
|
|
||||||
let lightPositionVS = (_Global.matrixWStoVS * vec4(light.positionWS, 1.0)).xyz;
|
|
||||||
let lightDirectionVS = normalize(lightPositionVS - positionVS);
|
|
||||||
let lightDistance = distance(positionVS, lightPositionVS);
|
|
||||||
let lightAttenuation = 1.0 / (lightDistance * lightDistance);
|
|
||||||
let incomingRadiance = light.color * lightAttenuation;
|
|
||||||
|
|
||||||
outgoingRadiance += lightOutgoingRadiance(
|
|
||||||
viewDirectionVS, actualNormalVS, dotNV,
|
|
||||||
baseColor, alpha, metallic, f0,
|
|
||||||
incomingRadiance, lightDirectionVS,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
for (var i: u32 = 0; i < _Global.directionalLightCount; i++) {
|
|
||||||
let light = _DirectionalLights[i];
|
|
||||||
|
|
||||||
let lightDirectionVS = normalize((_Global.matrixWStoVS * vec4(light.directionWS, 0.0)).xyz);
|
|
||||||
let incomingRadiance = light.color;
|
|
||||||
|
|
||||||
outgoingRadiance += lightOutgoingRadiance(
|
|
||||||
viewDirectionVS, actualNormalVS, dotNV,
|
|
||||||
baseColor, alpha, metallic, f0,
|
|
||||||
incomingRadiance, lightDirectionVS,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
outgoingRadiance += _Global.ambientLight * baseColor * occlusion;
|
|
||||||
|
|
||||||
let toneMappedLinearColor = toneMapAcesNarkowicz(outgoingRadiance);
|
|
||||||
let toneMappedSrgbColor = pow(toneMappedLinearColor, vec3(1.0 / 2.2));
|
|
||||||
|
|
||||||
return vec4(toneMappedSrgbColor, 1.0);
|
|
||||||
}`;
|
}`;
|
||||||
}
|
}
|
||||||
|
@ -53,5 +53,4 @@
|
|||||||
|
|
||||||
"skipLibCheck": false,
|
"skipLibCheck": false,
|
||||||
},
|
},
|
||||||
"include": ["./src/**/*"],
|
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user