Skip to content

Instantly share code, notes, and snippets.

@mathdoodle
Last active July 7, 2020 20:35
Show Gist options
  • Save mathdoodle/47e0cf9bffd69d8ee6a8fc6188b98955 to your computer and use it in GitHub Desktop.
Save mathdoodle/47e0cf9bffd69d8ee6a8fc6188b98955 to your computer and use it in GitHub Desktop.
Geometry using WebGL from Ground Zero

Geometry using WebGL from Ground Zero

Overview

A starting point for learning Geometry using the WebGL API.

/**
* Displays an exception by writing it to a <pre> element.
*/
export function displayError(e: any) {
const stderr = <HTMLPreElement> document.getElementById('my-output')
stderr.style.color = "#FF0000"
stderr.innerHTML = `${e}`
}
/**
* Returns the canvas element identified by elementId.
* If the element is not found, or if it is not a canvas element,
* an Error is thrown.
*/
export function getCanvasElementById(elementId: string): HTMLCanvasElement {
const element = document.getElementById(elementId)
if (element) {
// Further checks here that we have the script element we need?
if (element.nodeName.toLowerCase() === 'canvas') {
return element as HTMLCanvasElement
}
else {
throw new Error(`elementId: ${element.nodeName} ${elementId} does not identify a canvas element.`)
}
}
else {
throw new Error(`elementId ${elementId} does not identify an element.`)
}
}
/**
* Returns the script element identified by elementId.
* If the element is not found, or if it is not a script element,
* an Error is thrown.
*/
export function getScriptElementById(elementId: string): HTMLScriptElement {
const element = document.getElementById(elementId)
if (element) {
// Further checks here that we have the script element we need?
if (element.nodeName.toLowerCase() === 'script') {
return element as HTMLScriptElement
}
else {
throw new Error(`elementId ${elementId} does not identify a script element.`)
}
}
else {
throw new Error(`elementId ${elementId} does not identify an element.`)
}
}
/**
*
*/
export function getWebGLContext(canvas: HTMLCanvasElement): WebGLRenderingContext {
// Try to grab the standard context. If it fails, fallback to experimental.
const context = canvas.getContext('webgl') || canvas.getContext('experimental-webgl')
if (context) {
return context
}
else {
throw new Error("Unable to initialize WebGL. Your browser may not support it.")
}
}
<!DOCTYPE html>
<html>
<head>
<script src="https://jspm.io/system.js"></script>
<link rel='stylesheet' href='style.css'>
</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>
System.defaultJSExtensions = true
System.import('./script')
</script>
</body>
</html>
import { displayError } from './displayError'
/**
* Creates a WebGLProgram with compiled and linked shaders.
*/
function makeProgramInternal(gl: WebGLRenderingContext, vertexShader: string, fragmentShader: string): WebGLProgram {
const vs = gl.createShader(gl.VERTEX_SHADER)
gl.shaderSource(vs, vertexShader)
gl.compileShader(vs)
if (!gl.getShaderParameter(vs, gl.COMPILE_STATUS)) {
throw new Error(`VERTEX_SHADER: ${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(`FRAGMENT_SHADER: ${gl.getShaderInfoLog(fs)}`)
}
const program = gl.createProgram()
if (program) {
gl.attachShader(program, vs)
gl.attachShader(program, fs)
gl.linkProgram(program)
if (!gl.getProgramParameter(program, gl.LINK_STATUS)) {
const msg = gl.getProgramInfoLog(program)
if (typeof msg === 'string') {
throw new Error(msg)
}
}
return program
}
else {
throw new Error("Out of memory")
}
}
export function makeProgram(gl: WebGLRenderingContext, vertexShader: string, fragmentShader: string): WebGLProgram {
try {
return makeProgramInternal(gl, vertexShader, fragmentShader)
}
catch (e) {
displayError(e)
throw e
}
}
{
"name": "copy-of-webgl-from-ground-zero",
"version": "0.1.0",
"description": "Geometry using WebGL from Ground Zero",
"dependencies": {
"DomReady": "1.0.0",
"gl-matrix": "2.3.2"
},
"operatorOverloading": true,
"keywords": [
"WebGL",
"shaders",
"low level",
"GLSL",
"Graphics",
"Geometry",
"mathdoodle"
],
"author": "David Geo Holmes",
"linting": true,
"hideConfigFiles": true
}
import { displayError } from './displayError'
/**
* Catches exceptions thrown in the animation callback and displays them.
*/
export function requestFrame(callback: FrameRequestCallback): number {
const wrapper: FrameRequestCallback = function(time: number) {
try {
callback(time)
}
catch (e) {
displayError(e)
}
}
return window.requestAnimationFrame(wrapper)
}
import { displayError } from './displayError'
import { getWebGLContext } from './getWebGLContext'
import { makeProgram } from './makeProgram'
import { requestFrame } from './requestFrame'
import { getCanvasElementById, getScriptElementById } from './domUtils'
const canvas = getCanvasElementById('my-canvas')
const gl = getWebGLContext(canvas)
const vertexShader = getScriptElementById('shader-vs').innerText
const fragmentShader = getScriptElementById('shader-fs').innerText
const program = makeProgram(gl, vertexShader, fragmentShader)
gl.useProgram(program)
const vbo = gl.createBuffer()
const data = new Float32Array(2)
try {
data[0] = 0.0
data[1] = 0.0
gl.bindBuffer(gl.ARRAY_BUFFER, vbo)
gl.bufferData(gl.ARRAY_BUFFER, data, gl.DYNAMIC_DRAW)
}
catch (e) {
displayError(e)
}
function animate() {
const aVertexPosition = gl.getAttribLocation(program, 'aPosition')
gl.enableVertexAttribArray(aVertexPosition)
gl.bindBuffer(gl.ARRAY_BUFFER, vbo)
gl.vertexAttribPointer(aVertexPosition, 2, gl.FLOAT, false, 0, 0)
gl.drawArrays(gl.POINTS, 0, 1)
}
requestFrame(animate)
void main(void) {
gl_FragColor = vec4(1.0, 1.0, 1.0, 1.0);
}
attribute vec2 aPosition;
void main(void) {
gl_Position = vec4(aPosition, 0.0, 1.0);
gl_PointSize = 6.0;
}
body { background-color: white; }
canvas {
background-color: black;
position: absolute;
left: 15px;
top: 15px;
z-index: 0;
}
pre {
position: relative;
color: rgb(255, 255, 255);
left: 15px;
z-index: 1;
font-size: 18px;
}
{
"allowJs": true,
"checkJs": true,
"declaration": true,
"emitDecoratorMetadata": true,
"experimentalDecorators": true,
"jsx": "react",
"module": "system",
"noImplicitAny": true,
"noImplicitReturns": true,
"noImplicitThis": true,
"noUnusedLocals": true,
"noUnusedParameters": true,
"preserveConstEnums": true,
"removeComments": false,
"skipLibCheck": true,
"sourceMap": true,
"strictNullChecks": true,
"suppressImplicitAnyIndexErrors": true,
"target": "es5",
"traceResolution": true
}
{
"rules": {
"array-type": [
true,
"array"
],
"curly": false,
"comment-format": [
true,
"check-space"
],
"eofline": true,
"forin": true,
"jsdoc-format": true,
"new-parens": true,
"no-conditional-assignment": false,
"no-consecutive-blank-lines": true,
"no-construct": true,
"no-for-in-array": true,
"no-inferrable-types": [
true
],
"no-magic-numbers": false,
"no-shadowed-variable": true,
"no-string-throw": true,
"no-trailing-whitespace": [
true,
"ignore-jsdoc"
],
"no-var-keyword": true,
"one-variable-per-declaration": [
true,
"ignore-for-loop"
],
"prefer-const": true,
"prefer-for-of": true,
"prefer-function-over-method": false,
"prefer-method-signature": true,
"radix": true,
"semicolon": [
true,
"never"
],
"trailing-comma": [
true,
{
"multiline": "never",
"singleline": "never"
}
],
"triple-equals": true,
"use-isnan": true
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment