Skip to content
Permalink
master
Switch branches/tags

Name already in use

A tag already exists with the provided branch name. Many Git commands accept both tag and branch names, so creating this branch may cause unexpected behavior. Are you sure you want to create this branch?
Go to file
 
 
Cannot retrieve contributors at this time
<!doctype html>
<html lang=en>
<head>
<meta name="author" content="Eberhard Gräther">
<title>egraether - booleans</title>
<style type="text/css">
* {
padding: 0;
margin: 0;
overflow: hidden;
}
#canvas {
z-index: -1;
position: absolute;
top: 0px;
}
p {
font: 16px/24px Georgia, serif;
padding: 10px;
}
</style>
<script type="text/javascript">
var _gaq = _gaq || [];
_gaq.push(['_setAccount', 'UA-18871992-2']);
_gaq.push(['_trackPageview']);
(function() {
var ga = document.createElement('script'); ga.type = 'text/javascript'; ga.async = true;
ga.src = ('https:' == document.location.protocol ? 'https://ssl' : 'http://www') + '.google-analytics.com/ga.js';
var s = document.getElementsByTagName('script')[0]; s.parentNode.insertBefore(ga, s);
})();
</script>
</head>
<body>
<p>
boolean mesh operations by <a href="http://egraether.com">egraether</a> - click to switch between intersection and subtraction - <a href="http://egraether.com/index.html#booleans">more</a>
</p>
<canvas id="canvas">
<p style="padding-top: 50px">
Your browser does not support &lt;canvas&gt; and WebGL<br/>
<a href="http://get.webgl.org">http://get.webgl.org</a>
</p>
</canvas>
<script id="vertex-shader" type="x-shader/x-vertex" charset="utf-8">
uniform mat4 uMVMatrix;
uniform mat4 uPMatrix;
uniform float uNormalDirection;
attribute vec3 aPosition;
attribute vec3 aNormal;
varying vec3 vNormal;
void main( void ) {
gl_Position = uPMatrix * uMVMatrix * vec4( aPosition, 1.0 );
vNormal = aNormal * uNormalDirection;
}
</script>
<script id="fragment-shader" type="x-shader/x-fragment" charset="utf-8">
#ifdef GL_ES
precision highp float;
#endif
uniform vec3 uColor;
uniform vec3 uLight;
varying vec3 vNormal;
const float alpha = 0.7;
void main(void) {
float val = dot( vNormal, uLight ) * 0.4 + 0.6;
gl_FragColor = vec4( uColor * val, alpha);
}
</script>
<script type="text/javascript" charset="utf-8" src="lib/requestAnimationFrame.js"></script>
<script type="text/javascript" charset="utf-8" src="lib/glMatrix.js"></script>
<script type="text/javascript" charset="utf-8">
function loadShader(gl, vertexShaderID, fragmentShaderID) {
var vertexShader = loadShaderScript(gl, vertexShaderID),
fragmentShader = loadShaderScript(gl, fragmentShaderID),
shaderProgram = gl.createProgram();
gl.attachShader(shaderProgram, vertexShader);
gl.attachShader(shaderProgram, fragmentShader);
gl.linkProgram(shaderProgram);
if (!gl.getProgramParameter(shaderProgram, gl.LINK_STATUS)) {
console.log("Unable to initialize the shader program.");
}
return shaderProgram;
};
function loadShaderScript(gl, shaderScriptID) {
var shaderScript = document.getElementById(shaderScriptID),
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, shaderScript.text);
gl.compileShader(shader);
if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) {
console.log("shader " + gl.getShaderInfoLog(shader));
return null;
}
return shader;
};
function initShader(gl, vertexShaderID, fragmentShaderID) {
var shader = loadShader(gl, vertexShaderID, fragmentShaderID);
gl.useProgram(shader);
shader.mvMatrixUniform = gl.getUniformLocation(shader, "uMVMatrix");
shader.pMatrixUniform = gl.getUniformLocation(shader, "uPMatrix");
shader.normalDirectionUniform = gl.getUniformLocation(shader, "uNormalDirection");
shader.colorUniform = gl.getUniformLocation(shader, "uColor");
shader.lightUniform = gl.getUniformLocation(shader, "uLight");
shader.positionAttribute = gl.getAttribLocation(shader, "aPosition");
gl.enableVertexAttribArray(shader.positionAttribute);
shader.normalAttribute = gl.getAttribLocation(shader, "aNormal");
gl.enableVertexAttribArray(shader.normalAttribute);
return shader;
}
function pushMatrix() {
if (!matrixStack) {
matrixStack = [];
}
matrixStack.push(mat4.create(mvMatrix));
};
function popMatrix() {
if (!matrixStack || !matrixStack.length) {
throw "error: popMatrix failed";
}
mvMatrix = matrixStack.pop();
}
var Mesh = function(type) {
this.type = type;
this.position = [
Math.random() - 0.5,
Math.random() - 0.5,
Math.random() - 0.5
];
this.direction = [
Math.random() - 0.5,
Math.random() - 0.5,
Math.random() - 0.5
];
vec3.normalize(this.direction);
vec3.scale(this.direction, 0.01);
};
Mesh.prototype = {
update : function() {
vec3.add(this.position, this.direction);
for (var i = 0; i < 3; i++) {
if (this.position[i] > 1.0 || this.position[i] < -1.0) {
this.direction[i] *= -1;
}
}
},
draw : function(gl) {
pushMatrix();
mat4.translate(mvMatrix, this.position);
gl.uniformMatrix4fv(shader.mvMatrixUniform, false, mvMatrix);
gl.bindBuffer(gl.ARRAY_BUFFER, this.type.vertexBuffer);
gl.vertexAttribPointer(shader.positionAttribute, 3, gl.FLOAT, false, 0, 0);
gl.bindBuffer(gl.ARRAY_BUFFER, this.type.normalBuffer);
gl.vertexAttribPointer(shader.normalAttribute, 3, gl.FLOAT, false, 0, 0);
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, this.type.indexBuffer);
gl.drawElements(gl.TRIANGLES, this.type.indexCount, gl.UNSIGNED_SHORT, 0);
popMatrix();
}
};
var Cube = {
init : function(gl) {
var vertices = [
// front
1, 1, 1,
1, -1, 1,
1, -1, -1,
1, 1, -1,
// back
-1, 1, 1,
-1, 1, -1,
-1, -1, -1,
-1, -1, 1,
// right
1, 1, 1,
1, 1, -1,
-1, 1, -1,
-1, 1, 1,
// left
1, -1, 1,
-1, -1, 1,
-1, -1, -1,
1, -1, -1,
// top
1, 1, 1,
-1, 1, 1,
-1, -1, 1,
1, -1, 1,
// bottom
1, 1, -1,
1, -1, -1,
-1, -1, -1,
-1, 1, -1
];
var normals = [
// front
1, 0, 0,
1, 0, 0,
1, 0, 0,
1, 0, 0,
// back
-1, 0, 0,
-1, 0, 0,
-1, 0, 0,
-1, 0, 0,
// right
0, 1, 0,
0, 1, 0,
0, 1, 0,
0, 1, 0,
// left
0, -1, 0,
0, -1, 0,
0, -1, 0,
0, -1, 0,
// top
0, 0, 1,
0, 0, 1,
0, 0, 1,
0, 0, 1,
// bottom
0, 0, -1,
0, 0, -1,
0, 0, -1,
0, 0, -1
];
var indices = [
// front
0, 1, 2, 0, 2, 3,
// back
4, 5, 6, 4, 6, 7,
// right
8, 9, 10, 8, 10, 11,
// left
12, 13, 14, 12, 14, 15,
// top
16, 17, 18, 16, 18, 19,
// bottom
20, 21, 22, 20, 22, 23
];
this.vertexBuffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, this.vertexBuffer);
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(vertices), gl.STATIC_DRAW);
this.normalBuffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, this.normalBuffer);
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(normals), gl.STATIC_DRAW);
this.indexBuffer = gl.createBuffer();
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, this.indexBuffer);
gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, new Uint16Array(indices), gl.STREAM_DRAW);
this.indexCount = indices.length;
}
};
// http://learningwebgl.com/cookbook/index.php/How_to_draw_a_sphere
var Sphere = {
init: function(gl, slices, stacks) {
var vertices = [],
normals = [],
indices = [];
for (var stack = 0; stack <= stacks; stack++) {
var theta = stack * Math.PI / stacks,
sinTheta = Math.sin(theta),
cosTheta = Math.cos(theta);
for (var slice = 0; slice <= slices; slice++) {
var phi = slice * 2 * Math.PI / slices,
sinPhi = Math.sin(phi),
cosPhi = Math.cos(phi);
var x = cosPhi * sinTheta,
y = cosTheta,
z = sinPhi * sinTheta;
vertices.push(x, y, z);
normals.push(x, y, z);
if (stack < stacks && slice < slices) {
var first = (stack * (slices + 1)) + slice,
second = first + slices + 1;
indices.push(first, first + 1, second, second + 1, second, first + 1);
}
}
}
this.vertexBuffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, this.vertexBuffer);
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(vertices), gl.STATIC_DRAW);
this.normalBuffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, this.normalBuffer);
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(normals), gl.STATIC_DRAW);
this.indexBuffer = gl.createBuffer();
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, this.indexBuffer);
gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, new Uint16Array(indices), gl.STREAM_DRAW);
this.indexCount = indices.length;
}
};
document.onmousemove = function(event) {
mouse.x = event.clientX / canvas.width * 2 - 1;
mouse.y = event.clientY / canvas.height * -2 + 1;
};
document.onmouseup = function(event) {
if (!intersection) {
var m = meshA;
meshA = meshB;
meshB = m;
meshA.type = meshA.type === Sphere ? Cube : Sphere;
}
intersection = !intersection;
};
function render() {
requestAnimationFrame(render, canvas);
update();
draw(gl);
};
function rotate(angle, axis, vector) {
mat4.identity(rotMatrix);
mat4.rotate(rotMatrix, angle, axis);
mat4.multiply(mvMatrix, rotMatrix);
mat4.inverse(rotMatrix);
mat4.multiplyVec3(rotMatrix, vector);
};
function update() {
if (mouse.x > 0.2 || mouse.x < -0.2) {
rotate(mouse.x * 0.1, up, axis);
}
if (mouse.y > 0.2 || mouse.y < -0.2) {
rotate(mouse.y * 0.1, axis, up);
}
meshA.update();
meshB.update();
};
function draw(gl) {
gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT | gl.STENCIL_BUFFER_BIT);
gl.depthMask(false);
gl.uniform3f(shader.colorUniform, 0.0, 0.6, 0.0);
meshA.draw(gl);
gl.uniform3f(shader.colorUniform, 0.6, 0.0, 0.0);
meshB.draw(gl);
gl.depthMask(true);
gl.disable(gl.BLEND);
if (intersection) {
gl.uniform3f(shader.colorUniform, 1.0, 1.0, 0.5);
drawIntersections(meshA, meshB);
} else {
gl.uniform3f(shader.colorUniform, 1.0, 1.0, 0.5);
drawSubtraction(meshA, meshB);
}
gl.enable(gl.BLEND);
};
function drawIntersections(meshA, meshB) {
drawIntersection(meshA, meshB);
gl.clear(gl.DEPTH_BUFFER_BIT | gl.STENCIL_BUFFER_BIT);
drawIntersection(meshB, meshA);
};
function drawIntersection(meshA, meshB) {
// write A in depth buffer
gl.colorMask(false, false, false, false);
meshA.draw(gl);
// increment stencil each fragment where B is in front of A
gl.depthMask(false);
gl.disable(gl.CULL_FACE);
gl.stencilOp(gl.KEEP, gl.KEEP, gl.INCR);
meshB.draw(gl);
// draw A where B hides it once
gl.colorMask(true, true, true, true);
gl.enable(gl.CULL_FACE);
gl.depthFunc(gl.ALWAYS);
gl.stencilFunc(gl.EQUAL, 1, 1);
gl.stencilOp(gl.KEEP, gl.KEEP, gl.KEEP);
meshA.draw(gl);
// reset buffer behavior
gl.stencilFunc(gl.ALWAYS, 0, 0);
gl.depthMask(true);
gl.depthFunc(gl.LESS);
};
function drawSubtraction(meshA, meshB) {
// write back of A in depth and stencil
gl.colorMask(false, false, false, false);
gl.cullFace(gl.FRONT);
gl.stencilOp(gl.KEEP, gl.KEEP, gl.INCR);
meshA.draw(gl);
// draw back of B where it hides backside of A
gl.colorMask(true, true, true, true);
gl.uniform1f(shader.normalDirectionUniform, -1.0);
gl.stencilFunc(gl.EQUAL, 1, 1);
meshB.draw(gl);
// draw front of A where it is falsly hiden by back of B
gl.cullFace(gl.BACK);
gl.uniform1f(shader.normalDirectionUniform, 1.0);
gl.stencilFunc(gl.EQUAL, 2, 2);
gl.depthFunc(gl.GREATER);
meshA.draw(gl);
gl.clear(gl.DEPTH_BUFFER_BIT | gl.STENCIL_BUFFER_BIT);
// write front of B in depth
gl.stencilFunc(gl.ALWAYS, 0, 0);
gl.stencilOp(gl.KEEP, gl.KEEP, gl.KEEP);
gl.depthFunc(gl.LESS);
gl.colorMask(false, false, false, false);
meshB.draw(gl);
// draw front of A
gl.colorMask(true, true, true, true);
meshA.draw(gl);
};
var gl, shader,
meshA, meshB,
up, axis,
mvMatrix, rotMatrix = mat4.create(),
matrixStack = [],
mouse = {x : 0, y : 0},
intersection = true,
canvas;
window.onload = function() {
canvas = document.querySelector("canvas");
if ( !!window.WebGLRenderingContext ) {
gl = canvas.getContext("experimental-webgl", {stencil: true});
}
if ( !gl ) {
canvas.parentNode.innerHTML += '<p>Your browser does not support WebGL.<br/>' +
'<a href="http://get.webgl.org">http://get.webgl.org</a></p>';
return;
}
canvas.style.backgroundColor = "black";
canvas.width = window.innerWidth,
canvas.height = window.innerHeight;
gl.clearColor(0.9, 0.9, 0.9, 1.0);
gl.viewport(0, 0, canvas.width, canvas.height);
gl.blendFunc(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA);
gl.enable(gl.CULL_FACE);
gl.enable(gl.DEPTH_TEST);
gl.enable(gl.STENCIL_TEST);
shader = initShader(gl, "vertex-shader", "fragment-shader");
var light = [3.0, 4.0, 5.0];
vec3.normalize(light);
gl.uniform3fv(shader.lightUniform, light);
gl.uniform1f(shader.normalDirectionUniform, 1.0);
var pMatrix = mat4.create();
mat4.perspective(45, canvas.width / canvas.height, 0.1, 1000, pMatrix);
gl.uniformMatrix4fv(shader.pMatrixUniform, false, pMatrix);
var center = [0, 0, 0],
eye = [6, 0, 0];
up = [0, 0, 1];
axis = [0, -1, 0];
mvMatrix = mat4.lookAt(eye, center, up);
Cube.init(gl);
Sphere.init(gl, 64, 64);
meshA = new Mesh(Cube);
meshB = new Mesh(Sphere);
render();
};
</script>
</body>
</html>