Optimize forward rendering and add benchmark
Amp-Thread-ID: https://ampcode.com/threads/T-01a01380-b478-77d0-84a0-102880a5c5ae Co-authored-by: Heaust Azure <heaust.azure@gmail.com>
This commit is contained in:
@@ -1,6 +1,22 @@
|
||||
import { Node, type NodeOptions } from "../Node";
|
||||
import type { Scene } from "../Scene";
|
||||
|
||||
function rotate(q: ArrayLike<number>, value: number[]) {
|
||||
const [x, y, z] = value;
|
||||
const tx = 2 * (q[1] * z - q[2] * y);
|
||||
const ty = 2 * (q[2] * x - q[0] * z);
|
||||
const tz = 2 * (q[0] * y - q[1] * x);
|
||||
return [
|
||||
x + q[3] * tx + q[1] * tz - q[2] * ty,
|
||||
y + q[3] * ty + q[2] * tx - q[0] * tz,
|
||||
z + q[3] * tz + q[0] * ty - q[1] * tx,
|
||||
];
|
||||
}
|
||||
|
||||
function dot(left: number[], right: ArrayLike<number>) {
|
||||
return left[0] * right[0] + left[1] * right[1] + left[2] * right[2];
|
||||
}
|
||||
|
||||
export type CameraOptions = NodeOptions & {
|
||||
fov?: number;
|
||||
near?: number;
|
||||
@@ -24,6 +40,7 @@ export class Camera extends Node {
|
||||
const nodeReady = this.ready;
|
||||
this.ready = nodeReady.then(async () => {
|
||||
this.cameraId = await scene.core.allocateObject("cameras");
|
||||
await scene.ensureRows("cameraMatrices", this.cameraId + 1, 80, "f32");
|
||||
const row = scene.array("cameras").row(this.cameraId);
|
||||
row.set([
|
||||
options.fov ?? Math.PI / 3,
|
||||
@@ -39,6 +56,7 @@ export class Camera extends Node {
|
||||
options.sensorWidth ?? 36,
|
||||
1,
|
||||
]);
|
||||
this.refreshMatrix();
|
||||
return this;
|
||||
});
|
||||
}
|
||||
@@ -49,17 +67,20 @@ export class Camera extends Node {
|
||||
}
|
||||
|
||||
get fov() { return this.cameraRow()[0]; }
|
||||
set fov(value: number) { this.cameraRow()[0] = value; }
|
||||
set fov(value: number) { this.cameraRow()[0] = value; this.refreshMatrix(); }
|
||||
get aspect() { return this.cameraRow()[1]; }
|
||||
set aspect(value: number) { this.cameraRow()[1] = value; }
|
||||
set aspect(value: number) { this.cameraRow()[1] = value; this.refreshMatrix(); }
|
||||
get near() { return this.cameraRow()[2]; }
|
||||
set near(value: number) { this.cameraRow()[2] = value; }
|
||||
set near(value: number) { this.cameraRow()[2] = value; this.refreshMatrix(); }
|
||||
get far() { return this.cameraRow()[3]; }
|
||||
set far(value: number) { this.cameraRow()[3] = value; }
|
||||
set far(value: number) { this.cameraRow()[3] = value; this.refreshMatrix(); }
|
||||
get projection() { return this.cameraRow()[5] === 1 ? "orthographic" : "perspective"; }
|
||||
set projection(value: "perspective" | "orthographic") { this.cameraRow()[5] = value === "orthographic" ? 1 : 0; }
|
||||
set projection(value: "perspective" | "orthographic") {
|
||||
this.cameraRow()[5] = value === "orthographic" ? 1 : 0;
|
||||
this.refreshMatrix();
|
||||
}
|
||||
get orthoSize() { return this.cameraRow()[6]; }
|
||||
set orthoSize(value: number) { this.cameraRow()[6] = value; }
|
||||
set orthoSize(value: number) { this.cameraRow()[6] = value; this.refreshMatrix(); }
|
||||
get focalLength() { return this.cameraRow()[7]; }
|
||||
set focalLength(value: number) { this.cameraRow()[7] = value; }
|
||||
get aperture() { return this.cameraRow()[8]; }
|
||||
@@ -69,6 +90,18 @@ export class Camera extends Node {
|
||||
get sensorWidth() { return this.cameraRow()[10]; }
|
||||
set sensorWidth(value: number) { this.cameraRow()[10] = value; }
|
||||
|
||||
override get position(): Float32Array { return super.position; }
|
||||
override set position(value: ArrayLike<number>) {
|
||||
super.position = value;
|
||||
this.refreshMatrix();
|
||||
}
|
||||
|
||||
override get quaternion(): Float32Array { return super.quaternion; }
|
||||
override set quaternion(value: ArrayLike<number>) {
|
||||
super.quaternion = value;
|
||||
this.refreshMatrix();
|
||||
}
|
||||
|
||||
lookAt(target: ArrayLike<number>) {
|
||||
if (target.length !== 3) throw new RangeError("camera target");
|
||||
const position = this.position;
|
||||
@@ -86,6 +119,47 @@ export class Camera extends Node {
|
||||
return this;
|
||||
}
|
||||
|
||||
protected refreshMatrix() {
|
||||
if (this.id < 0 || this.cameraId < 0) return;
|
||||
const camera = this.cameraRow();
|
||||
const position = this.position;
|
||||
const quaternion = this.quaternion;
|
||||
const right = rotate(quaternion, [1, 0, 0]);
|
||||
const up = rotate(quaternion, [0, 1, 0]);
|
||||
const forward = rotate(quaternion, [0, 0, 1]);
|
||||
let rows: number[][];
|
||||
if (camera[5] === 1) {
|
||||
const size = Math.max(camera[6], 0.0001);
|
||||
const x = 2 / (size * camera[1]);
|
||||
const y = 2 / size;
|
||||
const z = -1 / camera[3];
|
||||
rows = [
|
||||
[...right.map((value) => value * x), -dot(right, position) * x],
|
||||
[...up.map((value) => value * y), -dot(up, position) * y],
|
||||
[...forward.map((value) => value * z), -dot(forward, position) * z],
|
||||
[0, 0, 0, 1],
|
||||
];
|
||||
} else {
|
||||
const focal = 1 / Math.tan(camera[0] * 0.5);
|
||||
const x = focal / camera[1];
|
||||
const z = -camera[3] / (camera[3] - camera[2]);
|
||||
const translation = (-camera[2] * camera[3]) / (camera[3] - camera[2]);
|
||||
rows = [
|
||||
[...right.map((value) => value * x), -dot(right, position) * x],
|
||||
[...up.map((value) => value * focal), -dot(up, position) * focal],
|
||||
[
|
||||
...forward.map((value) => value * z),
|
||||
-dot(forward, position) * z + translation,
|
||||
],
|
||||
[...forward.map((value) => -value), dot(forward, position)],
|
||||
];
|
||||
}
|
||||
this.scene
|
||||
.array("cameraMatrices")
|
||||
.row(this.cameraId)
|
||||
.set([...rows.flat(), ...position, camera[11]]);
|
||||
}
|
||||
|
||||
override async dispose() {
|
||||
await this.ready;
|
||||
if (this.#cameraDisposed) return;
|
||||
|
||||
@@ -91,6 +91,7 @@ export class FreeCamera extends Camera {
|
||||
this.position[0] += (x * Math.cos(yaw) + z * Math.sin(yaw)) * speed;
|
||||
this.position[1] += y * speed;
|
||||
this.position[2] += (x * -Math.sin(yaw) + z * Math.cos(yaw)) * speed;
|
||||
this.refreshMatrix();
|
||||
this.#frame = requestAnimationFrame(this.#update);
|
||||
};
|
||||
|
||||
|
||||
Reference in New Issue
Block a user