This JavaScript program demonstrates how to draw a rotating solid colored cross with lighting WebGL program. This program shows how to specify a set of vertices and normal vectors with indices to specify the geometry.
<head> <title>XoaX.net's WebGL</title> <script id="idVertexShader" type="c"> attribute vec4 av4Vertex; attribute vec4 av4Normal; uniform mat4 um4MvpMatrix; uniform vec3 uv3ObjectColor; uniform vec3 uv3LightColor; uniform vec3 uv3LightDirection; varying vec4 vv4Color; void main() { gl_Position = um4MvpMatrix*av4Vertex; vec3 v3Normal = normalize(av4Normal.xyz); float fIntensity = max(dot(uv3LightDirection, v3Normal), 0.0); vec3 v3Diffuse = fIntensity*uv3LightColor*uv3ObjectColor; vv4Color = vec4(v3Diffuse, 1.0); } </script> <script id="idFragmantShader" type="c"> precision mediump float; varying vec4 vv4Color; void main() { gl_FragColor = vv4Color; } </script> <script type="text/javascript"> var gqTriangleSolid = null; var gfAngle = 0.0; function Initialization() { gfAngle = 0.0; var faVertices = [ // Bottoms .25, -2.25, .25, -.25, -2.25, .25, -.25, -2.25, -.25, .25, -2.25, -.25, 1.25, -.25, -.25, 1.25, -.25, .25, .25, -.25, .25, .25, -.25, -.25, -1.25, -.25, .25, -1.25, -.25, -.25, -.25, -.25, -.25, -.25, -.25, .25, // Tops -.25, 1.25, -.25, -.25, 1.25, .25, .25, 1.25, .25, .25, 1.25, -.25, 1.25, .25, .25, 1.25, .25, -.25, .25, .25, -.25, .25, .25, .25, -1.25, .25, -.25, -1.25, .25, .25, -.25, .25, .25, -.25, .25, -.25, ]; var iaSideDefinitions = [ // Bottom sides 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, // Top Sides 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, // Front Sides 1, 0, 14, 13, 5, 16, 19, 6, 21, 8, 11, 22, // Back Sides 3, 2, 12, 15, 17, 4, 7, 18, 9, 20, 23, 10, // Left Sides 13, 12, 23, 22, 21, 20, 9, 8, 11, 10, 2, 1, // Right Sides 15, 14, 19, 18, 17, 16, 5, 4, 7, 6, 0, 3 ]; var iSides = 18; var iVertsPerSide = 4; // This can be used to render any object with a consistent number of side, like a regular solid. gqTriangleSolid = new TriangleSolid(faVertices, iaSideDefinitions, iSides, iVertsPerSide); // Begin the animation loop with half second intervals. const kiIntervalId = setInterval(RotationRender, 20); } function RotationRender() { var faRotationMatrix = CreateARotationAroundZMatrix(gfAngle); gqTriangleSolid.mfnRender(faRotationMatrix); gfAngle += .03; } function Normalize(faV) { var fL = Math.sqrt(faV[0]*faV[0] + faV[1]*faV[1] + faV[2]*faV[2]); faV[0] /= fL; faV[1] /= fL; faV[2] /= fL; } function Dot(faV1, faV2) { return (faV1[0]*faV2[0] + faV1[1]*faV2[1] + faV1[2]*faV2[2]); } function Cross(faV1, faV2) { return [faV1[1]*faV2[2]-faV1[2]*faV2[1], faV1[2]*faV2[0]-faV1[0]*faV2[2], faV1[0]*faV2[1]-faV1[1]*faV2[0]]; } function Difference(faV1, faV2) { return [faV1[0]-faV2[0], faV1[1]-faV2[1], faV1[2]-faV2[2]]; } function MultiplyMatrices(faaM, faaA) { // M = M*A, Note M != A var faRow = [0,0,0,0]; for (iRow = 0; iRow < 4; ++iRow) { // Copy the current row for(iCol = 0; iCol < 4; ++iCol) { faRow[iCol] = faaM[iRow + 4*iCol]; } for(iCol = 0; iCol < 4; ++iCol) { faaM[iRow + 4*iCol] = 0.0; for (k = 0; k < 4; ++k) { faaM[iRow + 4*iCol] += faRow[k]*faaA[4*iCol + k]; } } } } function CreateARotationAroundZMatrix(fRotateRadians) { var fSin = Math.sin(fRotateRadians); var fCos = Math.cos(fRotateRadians); var faMatrix = new Float32Array([ fCos, 0.0, -fSin, 0.0, 0.0, 1.0, 0.0, 0.0, fSin, 0.0, fCos, 0.0, 0.0, 0.0, 0.0, 1.0]); return faMatrix; } function ApplyMatrixToPoint3D(faMatrix, faPoint) { var faCopyPoint = [0,0,0]; // Copy the point for (i = 0; i < 3; ++i) { faCopyPoint[i] = faPoint[i]; } for(iCol = 0; iCol < 3; ++iCol) { faPoint[iCol] = 0.0; for (iRow = 0; iRow < 3; ++iRow) { faPoint[iCol] += faMatrix[iRow + 4*iCol]*faCopyPoint[iRow]; } } } function CreatePerspectiveMatrix(fFieldOfViewDeg, fAspectRatio, fNearPlane, fFarPlane) { var fFieldOfViewRad = Math.PI*fFieldOfViewDeg/180; var fSin = Math.sin(fFieldOfViewRad); var fCos = Math.cos(fFieldOfViewRad); var fCot = fCos/fSin; var fDepth = fFarPlane - fNearPlane; var faMatrix = new Float32Array([ fCot/fAspectRatio, 0.0, 0.0, 0.0, 0.0, fCot, 0.0, 0.0, 0.0, 0.0, -(fFarPlane + fNearPlane)/fDepth, -1.0, 0.0, 0.0, -(2*fFarPlane*fNearPlane)/fDepth, 0.0]); return faMatrix; } function CreateLookAtMatrix(faEye, faObject, faUp) { var faViewDirection = Difference(faObject, faEye); Normalize(faViewDirection); var faRight = Cross(faViewDirection, faUp); Normalize(faRight); var faStraightUp = Cross(faRight, faViewDirection); var faMatrix = new Float32Array([ faRight[0], faStraightUp[0], -faViewDirection[0], 0.0, faRight[1], faStraightUp[1], -faViewDirection[1], 0.0, faRight[2], faStraightUp[2], -faViewDirection[2], 0.0, -Dot(faEye, faRight), -Dot(faEye, faStraightUp), Dot(faEye, faViewDirection), 1.0]); return faMatrix; } function TriangleSolid(faVertices, iaSideDefinitions, iSides, iVertsPerSide) { // Get the WebGL Context var qCanvas = document.querySelector("#idCanvasWebGL"); var qGL = qCanvas.getContext("webgl"); // Compile the vertex shader var sVertexShaderCode = document.querySelector("#idVertexShader").text; var qVertexShader = qGL.createShader(qGL.VERTEX_SHADER); qGL.shaderSource(qVertexShader, sVertexShaderCode); qGL.compileShader(qVertexShader); // Compile the fragment shader var sFragmentShaderCode = document.querySelector("#idFragmantShader").text; var qFragmentShader = qGL.createShader(qGL.FRAGMENT_SHADER); qGL.shaderSource(qFragmentShader, sFragmentShaderCode); qGL.compileShader(qFragmentShader); // Compile and link the program var qProgram = qGL.createProgram(); qGL.attachShader(qProgram, qVertexShader); qGL.attachShader(qProgram, qFragmentShader); qGL.linkProgram(qProgram); qGL.useProgram(qProgram); this.mqGL = qGL; this.mqProgram = qProgram; // Sides times vertices per side times spatial dimensions var iTrianglesPerSide = iVertsPerSide - 2; this.mfaRenderVertices = new Float32Array(iSides*iVertsPerSide*3); this.mfaRenderNormals = new Float32Array(iSides*iVertsPerSide*3); this.mui8aIndices = new Uint8Array(iSides*iTrianglesPerSide*3); this.miSides = iSides; this.miVertsPerSide = iVertsPerSide; this.miIndices = this.mui8aIndices.length; var faV1 = new Float32Array(3); var faV2 = new Float32Array(3); // Create buffers for (var iFace = 0; iFace < iSides; ++iFace) { var iFaceOffset = 3*iVertsPerSide*iFace; for (var iVertex = 0; iVertex < iVertsPerSide; ++iVertex) { // Get the starting index var iVert = 3*iaSideDefinitions[iVertex + iVertsPerSide*iFace]; for (var iDim = 0; iDim < 3; ++iDim) { this.mfaRenderVertices[iFaceOffset + 3*iVertex + iDim] = faVertices[iVert + iDim]; } } // Get the normal for the current face // Use the middle vertices at each third of the side var iOneThirdIndex = Math.floor(iVertsPerSide/3); var iTwoThirdsIndex = Math.floor(2*iVertsPerSide/3); for (var iDim = 0; iDim < 3; ++iDim) { // Take the first index and the one just half past the middle faV1[iDim] = this.mfaRenderVertices[iFaceOffset + 3*iOneThirdIndex + iDim] - this.mfaRenderVertices[iFaceOffset + iDim]; faV2[iDim] = this.mfaRenderVertices[iFaceOffset + 3*iTwoThirdsIndex + iDim] - this.mfaRenderVertices[iFaceOffset + iDim]; } var faNormal = Cross(faV1, faV2); Normalize(faNormal); // Set the normal for all three vertices on this side for (var iVertex = 0; iVertex < iVertsPerSide; ++iVertex) { for (var iDim = 0; iDim < 3; ++iDim) { this.mfaRenderNormals[iFaceOffset + 3*iVertex + iDim] = faNormal[iDim]; } } // Write the indices for the triangles on this face // There are (iVertsPerSide - 2) of them on each side for (var iTriangle = 0; iTriangle < iTrianglesPerSide; ++iTriangle) { this.mui8aIndices[3*(iTrianglesPerSide*iFace + iTriangle)] = iVertsPerSide*iFace; this.mui8aIndices[3*(iTrianglesPerSide*iFace + iTriangle) + 1] = iTriangle + iVertsPerSide*iFace + 1; this.mui8aIndices[3*(iTrianglesPerSide*iFace + iTriangle) + 2] = iTriangle + iVertsPerSide*iFace + 2; } } var aqBufferData = [ ['av4Vertex', this.mfaRenderVertices], ['av4Normal', this.mfaRenderNormals] ]; for (var qBufferData of aqBufferData) { var qBuffer = qGL.createBuffer(); qGL.bindBuffer(qGL.ARRAY_BUFFER, qBuffer); qGL.bufferData(qGL.ARRAY_BUFFER, qBufferData[1], qGL.STATIC_DRAW); var qAttribute = qGL.getAttribLocation(qProgram, qBufferData[0]); // There are 3 coordinates per vertex qGL.vertexAttribPointer(qAttribute, 3, qGL.FLOAT, false, 0, 0); qGL.enableVertexAttribArray(qAttribute); qGL.bindBuffer(qGL.ARRAY_BUFFER, null); } // Store the indices in the element buffer var qIndexBuffer = qGL.createBuffer(); qGL.bindBuffer(qGL.ELEMENT_ARRAY_BUFFER, qIndexBuffer); qGL.bufferData(qGL.ELEMENT_ARRAY_BUFFER, this.mui8aIndices, qGL.STATIC_DRAW); // The member functions this.mfnRender = function(faRotationMatrix) { var qGL = this.mqGL; var qProgram = this.mqProgram; // Get the storage locations of uniform variables and so on var qMvpMatrix = qGL.getUniformLocation(qProgram, 'um4MvpMatrix'); var qObjectColor = qGL.getUniformLocation(qProgram, 'uv3ObjectColor'); var qLightColor = qGL.getUniformLocation(qProgram, 'uv3LightColor'); var qLightDirection = qGL.getUniformLocation(qProgram, 'uv3LightDirection'); // Set the cube color to red and the light color to white qGL.uniform3f(qObjectColor, 0.4, 0.3, 0.0); qGL.uniform3f(qLightColor, 1.0, 1.0, 1.0); // Set a directional light source, like the Sun var faDirectionOfLight = new Float32Array([.5, 1.0, 2.0]); // Rotate the light direction to keep the front lit ApplyMatrixToPoint3D(faRotationMatrix, faDirectionOfLight); Normalize(faDirectionOfLight); qGL.uniform3fv(qLightDirection, faDirectionOfLight); var faModelViewProj = CreatePerspectiveMatrix(15, qCanvas.width/qCanvas.height, 1, 10); var faLookAt = CreateLookAtMatrix([0, 3, 8],[0, 0, 0],[0, 1, 0]); // Proj*Look*Rotate MultiplyMatrices(faModelViewProj, faLookAt); MultiplyMatrices(faModelViewProj, faRotationMatrix); qGL.uniformMatrix4fv(qMvpMatrix, false, faModelViewProj); qGL.clearColor(0.9, 0.9, 0.9, 1.0); qGL.enable(qGL.DEPTH_TEST); qGL.clear(qGL.COLOR_BUFFER_BIT | qGL.DEPTH_BUFFER_BIT); qGL.drawElements(qGL.TRIANGLES, this.miIndices, qGL.UNSIGNED_BYTE, 0); } } </script> </head> <body onload="Initialization();"> <canvas id="idCanvasWebGL" width="400", height="400" style="border:1px solid brown"></canvas> </body> </html>
© 20072024 XoaX.net LLC. All rights reserved.