This JavaScript program demonstrates how to draw a rotating solid colored cube with lighting WebGL program. This program shows how to specify a set of vertices and normal vectors with indices to specify the geometry.
<!DOCTYPE html> <html> <head> <title>XoaX.net's WebGL</title> <script id="idVertexShader" type="c"> attribute vec4 av4Vertex; attribute vec4 av4Normal; uniform mat4 um4MvpMatrix; uniform vec3 uv3CubeColor; 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*uv3CubeColor; 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 gfAngle = 0.0; function Initialization() { gfAngle = 0.0; // Begin the animation loop with half second intervals. const kiIntervalId = setInterval(RotationRender, 20); } function RotationRender() { var faRotationMatrix = CreateARotationAroundZMatrix(gfAngle); Render(faRotationMatrix); gfAngle += .03; } function Render(faRotationMatrix) { // 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); // Get the storage locations of uniform variables and so on var qMvpMatrix = qGL.getUniformLocation(qProgram, 'um4MvpMatrix'); var qCubeColor = qGL.getUniformLocation(qProgram, 'uv3CubeColor'); 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(qCubeColor, 1.0, 0.0, 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([4, 5, 6],[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); // There are 6 sides with 2 triangles per side and 3 vertices per triangle: 6x2x3 = 36 var iVertexCount = CreateBuffers(qGL, qProgram); qGL.drawElements(qGL.TRIANGLES, iVertexCount, qGL.UNSIGNED_BYTE, 0); } 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 CreateBuffers(qGL, qProgram) { // 6 sides, 4 vertices per side, 3 coordinates per vertex var faVertices = new Float32Array(6*4*3); var faNormalVectors = new Float32Array(6*4*3); for (var iDim = 0; iDim < 3; ++iDim) { // Loop over the front and back: xyz, xzy, yzx, yxz, zxy, zyx for (var iFace = 0; iFace < 2; ++iFace) { var iBaseFaceIndex = 12*(iDim + 3*iFace); // Create the vertices for (var iVertex = 0; iVertex < 4; ++iVertex) { // (1, 1), (-1, 1), (-1, -1), (1, -1), 00, 01, 10, 11 var iBaseIndex = iBaseFaceIndex + iVertex*3; var bHighBit = (iVertex > 1); var bLowBit = (iVertex == 1 || iVertex == 3); if (iFace == 0) { // The iDim dimension is kept constant faVertices[iBaseIndex + iDim] = 1.0; faVertices[iBaseIndex + ((iDim + 1)%3)] = ((iVertex == 0 || iVertex == 3) ? 1.0 : -1.0); faVertices[iBaseIndex + ((iDim + 2)%3)] = ((iVertex == 0 || iVertex == 1) ? 1.0 : -1.0); } else { faVertices[12*(iDim + 3*iFace) + iVertex*3 + iDim] = -1.0; faVertices[iBaseIndex + ((iDim + 2)%3)] = ((iVertex == 0 || iVertex == 3) ? 1.0 : -1.0); faVertices[iBaseIndex + ((iDim + 1)%3)] = ((iVertex == 0 || iVertex == 1) ? 1.0 : -1.0); } } // Create the normals. They are constant over each face for (var iVertex = 0; iVertex < 4; ++iVertex) { var iBaseIndex = iBaseFaceIndex + iVertex*3; faNormalVectors[iBaseIndex + iDim] = ((iFace == 0) ? 1.0 : -1.0); faNormalVectors[iBaseIndex + ((iDim + 1)%3)] = 0.0; faNormalVectors[iBaseIndex + ((iDim + 2)%3)] = 0.0; } } } // There are 6 sides, 2 triangles per side, 3 vertices per triangle var ui8aIndices = new Uint8Array(6*2*3); for (var iSide = 0; iSide < 6; ++iSide) { var iBaseIndex = 6*iSide; for (var iTriangle = 0; iTriangle < 2; ++iTriangle) { // We index in terms of coordinate triples. So, our indices arer from 0 to 23 ui8aIndices[iBaseIndex + 3*iTriangle] = 4*iSide; ui8aIndices[iBaseIndex + 1 + 3*iTriangle] = 4*iSide + 1 + iTriangle; ui8aIndices[iBaseIndex + 2 + 3*iTriangle] = 4*iSide + 2 + iTriangle; } } var aqBufferData = [ ['av4Vertex', faVertices], ['av4Normal', faNormalVectors] ]; 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 point and 3 vertices per triangle 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, ui8aIndices, qGL.STATIC_DRAW); return ui8aIndices.length; } </script> </head> <body onload="Initialization();"> <canvas id="idCanvasWebGL" width="400", height="400" style="border:1px solid red"></canvas> </body> </html>
© 20072024 XoaX.net LLC. All rights reserved.