Skip to content

Instantly share code, notes, and snippets.

@mathdoodle
Last active August 17, 2016 15:16
Show Gist options
  • Save mathdoodle/9fc93fc5b7b39c2d01915d0387560cc2 to your computer and use it in GitHub Desktop.
Save mathdoodle/9fc93fc5b7b39c2d01915d0387560cc2 to your computer and use it in GitHub Desktop.
Computer Graphics using WebGL

Introduction to Computer Graphics using WebGL

Overview

A WebGL scaffold.

/**
* Creates a WebGLProgram with compiled and linked shaders.
*/
export default function createProgram(gl: WebGLRenderingContext, vertexShader: string, fragmentShader: string): WebGLProgram {
// TODO: Proper cleanup if we throw an error at any point.
const vs = gl.createShader(gl.VERTEX_SHADER)
gl.shaderSource(vs, vertexShader)
gl.compileShader(vs)
if (!gl.getShaderParameter(vs, gl.COMPILE_STATUS)) {
throw new Error(gl.getShaderInfoLog(vs))
}
const fs = gl.createShader(gl.FRAGMENT_SHADER)
gl.shaderSource(fs, fragmentShader)
gl.compileShader(fs)
if (!gl.getShaderParameter(fs, gl.COMPILE_STATUS)) {
throw new Error(gl.getShaderInfoLog(fs))
}
const program = gl.createProgram()
gl.attachShader(program, vs)
gl.attachShader(program, fs)
gl.linkProgram(program)
if (!gl.getProgramParameter(program, gl.LINK_STATUS)) {
throw new Error(gl.getProgramInfoLog(program))
}
return program
}
/**
* Displays an exception by writing it to a <pre> element.
*/
export default function displayError(e: any) {
const stderr = <HTMLPreElement>document.getElementById('my-output')
stderr.style.color = "#FFFF00"
stderr.innerHTML = `${e}`
}
/**
*
*/
export default function getWebGLContext(canvas: HTMLCanvasElement): WebGLRenderingContext {
if (!(canvas instanceof HTMLCanvasElement)) {
throw new Error("canvas must be an HTMLCanvasElement")
}
let context: WebGLRenderingContext = null
try {
// Try to grab the standard context. If it fails, fallback to experimental.
context = canvas.getContext('webgl') || canvas.getContext('experimental-webgl')
}
catch(e) {
}
if (context) {
return context
}
else {
throw new Error("Unable to get WebGL context. Your browser may not support it.")
}
}
<!doctype html>
<html>
<head>
<style>
/* STYLE-MARKER */
</style>
<script src="https://jspm.io/system.js"></script>
<!-- SHADERS-MARKER -->
<!-- SCRIPTS-MARKER -->
</head>
<body>
<canvas id='my-canvas' width='500' height='500'>
Your browser does not support the HTML5 canvas element.
</canvas>
<pre id='my-output'></pre>
<script>
// CODE-MARKER
</script>
<script>
System.import('./script.js')
</script>
</body>
</html>
{
"name": "intro-webgl",
"version": "0.1.0",
"description": "Computer Graphics using WebGL",
"dependencies": {},
"keywords": [
"WebGL",
"shaders",
"low level",
"GLSL",
"Graphics",
"functions",
"3D",
"Introduction"
],
"author": "David Geo Holmes"
}
import displayError from './displayError'
/**
* Catches exceptions thrown in the animation callback and displays them.
*/
export default function requestFrame(callback: FrameRequestCallback): number {
const wrapper: FrameRequestCallback = function(time: number) {
try {
callback(time)
}
catch(e) {
displayError(e)
}
}
return window.requestAnimationFrame(wrapper)
}
import requestFrame from './requestFrame'
import displayError from './displayError'
import getWebGLContext from './getWebGLContext'
import createProgram from './createProgram'
try {
const canvasId = 'my-canvas'
const canvas = <HTMLCanvasElement>document.getElementById(canvasId)
if (!canvas) {
throw new Error(`Unable to get element with id '${canvasId}'`)
}
const gl = getWebGLContext(canvas)
gl.clearColor(0.0, 0.0, 0.0, 1.0)
gl.viewport(0, 0, canvas.width, canvas.height)
// Compile and link the shaders into a program.
const vs_source = document.getElementById('shader-vs').innerText
const fs_source = document.getElementById('shader-fs').innerText
const program = createProgram(gl, vs_source, fs_source)
gl.useProgram(program)
const xs = [-1.0, -0.8, -0.5, -0.2, 0.0, 0.2, 0.5, 0.8, 1.0]
const data = new Float32Array(xs)
const vbo = gl.createBuffer()
gl.bindBuffer(gl.ARRAY_BUFFER, vbo)
gl.bufferData(gl.ARRAY_BUFFER, data, gl.STATIC_DRAW)
gl.bindBuffer(gl.ARRAY_BUFFER, null)
const animate = function() {
gl.clear(gl.COLOR_BUFFER_BIT)
const x = gl.getAttribLocation(program, 'x')
gl.enableVertexAttribArray(x)
gl.bindBuffer(gl.ARRAY_BUFFER, vbo)
gl.vertexAttribPointer(x, 1, gl.FLOAT, false, 0, 0)
gl.bindBuffer(gl.ARRAY_BUFFER, null)
gl.bindBuffer(gl.ARRAY_BUFFER, vbo)
gl.drawArrays(gl.POINTS, 0, xs.length)
gl.bindBuffer(gl.ARRAY_BUFFER, null)
gl.disableVertexAttribArray(x)
requestFrame(animate)
}
requestFrame(animate)
}
catch(e) {
displayError(e)
}
/**
* Fragment Shader.
*
* The primary purpose of this GLSL function is to determine the color and alpha value
* of a pixel. This is done by assigning a vec4, interpreted as [red, green, blue, alpha],
* to the reserved gl_FragColor variable.
*/
void main(void) {
gl_FragColor = vec4(0.0, 1.0, 0.0, 1.0);
}
/**
* Vertex Shader
*
* The primary purpose of this GLSL function is to determine a vertex position
* in space from the specifies attribute and uniform parameters. This is done by assigning
* a vec4, interpreted as [x, y, z, w], to the reserved gl_Position variable.
*/
attribute float x;
void main(void) {
gl_Position = vec4(x, 0.0, 1.0, 1.0);
gl_PointSize = 16.0;
}
body {
background-color: #242424;
overflow: hidden;
}
canvas {
background-color: white;
position: relative;
left: 10px;
top: 10px;
z-index: 2;
}
pre {
position: relative;
color: rgb(255, 255, 255);
left: 10px;
z-index: 1;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment