WebGL JavaScript

Rotate Primitives Around the z-axis

This JavaScript program demonstrates how rotate various geometric primitives around the z-axis in WebGL.

RotatePrimitivesAroundZ.html

<!DOCTYPE html>
<html lang="en">
	<head>
		<meta charset="utf-8" />
		<meta name="viewport" content="width=device-width" />
		<title>XoaX.net's Javascript</title>
		<!-- Supply a transparent 1x1 favicon -->
		<link rel="icon" href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mNkYAAAAAYAAjCB0C8AAAAASUVORK5CYII=">
		<style>
			#idCanvas {
				border: 1px solid black;
				width: 800px;
				height: 600px
			}
		</style>
		<script  id="idVertexShader" type="c">
			attribute vec4 av4VertexPosition;
			attribute vec4 av4VertexColor;
			
			varying vec4 vv4Color;
			
			uniform mat4 um4Model;
			uniform mat4 um4Projection;
			void main() {
				gl_Position = um4Projection*um4Model*av4VertexPosition;
				vv4Color = av4VertexColor;
				gl_PointSize = 25.0;
			}
		</script>
		<script  id="idFragmantShader" type="c">
			precision mediump float;
			varying vec4 vv4Color;
			void main() {
				gl_FragColor = vv4Color;
			}
		</script>
		<script type="text/javascript" src="RotatePrimitivesAroundZ.js"></script>
	</head>
	<body onload="Initialize()">
		<canvas id="idCanvas"></canvas>
	</body>
</html>

RotatePrimitivesAroundZ.js

let gqGL = null;
let gqProgram = null;
let gqVertexBuffer = null;
let gqColorBuffer = null;

let gqVertexAttributeLoc = null;
let gqColorAttributeLoc = null;
let gqModelMatrixAttributeLoc = null;
let gqProjMatrixAttributeLoc = null;

// Animation controls
let gdPrevMS = 0;
let gdRotationAngle = 0;

function Initialize() {

	const qCanvas = document.getElementById("idCanvas");
	qCanvas.width = qCanvas.clientWidth;
	qCanvas.height = qCanvas.clientHeight;
	gqGL = qCanvas.getContext("webgl");
	
	gqProgram = CreateProgram(gqGL);
	
	if (!gqGL.getProgramParameter(gqProgram, gqGL.LINK_STATUS)) {
		alert('The program failed to initialize', gqGL.getProgramInfoLog(gqProgram));
	}
	
	gqVertexBuffer = CreateVertexBuffer(gqGL);
	gqColorBuffer = CreateColorBuffer(gqGL);
	
	gqVertexAttributeLoc = gqGL.getAttribLocation(gqProgram, "av4VertexPosition");
	gqColorAttributeLoc = gqGL.getAttribLocation(gqProgram, "av4VertexColor");
	gqModelMatrixAttributeLoc = gqGL.getUniformLocation(gqProgram, "um4Model");
	gqProjMatrixAttributeLoc = gqGL.getUniformLocation(gqProgram, "um4Projection");

	// Begin the rendering loop
	requestAnimationFrame(Render);
}

function CreateProgram(kqGL) {

	// Compile the vertex shader
	const ksVertexShaderCode = document.getElementById("idVertexShader").innerHTML;
	let qVertexShader = kqGL.createShader(kqGL.VERTEX_SHADER);
	kqGL.shaderSource(qVertexShader, ksVertexShaderCode);
	kqGL.compileShader(qVertexShader);

	// Compile the fragment shader
	const ksFragmentShaderCode = document.getElementById("idFragmantShader").innerHTML;
	let qFragmentShader = kqGL.createShader(kqGL.FRAGMENT_SHADER);
	kqGL.shaderSource(qFragmentShader, ksFragmentShaderCode);
	kqGL.compileShader(qFragmentShader);

	// Compile and link the program
	let qProgram = kqGL.createProgram();
	kqGL.attachShader(qProgram, qVertexShader);
	kqGL.attachShader(qProgram, qFragmentShader);
	kqGL.linkProgram(qProgram);

	if (!kqGL.getProgramParameter(qProgram, kqGL.LINK_STATUS)) {
		alert('The program failed to initialize', kqGL.getProgramInfoLog(qProgram));
	}

	return qProgram;
}

function CreateVertexBuffer(kqGL) {
	const kqVertexBuffer = kqGL.createBuffer();
	// Set this as the current buffer
	kqGL.bindBuffer(kqGL.ARRAY_BUFFER, kqVertexBuffer);
	// The vertices form a reversed z. The first three are in counter-clockwise order
	const kfaVertices =  new Float32Array([1.0, 1.0,  -1.0, 1.0,  1.0, -1.0,  -1.0, -1.0]);
	kqGL.bufferData(kqGL.ARRAY_BUFFER, kfaVertices, kqGL.STATIC_DRAW);
	return kqVertexBuffer;
}

function CreateColorBuffer(kqGL) {
	const kqColorBuffer = kqGL.createBuffer();
	kqGL.bindBuffer(kqGL.ARRAY_BUFFER, kqColorBuffer);
	// Four RGBA colors
	const kfaColors = new Float32Array([1.0, 1.0, 1.0, 1.0,  1.0, 0.0, 0.0, 1.0,  0.0, 1.0, 0.0, 1.0,  0.0, 0.0, 1.0, 1.0]);
	kqGL.bufferData(kqGL.ARRAY_BUFFER, kfaColors, kqGL.STATIC_DRAW);
	return kqColorBuffer;
}
 
function SetVertexAttribute(kqGL) {
	const kiCoordinatesPerVector = 2;
	const kiType = kqGL.FLOAT;
	const kbNormalizeVectors = false;
	const kiStride = 0;					// Byte offset between successive values
	const kiInitialOffset = 0;	// Byte offset for first value
	kqGL.bindBuffer(kqGL.ARRAY_BUFFER, gqVertexBuffer);
	kqGL.vertexAttribPointer(gqVertexAttributeLoc, kiCoordinatesPerVector, kiType, kbNormalizeVectors, kiStride, kiInitialOffset);
	kqGL.enableVertexAttribArray(gqVertexAttributeLoc);
}
  
function SetColorAttribute(kqGL) {
	const kiCoordinatesPerVector = 4;
	const kiType = kqGL.FLOAT;
	const kbNormalizeVectors = false;
	const kiStride = 0;					// Byte offset between successive values
	const kiInitialOffset = 0;	// Byte offset for first value
	kqGL.bindBuffer(kqGL.ARRAY_BUFFER, gqColorBuffer);
	kqGL.vertexAttribPointer(gqColorAttributeLoc, kiCoordinatesPerVector, kiType, kbNormalizeVectors, kiStride, kiInitialOffset);
	kqGL.enableVertexAttribArray(gqColorAttributeLoc);
}

function Render(dCurrMS) {
	let dDiffMS = dCurrMS - gdPrevMS;
	gdRotationAngle += dDiffMS*(Math.PI/5000);
	gdRotationAngle = gdRotationAngle - 2.0*Math.PI*Math.floor(gdRotationAngle/(2.0*Math.PI));
	gdPrevMS = dCurrMS;

	Draw(gqGL);
	
	requestAnimationFrame(Render);
}
 
function Draw(kqGL) {
	// Set up
	kqGL.clearColor(0.5, 0.5, 0.5, 1.0);
	kqGL.clearDepth(1.0);
	kqGL.enable(kqGL.DEPTH_TEST);
	kqGL.depthFunc(kqGL.LEQUAL);
	kqGL.clear(kqGL.COLOR_BUFFER_BIT | kqGL.DEPTH_BUFFER_BIT);
	
	let dSin = Math.sin(gdRotationAngle);
	let dCos = Math.cos(gdRotationAngle);
	
	// A rotation around the origin followed by a translation - matrix
	// The bottom row: indices 12, 13, 14 define translation.
	// Consider that multiplication looks like a row times the matrix vM, when we write Mv.
	let faM = new Float32Array(16);
	faM[0] = dCos;	faM[1] = dSin;	faM[2] = 0.0;		faM[3] = 0.0;
	faM[4] = -dSin;	faM[5] = dCos;	faM[6] = 0.0;		faM[7] = 0.0;
	faM[8] = 0.0;		faM[9] = 0.0;		faM[10] = 1.0;	faM[11] = 0.0;
	faM[12] = 0.0;	faM[13] = 0.0;	faM[14] = -4.0;	faM[15] = 1.0;
	
	const kdFieldOfView = Math.PI/4.0;
	const kdAspect = kqGL.canvas.clientWidth/kqGL.canvas.clientHeight;
	const kdNearPlane = .1;
	const kdFarPlane = 10.0;
	let faProj = GetPerspectiveProjection(kdFieldOfView, kdAspect, kdNearPlane, kdFarPlane);
	
	SetVertexAttribute(kqGL);
	SetColorAttribute(kqGL);

	kqGL.useProgram(gqProgram);

	const kbUseTranspose = false;
	kqGL.uniformMatrix4fv(gqModelMatrixAttributeLoc, kbUseTranspose, faM);
	kqGL.uniformMatrix4fv(gqProjMatrixAttributeLoc, kbUseTranspose, faProj);
	
	// Use indices to render line segments between each pair of points.
	var ui8aIndices = new Uint8Array([0,1, 0,2, 0,3, 1,2, 1,3, 2,3]);
	var qIndexBuffer = kqGL.createBuffer();
	kqGL.bindBuffer(kqGL.ELEMENT_ARRAY_BUFFER, qIndexBuffer);
	kqGL.bufferData(kqGL.ELEMENT_ARRAY_BUFFER, ui8aIndices, kqGL.STATIC_DRAW);
	kqGL.drawElements(kqGL.LINES, 12, kqGL.UNSIGNED_BYTE, 0);

	const kiOffset = 0;
	const kiVertices = 4;
	// Uncomment to see various primitives rendered. Rendering is in array order.
	kqGL.drawArrays(kqGL.POINTS, kiOffset, kiVertices);
	//kqGL.drawArrays(kqGL.TRIANGLE_STRIP, kiOffset, kiVertices);
	//kqGL.drawArrays(kqGL.LINE_STRIP, kiOffset, kiVertices);
	//kqGL.drawArrays(kqGL.LINE_LOOP, kiOffset, kiVertices);
	//kqGL.drawArrays(kqGL.LINES, kiOffset, kiVertices);
	//kqGL.drawArrays(kqGL.TRIANGLES, kiOffset, kiVertices);
	//kqGL.drawArrays(kqGL.TRIANGLE_FAN, kiOffset, kiVertices);
}

function GetPerspectiveProjection(dFOV, dAspect, dNear, dFar) {
	let faM = new Float32Array(16);
	let dF = 1.0/Math.tan(dFOV/2.0);
	let dNF = 1/(dNear-dFar);
	faM[0] = dF/dAspect; faM[1] = 0.0;  faM[2] = 0.0;                 faM[3] = 0.0;
	faM[4] = 0.0;        faM[5] = dF;   faM[6] = 0.0;                 faM[7] = 0.0;
	faM[8] = 0.0;        faM[9] = 0.0;  faM[10] = (dNear+dFar)*dNF;   faM[11] = -1.0;
	faM[12] = 0.0;       faM[13] = 0.0; faM[14] = 2.0*dNear*dFar*dNF; faM[15] = 0.0;
	return faM;
}
 

Output

 
 

© 2007–2026 XoaX.net LLC. All rights reserved.