The bezier curve below is anti-aliased in browsers that support the OES_standard_derivatives extension (shows blue anti-aliased shape)
Falls back to aliased in unsupported browsers (shows red aliased shape).
Author: Claus Wahlers
<!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> |