Things are Shaping Up
The HTML file:
<html>
<head>
<script id="vertex-shader" type="x-shader/x-vertex" >
precision mediump float;
attribute vec4 attributePosition;
attribute float attributeBC;
uniform mat4 uniformTransform;
uniform mat4 uniformProject;
varying vec3 varyingBC;
void main() {
gl_Position = uniformProject * uniformTransform * attributePosition;
if (attributeBC == 0.0) {
varyingBC = vec3(1.0, 0.0, 0.0);
} else if (attributeBC == 1.0) {
varyingBC = vec3(0.0, 1.0, 0.0);
} else {
varyingBC = vec3(0.0, 0.0, 1.0);
}
}
</script>
<script id="fragment-shader" type="x-shader/x-fragment">
precision mediump float;
varying vec3 varyingBC;
uniform vec4 uniformEdgeColor;
// wireframe shader
void main(){
// front face
if (gl_FrontFacing) {
if (any(lessThan(varyingBC, vec3(0.03)))) {
gl_FragColor= uniformEdgeColor;
} else {
gl_FragColor= vec4(0.0, 0.0, 0.0, 1.0);
}
} else {
// back facing
if (any(lessThan(varyingBC, vec3(0.05)))) {
gl_FragColor= uniformEdgeColor;
} else {
gl_FragColor = vec4(1.0, 0.0, 0.0, 0.05);
discard;
}
}
}
</script>
<script type="text/javascript" src="../Common/webgl-utils.js"></script>
<script type="text/javascript" src="../Common/initShaders.js"></script>
<script type="text/javascript" src="../Common/MV.js"></script>
<script type="text/javascript" src="../Models/teapot.js"></script>
<script type="text/javascript" src="../Models/bunny.js"> </script>
<script type="text/javascript" src="../Models/cone.js"> </script>
<script type="text/javascript" src="../Models/dragon.js"> </script>
<script type="text/javascript" src="../Models/head.js"></script>
<script type="text/javascript" src="../Models/stego.js"></script>
<script type="text/javascript" src="../Models/xwing.js"></script>
<script type="text/javascript" src="../Models/butterfly.js"></script>
<script type="text/javascript" src="../Models/copter.js"></script>
<script type="text/javascript" src="../Models/enterprise.js"></script>
<script type="text/javascript" src="../Models/lizard.js"></script>
<script type="text/javascript" src="../Models/tank.js"></script>
<script type="text/javascript" src="../Models/cube.js"></script>
<script type="text/javascript" src="../Models/epcot.js"></script>
<script type="text/javascript" src="../Models/plane.js"></script>
<script type="text/javascript" src="GLCanvas.js"></script>
<script type="text/javascript" src="Widget.js"></script>
<style>
input {
text-align: right;
}
</style>
</head>
<body>
<h1>Things are Shaping Up</h1>
<script>
var canvas = new Canvas(500, 500, Keypress);
var teapot = new Widget(canvas.GL(), canvas.Program(), Teapot_Triangles);
// set up this teapot
teapot.tz = .75
teapot.rx = -90
function Keypress(evnt) {
switch(evnt.key) {
case 'x': teapot.rx++; break;
case 'y': teapot.ry++; break;
case 'z': teapot.rz++; break;
case 'X': teapot.rx--; break;
case 'Y': teapot.ry--; break;
case 'Z': teapot.rz--; break;
}
Redisplay();
}
function Redisplay() {
canvas.Clear();
teapot.Display(canvas.GL(), mat4(), canvas.Translate());
}
Redisplay();
</script>
</body>
GLCanvas.js
'use strict'
class Canvas {
constructor (width, height, Keypress) {
this.height = height;
this.width = width;
this.MakeCanvas();
this.canvas.addEventListener("keypress", Keypress);
this.SetupGL();
this.MakeShaders();
this.Init();
var eye = lookAt([0,0,-1],[0,0,0],[0,1,0]);
var proj = this.Frustum(-0.5,0.5,-0.5,0.5,1,20);
proj = mult(proj,eye);
this.gl.uniformMatrix4fv(this.projLoc, false,flatten(proj));
}
Frustum(l,r,b,t,n,f) {
var m = mat4(1);
m[0][0] = 2 * n / (r - l);
m[0][1] = 0;
m[0][2] = (r + l) / (r - l);
m[0][3] = 0;
m[1][0] = 0;
m[1][1] = 2 * n / (t - b);
m[1][2] = (t + b) / (t - b);
m[1][3] = 0;
m[2][0] = 0;
m[2][1] = 0;
m[2][2] = -(f + n) / (f - n);
m[2][3] = -2 * f * n / (f - n);
m[3][0] = 0;
m[3][1] = 0;
m[3][2] = -1;
m[3][3] = 0;
return m;
}
MakeCanvas() {
if (this.width == undefined || this.width < 0) {
this.width = 300;
}
if (this.height == undefined || this.height < 0) {
this.height = 300;
}
this.canvas = document.createElement('canvas')
this.canvas.tabIndex=0;
this.canvas.height = this.height;
this.canvas.width = this.width;
this.canvas.style.border = '1px solid #000';
document.body.appendChild(this.canvas);
}
SetupGL() {
this.gl = WebGLUtils.setupWebGL(this.canvas);
if (!this.gl) {
alert ("WebGL isn't available");
return;
}
this.gl.getExtension('OES_standard_derivatives');
}
MakeShaders() {
var gl = this.gl;
this.program = initShaders(gl, "vertex-shader","fragment-shader");
gl.useProgram(this.program);
this.projLoc = gl.getUniformLocation(this.program, "uniformProject");
this.transLoc = gl.getUniformLocation(this.program, "uniformTransform");
this.colorLoc = gl.getUniformLocation(this.program, "uniformEdgeColor");
}
Init() {
var gl = this.gl;
gl.clearColor(1.0, 1.0, 1.0, 1.0);
gl.viewport(0,0, this.width, this.height);
gl.enable(gl.BLEND);
//suggested here https://limnu.com/webgl-blending-youre-probably-wrong/
gl.blendFuncSeparate(gl.SRC_ALPHA,
gl.ONE_MINUS_SRC_ALPHA, gl.ONE, gl.ONE_MINUS_SRC_ALPHA);
//gl.blendFunc(gl.ONE, gl.ONE_MINUS_SRC_ALPHA);
gl.enable(gl.DEPTH_TEST);
// not needed, LESS is the default
// gl.depthFunc(gl.LESS);
// From https://learnopengl.com/Advanced-OpenGL/Depth-testing
// gl.TRUE is not a value of true apparently
// but this is not needed, it is true by default.
// gl.depthMask(true);
// console.log(gl.getParameter(gl.DEPTH_WRITEMASK));
//gl.enable(gl.CULL_FACE);
//gl.cullFace(gl.BACK);
gl.frontFace(gl.CCW);
// set the default edge color for everything
this.NewEdgeColor([1.0, 0.0, 0.0, 1.0]);
}
NewEdgeColor(c) {
this.gl.uniform4fv(this.colorLoc, c);
}
Program() {
return this.program;
}
GL() {
return this.gl;
}
Translate() {
return this.transLoc;
}
Clear() {
this.gl.clear(this.gl.COLOR_BUFFER_BIT | this.gl.DEPTH_BUFFER_BIT);
}
};
Widget.js
'use strict'
class Widget {
constructor(gl, program, tris) {
this.size = tris.length;
var bcs = []
for (var i=0;i<tris.length/3;i++) {
bcs.push([0.0,1.0,2.0]);
}
this.SetupVBO(gl, program, tris, bcs);
this.Reset();
this.Transform();
}
// things we might want to have to totally reset the item.
Reset() {
this.visible = true;
this.rx = 0;
this.ry = 0;
this.rz = 0;
this.sx = 1;
this.sy = 1;
this.sz = 1;
this.tx = 0;
this.ty = 0;
this.tz = 0;
}
SetupVBO(gl, program, tris, bcs) {
this.vPos = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, this.vPos);
this.aPos = gl.getAttribLocation(program, "attributePosition");
gl.vertexAttribPointer(this.aPos, 3, gl.FLOAT, false, 0, 0);
gl.enableVertexAttribArray(this.aPos);
gl.bufferData(gl.ARRAY_BUFFER,flatten(tris),gl.STATIC_DRAW);
this.vBC = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, this.vBC);
this.aBC = gl.getAttribLocation(program, "attributeBC");
gl.vertexAttribPointer(this.aBC,1,gl.FLOAT, false,0,0);
gl.enableVertexAttribArray(this.aBC);
gl.bufferData(gl.ARRAY_BUFFER,flatten(bcs),gl.STATIC_DRAW);
}
Show() {
this.visible = true;
}
Hide() {
this.visible = false;
}
Visible() {
return this.visible;
}
Transform() {
var tmp = translate(this.tx, this.ty, this.tz);
tmp = mult(tmp, scalem(this.sx, this.sy, this.sz));
tmp = mult(tmp, rotate(this.rz, [0,0,1]));
tmp = mult(tmp, rotate(this.ry, [0,1,0]));
tmp = mult(tmp, rotate(this.rx, [1,0,0]));
this.transform = tmp;
}
Display(gl, transform, transLoc) {
if (this.visible) {
// make sure that the transform matrix is up to date.
this.Transform();
// multiply it by any incoming transformation matrix
let tx =
mult(transform, this.transform);
// use it
gl.uniformMatrix4fv(transLoc, false, flatten(tx));
gl.bindBuffer(gl.ARRAY_BUFFER, this.vPos);
gl.vertexAttribPointer(this.aPos, 3, gl.FLOAT, false, 0, 0);
gl.bindBuffer(gl.ARRAY_BUFFER, this.vBC);
gl.vertexAttribPointer(this.aBC,1,gl.FLOAT, false,0,0);
gl.drawArrays(gl.TRIANGLES, 0, this.size);
}
}
}