Update readme, example project, fix runtime errors
This commit is contained in:
@@ -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);
|
||||
|
@@ -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;
|
||||
|
||||
|
@@ -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;
|
||||
|
@@ -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;
|
||||
|
||||
|
@@ -34,7 +34,7 @@ export interface MaterialProps {
|
||||
|
||||
export class Material {
|
||||
|
||||
readonly type!: "Material";
|
||||
declare readonly type: "Material";
|
||||
|
||||
_name: string;
|
||||
|
||||
|
@@ -34,7 +34,7 @@ export type Matrix4x4Tuple = readonly [
|
||||
|
||||
export class Matrix4x4 {
|
||||
|
||||
readonly type!: "Matrix4x4";
|
||||
declare readonly type: "Matrix4x4";
|
||||
|
||||
ix: number;
|
||||
iy: number;
|
||||
|
@@ -23,7 +23,7 @@ export interface NodeProps {
|
||||
|
||||
export class Node {
|
||||
|
||||
readonly type!: "Node";
|
||||
declare readonly type: "Node";
|
||||
|
||||
_name: string;
|
||||
|
||||
|
@@ -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;
|
||||
|
@@ -16,7 +16,7 @@ export interface SceneProps {
|
||||
|
||||
export class Scene {
|
||||
|
||||
readonly type!: "Scene";
|
||||
declare readonly type: "Scene";
|
||||
|
||||
_name: string;
|
||||
|
||||
|
@@ -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;
|
||||
|
@@ -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;
|
||||
|
@@ -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;
|
||||
|
@@ -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]);
|
||||
|
@@ -20,7 +20,7 @@ export interface IndexBufferResizeProps {
|
||||
|
||||
export class IndexBuffer {
|
||||
|
||||
readonly type!: "IndexBuffer";
|
||||
declare readonly type: "IndexBuffer";
|
||||
_renderer: Renderer;
|
||||
|
||||
_name: string;
|
||||
|
@@ -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
|
||||
|
@@ -51,7 +51,7 @@ export interface VertexBufferWriteTypedArrayProps {
|
||||
|
||||
export class VertexBuffer {
|
||||
|
||||
readonly type!: "VertexBuffer";
|
||||
declare readonly type: "VertexBuffer";
|
||||
_renderer: Renderer;
|
||||
|
||||
_name: string;
|
||||
|
@@ -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);
|
||||
|
Reference in New Issue
Block a user