stars/main.ts

749 lines
25 KiB
TypeScript

import untypedStars from './data/stars.json';
import untypedConstellations from './data/constellations.json';
import { Stats } from 'stats.ts';
import GUI from 'lil-gui';
const shaderCode = `
struct VertexOut {
@builtin(position) position: vec4f,
@location(1) uv: vec2f
}
struct Star {
dec: f32,
ra: f32,
dist: f32,
lum: f32
}
struct Uniform {
_View: mat4x4f,
_Projection: mat4x4f,
_Distance: f32,
_Intensity: f32
}
@group(0) @binding(0) var<uniform> uniforms: Uniform;
@group(0) @binding(1) var<storage, read> stars: array<Star>;
@vertex
fn vertex_main(@location(0) position: vec2f, @location(1) uv: vec2f, @builtin(instance_index) instance_index: u32) -> VertexOut
{
var star: Star = stars[instance_index];
var output: VertexOut;
var decCos: f32 = cos(star.dec);
var decSin: f32 = sin(star.dec);
var raCos: f32 = cos(star.ra);
var raSin: f32 = sin(star.ra);
var dist: f32 = uniforms._Distance;
var star_pos: vec4f = vec4f(dist * decCos * raCos, dist * decCos * raSin, dist * decSin, 1.0);
output.position = uniforms._Projection * (uniforms._View * star_pos + vec4f(position * star.lum * uniforms._Intensity, 0.0, 0.0));
output.uv = uv;
return output;
}
@fragment
fn fragment_main(fragData: VertexOut) -> @location(0) vec4f
{
var dist = distance(fragData.uv * 2.0, vec2f(1, 1));
return vec4f(1 - dist, 1 - dist, 1 - dist, 1 - dist);
}
`;
type StarID = string;
type Star = {
id: StarID;
bayer: string;
hip: string;
ra: string;
dec: string;
mag: string;
dist: string;
color: [number, number, number];
};
type Constellation = {
con: string;
con_id: string;
lines: StarID[][];
certainty: string | null;
description: string | null;
semantics: string[] | null;
};
type Position = {
x: number;
y: number;
z: number;
}
class Matrix4x4
{
private _buffer: ArrayBuffer;
private _view: Float32Array;
constructor()
{
this._view = new Float32Array(16);
this._buffer = this._view.buffer as ArrayBuffer;
}
static translation(x: number, y: number, z: number): Matrix4x4
{
const mat = new Matrix4x4();
mat.view[ 0] = 1; mat.view[ 1] = 0; mat.view[ 2] = 0; mat.view[ 3] = 0;
mat.view[ 4] = 0; mat.view[ 5] = 1; mat.view[ 6] = 0; mat.view[ 7] = 0;
mat.view[ 8] = 0; mat.view[ 9] = 0; mat.view[10] = 1; mat.view[11] = 0;
mat.view[12] = x; mat.view[13] = y; mat.view[14] = z; mat.view[15] = 1;
return mat;
}
static rotationX(angle: number): Matrix4x4
{
const c = Math.cos(angle);
const s = Math.sin(angle);
const mat = new Matrix4x4();
mat.view[ 0] = 1; mat.view[ 1] = 0; mat.view[ 2] = 0; mat.view[ 3] = 0;
mat.view[ 4] = 0; mat.view[ 5] = c; mat.view[ 6] = s; mat.view[ 7] = 0;
mat.view[ 8] = 0; mat.view[ 9] = -s; mat.view[10] = c; mat.view[11] = 0;
mat.view[12] = 0; mat.view[13] = 0; mat.view[14] = 0; mat.view[15] = 1;
return mat;
}
static rotationY(angle: number): Matrix4x4
{
const c = Math.cos(angle);
const s = Math.sin(angle);
const mat = new Matrix4x4();
mat.view[ 0] = c; mat.view[ 1] = 0; mat.view[ 2] = -s; mat.view[ 3] = 0;
mat.view[ 4] = 0; mat.view[ 5] = 1; mat.view[ 6] = 0; mat.view[ 7] = 0;
mat.view[ 8] = s; mat.view[ 9] = 0; mat.view[10] = c; mat.view[11] = 0;
mat.view[12] = 0; mat.view[13] = 0; mat.view[14] = 0; mat.view[15] = 1;
return mat;
}
static rotationZ(angle: number): Matrix4x4
{
const c = Math.cos(angle);
const s = Math.sin(angle);
const mat = new Matrix4x4();
mat.view[ 0] = c; mat.view[ 1] = s; mat.view[ 2] = 0; mat.view[ 3] = 0;
mat.view[ 4] = -s; mat.view[ 5] = c; mat.view[ 6] = 0; mat.view[ 7] = 0;
mat.view[ 8] = 0; mat.view[ 9] = 0; mat.view[10] = 1; mat.view[11] = 0;
mat.view[12] = 0; mat.view[13] = 0; mat.view[14] = 0; mat.view[15] = 1;
return mat;
}
static scaling(sx: number, sy: number, sz: number): Matrix4x4
{
const mat = new Matrix4x4();
mat.view[ 0] = sx; mat.view[ 1] = 0; mat.view[ 2] = 0; mat.view[ 3] = 0;
mat.view[ 4] = 0; mat.view[ 5] = sy; mat.view[ 6] = 0; mat.view[ 7] = 0;
mat.view[ 8] = 0; mat.view[ 9] = 0; mat.view[10] = sz; mat.view[11] = 0;
mat.view[12] = 0; mat.view[13] = 0; mat.view[14] = 0; mat.view[15] = 1;
return mat;
}
identity(value: number = 1): this
{
this._view[0] = value;
this._view[1] = 0;
this._view[2] = 0;
this._view[3] = 0;
this._view[4] = 0;
this._view[5] = value;
this._view[6] = 0;
this._view[7] = 0;
this._view[8] = 0;
this._view[9] = 0;
this._view[10] = value;
this._view[11] = 0;
this._view[12] = 0;
this._view[13] = 0;
this._view[14] = 0;
this._view[15] = value;
return this;
}
translate(x: number, y: number, z: number): this
{
return this.multiply(this, Matrix4x4.translation(x, y, z));
}
rotateX(angle: number): this
{
return this.multiply(this, Matrix4x4.rotationX(angle));
}
rotateY(angle: number): this
{
return this.multiply(this, Matrix4x4.rotationY(angle));
}
rotateZ(angle: number): this
{
return this.multiply(this, Matrix4x4.rotationZ(angle));
}
scale(sx: number, sy: number, sz: number): this
{
return this.multiply(this, Matrix4x4.scaling(sx, sy, sz));
}
multiply(a: Matrix4x4, b: Matrix4x4): this
{
const b00 = b.view[0 * 4 + 0]!;
const b01 = b.view[0 * 4 + 1]!;
const b02 = b.view[0 * 4 + 2]!;
const b03 = b.view[0 * 4 + 3]!;
const b10 = b.view[1 * 4 + 0]!;
const b11 = b.view[1 * 4 + 1]!;
const b12 = b.view[1 * 4 + 2]!;
const b13 = b.view[1 * 4 + 3]!;
const b20 = b.view[2 * 4 + 0]!;
const b21 = b.view[2 * 4 + 1]!;
const b22 = b.view[2 * 4 + 2]!;
const b23 = b.view[2 * 4 + 3]!;
const b30 = b.view[3 * 4 + 0]!;
const b31 = b.view[3 * 4 + 1]!;
const b32 = b.view[3 * 4 + 2]!;
const b33 = b.view[3 * 4 + 3]!;
const a00 = a.view[0 * 4 + 0]!;
const a01 = a.view[0 * 4 + 1]!;
const a02 = a.view[0 * 4 + 2]!;
const a03 = a.view[0 * 4 + 3]!;
const a10 = a.view[1 * 4 + 0]!;
const a11 = a.view[1 * 4 + 1]!;
const a12 = a.view[1 * 4 + 2]!;
const a13 = a.view[1 * 4 + 3]!;
const a20 = a.view[2 * 4 + 0]!;
const a21 = a.view[2 * 4 + 1]!;
const a22 = a.view[2 * 4 + 2]!;
const a23 = a.view[2 * 4 + 3]!;
const a30 = a.view[3 * 4 + 0]!;
const a31 = a.view[3 * 4 + 1]!;
const a32 = a.view[3 * 4 + 2]!;
const a33 = a.view[3 * 4 + 3]!;
this._view[0] = b00 * a00 + b01 * a10 + b02 * a20 + b03 * a30;
this._view[1] = b00 * a01 + b01 * a11 + b02 * a21 + b03 * a31;
this._view[2] = b00 * a02 + b01 * a12 + b02 * a22 + b03 * a32;
this._view[3] = b00 * a03 + b01 * a13 + b02 * a23 + b03 * a33;
this._view[4] = b10 * a00 + b11 * a10 + b12 * a20 + b13 * a30;
this._view[5] = b10 * a01 + b11 * a11 + b12 * a21 + b13 * a31;
this._view[6] = b10 * a02 + b11 * a12 + b12 * a22 + b13 * a32;
this._view[7] = b10 * a03 + b11 * a13 + b12 * a23 + b13 * a33;
this._view[8] = b20 * a00 + b21 * a10 + b22 * a20 + b23 * a30;
this._view[9] = b20 * a01 + b21 * a11 + b22 * a21 + b23 * a31;
this._view[10] = b20 * a02 + b21 * a12 + b22 * a22 + b23 * a32;
this._view[11] = b20 * a03 + b21 * a13 + b22 * a23 + b23 * a33;
this._view[12] = b30 * a00 + b31 * a10 + b32 * a20 + b33 * a30;
this._view[13] = b30 * a01 + b31 * a11 + b32 * a21 + b33 * a31;
this._view[14] = b30 * a02 + b31 * a12 + b32 * a22 + b33 * a32;
this._view[15] = b30 * a03 + b31 * a13 + b32 * a23 + b33 * a33;
return this;
}
invert()
{
const m00 = this._view[0 * 4 + 0]!;
const m01 = this._view[0 * 4 + 1]!;
const m02 = this._view[0 * 4 + 2]!;
const m03 = this._view[0 * 4 + 3]!;
const m10 = this._view[1 * 4 + 0]!;
const m11 = this._view[1 * 4 + 1]!;
const m12 = this._view[1 * 4 + 2]!;
const m13 = this._view[1 * 4 + 3]!;
const m20 = this._view[2 * 4 + 0]!;
const m21 = this._view[2 * 4 + 1]!;
const m22 = this._view[2 * 4 + 2]!;
const m23 = this._view[2 * 4 + 3]!;
const m30 = this._view[3 * 4 + 0]!;
const m31 = this._view[3 * 4 + 1]!;
const m32 = this._view[3 * 4 + 2]!;
const m33 = this._view[3 * 4 + 3]!;
const tmp0 = m22 * m33;
const tmp1 = m32 * m23;
const tmp2 = m12 * m33;
const tmp3 = m32 * m13;
const tmp4 = m12 * m23;
const tmp5 = m22 * m13;
const tmp6 = m02 * m33;
const tmp7 = m32 * m03;
const tmp8 = m02 * m23;
const tmp9 = m22 * m03;
const tmp10 = m02 * m13;
const tmp11 = m12 * m03;
const tmp12 = m20 * m31;
const tmp13 = m30 * m21;
const tmp14 = m10 * m31;
const tmp15 = m30 * m11;
const tmp16 = m10 * m21;
const tmp17 = m20 * m11;
const tmp18 = m00 * m31;
const tmp19 = m30 * m01;
const tmp20 = m00 * m21;
const tmp21 = m20 * m01;
const tmp22 = m00 * m11;
const tmp23 = m10 * m01;
const t0 = (tmp0 * m11 + tmp3 * m21 + tmp4 * m31) -
(tmp1 * m11 + tmp2 * m21 + tmp5 * m31);
const t1 = (tmp1 * m01 + tmp6 * m21 + tmp9 * m31) -
(tmp0 * m01 + tmp7 * m21 + tmp8 * m31);
const t2 = (tmp2 * m01 + tmp7 * m11 + tmp10 * m31) -
(tmp3 * m01 + tmp6 * m11 + tmp11 * m31);
const t3 = (tmp5 * m01 + tmp8 * m11 + tmp11 * m21) -
(tmp4 * m01 + tmp9 * m11 + tmp10 * m21);
const d = 1 / (m00 * t0 + m10 * t1 + m20 * t2 + m30 * t3);
this._view[0] = d * t0;
this._view[1] = d * t1;
this._view[2] = d * t2;
this._view[3] = d * t3;
this._view[4] = d * ((tmp1 * m10 + tmp2 * m20 + tmp5 * m30) - (tmp0 * m10 + tmp3 * m20 + tmp4 * m30));
this._view[5] = d * ((tmp0 * m00 + tmp7 * m20 + tmp8 * m30) - (tmp1 * m00 + tmp6 * m20 + tmp9 * m30));
this._view[6] = d * ((tmp3 * m00 + tmp6 * m10 + tmp11 * m30) - (tmp2 * m00 + tmp7 * m10 + tmp10 * m30));
this._view[7] = d * ((tmp4 * m00 + tmp9 * m10 + tmp10 * m20) - (tmp5 * m00 + tmp8 * m10 + tmp11 * m20));
this._view[8] = d * ((tmp12 * m13 + tmp15 * m23 + tmp16 * m33) - (tmp13 * m13 + tmp14 * m23 + tmp17 * m33));
this._view[9] = d * ((tmp13 * m03 + tmp18 * m23 + tmp21 * m33) - (tmp12 * m03 + tmp19 * m23 + tmp20 * m33));
this._view[10] = d * ((tmp14 * m03 + tmp19 * m13 + tmp22 * m33) - (tmp15 * m03 + tmp18 * m13 + tmp23 * m33));
this._view[11] = d * ((tmp17 * m03 + tmp20 * m13 + tmp23 * m23) - (tmp16 * m03 + tmp21 * m13 + tmp22 * m23));
this._view[12] = d * ((tmp14 * m22 + tmp17 * m32 + tmp13 * m12) - (tmp16 * m32 + tmp12 * m12 + tmp15 * m22));
this._view[13] = d * ((tmp20 * m32 + tmp12 * m02 + tmp19 * m22) - (tmp18 * m22 + tmp21 * m32 + tmp13 * m02));
this._view[14] = d * ((tmp18 * m12 + tmp23 * m32 + tmp15 * m02) - (tmp22 * m32 + tmp14 * m02 + tmp19 * m12));
this._view[15] = d * ((tmp22 * m22 + tmp16 * m02 + tmp21 * m12) - (tmp20 * m12 + tmp23 * m22 + tmp17 * m02));
return this;
}
aim(eye: Position, target: Position, up: Position): this
{
const z = {
x: eye.x - target.x,
y: eye.y - target.y,
z: eye.z - target.z
};
// Normalize
const zLen = Math.sqrt(z.x * z.x + z.y * z.y + z.z * z.z);
z.x /= zLen;
z.y /= zLen;
z.z /= zLen;
const x = {
x: up.y * z.z - up.z * z.y,
y: up.z * z.x - up.x * z.z,
z: up.x * z.y - up.y * z.x
};
// Normalize
const xLen = Math.sqrt(x.x * x.x + x.y * x.y + x.z * x.z);
x.x /= xLen;
x.y /= xLen;
x.z /= xLen;
const y = {
x: z.y * x.z - z.z * x.y,
y: z.z * x.x - z.x * x.z,
z: z.x * x.y - z.y * x.x
};
// Normalize
const yLen = Math.sqrt(y.x * y.x + y.y * y.y + y.z * y.z);
y.x /= yLen;
y.y /= yLen;
y.z /= yLen;
this._view[ 0] = x.x;
this._view[ 1] = x.y;
this._view[ 2] = x.z;
this._view[ 3] = 0;
this._view[ 4] = y.x;
this._view[ 5] = y.y;
this._view[ 6] = y.z;
this._view[ 7] = 0;
this._view[ 8] = z.x;
this._view[ 9] = z.y;
this._view[10] = z.z;
this._view[11] = 0;
this._view[12] = eye.x;
this._view[13] = eye.y;
this._view[14] = eye.z;
this._view[15] = 1;
return this;
}
perspective(left: number, right: number, top: number, bottom: number, near: number, far: number): this
{
const x = 2 * near / ( right - left );
const y = 2 * near / ( top - bottom );
const a = ( right + left ) / ( right - left );
const b = ( top + bottom ) / ( top - bottom );
const c = -( far + near ) / ( far - near );
const d = -( 2 * far * near ) / ( far - near );
this._view[0 ] = x;
this._view[1 ] = 0;
this._view[2 ] = 0;
this._view[3 ] = 0;
this._view[4 ] = 0;
this._view[5 ] = y;
this._view[6 ] = 0;
this._view[7 ] = 0;
this._view[8 ] = a;
this._view[9 ] = b;
this._view[10] = c;
this._view[11] = -1;
this._view[12] = 0;
this._view[13] = 0;
this._view[14] = d;
this._view[15] = 0;
return this;
}
get view()
{
return this._view;
}
}
class Inputs
{
static inputs: Record<string, boolean> = {};
private static _movementX = 0;
private static _movementY = 0;
private static _zoom = 0;
private static _dragging = false;
static init(eventTarget: HTMLElement)
{
const dragstart = (e: MouseEvent) => {
if(e.buttons & 1)
{
Inputs._dragging = true;
window.addEventListener('mousemove', dragmove);
window.addEventListener('mouseup', dragend);
}
};
const dragmove = (e: MouseEvent) => {
Inputs._movementX += e.movementX;
Inputs._movementY += e.movementY;
};
const dragend = () => {
Inputs._dragging = false;
window.removeEventListener('mousemove', dragmove);
window.removeEventListener('mouseup', dragend);
};
eventTarget.addEventListener('keydown', (e: KeyboardEvent) => {
Inputs.inputs[e.key.toLowerCase()] = true;
});
eventTarget.addEventListener('keyup', (e: KeyboardEvent) => {
Inputs.inputs[e.key.toLowerCase()] = false;
});
eventTarget.addEventListener('mousedown', dragstart);
eventTarget.addEventListener('wheel', (e: WheelEvent) => Inputs._zoom += e.deltaY);
}
static get dragging()
{
return Inputs._dragging;
}
static get movementX()
{
const movement = Inputs._movementX;
Inputs._movementX = 0;
return movement;
}
static get movementY()
{
const movement = Inputs._movementY;
Inputs._movementY = 0;
return movement;
}
static get zoom()
{
const zoom = Inputs._zoom;
Inputs._zoom = 0;
return zoom;
}
}
class Camera
{
pitch = 0;
yaw = 0;
distance = 25;
view = new Matrix4x4();
constructor(distance?: number, yaw?: number, pitch?: number)
{
this.pitch = pitch ?? 0;
this.yaw = yaw ?? 0;
this.distance = distance ?? 25;
}
update(elapsed: number, target: GPUBuffer)
{
elapsed /= 1 << 15;
this.yaw -= Inputs.movementX * elapsed;
this.pitch += Inputs.movementY * elapsed;
this.yaw = (this.yaw + Math.PI * 2) % (Math.PI * 2);
this.pitch = clamp(this.pitch, -Math.PI / 2, Math.PI / 2);
const pitchCos = Math.cos(this.pitch), pitchSin = Math.sin(this.pitch);
const yawCos = Math.cos(this.yaw), yawSin = Math.sin(this.yaw);
this.distance = clamp(this.distance + (Inputs.zoom * elapsed * 1000), 0.1, 10000);
const x = this.distance * yawCos * pitchCos;
const y = this.distance * yawCos * pitchSin;
const z = this.distance * yawSin;
device!.queue.writeBuffer(target, 0, this.view.aim({ x, y, z }, { x: 0, y: 0, z: 0 }, { x: 0, y: 1, z: 0 }).invert().view.buffer);
}
}
//@ts-ignore
const stars: Star[] = untypedStars;
//const constellations: Constellation[] = untypedConstellations;
let canvas: HTMLCanvasElement, device: GPUDevice | undefined, pipeline: GPURenderPipeline, context: GPUCanvasContext | null, bindGroup: GPUBindGroup, viewport: DOMRect, uniforms: GPUBuffer, indirect: GPUBuffer;
let vertex: GPUBuffer, index: GPUBuffer;
let camera: Camera, projectionMatrix = new Matrix4x4(), lastTime: DOMHighResTimeStamp;
let stats: Stats, dom: HTMLElement, gui: GUI, parameters: Record<string, any> = { distance: 125, intensity: 5 };
function clamp(x: number, min: number, max: number): number
{
if(x < min)
return min;
if(x > max)
return max;
return x;
}
function fillBuffer(buffer: ArrayBuffer)
{
const array = new Float32Array(buffer);
for(let i = 0; i < stars.length; i++)
{
const star = stars[i]!;
array[i * 5 + 0] = parseFloat(star.dec) / 180 * Math.PI;
array[i * 5 + 1] = parseFloat(star.ra) / 12 * Math.PI;
array[i * 5 + 2] = parseFloat(star.dist);
array[i * 5 + 3] = parseFloat(star.mag);
}
}
function resize()
{
viewport = document.body.getBoundingClientRect();
canvas!.width = viewport.width; canvas!.height = viewport.height;
device!.queue.writeBuffer(uniforms, 64, projectionMatrix.perspective(-viewport.width / 2, viewport.width / 2, viewport.height / 2, -viewport.height / 2, 0.1, 10000).view.buffer);
}
async function init()
{
canvas = document.createElement("canvas");
context = canvas.getContext('webgpu');
Inputs.init(canvas);
stats = new Stats();
gui = new GUI();
document.body.appendChild(stats.dom);
document.body.appendChild(gui.domElement);
const adapter = await navigator.gpu.requestAdapter({ });
device = await adapter?.requestDevice({ label: 'Device' });
if(device && context)
{
const shader = device.createShaderModule({
code: shaderCode,
label: 'default-shader'
});
const buffer = device.createBuffer({ size: stars.length * 4 * 4, usage: GPUBufferUsage.STORAGE | GPUBufferUsage.COPY_DST, label: 'instanced_buffer' });
const arrayBuffer = new ArrayBuffer(buffer.size);
fillBuffer(arrayBuffer);
device.queue.writeBuffer(buffer, 0, arrayBuffer);
uniforms = device.createBuffer({ size: 16 * 4 + 16 * 4 + 16, usage: GPUBufferUsage.UNIFORM | GPUBufferUsage.COPY_DST, label: 'uniforms_buffer' });
const tmpBuffer = new Float32Array([parameters.distance, parameters.intensity]);
gui.add(parameters, 'distance', 0, 1000).onChange((v: number) => {
tmpBuffer[0] = v;
device!.queue.writeBuffer(uniforms, 128, tmpBuffer);
});
gui.add(parameters, 'intensity', 0.01, 10).onChange((v: number) => {
tmpBuffer[1] = v;
device!.queue.writeBuffer(uniforms, 128, tmpBuffer);
});
camera = new Camera();
{
indirect = device.createBuffer({ size: 5 * 4, usage: GPUBufferUsage.INDIRECT | GPUBufferUsage.COPY_DST, label: 'indirect_buffer' });
const indirectArray = new Uint32Array([ 6, stars.length, 0, 0, 0 ]);
const indirectSignedArray = new Int32Array(indirectArray.buffer);
indirectSignedArray[3] = 0;
device.queue.writeBuffer(indirect, 0, indirectArray.buffer);
}
vertex = device.createBuffer({ size: 4 * 4 * 4, usage: GPUBufferUsage.VERTEX | GPUBufferUsage.COPY_DST, label: 'vertex_buffer' });
device.queue.writeBuffer(vertex, 0, new Float32Array([ -0.5, -0.5, 0, 0, 0.5, -0.5, 1, 0, 0.5, 0.5, 1, 1, -0.5, 0.5, 0, 1, ]));
index = device.createBuffer({ size: 6 * 2, usage: GPUBufferUsage.INDEX | GPUBufferUsage.COPY_DST, label: 'index_buffer' });
device.queue.writeBuffer(index, 0, new Uint16Array([ 0, 1, 2, 0, 2, 3 ]));
const bindGroupLayout = device.createBindGroupLayout({
entries: [{
binding: 0,
visibility: GPUShaderStage.VERTEX,
buffer: {
type: 'uniform',
}
}, {
binding: 1,
visibility: GPUShaderStage.VERTEX,
buffer: {
type: 'read-only-storage',
}
}]
});
bindGroup = device.createBindGroup({
layout: bindGroupLayout,
entries: [{
binding: 0,
resource: { buffer: uniforms }
}, {
binding: 1,
resource: { buffer: buffer, }
}]
});
pipeline = device.createRenderPipeline({
layout: device.createPipelineLayout({
bindGroupLayouts: [ bindGroupLayout ],
}),
vertex: {
module: shader,
entryPoint: 'vertex_main',
buffers: [{
attributes: [{
format: 'float32x2',
offset: 0,
shaderLocation: 0
}, {
format: 'float32x2',
offset: 8,
shaderLocation: 1
}],
arrayStride: 4 * 4,
}]
},
fragment: {
targets: [{
format: navigator.gpu.getPreferredCanvasFormat(),
blend: {
color: {
operation: 'add',
srcFactor: 'one',
dstFactor: 'one'
},
alpha: {
operation: 'add',
srcFactor: 'one',
dstFactor: 'one'
},
},
}],
module: shader,
entryPoint: 'fragment_main',
},
primitive: {
topology: 'triangle-list',
cullMode: 'none',
frontFace: 'cw'
}
});
document.body.appendChild(canvas);
resize();
document.addEventListener('resize', resize);
context.configure({
device,
format: navigator.gpu.getPreferredCanvasFormat(),
alphaMode: 'opaque',
});
dom = document.createElement('pre');
Object.assign(dom.style, {
"position": "absolute",
"right": "0px",
"top": "0px",
"color": "white"
});
document.body.append(dom);
lastTime = performance.now();
requestAnimationFrame(render);
}
else
{
document.body.append('Erreur de chargement (aucun GPU disponible)');
}
}
function render(time: DOMHighResTimeStamp)
{
const elapsed = time - lastTime;
lastTime = time;
stats.begin();
const commandEncoder = device!.createCommandEncoder({ label: 'command-encoder' });
const pass = commandEncoder.beginRenderPass({ label: 'render-pass', colorAttachments: [{
loadOp: "clear",
storeOp: "store",
clearValue: { r: 0, g: 0, b: 0, a: 1 },
view: context!.getCurrentTexture().createView({ label: 'render-target', usage: GPUTextureUsage.RENDER_ATTACHMENT }),
}], });
camera.update(elapsed, uniforms);
dom.textContent = `Pitch: ${camera.pitch}\nYaw: ${camera.yaw}\nDistance: ${camera.distance}`;
pass.setPipeline(pipeline!);
pass.setBindGroup(0, bindGroup);
pass.setVertexBuffer(0, vertex);
pass.setIndexBuffer(index, 'uint16');
pass.setViewport(viewport.x, viewport.y, viewport.width, viewport.height, 0, 1);
pass.drawIndexedIndirect(indirect, 0);
pass.end();
device!.queue.submit([ commandEncoder.finish() ]);
device!.queue.onSubmittedWorkDone().then(() => stats.end());
requestAnimationFrame(render);
}
document.addEventListener('DOMContentLoaded', init);