-
-
Save dvingo/7009b44bb827d9d511caf46f5a78a591 to your computer and use it in GitHub Desktop.
Resolution independent rendering of Bezier curves in WebGL
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<!doctype html> | |
<html> | |
<head> | |
<title>Resolution independent rendering of Bezier curves in WebGL</title> | |
<meta http-equiv="content-type" content="text/html; charset=UTF-8"> | |
<script src="glMatrix-0.9.6.min.js"></script> | |
<script id="shader-vs" type="x-shader/x-vertex"> | |
attribute vec3 aVertexPosition; | |
attribute vec2 aBezierCoord; | |
uniform mat4 uMVMatrix; | |
uniform mat4 uPMatrix; | |
varying vec2 vBezierCoord; | |
void main(void) { | |
gl_Position = uPMatrix * uMVMatrix * vec4(aVertexPosition, 1.0); | |
vBezierCoord = aBezierCoord; | |
} | |
</script> | |
<script id="shader-fs-aa" type="x-shader/x-fragment"> | |
#extension GL_OES_standard_derivatives : enable | |
#ifdef GL_ES | |
precision highp float; | |
#endif | |
varying vec2 vBezierCoord; | |
void main(void) { | |
vec2 px = dFdx(vBezierCoord); | |
vec2 py = dFdy(vBezierCoord); | |
float fx = 2.0 * vBezierCoord.x * px.x - px.y; | |
float fy = 2.0 * vBezierCoord.y * py.x - py.y; | |
float sd = (vBezierCoord.x * vBezierCoord.x - vBezierCoord.y) / sqrt(fx * fx + fy * fy); | |
gl_FragColor = vec4(0.0, 0.0, 1.0, clamp(0.5 - sd, 0.0, 1.0)); | |
} | |
</script> | |
<script id="shader-fs" type="x-shader/x-fragment"> | |
#ifdef GL_ES | |
precision highp float; | |
#endif | |
varying vec2 vBezierCoord; | |
void main(void) { | |
float d = (vBezierCoord.s * vBezierCoord.s) - vBezierCoord.t; | |
if(d < 0.0) { | |
gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0); | |
} else { | |
discard; | |
} | |
} | |
</script> | |
<script> | |
var gl; | |
function initGL(canvas) { | |
try { | |
gl = canvas.getContext("experimental-webgl"); | |
gl.viewportWidth = canvas.width; | |
gl.viewportHeight = canvas.height; | |
} catch (e) { | |
} | |
if (!gl) { | |
alert("Could not initialise WebGL, sorry :-("); | |
} | |
} | |
function getShader(gl, id) { | |
var shaderScript = document.getElementById(id); | |
if (!shaderScript) { | |
return null; | |
} | |
var str = ""; | |
var k = shaderScript.firstChild; | |
while (k) { | |
if (k.nodeType == 3) { | |
str += k.textContent; | |
} | |
k = k.nextSibling; | |
} | |
var shader; | |
if (shaderScript.type == "x-shader/x-fragment") { | |
shader = gl.createShader(gl.FRAGMENT_SHADER); | |
} else if (shaderScript.type == "x-shader/x-vertex") { | |
shader = gl.createShader(gl.VERTEX_SHADER); | |
} else { | |
return null; | |
} | |
gl.shaderSource(shader, str); | |
gl.compileShader(shader); | |
if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) { | |
alert(gl.getShaderInfoLog(shader)); | |
return null; | |
} | |
return shader; | |
} | |
var shaderProgram; | |
function initShaders() { | |
var derivatesExt = "OES_standard_derivatives"; | |
var derivatesSupported = (gl.getSupportedExtensions().indexOf(derivatesExt) >= 0); | |
if(derivatesSupported) { | |
gl.getExtension(derivatesExt); | |
} | |
var fragmentShader = getShader(gl, derivatesSupported ? "shader-fs-aa" : "shader-fs"); | |
var vertexShader = getShader(gl, "shader-vs"); | |
shaderProgram = gl.createProgram(); | |
gl.attachShader(shaderProgram, vertexShader); | |
gl.attachShader(shaderProgram, fragmentShader); | |
gl.linkProgram(shaderProgram); | |
if (!gl.getProgramParameter(shaderProgram, gl.LINK_STATUS)) { | |
alert("Could not initialise shaders"); | |
} | |
gl.useProgram(shaderProgram); | |
shaderProgram.vertexPositionAttribute = gl.getAttribLocation(shaderProgram, "aVertexPosition"); | |
gl.enableVertexAttribArray(shaderProgram.vertexPositionAttribute); | |
shaderProgram.bezierCoordAttribute = gl.getAttribLocation(shaderProgram, "aBezierCoord"); | |
gl.enableVertexAttribArray(shaderProgram.bezierCoordAttribute); | |
shaderProgram.pMatrixUniform = gl.getUniformLocation(shaderProgram, "uPMatrix"); | |
shaderProgram.mvMatrixUniform = gl.getUniformLocation(shaderProgram, "uMVMatrix"); | |
} | |
var mvMatrix = mat4.create(); | |
var pMatrix = mat4.create(); | |
function setMatrixUniforms() { | |
gl.uniformMatrix4fv(shaderProgram.pMatrixUniform, false, pMatrix); | |
gl.uniformMatrix4fv(shaderProgram.mvMatrixUniform, false, mvMatrix); | |
} | |
var triangleVertexPositionBuffer; | |
var bezierCoordBuffer; | |
function initBuffers() { | |
squareVertexPositionBuffer = gl.createBuffer(); | |
gl.bindBuffer(gl.ARRAY_BUFFER, squareVertexPositionBuffer); | |
vertices = [ | |
-1.0, -1.0, 0.0, | |
0.0, 1.0, 0.0, | |
1.0, -1.0, 0.0 | |
]; | |
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(vertices), gl.STATIC_DRAW); | |
squareVertexPositionBuffer.itemSize = 3; | |
squareVertexPositionBuffer.numItems = 3; | |
bezierCoordBuffer = gl.createBuffer(); | |
gl.bindBuffer(gl.ARRAY_BUFFER, bezierCoordBuffer); | |
var bezierCoords = [ | |
1.0, 1.0, | |
0.5, 0.0, | |
0.0, 0.0 | |
]; | |
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(bezierCoords), gl.STATIC_DRAW); | |
bezierCoordBuffer.itemSize = 2; | |
bezierCoordBuffer.numItems = 3; | |
} | |
function drawScene() { | |
gl.viewport(0, 0, gl.viewportWidth, gl.viewportHeight); | |
gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT); | |
mat4.perspective(45, gl.viewportWidth / gl.viewportHeight, 0.1, 100.0, pMatrix); | |
mat4.identity(mvMatrix); | |
mat4.translate(mvMatrix, [0.0, 0.0, -4.0]); | |
gl.bindBuffer(gl.ARRAY_BUFFER, squareVertexPositionBuffer); | |
gl.vertexAttribPointer(shaderProgram.vertexPositionAttribute, squareVertexPositionBuffer.itemSize, gl.FLOAT, false, 0, 0); | |
gl.bindBuffer(gl.ARRAY_BUFFER, bezierCoordBuffer); | |
gl.vertexAttribPointer(shaderProgram.bezierCoordAttribute, bezierCoordBuffer.itemSize, gl.FLOAT, false, 0, 0); | |
setMatrixUniforms(); | |
gl.drawArrays(gl.TRIANGLES, 0, squareVertexPositionBuffer.numItems); | |
} | |
function webGLStart() { | |
var canvas = document.getElementById("canvas"); | |
initGL(canvas); | |
initShaders(); | |
initBuffers(); | |
gl.clearColor(0.0, 0.0, 0.0, 0.05); | |
gl.enable(gl.DEPTH_TEST); | |
gl.enable(gl.BLEND); | |
gl.blendFunc(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA); | |
drawScene(); | |
} | |
</script> | |
</head> | |
<body onload="webGLStart();"> | |
<canvas id="canvas" style="border:none;" width="500" height="500"></canvas> | |
</body> | |
</html> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment