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>© 20072025 XoaX.net LLC. All rights reserved.