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