glcanvas/js/main.js

212 lines
5.9 KiB
JavaScript

/* Canvas set-up */
const canvas = document.getElementsByTagName("canvas")[0];
const gl = canvas.getContext("webgl", { antialias: false, preserveDrawingBuffer: false });
/* Utility */
const fetchText = async url => (await fetch(url)).text();
/* Viewport, etc. */
/* The actual canvas is statically sized in HTML, and from there scaled with CSS */
/* So, this does not need to be updated ever */
var projectionMatrix = mat4.create();
mat4.perspective(
projectionMatrix,
(45 * Math.PI) / 180,
gl.drawingBufferWidth / gl.drawingBufferHeight,
0.1, 100.0
);
gl.viewport(0, 0, gl.drawingBufferWidth, gl.drawingBufferHeight);
/* WebGL set-up */
// gl.clearColor(.1137, .1254, .1294, 1.0);
gl.clearColor(0.0, 0.0, 0.0, 1.0);
gl.enable(gl.DEPTH_TEST);
gl.enable(gl.CULL_FACE);
// If this gets gc'd the extension stops working supposedly
const ext_depthTexture = gl.getExtension('WEBGL_depth_texture');
if (!ext_depthTexture) {
console.log("no depth texture support!");
}
/* Load model */
const [vertsOut, vertsCount] = parseObj(await fetchText("/model/untitled.obj"));
const modelViewMatrix = mat4.create();
mat4.translate(
modelViewMatrix,
modelViewMatrix,
[0.0, -0.6, -4.0]
);
/* Create vertex buffer */
const vertexBuffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, vertexBuffer);
gl.bufferData(gl.ARRAY_BUFFER, vertsOut, gl.STATIC_DRAW);
/* Shader programs */
const FSIZE = vertsOut.BYTES_PER_ELEMENT;
class ShaderProgram {
constructor(vsSrc, fsSrc, skipNormals = false) {
this.pgm = compile(gl, vsSrc, fsSrc);
/* get attributes that we need for rendering */
this.a_position = gl.getAttribLocation(this.pgm, "a_position");
this.a_normal = gl.getAttribLocation(this.pgm, "a_normal");
this.u_projectionMtx = gl.getUniformLocation(this.pgm, "u_projectionMtx");
this.u_modelViewMtx = gl.getUniformLocation(this.pgm, "u_modelViewMtx");
/* vertex attributes */
gl.vertexAttribPointer(this.a_position, 3, gl.FLOAT, false, FSIZE * 6, 0);
gl.enableVertexAttribArray(this.a_position);
if (!skipNormals) {
gl.vertexAttribPointer(this.a_normal, 3, gl.FLOAT, false, FSIZE * 6, FSIZE * 3);
gl.enableVertexAttribArray(this.a_normal);
}
/* set our projection matrix, because it doesn't change */
gl.uniformMatrix4fv(this.u_projectionMtx, false, projectionMatrix);
}
use() {
gl.useProgram(this.pgm);
}
preRender(mvMtx) {
gl.uniformMatrix4fv(this.u_modelViewMtx, false, mvMtx);
}
}
const spVis = new ShaderProgram(
await fetchText("/shader/my.vert"),
await fetchText("/shader/my.frag")
);
const spVis_u_clock = gl.getUniformLocation(spVis.pgm, "u_clock");
const spVis_u_depthSampler = gl.getUniformLocation(spVis.pgm, "u_depthSampler");
const spDepth = new ShaderProgram(
await fetchText("/shader/depth.vert"),
await fetchText("/shader/depth.frag"),
true
);
/* Utility function */
const rotate3D = (mtx, x, y, z) => {
mat4.rotate(
mtx, mtx,
x,
[1, 0, 0]
);
mat4.rotate(
mtx, mtx,
y,
[0, 1, 0]
);
mat4.rotate(
mtx, mtx,
z,
[0, 0, 1]
);
};
rotate3D(modelViewMatrix, 0.0, 0.0, 0.0);
/* Set up depth texture... */
// Color texture
const colorTexture = gl.createTexture();
gl.bindTexture(gl.TEXTURE_2D, colorTexture);
// gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
gl.texImage2D(
gl.TEXTURE_2D, 0, gl.RGBA,
gl.drawingBufferWidth, gl.drawingBufferHeight,
0, gl.RGBA, gl.UNSIGNED_BYTE, null
);
// Depth texture
const depthTexture = gl.createTexture();
gl.bindTexture(gl.TEXTURE_2D, depthTexture);
// gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
gl.texImage2D(
gl.TEXTURE_2D, 0, gl.DEPTH_COMPONENT,
gl.drawingBufferWidth, gl.drawingBufferHeight,
0, gl.DEPTH_COMPONENT, gl.UNSIGNED_SHORT, null
);
// Framebuffer to render the texture with
const frameBuffer = gl.createFramebuffer();
gl.bindFramebuffer(gl.FRAMEBUFFER, frameBuffer);
gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, colorTexture, 0);
gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.DEPTH_ATTACHMENT, gl.TEXTURE_2D, depthTexture, 0);
gl.bindFramebuffer(gl.FRAMEBUFFER, null);
/* Set uniforms we don't update */
spVis.use();
gl.activeTexture(gl.TEXTURE0);
gl.bindTexture(gl.TEXTURE_2D, depthTexture);
gl.uniform1i(spVis_u_depthSampler, 0);
const draw_update = () => {
// Rotate camera
var delta = performance.now() - lastDraw + 0.001;
lastDraw = performance.now();
var r1 = 1 / delta;
r1 = (r1 * Math.PI) / 180;
// rotate3D(modelViewMatrix, 0, r1, 0);
};
const draw_depthPass = () => {
// Bind to framebuffer for depth texture
gl.bindFramebuffer(gl.FRAMEBUFFER, frameBuffer);
gl.colorMask(false, false, false, false);
// Do I even need to do this?
gl.clear(gl.DEPTH_BUFFER_BIT);
// Draw geometry
gl.drawArrays(gl.TRIANGLES, 0, vertsCount);
};
const draw_visualPass = () => {
// Bind back
gl.bindFramebuffer(gl.FRAMEBUFFER, null);
gl.colorMask(true, true, true, true);
gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
// Draw everything
gl.drawArrays(gl.TRIANGLES, 0, vertsCount);
};
/* Render */
var lastDraw = performance.now();
const draw = () => {
requestAnimationFrame(draw);
// Perform any updates to the scene
draw_update();
// Pre-render setup (depth)
spDepth.use();
spDepth.preRender(modelViewMatrix);
// Do depth pass
draw_depthPass();
// Pre-render setup (visual)
spVis.use();
spVis.preRender(modelViewMatrix);
gl.uniform1f(spVis_u_clock, performance.now() % 4000);
// Do the real visual pass
draw_visualPass();
}
/* Start */
requestAnimationFrame(draw);