Vindmølle
Det kan være lurt å kikke på OpenGL Shading Language (GLSL) Reference Pages [2] for å få en forklaring av de enkelte metodene i shading-eksemplene og OpenGL ES 2.0 Reference Pages [3] for å få en forklaring av de enkelte metodene i javascriptet.
Du finner noen litt mer fancy framstillinger på sidene:
Geometrien i vidmølla er bygget opp av tre typer elementer: cone, skive, halvkule, der en cone er en sylinder med forskjellig diameter i de to endene. Vi kjenner disse grunnformene, untatt cone, fra de andre WGL-eksemplene i dette materialet. Det er heller ikke noe spesielt ved shaderprogrammene som skiller dem fra de andre eksemplene.
Shaderene er slik (merk at de er laget for textur, som ikke er brukt her):
#ifdef GL_ES precision mediump float; #endif varying vec3 vTransformedNormal; varying vec4 vPosition; // texture related varying mediump vec2 vTextureCoord; varying float vIsTextured; uniform sampler2D uSampler; uniform vec3 uMaterialAmbientColor; uniform vec3 uMaterialDiffuseColor; uniform vec3 uMaterialSpecularColor; uniform float uMaterialShininess; uniform vec3 uMaterialEmissiveColor; uniform vec3 uAmbientLightingColor; uniform vec3 uPointLightingDiffuseColor; uniform vec3 uPointLightingSpecularColor; uniform vec3 uPointLightingLocation; void main(void) { vec3 ambientLightWeighting = uAmbientLightingColor; vec3 lightDirection = normalize(uPointLightingLocation - vPosition.xyz); vec3 normal = normalize(vTransformedNormal); vec3 specularLightWeighting = vec3(0.0, 0.0, 0.0); vec3 eyeDirection = normalize(-vPosition.xyz); vec3 reflectionDirection = reflect(-lightDirection, normal); float specularLightBrightness = pow(max(dot(reflectionDirection, eyeDirection), 0.0), uMaterialShininess); specularLightWeighting = uPointLightingSpecularColor * specularLightBrightness; float diffuseLightBrightness = max(dot(normal, lightDirection), 0.0); vec3 diffuseLightWeighting = uPointLightingDiffuseColor * diffuseLightBrightness; vec3 materialAmbientColor = uMaterialAmbientColor; vec3 materialDiffuseColor = uMaterialDiffuseColor; vec3 materialSpecularColor = uMaterialSpecularColor; vec3 materialEmissiveColor = uMaterialEmissiveColor; float alpha = 1.0; gl_FragColor = vec4( materialAmbientColor * ambientLightWeighting + materialDiffuseColor * diffuseLightWeighting + materialSpecularColor * specularLightWeighting + materialEmissiveColor, alpha ); }
attribute mediump vec3 aVertexNormal; // or in attribute mediump vec3 aVertexPosition; attribute mediump vec2 aTextureCoord; uniform mediump mat4 uNormalMatrix; uniform mediump mat4 uMVMatrix; uniform mediump mat4 uPMatrix; uniform float uIsTextured; varying vec3 vTransformedNormal; varying vec4 vPosition; //varying lowp vec4 vColor; //varying mediump vec3 vLighting; varying float vIsTextured; varying mediump vec2 vTextureCoord; void main(void) { vPosition = uMVMatrix * vec4(aVertexPosition, 1.0); gl_Position = uPMatrix * vPosition; vTransformedNormal=(uNormalMatrix * vec4(aVertexNormal, 1.0)).xyz; vIsTextured=uIsTextured; vTextureCoord = aTextureCoord; }
Andre involverte javascript
//globals var canvas; var gl; var shaderProgram; var perspectiveMatrix; // drawing precision on shape nets var Precision=40; //shapes var aCone; var aDisk; var aCylinder; var aHat; var aTower; var windMill; // tower var towerLength=18.0; var towerRadiusBottom=2.0; var towerRadiusFactor=0.5; // top is towerRadiusFactor of bottom // house on top var houseLength=4.0; var houseRadius=1.2; // wing rotation speed var delta_wRot=2.0; // mill rotations var mRot=0.0; //start function start() { canvas = document.getElementById("glcanvas"); initWebGL(canvas); if (gl) { gl.clearColor(1.0, 1.0, 1.0, 1.0); gl.clearDepth(1.0); gl.enable(gl.DEPTH_TEST); gl.depthFunc(gl.LEQUAL); initBuffers(); initShaders(); tick(); } } //eofstart function tick() { // using webgl-utils.js requestAnimFrame(tick,canvas); drawScene(); } //initWebGL function initWebGL() { gl = null; // browser and versions reckognize webgl differently ? var names = ["webgl", "experimental-webgl", "webkit-3d", "moz-webgl"]; for(var i = 0; i < names.length; i++){ try {gl = canvas.getContext("experimental-webgl");} catch(e) {} if(gl){break;} } if (!gl) { // show captures instead showCaptureInstead(); } } //eofinitWebGL //initBuffers function initBuffers() { // set up all shapes aTower=new Cone(1.0,towerRadiusFactor,1.0,Precision); aCone=new Cone(1.0,1.0,1.0,Precision); aCylinder=new Cylinder(1.0,1.0,Precision); aDisk=new Disk(1.0,Precision); aHat=new Hat(1.0,Precision); windMill=new WindMill(); } //eofinitBuffers //drawScene function drawScene() { gl.viewport(0, 0, canvas.width, canvas.height); gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT); perspectiveMatrix = makePerspective(45, canvas.width / canvas.height, 0.1, 1000.0); mvMatrix=makeIdentity(); setLight(); setWhiteMaterial(); // Move the drawing a bit from default eye-pos (0,0,0) // overall rotation mvTranslate([1.0, -5.0, -50.0]); mvRotate(-90.0, [1, 0, 0]); mvRotate(90.0, [0, 0, 1]); mvTranslate([0.0, 0.0, -10.0]); windMill.draw(); // speed windMill.rotate(delta_wRot); } //eofdrawScene //initShaders function initShaders() { var fragmentShader = getShader(gl, "shader-fs"); var vertexShader = getShader(gl, "shader-vs"); // Create the shader program shaderProgram = gl.createProgram(); gl.attachShader(shaderProgram, vertexShader); gl.attachShader(shaderProgram, fragmentShader); gl.linkProgram(shaderProgram); // ok? if (!gl.getProgramParameter(shaderProgram, gl.LINK_STATUS)) { alert("Unable to initialize the shader program."); } gl.useProgram(shaderProgram); // let the shaderprogram remember the addresses // so we can use when we fill the attribute buffers shaderProgram.vertexPositionAttribute = gl.getAttribLocation(shaderProgram, "aVertexPosition"); gl.enableVertexAttribArray(shaderProgram.vertexPositionAttribute); shaderProgram.vertexNormalAttribute = gl.getAttribLocation(shaderProgram, "aVertexNormal"); gl.enableVertexAttribArray(shaderProgram.vertexNormalAttribute); // mark light and material shaderProgram.materialAmbientColorUniform = gl.getUniformLocation(shaderProgram, "uMaterialAmbientColor"); shaderProgram.materialDiffuseColorUniform = gl.getUniformLocation(shaderProgram, "uMaterialDiffuseColor"); shaderProgram.materialSpecularColorUniform = gl.getUniformLocation(shaderProgram, "uMaterialSpecularColor"); shaderProgram.materialShininessUniform = gl.getUniformLocation(shaderProgram, "uMaterialShininess"); shaderProgram.materialEmissiveColorUniform = gl.getUniformLocation(shaderProgram, "uMaterialEmissiveColor"); shaderProgram.showSpecularHighlightsUniform = gl.getUniformLocation(shaderProgram, "uShowSpecularHighlights"); shaderProgram.ambientLightingColorUniform = gl.getUniformLocation(shaderProgram, "uAmbientLightingColor"); shaderProgram.pointLightingLocationUniform = gl.getUniformLocation(shaderProgram, "uPointLightingLocation"); shaderProgram.pointLightingDiffuseColorUniform = gl.getUniformLocation(shaderProgram, "uPointLightingDiffuseColor"); shaderProgram.pointLightingSpecularColorUniform = gl.getUniformLocation(shaderProgram, "uPointLightingSpecularColor"); } //eofinitShaders //getShader function getShader(gl, id) { var shaderScript = document.getElementById(id); // ok? if (!shaderScript) { return null; } // Building the shader source string. var theSource = ""; var currentChild = shaderScript.firstChild; while(currentChild) { if (currentChild.nodeType == 3) { theSource += currentChild.textContent; } currentChild = currentChild.nextSibling; } // What type of shader, based on its MIME type. 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; // Unknown shader type } // Send the source to the shader object gl.shaderSource(shader, theSource); // Compile the shader program gl.compileShader(shader); // See if it compiled successfully if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) { alert("An error occurred compiling the shaders: " + gl.getShaderInfoLog(shader)); return null; } return shader; } //eofgetShader //set light and color function setLight() { gl.uniform3f(shaderProgram.pointLightingLocationUniform, 100.0, 50.0, 0.0); gl.uniform3f(shaderProgram.ambientLightingColorUniform, 1.0, 1.0, 1.0); gl.uniform3f(shaderProgram.pointLightingDiffuseColorUniform, 1.0, 1.0, 1.0); gl.uniform3f(shaderProgram.pointLightingSpecularColorUniform, 1.0, 1.0, 1.0); gl.uniform1i(shaderProgram.showSpecularHighlightsUniform, true); } function setWhiteMaterial() { // material: White rubber, modified gl.uniform3f(shaderProgram.materialAmbientColorUniform, 0.3,0.3,0.3); gl.uniform3f(shaderProgram.materialDiffuseColorUniform, 0.5,0.5,0.5); gl.uniform3f(shaderProgram.materialSpecularColorUniform, 0.7,0.7,0.7); gl.uniform1f(shaderProgram.materialShininessUniform, 30.0); gl.uniform3f(shaderProgram.materialEmissiveColorUniform, 0.0, 0.0, 0.0); } //eofsetlightandcolor //setMatrixUniforms function setMatrixUniforms() { // set all the transformation matrices (ModelView, perspective and normal var pUniform = gl.getUniformLocation(shaderProgram, "uPMatrix"); gl.uniformMatrix4fv(pUniform, false, new Float32Array(perspectiveMatrix.flatten())); var mvUniform = gl.getUniformLocation(shaderProgram, "uMVMatrix"); gl.uniformMatrix4fv(mvUniform, false, new Float32Array(mvMatrix.flatten())); var normalMatrix = mvMatrix.inverse(); normalMatrix = normalMatrix.transpose(); var nUniform = gl.getUniformLocation(shaderProgram, "uNormalMatrix"); gl.uniformMatrix4fv(nUniform, false, new Float32Array(normalMatrix.flatten())); } //eofsetMatrixUniforms //requestFrame //requestAnimationFrame in a cross browser way, from Googles webgl-lib window.requestAnimFrame = (function() { return window.requestAnimationFrame || window.webkitRequestAnimationFrame || window.mozRequestAnimationFrame || window.oRequestAnimationFrame || window.msRequestAnimationFrame || function(/* function FrameRequestCallback */ callback, /* DOMElement Element */ element) { window.setTimeout(callback, 1000.0/60); }; })(); //eofrequestFrame
//cone function Cone(r1,r2,H,n) { // radius1, radius2, height, precision (both ways) // produce a cone without ends var vertices=[]; //each points coordinates var normals=[]; //each points normal var indices=[]; //each drawobjects index to points (Gl_TRIANGLE_STRIPS) // calculate var dw=2*Math.PI/(1.0*n); var dh=H/(1.0*n); var w=0.0; var h=0.0; var index=0; // along the length while(h < H) { w=0.0; // round the cone while(w < 2*Math.PI+0.001) { var R1=r1+(r2-r1)*(h/H); var P1=[R1*Math.cos(w),R1*Math.sin(w),h]; var R2=r1+(r2-r1)*((h+dh)/H); var P2=[R2*Math.cos(w),R2*Math.sin(w),h+dh]; var P3=[R1*Math.cos(w+dw),R1*Math.sin(w+dw),h]; var A=[P3[0]-P1[0],P3[1]-P1[1],P3[2]-P1[2]]; var B=[P2[0]-P1[0],P2[1]-P1[1],P2[2]-P1[2]]; // C=AxB=(a1b2-a2b1,a2b0-a0b2, a0b1-a1b0) var N1=[A[1]*B[2]-A[2]*B[1],A[2]*B[0]-A[0]*B[2],A[0]*B[1]-A[1]*B[0]]; A=[P3[0]-P2[0],P3[1]-P2[1],P3[2]-P2[2]]; B=[B[0],B[1],B[2]]; var N2=[A[1]*B[2]-A[2]*B[1],A[2]*B[0]-A[0]*B[2],A[0]*B[1]-A[1]*B[0]]; vertices=vertices.concat(P1); normals=normals.concat(N1); indices=indices.concat(index++); vertices=vertices.concat(P2); normals=normals.concat(N2); indices=indices.concat(index++); w+=dw; } h+=dh; } // prepare buffers once and for all this.verticesBuffer= gl.createBuffer(); gl.bindBuffer(gl.ARRAY_BUFFER, this.verticesBuffer); gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(vertices), gl.STATIC_DRAW); this.verticesNormalBuffer= gl.createBuffer(); gl.bindBuffer(gl.ARRAY_BUFFER, this.verticesNormalBuffer); gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(normals), gl.STATIC_DRAW); this.verticesIndexBuffer= gl.createBuffer(); gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, this.verticesIndexBuffer); gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, new Uint16Array(indices), gl.STATIC_DRAW); this.verticesIndexBuffer.COUNT=indices.length; this.draw=function() { gl.bindBuffer(gl.ARRAY_BUFFER,this.verticesBuffer); gl.vertexAttribPointer(shaderProgram.vertexPositionAttribute,3,gl.FLOAT,false,0,0); // Set the normal attribute for the vertices. gl.bindBuffer(gl.ARRAY_BUFFER,this.verticesNormalBuffer); gl.vertexAttribPointer(shaderProgram.vertexNormalAttribute,3,gl.FLOAT,false,0,0); // Draw the cylinder. gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, this.verticesIndexBuffer); gl.drawElements(gl.TRIANGLE_STRIP,this.verticesIndexBuffer.COUNT,gl.UNSIGNED_SHORT,0); } } //eofcone // cylinder function Cylinder(R,H,n) { // produce a cylinder without ends var vertices=[]; //each points coordinates var normals=[]; //each points normal var indices=[]; //each drawobjects index to points (Gl_TRIANGLE_STRIPS) // calculate var dw=2*Math.PI/(1.0*n); var dh=H/(1.0*n); var w=0.0; var h=0.0; var index=0; // along the length while(h < H+0.0001) { w=0.0; // round the cylinder while(w < 2*Math.PI+0.0001) { vertices=vertices.concat([R*Math.cos(w),R*Math.sin(w),h]); normals=normals.concat([R*Math.cos(w),R*Math.sin(w),0]); vertices=vertices.concat([R*Math.cos(w),R*Math.sin(w),h+dh]); normals=normals.concat([R*Math.cos(w),R*Math.sin(w),0]); indices=indices.concat(index++); indices=indices.concat(index++); w+=dw; } h+=dh; } // prepare buffers once and for all this.verticesBuffer= gl.createBuffer(); gl.bindBuffer(gl.ARRAY_BUFFER, this.verticesBuffer); gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(vertices), gl.STATIC_DRAW); this.verticesNormalBuffer= gl.createBuffer(); gl.bindBuffer(gl.ARRAY_BUFFER, this.verticesNormalBuffer); gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(normals), gl.STATIC_DRAW); this.verticesIndexBuffer= gl.createBuffer(); gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, this.verticesIndexBuffer); gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, new Uint16Array(indices), gl.STATIC_DRAW); this.verticesIndexBuffer.COUNT=indices.length; this.draw=function() { gl.bindBuffer(gl.ARRAY_BUFFER,this.verticesBuffer); gl.vertexAttribPointer(shaderProgram.vertexPositionAttribute,3,gl.FLOAT,false,0,0); // Set the normal attribute for the vertices. gl.bindBuffer(gl.ARRAY_BUFFER,this.verticesNormalBuffer); gl.vertexAttribPointer(shaderProgram.vertexNormalAttribute,3,gl.FLOAT,false,0,0); // Draw the cylinder. gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, this.verticesIndexBuffer); gl.drawElements(gl.TRIANGLE_STRIP,this.verticesIndexBuffer.COUNT,gl.UNSIGNED_SHORT,0); } } // eofcylinder function Hat(R,n) { // produce a half sphere var vertices=[]; //each points coordinates var normals=[]; //each points normal var indices=[]; //each drawobjects index to points (Gl_TRIANGLE_STRIPS) // calculate var dw=2*Math.PI/(1.0*n); var dv=(0.5)*Math.PI/(1.0*n); var w=0.0; var index=0; var v=0.0; // outer loop while(v < (0.5)*Math.PI+0.0001) { w=0.0; // inner loop, round the hat while(w < 2*Math.PI+0.0001) { vertices=vertices.concat([R*Math.sin(w)*Math.cos(v), R*Math.cos(w)*Math.cos(v), R*Math.sin(v)]); normals=normals.concat( [R*Math.sin(w)*Math.cos(v), R*Math.cos(w)*Math.cos(v), R*Math.sin(v)]); vertices=vertices.concat([R*Math.sin(w)*Math.cos(v+dv), R*Math.cos(w)*Math.cos(v+dv), R*Math.sin(v+dv)]); normals=normals.concat( [R*Math.sin(w)*Math.cos(v+dv), R*Math.cos(w)*Math.cos(v+dv), R*Math.sin(v+dv)]); indices=indices.concat(index++); indices=indices.concat(index++); w+=dw; } v+=dv; } // prepare buffers once and for all this.verticesBuffer= gl.createBuffer(); gl.bindBuffer(gl.ARRAY_BUFFER, this.verticesBuffer); gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(vertices), gl.STATIC_DRAW); this.verticesNormalBuffer= gl.createBuffer(); gl.bindBuffer(gl.ARRAY_BUFFER, this.verticesNormalBuffer); gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(normals), gl.STATIC_DRAW); this.verticesIndexBuffer= gl.createBuffer(); gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, this.verticesIndexBuffer); gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, new Uint16Array(indices), gl.STATIC_DRAW); this.verticesIndexBuffer.COUNT=indices.length; this.draw=function() { gl.bindBuffer(gl.ARRAY_BUFFER, this.verticesBuffer); gl.vertexAttribPointer(shaderProgram.vertexPositionAttribute, 3, gl.FLOAT, false, 0, 0); // Set the normal attribute for the vertices. gl.bindBuffer(gl.ARRAY_BUFFER, this.verticesNormalBuffer); gl.vertexAttribPointer(shaderProgram.vertexNormalAttribute, 3, gl.FLOAT, false, 0, 0); // Draw the hat. gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, this.verticesIndexBuffer); gl.drawElements(gl.TRIANGLE_STRIP, this.verticesIndexBuffer.COUNT, gl.UNSIGNED_SHORT, 0); } } function Disk(R,n) { // produce a simple disk var vertices=[]; //each points coordinates var normals=[]; //each points normal var indices=[]; //each drawobjects index to points (Gl_TIANGLE_FAN) var dw=2*Math.PI/(1.0*n); var w=0.0; var index=0; vertices=vertices.concat([0.0,0.0,0.0]); normals=normals.concat([0.0,0.0,-1.0]); indices=indices.concat(index++); while(w < 2*Math.PI+0.0001) { vertices=vertices.concat([R*Math.cos(w),R*Math.sin(w),0.0]); normals=normals.concat([0.0,0.0,-1.0]); indices=indices.concat(index++); w+=dw; } // prepare buffers once and for all this.verticesBuffer= gl.createBuffer(); gl.bindBuffer(gl.ARRAY_BUFFER, this.verticesBuffer); gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(vertices), gl.STATIC_DRAW); this.verticesNormalBuffer= gl.createBuffer(); gl.bindBuffer(gl.ARRAY_BUFFER, this.verticesNormalBuffer); gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(normals), gl.STATIC_DRAW); this.verticesIndexBuffer= gl.createBuffer(); gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, this.verticesIndexBuffer); gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, new Uint16Array(indices), gl.STATIC_DRAW); this.verticesIndexBuffer.COUNT=indices.length; this.draw=function() { gl.bindBuffer(gl.ARRAY_BUFFER, this.verticesBuffer); gl.vertexAttribPointer(shaderProgram.vertexPositionAttribute, 3, gl.FLOAT, false, 0, 0); // Set the normal attribute for the vertices. gl.bindBuffer(gl.ARRAY_BUFFER, this.verticesNormalBuffer); gl.vertexAttribPointer(shaderProgram.vertexNormalAttribute, 3, gl.FLOAT, false, 0, 0); // Draw the disk. gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, this.verticesIndexBuffer); gl.drawElements(gl.TRIANGLE_FAN, this.verticesIndexBuffer.COUNT, gl.UNSIGNED_SHORT, 0); } }
function WindMill() { this.wRot=Math.random()*100; this.rotate=function(r){ this.wRot=this.wRot+r*1.0; } this.draw=function() { //mvRotate(mRot,[0.0, 0.0, 1.0]); // Draw the tower. pushMatrix(); mvScale([towerRadiusBottom,towerRadiusBottom,towerLength]); setMatrixUniforms(); aTower.draw(); pushMatrix(); mvRotate(180.0,[1.0, 0.0, 0.0]); setMatrixUniforms(); aDisk.draw(); popMatrix(); popMatrix(); pushMatrix(); mvTranslate([0.0, 0.0, towerLength]); mvScale([towerRadiusBottom*towerRadiusFactor, towerRadiusBottom*towerRadiusFactor, towerLength]); setMatrixUniforms(); aDisk.draw(); popMatrix(); // Draw the house // some adjustments are going on here mvRotate(mRot,[0.0, 0.0, 1.0]); mvTranslate([0.0, 0.0, towerLength]); mvRotate(180.0,[1.0, 0.0, 1.0]); mvTranslate([0.0, 0.0,-houseLength/4 ]); mvScale([houseRadius,houseRadius,houseLength]); setMatrixUniforms(); aCylinder.draw(); pushMatrix(); mvRotate(180.0,[1.0, 0.0, 0.0]); setMatrixUniforms(); aHat.draw(); // draw wings pushMatrix(); mvTranslate([0.0, 0.0,houseLength/6 ]); mvRotate(this.wRot,[0.0, 0.0, 1.0]); pushMatrix(); mvRotate(90.0,[1.0, 0.0, 0.0]); mvScale([0.2,0.2,9.0]); setMatrixUniforms(); aCylinder.draw(); mvScale([1.0,1.0,0.45]); mvTranslate([0.0,0.0,2.1]); setMatrixUniforms(); aHat.draw(); popMatrix(); mvRotate(120.0,[0.0, 0.0, 1.0]); pushMatrix(); mvRotate(90.0,[1.0, 0.0, 0.0]); mvScale([0.2,0.2,9.0]); setMatrixUniforms(); aCylinder.draw(); mvScale([1.0,1.0,0.45]); mvTranslate([0.0,0.0,2.1]); setMatrixUniforms(); aHat.draw(); popMatrix(); mvRotate(120.0,[0.0, 0.0, 1.0]); pushMatrix(); mvRotate(90.0,[1.0, 0.0, 0.0]); mvScale([0.2,0.2,9.0]); setMatrixUniforms(); aCylinder.draw(); mvScale([1.0,1.0,0.45]); mvTranslate([0.0,0.0,2.1]); setMatrixUniforms(); aHat.draw(); popMatrix(); // finnished wings popMatrix(); // close the house end mvTranslate([0.0, 0.0, -1]);// since it is scaled setMatrixUniforms(); aDisk.draw(); // finnished house popMatrix(); } }