diff --git a/www/draw-scene.js b/www/draw-scene.js new file mode 100644 index 0000000..f470ae4 --- /dev/null +++ b/www/draw-scene.js @@ -0,0 +1,88 @@ +function drawScene(gl, programInfo, buffers) { + gl.clearColor(0.0, 0.0, 0.0, 1.0); // Clear to black, fully opaque + gl.clearDepth(1.0); // Clear everything + gl.enable(gl.DEPTH_TEST); // Enable depth testing + gl.depthFunc(gl.LEQUAL); // Near things obscure far things + + // Clear the canvas before we start drawing on it. + + gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT); + + // Create a perspective matrix, a special matrix that is + // used to simulate the distortion of perspective in a camera. + // Our field of view is 45 degrees, with a width/height + // ratio that matches the display size of the canvas + // and we only want to see objects between 0.1 units + // and 100 units away from the camera. + + const fieldOfView = (45 * Math.PI) / 180; // in radians + const aspect = gl.canvas.clientWidth / gl.canvas.clientHeight; + const zNear = 0.1; + const zFar = 100.0; + const projectionMatrix = mat4.create(); + + // note: glMatrix always has the first argument + // as the destination to receive the result. + mat4.perspective(projectionMatrix, fieldOfView, aspect, zNear, zFar); + + // Set the drawing position to the "identity" point, which is + // the center of the scene. + const modelViewMatrix = mat4.create(); + + // Now move the drawing position a bit to where we want to + // start drawing the square. + mat4.translate( + modelViewMatrix, // destination matrix + modelViewMatrix, // matrix to translate + [-0.0, 0.0, -6.0], + ); // amount to translate + + // Tell WebGL how to pull out the positions from the position + // buffer into the vertexPosition attribute. + setPositionAttribute(gl, buffers, programInfo); + + // Tell WebGL to use our program when drawing + gl.useProgram(programInfo.program); + + // Set the shader uniforms + gl.uniformMatrix4fv( + programInfo.uniformLocations.projectionMatrix, + false, + projectionMatrix, + ); + gl.uniformMatrix4fv( + programInfo.uniformLocations.modelViewMatrix, + false, + modelViewMatrix, + ); + + { + const offset = 0; + const vertexCount = 4; + gl.drawArrays(gl.TRIANGLE_STRIP, offset, vertexCount); + } +} + +// Tell WebGL how to pull out the positions from the position +// buffer into the vertexPosition attribute. +function setPositionAttribute(gl, buffers, programInfo) { + const numComponents = 2; // pull out 2 values per iteration + const type = gl.FLOAT; // the data in the buffer is 32bit floats + const normalize = false; // don't normalize + const stride = 0; // how many bytes to get from one set of values to the next + // 0 = use type and numComponents above + const offset = 0; // how many bytes inside the buffer to start from + gl.bindBuffer(gl.ARRAY_BUFFER, buffers.position); + gl.vertexAttribPointer( + programInfo.attribLocations.vertexPosition, + numComponents, + type, + normalize, + stride, + offset, + ); + gl.enableVertexAttribArray(programInfo.attribLocations.vertexPosition); +} + +export { drawScene }; + diff --git a/www/init-buffers.js b/www/init-buffers.js new file mode 100644 index 0000000..cb9db0c --- /dev/null +++ b/www/init-buffers.js @@ -0,0 +1,29 @@ +function initBuffers(gl) { + const positionBuffer = initPositionBuffer(gl); + + return { + position: positionBuffer, + }; +} + +function initPositionBuffer(gl) { + // Create a buffer for the square's positions. + const positionBuffer = gl.createBuffer(); + + // Select the positionBuffer as the one to apply buffer + // operations to from here out. + gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer); + + // Now create an array of positions for the square. + const positions = [1.0, 1.0, -1.0, 1.0, 1.0, -1.0, -1.0, -1.0]; + + // Now pass the list of positions into WebGL to build the + // shape. We do this by creating a Float32Array from the + // JavaScript array, then use it to fill the current buffer. + gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(positions), gl.STATIC_DRAW); + + return positionBuffer; +} + +export { initBuffers }; + diff --git a/www/shader.html b/www/shader.html new file mode 100644 index 0000000..26d87f4 --- /dev/null +++ b/www/shader.html @@ -0,0 +1,18 @@ + + + + + + WebGL Demo + + + + + + + + diff --git a/www/webgl-demo.js b/www/webgl-demo.js new file mode 100644 index 0000000..c0cb590 --- /dev/null +++ b/www/webgl-demo.js @@ -0,0 +1,108 @@ +import { initBuffers } from "./init-buffers.js"; +import { drawScene } from "./draw-scene.js"; + +main(); + +/* TODO: Avoid using alerts! Check return values instead, + * TODO: or create/use a Result like object. + */ + +// Initialize a shader program, so WebGL knows how to draw our data +function initShaderProgram(gl, vsSource, fsSource) { + const vertexShader = loadShader(gl, gl.VERTEX_SHADER, vsSource); + const fragmentShader = loadShader(gl, gl.FRAGMENT_SHADER, fsSource); + + // Create the shader program + const program = gl.createProgram(); + gl.attachShader(program, vertexShader); + gl.attachShader(program, fragmentShader); + gl.linkProgram(program); + + // If creating the shader program failed, alert + if (!gl.getProgramParameter(program, gl.LINK_STATUS)) { + alert( + `Unable to initialize the shader program: ${gl.getProgramInfoLog( + program, + )}`, + ); + return null; + } + + return program; +} + +// Creates a shader of the given type, uploads the source and compiles +function loadShader(gl, type, source) { + const shader = gl.createShader(type); + + // Send the source to the shader object + gl.shaderSource(shader, source); + + // Compile the shader program + gl.compileShader(shader); + + // See if it compiled successfully + if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) { + alert( + `An error occurred compiling the shaders: ${gl.getShaderInfoLog(shader)}`, + ); + gl.deleteShader(shader); + return null; + } + + return shader; +} + +function main() { + const canvas = document.querySelector("#gl-canvas"); + // Initialize the GL context + const gl = canvas.getContext("webgl"); + + // Only continue if WebGL is available and working + if (gl === null) { + alert( + "Unable to initialize WebGL. Your browser or machine may not support it.", + ); + return; + } + + // Vertex shader program + const vsSource = ` + attribute vec4 aVertexPosition; + uniform mat4 uModelViewMatrix; + uniform mat4 uProjectionMatrix; + void main() { + gl_Position = uProjectionMatrix * uModelViewMatrix * aVertexPosition; + } + `; + + const fsSource = ` + void main() { + gl_FragColor = vec4(1.0, 1.0, 1.0, 1.0); + } + `; + + const shaderProgram = initShaderProgram(gl, vsSource, fsSource); + + // Collect all the info needed to use the shader program. + // Look up which attribute our shader program is using + // for aVertexPosition and look up uniform locations. + const programInfo = { + program: shaderProgram, + attribLocations: { + vertexPosition: gl.getAttribLocation(shaderProgram, "aVertexPosition"), + }, + uniformLocations: { + projectionMatrix: gl.getUniformLocation(shaderProgram, "uProjectionMatrix"), + modelViewMatrix: gl.getUniformLocation(shaderProgram, "uModelViewMatrix"), + }, + }; + + // Here's where we call the routine that builds all the + // objects we'll be drawing. + const buffers = initBuffers(gl); + + // Draw the scene + drawScene(gl, programInfo, buffers); +} +