dylanebert's picture
dylanebert HF Staff
checkpoint
cc654e9
raw
history blame
8.51 kB
import type { Camera } from "../cameras/Camera";
import type { PerspectiveCamera } from "../cameras/PerspectiveCamera";
import type { Scene } from "../core/Scene";
import type { Renderer } from "./Renderer";
import { getViewMatrix } from "./webgl/utils/transformations";
import { createWorker } from "./webgl/utils/worker";
import { vertex } from "./webgl/shaders/vertex.glsl";
import { frag } from "./webgl/shaders/frag.glsl";
import type { Matrix4 } from "../math/Matrix4";
export class WebGLRenderer implements Renderer {
canvas: HTMLCanvasElement;
render: (scene: Scene, camera: Camera) => void;
dispose: () => void;
constructor(canvas?: HTMLCanvasElement) {
this.canvas =
canvas ?? (document.createElementNS("http://www.w3.org/1999/xhtml", "canvas") as HTMLCanvasElement);
const gl = (this.canvas.getContext("webgl") ||
this.canvas.getContext("experimental-webgl")) as WebGLRenderingContext;
let ext: ANGLE_instanced_arrays;
let worker: Worker;
let vertexShader: WebGLShader;
let fragmentShader: WebGLShader;
let program: WebGLProgram;
let u_projection: WebGLUniformLocation;
let u_viewport: WebGLUniformLocation;
let u_focal: WebGLUniformLocation;
let u_view: WebGLUniformLocation;
let vertexLocation: number;
let centerLocation: number;
let colorLocation: number;
let covALocation: number;
let covBLocation: number;
let vertexBuffer: WebGLBuffer;
let centerBuffer: WebGLBuffer;
let colorBuffer: WebGLBuffer;
let covABuffer: WebGLBuffer;
let covBBuffer: WebGLBuffer;
function initGLContext(width: number, height: number, scene: Scene, camera: PerspectiveCamera) {
ext = gl.getExtension("ANGLE_instanced_arrays") as ANGLE_instanced_arrays;
worker = new Worker(
URL.createObjectURL(
new Blob(["(", createWorker.toString(), ")(self)"], {
type: "application/javascript",
})
)
);
// Viewport
gl.viewport(0, 0, width, height);
camera.updateProjectionMatrix(width, height);
// Vertex shader
vertexShader = gl.createShader(gl.VERTEX_SHADER) as WebGLShader;
gl.shaderSource(vertexShader, vertex);
gl.compileShader(vertexShader);
if (!gl.getShaderParameter(vertexShader, gl.COMPILE_STATUS)) {
console.error(gl.getShaderInfoLog(vertexShader));
}
// Fragment shader
fragmentShader = gl.createShader(gl.FRAGMENT_SHADER) as WebGLShader;
gl.shaderSource(fragmentShader, frag);
gl.compileShader(fragmentShader);
if (!gl.getShaderParameter(fragmentShader, gl.COMPILE_STATUS)) {
console.error(gl.getShaderInfoLog(fragmentShader));
}
// Program
program = gl.createProgram() as WebGLProgram;
gl.attachShader(program, vertexShader);
gl.attachShader(program, fragmentShader);
gl.linkProgram(program);
gl.useProgram(program);
if (!gl.getProgramParameter(program, gl.LINK_STATUS)) {
console.error(gl.getProgramInfoLog(program));
}
// Blending
gl.disable(gl.DEPTH_TEST);
gl.enable(gl.BLEND);
gl.blendFuncSeparate(gl.ONE_MINUS_DST_ALPHA, gl.ONE, gl.ONE_MINUS_DST_ALPHA, gl.ONE);
gl.blendEquationSeparate(gl.FUNC_ADD, gl.FUNC_ADD);
// Uniforms
u_projection = gl.getUniformLocation(program, "projection") as WebGLUniformLocation;
gl.uniformMatrix4fv(u_projection, false, camera.projectionMatrix.buffer);
u_viewport = gl.getUniformLocation(program, "viewport") as WebGLUniformLocation;
gl.uniform2fv(u_viewport, new Float32Array([width, height]));
u_focal = gl.getUniformLocation(program, "focal") as WebGLUniformLocation;
gl.uniform2fv(u_focal, new Float32Array([camera.fx, camera.fy]));
const viewMatrix = getViewMatrix(camera);
u_view = gl.getUniformLocation(program, "view") as WebGLUniformLocation;
gl.uniformMatrix4fv(u_view, false, viewMatrix.buffer);
// Vertex buffer
const triangleVertices = new Float32Array([-2, -2, 2, -2, 2, 2, -2, 2]);
vertexBuffer = gl.createBuffer() as WebGLBuffer;
gl.bindBuffer(gl.ARRAY_BUFFER, vertexBuffer);
gl.bufferData(gl.ARRAY_BUFFER, triangleVertices, gl.STATIC_DRAW);
vertexLocation = gl.getAttribLocation(program, "position");
gl.enableVertexAttribArray(vertexLocation);
gl.vertexAttribPointer(vertexLocation, 2, gl.FLOAT, false, 0, 0);
// Center buffer
centerBuffer = gl.createBuffer() as WebGLBuffer;
centerLocation = gl.getAttribLocation(program, "center");
gl.enableVertexAttribArray(centerLocation);
gl.bindBuffer(gl.ARRAY_BUFFER, centerBuffer);
gl.vertexAttribPointer(centerLocation, 3, gl.FLOAT, false, 0, 0);
ext.vertexAttribDivisorANGLE(centerLocation, 1);
// Color buffer
colorBuffer = gl.createBuffer() as WebGLBuffer;
colorLocation = gl.getAttribLocation(program, "color");
gl.enableVertexAttribArray(colorLocation);
gl.bindBuffer(gl.ARRAY_BUFFER, colorBuffer);
gl.vertexAttribPointer(colorLocation, 4, gl.FLOAT, false, 0, 0);
ext.vertexAttribDivisorANGLE(colorLocation, 1);
// Covariance A buffer
covABuffer = gl.createBuffer() as WebGLBuffer;
covALocation = gl.getAttribLocation(program, "covA");
gl.enableVertexAttribArray(covALocation);
gl.bindBuffer(gl.ARRAY_BUFFER, covABuffer);
gl.vertexAttribPointer(covALocation, 3, gl.FLOAT, false, 0, 0);
ext.vertexAttribDivisorANGLE(covALocation, 1);
// Covariance B buffer
covBBuffer = gl.createBuffer() as WebGLBuffer;
covBLocation = gl.getAttribLocation(program, "covB");
gl.enableVertexAttribArray(covBLocation);
gl.bindBuffer(gl.ARRAY_BUFFER, covBBuffer);
gl.vertexAttribPointer(covBLocation, 3, gl.FLOAT, false, 0, 0);
ext.vertexAttribDivisorANGLE(covBLocation, 1);
worker.onmessage = (e) => {
if (e.data.center) {
let { covA, covB, center, color } = e.data;
gl.bindBuffer(gl.ARRAY_BUFFER, centerBuffer);
gl.bufferData(gl.ARRAY_BUFFER, center, gl.DYNAMIC_DRAW);
gl.bindBuffer(gl.ARRAY_BUFFER, colorBuffer);
gl.bufferData(gl.ARRAY_BUFFER, color, gl.DYNAMIC_DRAW);
gl.bindBuffer(gl.ARRAY_BUFFER, covABuffer);
gl.bufferData(gl.ARRAY_BUFFER, covA, gl.DYNAMIC_DRAW);
gl.bindBuffer(gl.ARRAY_BUFFER, covBBuffer);
gl.bufferData(gl.ARRAY_BUFFER, covB, gl.DYNAMIC_DRAW);
}
};
}
let currentScene: Scene | null = null;
let currentCamera: Camera | null = null;
this.render = function (scene: Scene, camera: Camera) {
if (scene !== currentScene || camera !== currentCamera) {
const perspectiveCamera = camera as PerspectiveCamera;
if (perspectiveCamera == null) {
throw new Error("Camera is not a PerspectiveCamera");
}
if (currentScene != null || currentCamera != null) {
this.dispose();
}
currentScene = scene;
currentCamera = camera;
initGLContext(this.canvas.width, this.canvas.height, scene, perspectiveCamera);
}
};
this.dispose = function () {
gl.deleteBuffer(vertexBuffer);
gl.deleteBuffer(centerBuffer);
gl.deleteBuffer(colorBuffer);
gl.deleteBuffer(covABuffer);
gl.deleteBuffer(covBBuffer);
gl.deleteShader(vertexShader);
gl.deleteShader(fragmentShader);
gl.deleteProgram(program);
worker.terminate();
};
}
}