/* 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);