This JavaScript program demonstrates how orthographic projections change the way on object is seen in a WebGL program. The view on the right displays the box that defines the space of the orthographic projection and the view on the left displays how that orthographic projection displays objects.
<!DOCTYPE html> <html> <head> <title>XoaX.net's WebGL</title> <script id="idModelVertexShader" type="c"> attribute vec4 av4Vertex; attribute vec4 av4Color; varying vec4 vv4Color; void main() { gl_Position = av4Vertex; vv4Color = av4Color; } </script> <script id="idModelFragmantShader" type="c"> precision mediump float; varying vec4 vv4Color; void main() { gl_FragColor = vv4Color; } </script> <script id="idViewVertexShader" type="c"> attribute vec4 av4Vertex; attribute vec4 av4Color; varying vec4 vv4Color; void main() { gl_Position = av4Vertex; vv4Color = av4Color; } </script> <script id="idViewFragmantShader" type="c"> precision mediump float; varying vec4 vv4Color; void main() { gl_FragColor = vv4Color; } </script> <script type="text/javascript"> var gqModelWebGL = null; var gqViewWebGL = null; function CreateProgramAndContext(qInstanceWebGL) { // Get the WebGL Context var qCanvas = document.querySelector("#"+qInstanceWebGL.msCanvasID); qInstanceWebGL.mqGL = qCanvas.getContext("webgl"); var qGL = qInstanceWebGL.mqGL; // Compile the vertex shader var sVertexShaderCode = document.querySelector("#"+qInstanceWebGL.msVertexShaderID).text; var qVertexShader = qGL.createShader(qGL.VERTEX_SHADER); qGL.shaderSource(qVertexShader, sVertexShaderCode); qGL.compileShader(qVertexShader); // Compile the fragment shader var sFragmentShaderCode = document.querySelector("#"+qInstanceWebGL.msFragmentShaderID).text; var qFragmentShader = qGL.createShader(qGL.FRAGMENT_SHADER); qGL.shaderSource(qFragmentShader, sFragmentShaderCode); qGL.compileShader(qFragmentShader); // Compile and link the program qInstanceWebGL.mqProgram = qGL.createProgram(); qGL.attachShader(qInstanceWebGL.mqProgram, qVertexShader); qGL.attachShader(qInstanceWebGL.mqProgram, qFragmentShader); qGL.linkProgram(qInstanceWebGL.mqProgram); qGL.useProgram(qInstanceWebGL.mqProgram); } function CreateBuffers(qInstanceWebGL) { var qGL = qInstanceWebGL.mqGL; var qVerticesBuffer = qGL.createBuffer(); qGL.bindBuffer(qGL.ARRAY_BUFFER, qVerticesBuffer); qGL.bufferData(qGL.ARRAY_BUFFER, qInstanceWebGL.mfaTransformedVertices, qGL.STATIC_DRAW); var qVertexLoc = qGL.getAttribLocation(qInstanceWebGL.mqProgram, 'av4Vertex'); qGL.vertexAttribPointer(qVertexLoc, 4, qGL.FLOAT, false, 0, 0); qGL.enableVertexAttribArray(qVertexLoc); var qColorsBuffer = qGL.createBuffer(); qGL.bindBuffer(qGL.ARRAY_BUFFER, qColorsBuffer); qGL.bufferData(qGL.ARRAY_BUFFER, qInstanceWebGL.mfaVertexColors, qGL.STATIC_DRAW); var qColors = qGL.getAttribLocation(qInstanceWebGL.mqProgram, 'av4Color'); qGL.vertexAttribPointer(qColors, 4, qGL.FLOAT, false, 0, 0); qGL.enableVertexAttribArray(qColors); } var gfaVertices = null; function Initialization() { gfaVertices = new Float32Array([ // Put the vertices for the diamond first, then the cube vertices 0.0, 0.85, 0.0, 1.0, 0.85, 0.0, 0.85, 1.0, -0.85, 0.0, -0.85, 1.0, 0.0, -0.85, 0.0, 1.0, // These must be drawn back to front to render it correctly with the alpha blending -1.0, 1.0, 1.0, 1.0, -1.0, 1.0, -1.0, 1.0, -1.0, -1.0, 1.0, 1.0, -1.0, -1.0, -1.0, 1.0, // x = -1 1.0, -1.0, 1.0, 1.0, 1.0, -1.0, -1.0, 1.0, -1.0, -1.0, 1.0, 1.0, -1.0, -1.0, -1.0, 1.0, // y = -1 1.0, 1.0, -1.0, 1.0, 1.0, -1.0, -1.0, 1.0, -1.0, 1.0, -1.0, 1.0, -1.0, -1.0, -1.0, 1.0, // z = -1 1.0, 1.0, 1.0, 1.0, 1.0, -1.0, 1.0, 1.0, 1.0, 1.0, -1.0, 1.0, 1.0, -1.0, -1.0, 1.0, // x = 1 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, -1.0, 1.0, -1.0, 1.0, 1.0, 1.0, -1.0, 1.0, -1.0, 1.0, // y = 1 1.0, 1.0, 1.0, 1.0, -1.0, 1.0, 1.0, 1.0, 1.0, -1.0, 1.0, 1.0, -1.0, -1.0, 1.0, 1.0 // z = 1 ]); gqModelWebGL = new CInstanceWebGL("idModelCanvas", "idModelVertexShader", "idModelFragmantShader", 4); gqViewWebGL = new CInstanceWebGL("idViewCanvas", "idViewVertexShader", "idViewFragmantShader", 7*4); CreateProgramAndContext(gqModelWebGL); CreateProgramAndContext(gqViewWebGL); CreateBuffers(gqModelWebGL); CreateBuffers(gqViewWebGL); // Begin the animation loop. const kiIntervalId = setInterval(Render, 20); } function Render() { RenderModel(gqModelWebGL); RenderModel(gqViewWebGL); } var gfAngle = 0.0; function RenderModel(qInstanceWebGL) { // Create the orthographic projection. var fLeft = -1.5 + Math.cos(1.2*gfAngle); var fRight = 1.5 + Math.cos(1.1*gfAngle); var fBottom = -1.5 + Math.cos(1.5*gfAngle); var fTop = 1.5 + Math.cos(1.25*gfAngle); var fNear = 1.5 + Math.cos(1.3*gfAngle); var fFar = -1.5 + Math.cos(1.4*gfAngle); var faViewedOrthoMatrix = CreateOrthographicMatrix(fLeft, fRight, fBottom, fTop, fNear, fFar); var qGL = qInstanceWebGL.mqGL; var faVert = qInstanceWebGL.mfaTransformedVertices; var faClr = qInstanceWebGL.mfaVertexColors; // Create the rotation matrix var faRotationMatrix = CreateARotationAroundYMatrix(gfAngle); gfAngle += .005; gfAngle = ((gfAngle >= 2000.0*Math.PI) ? (gfAngle - 2000.0*Math.PI) : gfAngle); // Transform the first four vertices by the rotation: 4 vertices with 4 coordinates // First copy the vertices before the transformation for (var i = 0; i < 16; ++i) { faVert[i] = gfaVertices[i]; } // Transform each diamond vertex for (var i = 0; i < 4; ++i) { MultiplyMatrixVertex(faRotationMatrix, faVert, 4*i); // Set the colors too faClr[4*i] = .25; faClr[4*i + 1] = .4 + Math.min(Math.max(Math.sin(faVert[4*i + 2]), -.4), .4); faClr[4*i + 2] = .25; faClr[4*i + 3] = 1.0; } if (qInstanceWebGL !== gqViewWebGL) { for (var i = 0; i < 4; ++i) { MultiplyMatrixVertex(faViewedOrthoMatrix, faVert, 4*i); } } else { //if (qInstanceWebGL === gqViewWebGL) { var faLookAtMatrix = CreateLookAtMatrix([ 1, 3, 2],[0, 0, 0],[0, 1, 0]); // Create the orthographic matrix var fOrthoSize = 5.0; var faOrthoMatrix = CreateOrthographicMatrix(-fOrthoSize, fOrthoSize, -fOrthoSize, fOrthoSize, fOrthoSize, -fOrthoSize); MultiplyMatrices(faOrthoMatrix, faLookAtMatrix); // Add the extra colors for the view cube for (var iFace = 0; iFace < 6; ++iFace) { var fBrightness = 0.0; if (iFace % 3 == 0) { fBrightness = 1.0/7.0; } else if (iFace % 3 == 2) { fBrightness = 2.0/7.0; } else { fBrightness = 4.0/7.0; } var iBase = 16 + 16*iFace; for (var iVertex = 0; iVertex < 4; ++iVertex) { var iOffset = iBase + 4*iVertex; faVert[iOffset] = ((gfaVertices[iOffset] == 1.0) ? fRight : fLeft); faVert[iOffset + 1] = ((gfaVertices[iOffset + 1] == 1.0) ? fTop : fBottom); faVert[iOffset + 2] = ((gfaVertices[iOffset + 2] == 1.0) ? fNear : fFar); faVert[iOffset + 3] = gfaVertices[iOffset + 3]; faClr[iOffset] = fBrightness; faClr[iOffset + 1] = fBrightness; faClr[iOffset + 2] = fBrightness; faClr[iOffset + 3] = .2; } } for (var i = 0; i < 28; ++i) { MultiplyMatrixVertex(faOrthoMatrix, faVert, 4*i); } } // We need to create the buffers afterward CreateBuffers(qInstanceWebGL); qGL.clearColor(0.0, 0.0, 0.0, 1.0); if (qInstanceWebGL !== gqViewWebGL) { qGL.clear(qGL.COLOR_BUFFER_BIT); qGL.drawArrays(qGL.TRIANGLE_STRIP, 0, 4); } else { qGL.enable(qGL.DEPTH_TEST); qGL.clear(qGL.COLOR_BUFFER_BIT | qGL.DEPTH_BUFFER_BIT); // Enable alpha blending qGL.enable(qGL.BLEND); // Set blending function qGL.blendFunc(qGL.SRC_ALPHA, qGL.ONE_MINUS_SRC_ALPHA); // Draw the diamond first, since it is totally opaque. qGL.drawArrays(qGL.TRIANGLE_STRIP, 0, 4); // The six sides qGL.drawArrays(qGL.TRIANGLE_STRIP, 4, 4); qGL.drawArrays(qGL.TRIANGLE_STRIP, 8, 4); qGL.drawArrays(qGL.TRIANGLE_STRIP, 12, 4); qGL.drawArrays(qGL.TRIANGLE_STRIP, 16, 4); qGL.drawArrays(qGL.TRIANGLE_STRIP, 20, 4); qGL.drawArrays(qGL.TRIANGLE_STRIP, 24, 4); } } function CreateARotationAroundYMatrix(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; } // Multiply the four coordinate vertex in V at the start index function MultiplyMatrixVertex(faM, faV, iStart) { // V = M*V var faCopy = [0,0,0,0]; for (var i = 0; i < 4; ++i) { faCopy[i] = faV[iStart + i]; } for (iRow = 0; iRow < 4; ++iRow) { faV[iStart + iRow] = faM[iRow]*faCopy[0] + faM[iRow + 4]*faCopy[1] + faM[iRow + 8]*faCopy[2] + faM[iRow + 12]*faCopy[3]; } } 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 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(faObject, faRight), -Dot(faObject, faStraightUp), -Dot(faObject, faViewDirection), 1.0]); return faMatrix; } function CreateOrthographicMatrix(fLeft, fRight, fBottom, fTop, fNear, fFar) { if (fLeft >= fRight || fBottom >= fTop || fFar >= fNear) { throw 'Improper Orthographic Projection Matrix'; } fDx = fRight - fLeft; fDy = fTop - fBottom; fDz = fNear - fFar; var faMatrix = new Float32Array([ 2.0/fDx, 0.0, 0.0, 0.0, 0.0, 2.0/fDy, 0.0, 0.0, 0.0, 0.0, 2.0/fDz, 0.0, -(fLeft + fRight)/fDx, -(fBottom + fTop)/fDy, -(fNear + fFar)/fDz, 1.0]); return faMatrix; } 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 CInstanceWebGL(sCanvasID, sVertexShaderID, sFragmentShaderID, iVertices) { this.mqGL = null; this.mqProgram = null; this.msCanvasID = sCanvasID; this.msVertexShaderID = sVertexShaderID; this.msFragmentShaderID = sFragmentShaderID; this.mfaTransformedVertices = new Float32Array(4*iVertices); this.mfaVertexColors = new Float32Array(4*iVertices); } </script> </head> <body onload="Initialization();"> <canvas id="idModelCanvas" width="400", height="400" style="border:1px solid lightgray"></canvas> <canvas id="idViewCanvas" width="400", height="400" style="border:1px solid lightgray"></canvas> </body> </html>
© 20072024 XoaX.net LLC. All rights reserved.