Circle in WebGL

Hi,

I am trying to build a circle in webgl but I feel hopeless because I fail to build it. Can you help me to find out the mistake in my coding?

<script id=“shader-fs” type=“x-shader/x-fragment”>
#ifdef GL_ES
precision highp float;
#endif

varying vec4 vColor;

void main(void) {
gl_FragColor = vColor;
}
</script>

<script id=“shader-vs” type=“x-shader/x-vertex”>
attribute vec3 aVertexPosition;
attribute vec4 aVertexColor;

uniform mat4 uMVMatrix;
uniform mat4 uPMatrix;

varying vec4 vColor;

void main(void) {
gl_Position = uPMatrix * uMVMatrix * vec4(aVertexPosition, 1.0);
vColor = aVertexColor;
}
</script>

<script type=“text/javascript”>

var gl;
function initGL(canvas) {
try {
gl = canvas.getContext(“experimental-webgl”);
gl.viewportWidth = canvas.width;
gl.viewportHeight = canvas.height;
} catch(e) {
}
if (!gl) {
alert(“Could not initialise WebGL, sorry :-(”);
}
}

function getShader(gl, id) {
var shaderScript = document.getElementById(id);
if (!shaderScript) {
return null;
}

var str = "";
var k = shaderScript.firstChild;
while (k) {
  if (k.nodeType == 3) {
    str += k.textContent;
  }
  k = k.nextSibling;
}

var shader;
if (shaderScript.type == "x-shader/x-fragment") {
  shader = gl.createShader(gl.FRAGMENT_SHADER);
} else if (shaderScript.type == "x-shader/x-vertex") {
  shader = gl.createShader(gl.VERTEX_SHADER);
} else {
  return null;
}

gl.shaderSource(shader, str);
gl.compileShader(shader);

if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) {
  alert(gl.getShaderInfoLog(shader));
  return null;
}

return shader;

}

var shaderProgram;
function initShaders() {
var fragmentShader = getShader(gl, “shader-fs”);
var vertexShader = getShader(gl, “shader-vs”);

shaderProgram = gl.createProgram();
gl.attachShader(shaderProgram, vertexShader);
gl.attachShader(shaderProgram, fragmentShader);
gl.linkProgram(shaderProgram);

if (!gl.getProgramParameter(shaderProgram, gl.LINK_STATUS)) {
  alert("Could not initialise shaders");
}

gl.useProgram(shaderProgram);

shaderProgram.vertexPositionAttribute = gl.getAttribLocation(shaderProgram, "aVertexPosition");
gl.enableVertexAttribArray(shaderProgram.vertexPositionAttribute);

shaderProgram.vertexColorAttribute  = gl.getAttribLocation(shaderProgram, "aVertexColor");
gl.enableVertexAttribArray(shaderProgram.vertexColorAttribute);
 
shaderProgram.pMatrixUniform = gl.getUniformLocation(shaderProgram, "uPMatrix");
shaderProgram.mvMatrixUniform = gl.getUniformLocation(shaderProgram, "uMVMatrix");

}

var mvMatrix;
var mvMatrixStack = [];

function mvPushMatrix(m) {
if (m) {
mvMatrixStack.push(m.dup());
mvMatrix = m.dup();
} else {
mvMatrixStack.push(mvMatrix.dup());
}
}

function mvPopMatrix() {
if (mvMatrixStack.length == 0) {
throw “Invalid popMatrix!”;
}
mvMatrix = mvMatrixStack.pop();
return mvMatrix;
}

function loadIdentity() {
mvMatrix = Matrix.I(4);
}

function multMatrix(m) {
mvMatrix = mvMatrix.x(m);
}

function mvTranslate(v) {
var m = Matrix.Translation($V([v[0], v[1], v[2]])).ensure4x4();
multMatrix(m);
}

function createRotationMatrix(angle, v) {
var arad = angle * Math.PI / 180.0;
return Matrix.Rotation(arad, $V([v[0], v[1], v[2]])).ensure4x4();
}

function mvRotate(ang, v) {
var arad = ang * Math.PI / 180.0;
var m = Matrix.Rotation(arad, $V([v[0], v[1], v[2]])).ensure4x4();
multMatrix(m);
}

var pMatrix;
function perspective(fovy, aspect, znear, zfar) {
pMatrix = makePerspective(fovy, aspect, znear, zfar);
}

function setMatrixUniforms() {
gl.uniformMatrix4fv(shaderProgram.pMatrixUniform, false, new Float32Array(pMatrix.flatten()));
gl.uniformMatrix4fv(shaderProgram.mvMatrixUniform, false, new Float32Array(mvMatrix.flatten()));
}

var z = -7.0;

var cubeVertexPositionBuffer;
var cubeVertexNormalBuffer;
var cubeVertexColorBuffer;
var cubeVertexIndexBuffer;
var unpackedColors;
var vectices;
var normalData;

function initBuffers() {
cubeVertexPositionBuffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, cubeVertexPositionBuffer);

var n = 4
var twoPi = 2.0 * 3.14159;

vertices = [0, 0, 0];

normalData = [0, 0, 1];
unpackedColors = [1, 0, 0, 1];

for (var j = 0; j &lt;= n; j++) {
    vertices = vertices.concat([Math.cos(j * twoPi / n), Math.sin(j * twoPi / n), 0.0]);
    normalData = normalData.concat([0, 0, 1]);
    unpackedColors = unpackedColors.concat([1, 0, 0, 1]);
}

gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(vertices), gl.STATIC_DRAW);
cubeVertexPositionBuffer.itemSize = 3;
cubeVertexPositionBuffer.numItems = cubeVertexPositionBuffer.length / cubeVertexPositionBuffer.itemSize;


cubeVertexColorBuffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, cubeVertexColorBuffer);

gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(unpackedColors), gl.STATIC_DRAW);
cubeVertexColorBuffer.itemSize = 4;
cubeVertexColorBuffer.numItems = 6;


cubeVertexNormalBuffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, cubeVertexNormalBuffer);
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(normalData), gl.STATIC_DRAW);
cubeVertexNormalBuffer.itemSize = 3;
cubeVertexNormalBuffer.numItems = cubeVertexNormalBuffer.length / cubeVertexNormalBuffer.itemSize;


cubeVertexIndexBuffer = gl.createBuffer();
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, cubeVertexIndexBuffer);
var cubeVertexIndices = [
  0, 1, 2,
  0, 2, 3,
  0, 3, 4,
  0, 4, 5,
  0, 5, 6,
  0, 6, 1
]
gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, new Uint16Array(cubeVertexIndices), gl.STATIC_DRAW);
cubeVertexIndexBuffer.itemSize = 1;
cubeVertexIndexBuffer.numItems = 18;

}

function drawScene() {
gl.viewport(0, 0, gl.viewportWidth, gl.viewportHeight);
gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);

perspective(45, gl.viewportWidth / gl.viewportHeight, 0.1, 100.0);
loadIdentity();

mvTranslate([0, 0.0, z]);
multMatrix(rotationMatrix);

mvPushMatrix();
mvTranslate([-1, -1, 0]);

gl.bindBuffer(gl.ARRAY_BUFFER, cubeVertexPositionBuffer);
gl.vertexAttribPointer(shaderProgram.vertexPositionAttribute, cubeVertexPositionBuffer.itemSize, gl.FLOAT, false, 0, 0);

gl.bindBuffer(gl.ARRAY_BUFFER, cubeVertexColorBuffer);
gl.vertexAttribPointer(shaderProgram.vertexColorAttribute, cubeVertexColorBuffer.itemSize, gl.FLOAT, false, 0, 0);

gl.bindBuffer(gl.ARRAY_BUFFER, cubeVertexNormalBuffer);
gl.vertexAttribPointer(shaderProgram.vertexNormalAttribute, cubeVertexNormalBuffer.itemSize, gl.FLOAT, false, 0, 0);

gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, cubeVertexIndexBuffer);
setMatrixUniforms();
gl.drawElements(gl.TRIANGLES, cubeVertexIndexBuffer.numItems, gl.UNSIGNED_SHORT, 0);

mvPopMatrix();

}

var mouseDown = false;
var lastMouseX = null;
var lastMouseY = null;
var rotationMatrix = Matrix.I(4);

function handleMouseDown(event) {
mouseDown = true;
lastMouseX = event.clientX;
lastMouseY = event.clientY;
}

function handleMouseUp(event) {
mouseDown = false;
}

function handleMouseMove(event) {
if (!mouseDown) {
return;
}

  var newX = event.clientX;
  var newY = event.clientY;
  var deltaX = (newX - lastMouseX) / 2;
  var deltaY = (newY - lastMouseY) / 2;

  var newRotationMatrix = createRotationMatrix(deltaX, [0, 1, 0]);
  newRotationMatrix = newRotationMatrix.x(createRotationMatrix(deltaY, [1, 0, 0]));
  rotationMatrix = newRotationMatrix.x(rotationMatrix);
  
  lastMouseX = newX
  lastMouseY = newY;

}

function tick() {
drawScene();
}

function webGLStart() {
var canvas = document.getElementById(“lesson01-canvas”);
initGL(canvas);
initShaders();
initBuffers();

gl.clearColor(0.0, 0.0, 0.0, 1.0);

gl.clearDepth(1.0);

gl.enable(gl.DEPTH_TEST);
gl.depthFunc(gl.LEQUAL);

canvas.onmousedown = handleMouseDown;
document.onmouseup = handleMouseUp;
document.onmousemove = handleMouseMove;

setInterval(tick, 15);

}

Thanks your help.

The main problem you had was that you were incorrectly calculating the .numItems. It should have been something like


XBuffer.numItems = Xarray.length / XBuffer.itemSize;

Also, your index array did not correspond correctly to your actual vertex points. Here is code that draws a circle.

also "you are not actually using the normal vector but I added code into the shader "attribute vec4 aVertexNormal;
" and corresponding getAttribLocation in initShaders() for when you actually start needing the normal later.

ps when posting to this forum, please put all your code in ‘[‘code’]’ your code ‘[’\code’]’ blocks (without the ’ character shown here to make escape sequence visible here only) to make it easier to read and maintain formatting.


<script id="shader-fs" type="x-shader/x-fragment"> 
#ifdef GL_ES
precision highp float;
#endif

varying vec4 vColor;

void main(void) {
gl_FragColor = vColor;
}
</script>

<script id="shader-vs" type="x-shader/x-vertex"> 
attribute vec3 aVertexPosition;
attribute vec4 aVertexColor;
attribute vec4 aVertexNormal;

uniform mat4 uMVMatrix;
uniform mat4 uPMatrix;

varying vec4 vColor;

void main(void) {
gl_Position = uPMatrix * uMVMatrix * vec4(aVertexPosition, 1.0);
vColor = aVertexColor;
}
</script>

<script type="text/javascript"> 

var gl;
function initGL(canvas) {
try {
gl = canvas.getContext("experimental-webgl");
gl.viewportWidth = canvas.width;
gl.viewportHeight = canvas.height;
} catch(e) {
}
if (!gl) {
alert("Could not initialise WebGL, sorry :-(");
}
} 

function getShader(gl, id) {
var shaderScript = document.getElementById(id);
if (!shaderScript) {
return null;
}

var str = "";
var k = shaderScript.firstChild;
while (k) {
if (k.nodeType == 3) {
str += k.textContent;
}
k = k.nextSibling;
}

var shader;
if (shaderScript.type == "x-shader/x-fragment") {
shader = gl.createShader(gl.FRAGMENT_SHADER);
} else if (shaderScript.type == "x-shader/x-vertex") {
shader = gl.createShader(gl.VERTEX_SHADER);
} else {
return null;
}

gl.shaderSource(shader, str);
gl.compileShader(shader);
window.console.log("Compiled Shader");


if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) {
alert(gl.getShaderInfoLog(shader));
return null;
}

return shader;
} 

var shaderProgram;
function initShaders() {
var fragmentShader = getShader(gl, "shader-fs");
var vertexShader = getShader(gl, "shader-vs");

shaderProgram = gl.createProgram();
gl.attachShader(shaderProgram, vertexShader);
gl.attachShader(shaderProgram, fragmentShader);
gl.linkProgram(shaderProgram);

if (!gl.getProgramParameter(shaderProgram, gl.LINK_STATUS)) {
alert("Could not initialise shaders");
}

gl.useProgram(shaderProgram);

shaderProgram.vertexPositionAttribute = gl.getAttribLocation(shaderProgram, "aVertexPosition");
gl.enableVertexAttribArray(shaderProgram.vertexPositionAttribute);

shaderProgram.vertexColorAttribute = gl.getAttribLocation(shaderProgram, "aVertexColor");
gl.enableVertexAttribArray(shaderProgram.vertexColorAttribute);

shaderProgram.vertexNormalAttribute = gl.getAttribLocation(shaderProgram, "aVertexNormal");
gl.enableVertexAttribArray(shaderProgram.vertexNormalAttribute);

shaderProgram.pMatrixUniform = gl.getUniformLocation(shaderProgram, "uPMatrix");
shaderProgram.mvMatrixUniform = gl.getUniformLocation(shaderProgram, "uMVMatrix");
} 

var mvMatrix;
var mvMatrixStack = [];

function mvPushMatrix(m) {
if (m) {
mvMatrixStack.push(m.dup());
mvMatrix = m.dup();
} else {
mvMatrixStack.push(mvMatrix.dup());
}
}

function mvPopMatrix() {
if (mvMatrixStack.length == 0) {
throw "Invalid popMatrix!";
}
mvMatrix = mvMatrixStack.pop();
return mvMatrix;
}

function loadIdentity() {
mvMatrix = Matrix.I(4);
}

function multMatrix(m) {
mvMatrix = mvMatrix.x(m);
} 

function mvTranslate(v) {
var m = Matrix.Translation($V([v[0], v[1], v[2]])).ensure4x4();
multMatrix(m);
}

function createRotationMatrix(angle, v) {
var arad = angle * Math.PI / 180.0;
return Matrix.Rotation(arad, $V([v[0], v[1], v[2]])).ensure4x4();
}

function mvRotate(ang, v) {
var arad = ang * Math.PI / 180.0;
var m = Matrix.Rotation(arad, $V([v[0], v[1], v[2]])).ensure4x4();
multMatrix(m);
}

var pMatrix;
function perspective(fovy, aspect, znear, zfar) {
pMatrix = makePerspective(fovy, aspect, znear, zfar);
} 

function setMatrixUniforms() {
gl.uniformMatrix4fv(shaderProgram.pMatrixUniform, false, new Float32Array(pMatrix.flatten()));
gl.uniformMatrix4fv(shaderProgram.mvMatrixUniform, false, new Float32Array(mvMatrix.flatten()));
}

var z = -7.0;

var cubeVertexPositionBuffer;
var cubeVertexNormalBuffer;
var cubeVertexColorBuffer;
var cubeVertexIndexBuffer;
var cubeVertexIndices;
var unpackedColors;
var vectices;
var normalData;

function initBuffers() {

var n = 12
var twoPi = 2.0 * 3.14159;

vertices = [0, 0, 0];
normalData = [0, 0, 1];
unpackedColors = [1, 0, 0, 1];
cubeVertexIndices = [];

for (var j = 0; j < n; j++) {
vertices = vertices.concat([Math.cos((j-1) * twoPi / n), Math.sin((j-1) * twoPi / n), 0.0]);
normalData = normalData.concat([0, 0, 1]);
unpackedColors = unpackedColors.concat([1.0, 0.0, 0.0, 1.0]);
cubeVertexIndices = cubeVertexIndices.concat([0,j+1,(j+1<n?j+2:1)]);
}

cubeVertexPositionBuffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, cubeVertexPositionBuffer);
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(vertices), gl.STATIC_DRAW);
cubeVertexPositionBuffer.itemSize = 3;
cubeVertexPositionBuffer.numItems = vertices.length / cubeVertexPositionBuffer.itemSize;

cubeVertexColorBuffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, cubeVertexColorBuffer);
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(unpackedColors), gl.STATIC_DRAW);
cubeVertexColorBuffer.itemSize = 4;
cubeVertexColorBuffer.numItems = unpackedColors.length / cubeVertexColorBuffer.itemSize;

cubeVertexNormalBuffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, cubeVertexNormalBuffer);
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(normalData), gl.STATIC_DRAW);
cubeVertexNormalBuffer.itemSize = 3;
cubeVertexNormalBuffer.numItems = normalData.length / cubeVertexNormalBuffer.itemSize;

cubeVertexIndexBuffer = gl.createBuffer();
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, cubeVertexIndexBuffer);
gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, new Uint16Array(cubeVertexIndices), gl.STATIC_DRAW);
cubeVertexIndexBuffer.itemSize = 1;
cubeVertexIndexBuffer.numItems = cubeVertexIndices.length / cubeVertexIndexBuffer.itemSize;

window.console.log(cubeVertexPositionBuffer.numItems);
window.console.log(cubeVertexNormalBuffer.numItems);
window.console.log(cubeVertexColorBuffer.numItems);
window.console.log(cubeVertexIndexBuffer.numItems);
}


function drawScene() {
gl.viewport(0, 0, gl.viewportWidth, gl.viewportHeight);
gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);

perspective(45, gl.viewportWidth / gl.viewportHeight, 0.1, 100.0);
loadIdentity();

mvTranslate([0, 0.0, z]);
multMatrix(rotationMatrix);

mvPushMatrix();
mvTranslate([-1, -1, 0]);

gl.bindBuffer(gl.ARRAY_BUFFER, cubeVertexPositionBuffer);
gl.vertexAttribPointer(shaderProgram.vertexPositionAttribute, cubeVertexPositionBuffer.itemSize, gl.FLOAT, false, 0, 0);

gl.bindBuffer(gl.ARRAY_BUFFER, cubeVertexColorBuffer);
gl.vertexAttribPointer(shaderProgram.vertexColorAttribute, cubeVertexColorBuffer.itemSize, gl.FLOAT, false, 0, 0);

gl.bindBuffer(gl.ARRAY_BUFFER, cubeVertexNormalBuffer);
gl.vertexAttribPointer(shaderProgram.vertexNormalAttribute, cubeVertexNormalBuffer.itemSize, gl.FLOAT, false, 0, 0);

gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, cubeVertexIndexBuffer);
setMatrixUniforms();
gl.drawElements(gl.TRIANGLES, cubeVertexIndexBuffer.numItems, gl.UNSIGNED_SHORT, 0);

mvPopMatrix();
}

var mouseDown = false;
var lastMouseX = null;
var lastMouseY = null;
var rotationMatrix = Matrix.I(4);

function handleMouseDown(event) {
mouseDown = true;
lastMouseX = event.clientX;
lastMouseY = event.clientY;
}

function handleMouseUp(event) {
mouseDown = false;
}

function handleMouseMove(event) {
if (!mouseDown) {
return;
}

var newX = event.clientX;
var newY = event.clientY;
var deltaX = (newX - lastMouseX) / 2;
var deltaY = (newY - lastMouseY) / 2;

var newRotationMatrix = createRotationMatrix(deltaX, [0, 1, 0]);
newRotationMatrix = newRotationMatrix.x(createRotationMatrix(deltaY, [1, 0, 0]));
rotationMatrix = newRotationMatrix.x(rotationMatrix);

lastMouseX = newX
lastMouseY = newY;
}

function tick() {
drawScene();
}

function webGLStart() {
var canvas = document.getElementById("lesson01-canvas");
initGL(canvas);
initShaders();
initBuffers();

gl.clearColor(0.0, 0.0, 0.0, 1.0);

gl.clearDepth(1.0);

gl.enable(gl.DEPTH_TEST);
gl.depthFunc(gl.LEQUAL);

canvas.onmousedown = handleMouseDown;
document.onmouseup = handleMouseUp;
document.onmousemove = handleMouseMove;

setInterval(tick, 15);
}

A follow-up to correct a mistake in my previous post
actually use without ’ characters
‘[‘code’]’
insert your code here
‘[’/code’]’
and note forward slash (not backward slash like my previous post)

this is from “What UBBCode can I use in my posts?”

Thanks for your great help.

May I know when will I use the normal vector?

May I know when will I use the normal vector?

When you start to get into models where you utilize light effects. The normal vector helps in computing things like reflection off the surface or how shinny it looks etc. After you start getting more into WebGL you may want to find some general openGL books on the subject. Randi Rost “OpenGL Shading Language” is one book I reference from time to time. But there are many online tutorials also.

I will use the light effect later.

Before light effect implementation, I would like to change part of the code into a function (refractor). However, I have no idea to refractor the following coding:

 
vertices = [0, 0, 0];
normalData = [0, 0, 1];
unpackedColors = [1, 0, 0, 1];
cubeVertexIndices = [];

for (var j = 0; j < n; j++) {
vertices = vertices.concat([Math.cos((j-1) * twoPi / n), Math.sin((j-1) * twoPi / n), 0.0]);
normalData = normalData.concat([0, 0, 1]);
unpackedColors = unpackedColors.concat([1.0, 0.0, 0.0, 1.0]);
cubeVertexIndices = cubeVertexIndices.concat([0,j+1,(j+1<n?j+2:1)]);
}

This is because I want to extract it (and its buffer) into one js file so I can re-use it later. However, if I do it, it cannot return these 4 parameters (vertices, normalData, unpackedColors, cubeVertexIndices).

I am really a C++ coder. This javascript stuff is similar enough to do most simple tasks but your question is more of a javascript than a openGL/webGL question. Do you have any helpful javascript forums to ask this question? I would be curious to know the answer also.

Not a javascript coder either, but I have been told that C struct equivalent can be done using JSON notation, probably something around:
var result = {vertices: [0, 0, 0], normalData: [0, 0, 1], etc};

I get the response in a JavaScript forum.

They ask me to create a JavaScript Class and do the refractoring.

function ClassA(day,month,year) {
  this.day = day;
  this.month = month;
  this.year = year;
}
ClassA.prototype.getDay = function() {
  return this.day;
}
ClassA.prototype.getMonth = function() {
  return this.month;
}
ClassA.prototype.getYear = function() {
  return this.year;
}
ClassA.prototype.setDay = function(day) {
  this.day = day;
}
ClassA.prototype.setMonth = function(month) {
  this.month = month;
}
ClassA.prototype.setYear = function(year) {
  this.year = year;
}

var objA = new ClassA(24,3,2010);
console.log(objA.getDay() + ' ' + objA.getMonth() + ' ' + objA.getYear());
objA.setDay(1);
objA.setMonth(1);
objA.setYear(2009);
console.log(objA.getDay() + ' ' + objA.getMonth() + ' ' + objA.getYear());

I’m thinking how to do it.

Doing a full blown class is the heavyweight version, compared to JSON.
Well, do as you like :slight_smile:

Does it means I can extract the for loop like this:

function abc(…) {

return {vertices: vertices, normalData: normalData, unpackedColors: unpackedColors, cubeVertexIndices: cubeVertexIndices}
}

If yes, how can I call the function?
vertices = abd(…)?

More like :
allData = abc(…);
computedVertices = allData.vertices;

That is neat trick to return multpile values in JSON.

One point: there is no real need to send around vertex arrays once you buffer the data to GL. In fact all you need are the buffer IDs and lengths ie in your circle case only 8 integers needed to completely draw the circle.


cubeVertexPositionBuffer
cubeVertexPositionBuffer.itemSize

cubeVertexColorBuffer
cubeVertexColorBuffer.itemSize

cubeVertexNormalBuffer
cubeVertexNormalBuffer.itemSize

cubeVertexIndexBuffer
cubeVertexIndexBuffer.numItems

Why not refactor the code to avoid possibly costly memory duplication (for large models later on for instance) of color,vertex,normal etc arrays like


<script id="shader-fs" type="x-shader/x-fragment"> 
#ifdef GL_ES
precision highp float;
#endif

varying vec4 vColor;

void main(void) {
gl_FragColor = vColor;
}
</script>

<script id="shader-vs" type="x-shader/x-vertex"> 
attribute vec3 aVertexPosition;
attribute vec4 aVertexColor;
attribute vec4 aVertexNormal;

uniform mat4 uMVMatrix;
uniform mat4 uPMatrix;

varying vec4 vColor;

void main(void) {
gl_Position = uPMatrix * uMVMatrix * vec4(aVertexPosition, 1.0);
vColor = aVertexColor;
}
</script>

<script type="text/javascript"> 

var gl;
function initGL(canvas) {
try {
gl = canvas.getContext("experimental-webgl");
gl.viewportWidth = canvas.width;
gl.viewportHeight = canvas.height;
} catch(e) {
}
if (!gl) {
alert("Could not initialise WebGL, sorry :-(");
}
} 

function getShader(gl, id) {
var shaderScript = document.getElementById(id);
if (!shaderScript) {
return null;
}

var str = "";
var k = shaderScript.firstChild;
while (k) {
if (k.nodeType == 3) {
str += k.textContent;
}
k = k.nextSibling;
}

var shader;
if (shaderScript.type == "x-shader/x-fragment") {
shader = gl.createShader(gl.FRAGMENT_SHADER);
} else if (shaderScript.type == "x-shader/x-vertex") {
shader = gl.createShader(gl.VERTEX_SHADER);
} else {
return null;
}

gl.shaderSource(shader, str);
gl.compileShader(shader);
window.console.log("Compiled Shader");


if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) {
alert(gl.getShaderInfoLog(shader));
return null;
}

return shader;
} 

var shaderProgram;
function initShaders() {
var fragmentShader = getShader(gl, "shader-fs");
var vertexShader = getShader(gl, "shader-vs");

shaderProgram = gl.createProgram();
gl.attachShader(shaderProgram, vertexShader);
gl.attachShader(shaderProgram, fragmentShader);
gl.linkProgram(shaderProgram);

if (!gl.getProgramParameter(shaderProgram, gl.LINK_STATUS)) {
alert("Could not initialise shaders");
}

gl.useProgram(shaderProgram);

shaderProgram.vertexPositionAttribute = gl.getAttribLocation(shaderProgram, "aVertexPosition");
gl.enableVertexAttribArray(shaderProgram.vertexPositionAttribute);

shaderProgram.vertexColorAttribute = gl.getAttribLocation(shaderProgram, "aVertexColor");
gl.enableVertexAttribArray(shaderProgram.vertexColorAttribute);

shaderProgram.vertexNormalAttribute = gl.getAttribLocation(shaderProgram, "aVertexNormal");
gl.enableVertexAttribArray(shaderProgram.vertexNormalAttribute);

shaderProgram.pMatrixUniform = gl.getUniformLocation(shaderProgram, "uPMatrix");
shaderProgram.mvMatrixUniform = gl.getUniformLocation(shaderProgram, "uMVMatrix");
} 

var mvMatrix;
var mvMatrixStack = [];

function mvPushMatrix(m) {
if (m) {
mvMatrixStack.push(m.dup());
mvMatrix = m.dup();
} else {
mvMatrixStack.push(mvMatrix.dup());
}
}

function mvPopMatrix() {
if (mvMatrixStack.length == 0) {
throw "Invalid popMatrix!";
}
mvMatrix = mvMatrixStack.pop();
return mvMatrix;
}

function loadIdentity() {
mvMatrix = Matrix.I(4);
}

function multMatrix(m) {
mvMatrix = mvMatrix.x(m);
} 

function mvTranslate(v) {
var m = Matrix.Translation($V([v[0], v[1], v[2]])).ensure4x4();
multMatrix(m);
}

function createRotationMatrix(angle, v) {
var arad = angle * Math.PI / 180.0;
return Matrix.Rotation(arad, $V([v[0], v[1], v[2]])).ensure4x4();
}

function mvRotate(ang, v) {
var arad = ang * Math.PI / 180.0;
var m = Matrix.Rotation(arad, $V([v[0], v[1], v[2]])).ensure4x4();
multMatrix(m);
}

var pMatrix;
function perspective(fovy, aspect, znear, zfar) {
pMatrix = makePerspective(fovy, aspect, znear, zfar);
} 

function setMatrixUniforms() {
gl.uniformMatrix4fv(shaderProgram.pMatrixUniform, false, new Float32Array(pMatrix.flatten()));
gl.uniformMatrix4fv(shaderProgram.mvMatrixUniform, false, new Float32Array(mvMatrix.flatten()));
}

var z = -7.0;

function initCircleBuffer() {

var n = 12;
var twoPi = 2.0 * 3.14159;

var vertices = [0, 0, 0];
var normalData = [0, 0, 1];
var unpackedColors = [1, 0, 0, 1];
var cubeVertexIndices = [];

for (var j = 0; j < n; j++) {
vertices = vertices.concat([Math.cos((j-1) * twoPi / n), Math.sin((j-1) * twoPi / n), 0.0]);
normalData = normalData.concat([0, 0, 1]);
unpackedColors = unpackedColors.concat([1.0, 0.0, 0.0, 1.0]);
cubeVertexIndices = cubeVertexIndices.concat([0,j+1,(j+1<n?j+2:1)]);
}

var cubeVertexPositionBuffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, cubeVertexPositionBuffer);
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(vertices), gl.STATIC_DRAW);
cubeVertexPositionBuffer.itemSize = 3;
cubeVertexPositionBuffer.numItems = vertices.length / cubeVertexPositionBuffer.itemSize;

var cubeVertexColorBuffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, cubeVertexColorBuffer);
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(unpackedColors), gl.STATIC_DRAW);
cubeVertexColorBuffer.itemSize = 4;
cubeVertexColorBuffer.numItems = unpackedColors.length / cubeVertexColorBuffer.itemSize;

var cubeVertexNormalBuffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, cubeVertexNormalBuffer);
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(normalData), gl.STATIC_DRAW);
cubeVertexNormalBuffer.itemSize = 3;
cubeVertexNormalBuffer.numItems = normalData.length / cubeVertexNormalBuffer.itemSize;

var cubeVertexIndexBuffer = gl.createBuffer();
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, cubeVertexIndexBuffer);
gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, new Uint16Array(cubeVertexIndices), gl.STATIC_DRAW);
cubeVertexIndexBuffer.itemSize = 1;
cubeVertexIndexBuffer.numItems = cubeVertexIndices.length / cubeVertexIndexBuffer.itemSize;

//window.console.log(cubeVertexPositionBuffer.numItems);
//window.console.log(cubeVertexNormalBuffer.numItems);
//window.console.log(cubeVertexColorBuffer.numItems);
//window.console.log(cubeVertexIndexBuffer.numItems);

return {cubeVertexPositionBuffer:cubeVertexPositionBuffer, 
        cubeVertexColorBuffer:cubeVertexColorBuffer,
        cubeVertexNormalBuffer:cubeVertexNormalBuffer,
        cubeVertexIndexBuffer:cubeVertexIndexBuffer,
        };
}


function drawScene() {
gl.viewport(0, 0, gl.viewportWidth, gl.viewportHeight);
gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);

perspective(45, gl.viewportWidth / gl.viewportHeight, 0.1, 100.0);
loadIdentity();

mvTranslate([0, 0.0, z]);
multMatrix(rotationMatrix);

mvPushMatrix();
mvTranslate([-1, -1, 0]);

gl.bindBuffer(gl.ARRAY_BUFFER, circle.cubeVertexPositionBuffer);
gl.vertexAttribPointer(shaderProgram.vertexPositionAttribute, circle.cubeVertexPositionBuffer.itemSize, gl.FLOAT, false, 0, 0);

gl.bindBuffer(gl.ARRAY_BUFFER, circle.cubeVertexColorBuffer);
gl.vertexAttribPointer(shaderProgram.vertexColorAttribute, circle.cubeVertexColorBuffer.itemSize, gl.FLOAT, false, 0, 0);

gl.bindBuffer(gl.ARRAY_BUFFER, circle.cubeVertexNormalBuffer);
gl.vertexAttribPointer(shaderProgram.vertexNormalAttribute, circle.cubeVertexNormalBuffer.itemSize, gl.FLOAT, false, 0, 0);

gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, circle.cubeVertexIndexBuffer);
setMatrixUniforms();
gl.drawElements(gl.TRIANGLES, circle.cubeVertexIndexBuffer.numItems, gl.UNSIGNED_SHORT, 0);

// just to show drawing second circle by reusing buffer
mvTranslate([2, 2, 0]);
setMatrixUniforms();
gl.drawElements(gl.TRIANGLES, circle.cubeVertexIndexBuffer.numItems, gl.UNSIGNED_SHORT, 0);

mvPopMatrix();
}

var mouseDown = false;
var lastMouseX = null;
var lastMouseY = null;
var rotationMatrix = Matrix.I(4);

function handleMouseDown(event) {
mouseDown = true;
lastMouseX = event.clientX;
lastMouseY = event.clientY;
}

function handleMouseUp(event) {
mouseDown = false;
}

function handleMouseMove(event) {
if (!mouseDown) {
return;
}

var newX = event.clientX;
var newY = event.clientY;
var deltaX = (newX - lastMouseX) / 2;
var deltaY = (newY - lastMouseY) / 2;

var newRotationMatrix = createRotationMatrix(deltaX, [0, 1, 0]);
newRotationMatrix = newRotationMatrix.x(createRotationMatrix(deltaY, [1, 0, 0]));
rotationMatrix = newRotationMatrix.x(rotationMatrix);

lastMouseX = newX
lastMouseY = newY;
}

function tick() {
drawScene();
}

var circle;

function webGLStart() {
var canvas = document.getElementById("lesson01-canvas");
initGL(canvas);
initShaders();
circle = initCircleBuffer();

gl.clearColor(0.0, 0.0, 0.0, 1.0);

gl.clearDepth(1.0);

gl.enable(gl.DEPTH_TEST);
gl.depthFunc(gl.LEQUAL);

canvas.onmousedown = handleMouseDown;
document.onmouseup = handleMouseUp;
document.onmousemove = handleMouseMove;

setInterval(tick, 15);
}
</script>

where initCircleBuffer() replaces intiBuffers() by returning only the minimal required buffer IDs and lengths rather than the detailed vertex arrays.

Thanks for your help.
I have created some classes to store the circle.

I will start to add the lighting effect in this following week. Hopefully I can solve the color problem in sphere first.