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)
}
}
}