X/x, Y/y, Z/z to rotate
F/f, B/b to turn on/off front/back face visibility.
<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="https://mirkwood.cs.edinboro.edu/~bennett/f360-19/Common/webgl-utils.js"></script> <script type="text/javascript" src="https://mirkwood.cs.edinboro.edu/~bennett/f360-19/Common/initShaders.js"></script> <script type="text/javascript" src="https://mirkwood.cs.edinboro.edu/~bennett/f360-19/Common/MV.js"></script> <script type="text/javascript" src="https://mirkwood.cs.edinboro.edu/~bennett/f360-19/Models/teapot.js"></script> <script type="text/javascript" src="GLCanvas.js"></script> <script type="text/javascript" src="Widget.js"></script> <script type="text/javascript" src="UI.js"></script> </head> <body> <h1>Things are Shaping Up</h1> <script> "use strict" var canvas = new Canvas(500, 500, Keypress) var object = new Widget(canvas.GL(), canvas.Program(), Teapot_Triangles) Reset() Redisplay() </script> <p> X/x, Y/y, Z/z to rotate <p> F/f, B/b to turn on/off front/back face visibility. </body>
'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) // From https://learnopengl.com/Advanced-OpenGL/Depth-testing gl.enable(gl.DEPTH_TEST) gl.frontFace(gl.CW) // 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) } }
"use strict" function Keypress(evnt) { switch(evnt.key) { case 'x': object.rx = (object.rx + 1) % 360; break case 'y': object.ry = (object.ry + 1) % 360; break case 'z': object.rz = (object.rz + 1) % 360; break case 'X': object.rx = (object.rx - 1) % 360; break case 'Y': object.ry = (object.ry - 1) % 360; break case 'Z': object.rz = (object.rz - 1) % 360; break case 'F': object.FrontOn(); break case 'f': object.FrontOff(); break case 'B': object.BackOn(); break case 'b': object.BackOff(); break case 'r': Reset(); break } Redisplay(); } function Reset() { object.tz = .75 object.ty = 0 object.tz = .55 object.rx = 270 object.ry = 0 object.rz = 0 object.FrontOn() object.BackOff() } function Redisplay() { canvas.Clear(); object.Display(canvas.GL(), mat4(), canvas.Translate()); }
'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 this.front = false this.back = true } FrontOn() { this.front = false } FrontOff() { this.front = true } BackOn() { this.back = false } BackOff() { this.back = true } 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 } SetCull(gl) { if (this.front && this.back) { gl.enable(gl.CULL_FACE) gl.cullFace(gl.FRONT_AND_BACK) } else if (this.front) { gl.enable(gl.CULL_FACE) gl.cullFace(gl.FRONT) } else if (this.back) { gl.enable(gl.CULL_FACE) gl.cullFace(gl.BACK) } else { gl.disable(gl.CULL_FACE) } } Display(gl, transform, transLoc) { if (this.visible) { this.SetCull(gl) // 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) } } }