How to calculate normals for an icosahedron? - javascript

I want to calculate the normals for an icosahedron with its origin at {0.0, 0.0, 0.0}, but I do not have any idea of how to do it!
For example, when i constructed a cube earlier, it was rather simple to get the normals, because each face of the cube was directed parallel either to the x, y or z-axis and the arrays containing the vertices and normals looked like that:
var vertices = [
-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, 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, 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,
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, -1.0, 1.0,
-1.0, 1.0, 1.0, -1.0, 1.0, -1.0
];
var normals = [
0.0, 0.0, 1.0, 0.0, 0.0, 1.0,
0.0, 0.0, 1.0, 0.0, 0.0, 1.0,
0.0, 0.0, -1.0, 0.0, 0.0, -1.0,
0.0, 0.0, -1.0, 0.0, 0.0, -1.0,
0.0, 1.0, 0.0, 0.0, 1.0, 0.0,
0.0, 1.0, 0.0, 0.0, 1.0, 0.0,
0.0, -1.0, 0.0, 0.0, -1.0, 0.0,
0.0, -1.0, 0.0, 0.0, -1.0, 0.0,
1.0, 0.0, 0.0, 1.0, 0.0, 0.0,
1.0, 0.0, 0.0, 1.0, 0.0, 0.0,
-1.0, 0.0, 0.0, -1.0, 0.0, 0.0,
-1.0, 0.0, 0.0, -1.0, 0.0, 0.0
];
But now, working on my icosahedron, things are getting a bit more complicated...
Edit 1:
Following the instructions of the answer given, i tried to calculate the normals this way:
First I stored the vertices in a multidimensional Array like that:
var r = (1 + Math.sqrt(5)) / 2;
var triangles = [
[ [-1.0, r, 0.0], [0.0, 1.0, r], [1.0, r, 0.0] ],
[ [1.0, r, 0.0], [0.0, 1.0, -r], [-1.0, r, 0.0] ],
[ [1.0, r, 0.0], [0.0, 1.0, r], [r, 0.0, 1.0] ],
[ [1.0, r, 0.0], [r, 0.0, -1.0], [0.0, 1.0, -r] ],
[ [r, 0.0, -1.0], [1.0, r, 0.0], [r, 0.0, 1.0] ],
[ [-1.0, -r, 0.0], [1.0, -r, 0.0], [0.0, -1.0, r] ],
[ [-1.0, -r, 0.0], [0.0, -1.0, -r], [1.0, -r, 0.0] ],
[ [-1.0, -r, 0.0], [0.0, -1.0, r], [-r, 0.0, 1.0] ],
[ [-1.0, -r, 0.0], [-r, 0.0, -1.0], [0.0, -1.0, -r] ],
[ [-r, 0.0, 1.0], [-r, 0.0, -1.0], [-1.0, -r, 0.0] ],
[ [-1.0, r, 0.0], [-r, 0.0, 1.0], [0.0, 1.0, r] ],
[ [-1.0, r, 0.0], [0.0, 1.0, -r], [-r, 0.0, -1.0] ],
[ [-1.0, r, 0.0], [-r, 0.0, -1.0], [-r, 0.0, 1.0] ],
[ [1.0, -r, 0.0], [r, 0.0, 1.0], [0.0, -1.0, r] ],
[ [1.0, -r, 0.0], [0.0, -1.0, -r], [r, 0.0, -1.0] ],
[ [1.0, -r, 0.0], [r, 0.0, -1.0], [r, 0.0, 1.0] ],
[ [0.0, -1.0, -r], [-r, 0.0, -1.0], [0.0, 1.0, -r] ],
[ [0.0, -1.0, -r], [0.0, 1.0, -r], [r, 0.0, -1.0] ],
[ [0.0, 1.0, r], [-r, 0.0, 1.0], [0.0, -1.0, r] ],
[ [0.0, 1.0, r], [0.0, -1.0, r], [r, 0.0, 1.0] ]
];
Then, based on the given answer, I wrote this function to calculate the normals...
var normals = [ ];
triangles.forEach(function (triangle) {
var v1 = triangle[0],
v2 = triangle[1],
v3 = triangle[2];
var p12 = new Array(3);
p12[0] = v2[0] - v1[0];
p12[1] = v2[1] - v1[1];
p12[2] = v2[2] - v1[2];
var p23 = new Array(3);
p23[0] = v3[0] - v2[0];
p23[1] = v3[1] - v2[1];
p23[2] = v3[2] - v2[2];
var cp = new Array(3);
var x1 = p12[0],
y1 = p12[1],
z1 = p12[2];
var x2 = p23[0],
y2 = p23[1],
z2 = p23[2];
cp[0] = y1 * z2 - z1 * y2;
cp[1] = z1 * x2 - x1 * z2;
cp[2] = x1 * y2 - y1 * x2;
var x = Math.pow(cp[0], 2),
y = Math.pow(cp[1], 2),
z = Math.pow(cp[2], 2);
var len = Math.sqrt(x + y + z);
var normal = new Array(3);
normal[0] = cp[0] / len;
normal[1] = cp[1] / len;
normal[2] = cp[2] / len;
for (var i = 0; i < 3; i++) {
normals.push(normal);
}
});
...to finally unpack and use them:
var unpackedNormals = [ ];
for (var n in normals) {
unpackedNormals = unpackedNormals.concat(normals[n]);
}
var vertexNormalData = unpackedNormals;
But somehow it doesn't work like it should!
I can see the icosahedron on screen, but the lighting of the triangles on the surface seems to be completly wrong.
I ran the same program with my earlier mentioned cube-function instead of the one constructing the icosahedron, and it all works perfectly fine, so I think the error must be located within this new function to calculate the normals.
Maybe someone has an idea what I did wrong?
I'd be thankful for any help!
PS: Please excuse my bad english.
Edit 2: PROBLEM SOLVED!
I changed the order of the vectors in the triangle-array and now all of the triangles behave like they should!
I updated the triangle-array in this post too, so the snipped further up now represents the correct order, AFAIK.

I think what you need is "cross-product", the vector product of two vectors. The cross product will always be perpendicular to the plane defined by the two vectors.
http://en.wikipedia.org/wiki/Cross_product
For each triangle of your icosahedron you have the vectors describing the sides (for example if v1, v2 and v3 are the vectors describing your vertices, the sides are p12 = v2-v1, p23 = v3-v2 and p31 = v1-v3). Your normal will be the cross-product of two of the three vectors normalized (divided by it's modulus), for example
n123 = (p12 x p23) / (|p12 x p23|)
Useful stuff you should know:
difference of two vectors
p12 = v2 - v1 = [x2, y2, z2] - [x1, y1, z1] = [x2-x1, y2-y1, z2-z1]
modulus (length) of a vector:
|v| = |[x, y, z]| = sqrt(x^2 + y^2 + z^2)
Hope this helps.

If you are interested in the results only, here below is a Wavefront icosahedron.obj file I exported from Blender with triangulated vertices, textures and normals.
# Blender v2.77 (sub 0) OBJ File: 'icosahedron.blend'
# www.blender.org
mtllib icosahedron.mtl
o Icosphere
v 0.000000 -1.000000 0.000000
v 0.723600 -0.447215 0.525720
v -0.276385 -0.447215 0.850640
v -0.894425 -0.447215 0.000000
v -0.276385 -0.447215 -0.850640
v 0.723600 -0.447215 -0.525720
v 0.276385 0.447215 0.850640
v -0.723600 0.447215 0.525720
v -0.723600 0.447215 -0.525720
v 0.276385 0.447215 -0.850640
v 0.894425 0.447215 0.000000
v 0.000000 1.000000 0.000000
vt 0.6739 0.5441
vt 0.5057 0.4329
vt 0.6731 0.1917
vt 0.5162 0.7848
vt 0.8418 0.4311
vt 0.8321 0.7831
vt 0.3418 0.5689
vt 0.3321 0.2169
vt 1.0162 0.2152
vt 1.0057 0.5671
vt 1.1731 0.8083
vt 0.1731 0.8083
vt 0.1739 0.4559
vt 0.0162 0.2152
vt 0.0057 0.5671
vn 0.1876 -0.7947 0.5774
vn 0.6071 -0.7947 0.0000
vn -0.4911 -0.7947 0.3568
vn -0.4911 -0.7947 -0.3568
vn 0.1876 -0.7947 -0.5774
vn 0.9822 -0.1876 0.0000
vn 0.3035 -0.1876 0.9342
vn -0.7946 -0.1876 0.5774
vn -0.7946 -0.1876 -0.5774
vn 0.3035 -0.1876 -0.9342
vn 0.7946 0.1876 0.5774
vn -0.3035 0.1876 0.9342
vn -0.9822 0.1876 0.0000
vn -0.3035 0.1876 -0.9342
vn 0.7946 0.1876 -0.5774
vn 0.4911 0.7947 0.3568
vn -0.1876 0.7947 0.5774
vn -0.6071 0.7947 0.0000
vn -0.1876 0.7947 -0.5774
vn 0.4911 0.7947 -0.3568
usemtl None
s off
f 1/1/1 2/2/1 3/3/1
f 2/2/2 1/1/2 6/4/2
f 1/1/3 3/3/3 4/5/3
f 1/1/4 4/5/4 5/6/4
f 1/1/5 5/6/5 6/4/5
f 2/2/6 6/4/6 11/7/6
f 3/3/7 2/2/7 7/8/7
f 4/5/8 3/3/8 8/9/8
f 5/6/9 4/5/9 9/10/9
f 6/4/10 5/6/10 10/11/10
f 2/2/11 11/7/11 7/8/11
f 3/3/12 7/8/12 8/9/12
f 4/5/13 8/9/13 9/10/13
f 5/6/14 9/10/14 10/11/14
f 6/4/15 10/12/15 11/7/15
f 7/8/16 11/7/16 12/13/16
f 8/14/17 7/8/17 12/13/17
f 9/15/18 8/14/18 12/13/18
f 10/12/19 9/15/19 12/13/19
f 11/7/20 10/12/20 12/13/20

Related

I wanna make an octagon using multiple triangles in WebGL but it does not run

i am trying to run this code in a json script, its just the webgl code.
I am trying to make this work but it doesnt. I am trying to make an octagon using mulptiple triangles but when im trying to create them with the indexes it does not work.
I think the problem is at the indexes numItems value.
It is also supposed to show a black backround but this doesnt show either.
Any help? (this is not the whole code but i figure here is where the problem is)
octagonIndexBuffer = gl.createBuffer(); // **
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, octagonIndexBuffer); // We bind the IBO as an ELEMENT_ARRAY_BUFFER.
let indexes = [ // The indexes that will be loaded in the buffer
0, 2, 4,//A
4, 0, 6,//B
6, 0, 7,//C
7, 0, 5,//D
5, 0, 3,//E
5, 0, 8,//F
8, 0, 9,//G
9, 0, 2//H
];
gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, new Uint16Array(indexes), gl.STATIC_DRAW);
octagonIndexBuffer.itemSize = 1; // item size = 1 because one index = one vertex.
octagonIndexBuffer.numItems = 24;
octagonVertexColorBuffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, octagonVertexColorBuffer);
// We define our vertex colors.
let colors = [
0.0, 1.0, 0.0, 1.0,
1.0, 0.0, 0.0, 1.0,
0.0, 0.0, 1.0, 1.0,
0.0, 1.0, 0.0, 1.0,
1.0, 0.0, 0.0, 1.0,
0.0, 0.0, 1.0, 1.0,
0.0, 1.0, 0.0, 1.0,
1.0, 0.0, 0.0, 1.0,
0.0, 0.0, 0.0, 1.0
];
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(colors), gl.STATIC_DRAW);
octagonVertexColorBuffer.itemSize = 4; // item size = 4 because (R, G, B, A).
octagonVertexColorBuffer.numItems = 9;
octagonVertexPositionBuffer = gl.createBuffer(); // We tell our GPU to create a buffer and give us a pointer to it.
gl.bindBuffer(gl.ARRAY_BUFFER, octagonVertexPositionBuffer); // We need to bind the buffer (make it active) in order to use it.
// We define our vertex positions.
let vertices = [
0.0, 0.0, 0.0, //1
1.0, 1.0, 0.0,//2
-1.0, 1.0, 0.0, //3
1.0, -1.0, 0.0,//4
-1.0, -1.0, 0.0, //5
0.5, -1.5, 0.0, //6
-0.5, -1.5, 0.0 , //7
-0.5, 1.5, 0.0 , //8
0.5, 1.5, 0.0 , //9
];
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(vertices), gl.STATIC_DRAW); // We send the vertex positions to the buffer in our GPU.
octagonVertexColorBufferVertexPositionBuffer.itemSize = 3; // item size = 3 because (x, y, z).
octagonVertexPositionBuffer.numItems = 9; // numItems = 3 because we have three vertices.

Range Slider for camera in WebGL

I'm trying to get a range slider to change the far plane and the angle of the camera in Webgl but I can't. How do I do in order for my code to read the value from the slider and then change the value in the perspective of the camera?
// Application info.
var app = app || {};
function initGL() {
var gl = app.gl;
gl.enable(gl.DEPTH_TEST);
gl.depthFunc(gl.LEQUAL);
gl.viewport(0, 0, app.can.width, app.can.height);
gl.clearColor(0., 0., 0., 1.0);
gl.clear(app.gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
var vs = createShaderFromElement(app.gl, "vs");
var fs = createShaderFromElement(app.gl, "fs");
app.progObject = buildProgram(app.gl, vs, fs);
gl.useProgram(app.progObject);
}
function initScene() {
var gl = app.gl;
// Creer le buffer de geometrie (vertex)
//
var positions = [
// Front face
-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, -1.0, 1.0,
// Back face
-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, -1.0, -1.0,
// Top face
-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, 1.0, -1.0,
// Bottom face
-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, -1.0, -1.0,
// Right face
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, -1.0, -1.0,
// Left face
-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, -1.0, -1.0
];
app.nPoints = positions.length / 3;
var colors = [
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0,
0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0,
0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1,
1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0,
1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1
];
var loc, buffer;
// Create and copy position buffer.
loc = gl.getAttribLocation(app.progObject, "pos");
buffer = gl.createBuffer();
gl.enableVertexAttribArray(loc);
gl.bindBuffer(gl.ARRAY_BUFFER, buffer);
gl.vertexAttribPointer(loc, 3, gl.FLOAT, false /*no normalization*/, 0 /*stride*/, 0 /*offset*/);
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(positions), gl.STATIC_DRAW);
// Create and copy color buffer.
loc = gl.getAttribLocation(app.progObject, "color");
buffer = gl.createBuffer();
gl.enableVertexAttribArray(loc);
gl.bindBuffer(gl.ARRAY_BUFFER, buffer);
gl.vertexAttribPointer(loc, 3, gl.FLOAT, false /*no normalization*/, 0 /*stride*/, 0 /*offset*/);
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(colors), gl.STATIC_DRAW);
// Look for uniforms.
app.pmLocation = gl.getUniformLocation(app.progObject, "projMatrix");
app.mmLocation = gl.getUniformLocation(app.progObject, "modelMatrix");
app.vmLocation = gl.getUniformLocation(app.progObject, "viewMatrix");
var mat4 = glMatrix.mat4;
app.projMatrix = mat4.create();
app.modelMatrix = mat4.create();
app.viewMatrix = mat4.create();
app.pcam = mat4.create();
app.mvpMatrix = mat4.create();
var near = 0.1;
mat4.perspective(app.projMatrix, Math.PI / 4.0 /*45 degrees*/, 1, 0.1, farChanged());
mat4.lookAt(app.viewMatrix, [ 15, 0, -10], [-5, 0, 0], [0, 1, 0]);
}
function animate(time) {
var gl = app.gl;
var mat4 = glMatrix.mat4;
// converts to seconds.
var seconds = time * 1E-3;
var dtime = time - app.oldTime;
var mm1 = mat4.create();
mat4.translate(mm1, app.modelMatrix, [2, 0, 0]);
var angle = time * 0.001;
mat4.rotateY(mm1, mm1, angle);
app.oldTime = time;
gl.clear(app.gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
gl.uniformMatrix4fv(app.pmLocation, false, app.projMatrix);
gl.uniformMatrix4fv(app.mmLocation, false, mm1);
gl.uniformMatrix4fv(app.vmLocation, false, app.viewMatrix);
gl.drawArrays(gl.TRIANGLES, 0, app.nPoints);
// Pour dessiner autre cube, calculer autre model matrix
// et redessiner ... gl.drawArrays(gl.TRIANGLES, 0, app.nPoints);
var mm2 = mat4.create();
mat4.translate(mm2, app.modelMatrix, [-2, 0, 0]);
mat4.rotateY(mm2, mm2, angle)
gl.uniformMatrix4fv(app.pmLocation, false, app.projMatrix);
gl.uniformMatrix4fv(app.mmLocation, false, mm2);
gl.uniformMatrix4fv(app.vmLocation, false, app.viewMatrix);
gl.drawArrays(gl.TRIANGLES, 0, app.nPoints);
//for the camera
var moveInAndOut = 0.1 * Math.cos(angle);
var moveLeftAndRight = 0.1 * Math.sin(angle);
app.pcam = ([moveInAndOut, 0, moveLeftAndRight]);
// app.viewMatrix = lookAt(app.pcam, [0, 0, 0], [0, 1, 0]);
// mat4.lookAt(app.viewMatrix, app.pcam , [0, 0, 0], [0, 1, 0]);
mat4.translate(app.viewMatrix, app.viewMatrix, app.pcam);
mat4.rotateY(app.viewMatrix, app.viewMatrix, 0.05);
window.requestAnimationFrame(animate);
}
function fovChanged(id, value) {
console.log("FOV Angle: ", value);
var label = document.getElementById('output-fov');
label.innerHTML = value;
}
function farChanged(id, value) {
console.log("Far Plane: ", value);
var label2 = document.getElementById('output-far');
label2.innerHTML = value;
}
function init() {
[app.can, app.gl] = getContextGL('can');
if (app.can == null || app.gl == null) {
alert("Can't init canvas or context");
return;
}
app.can.width = app.can.height * (app.can.clientWidth / app.can.clientHeight);
var rect = app.can.getBoundingClientRect();
app.scaleX = app.can.width / rect.width;
app.scaleY = app.can.height / rect.height;
initGL();
initScene();
app.oldTime = 0;
animate(0);
}
init();
// -----
function getContextGL(id) {
const can = document.getElementById(id);
const gl = can.getContext('webgl');
return [can, gl];
}
function createShaderFromElement(gl, id) {
const e = document.getElementById(id);
const s = gl.createShader(e.type.indexOf('vertex') >= 0 ? gl.VERTEX_SHADER : gl.FRAGMENT_SHADER);
gl.shaderSource(s, e.text);
gl.compileShader(s);
return s;
}
function buildProgram(gl, vs, fs) {
const p = gl.createProgram();
gl.attachShader(p, vs);
gl.attachShader(p, fs);
gl.linkProgram(p);
return p;
}
div
{
}
#main-div
{
display:inline-block;
}
#viewport, #manager
{
float: left;
margin: auto;
}
.color
{
width:100px;
height:50px;
}
.blue{
background:#0f0;
}
#viewport
{
width: 600px;
height:700px;
}
#can
{
width: 600px;
height: 500px;
border:1px solid orange;
}
#manager
{
width: 200px;
height:300px;
padding: 0 0 0 5px;
}
#obj-list
{
width: 200px;
}
<!DOCTYPE html>
<html>
<head>
<title>Cube Transform</title>
<link rel="stylesheet" type="text/css" href="style.css">
<script src="transforms.js"></script>
<script src="utils.js"></script>
<script src="gl-matrix-min.js"></script>
<script id="vs" type="x-shader/x-vertex">
precision mediump float;
uniform mat4 projMatrix;
uniform mat4 viewMatrix;
uniform mat4 modelMatrix;
attribute vec3 pos;
attribute vec3 color;
varying vec3 fColor;
void main()
{
fColor = color;
vec4 pt = vec4(pos, 1.0);
gl_Position = projMatrix * viewMatrix * modelMatrix * pt;
}
</script>
<script id="fs" type="x-shader/x-fragment">
precision mediump float;
varying vec3 fColor;
void main()
{
gl_FragColor = vec4(fColor,1);
}
</script>
</head>
<body onload="init();">
<div id="main-div">
<div id="viewport">
<canvas id="can" >Your browser doesn't seem to support canvas!</canvas>
<div class="slider">
<span class="slider-label">FOV cam</span>
<input id="fov" class="slider-input" type="range" min="20" max="90" step="1" value="0" onClick="fovChanged(this.id, this.value)" />
<span id="output-fov" class="slider-value">20</span>
</div>
<div class="slider">
<span class="slider-label">Far Plane</span>
<input id="far" class="slider-input" type="range" min="5" max="30" step="0.1" value="0" onClick="farChanged(this.id, this.value)" />
<span id="output-far" class="slider-value">5</span>
</div>
</div>
</div>
</body>
</html>
My code contains multiple files so here's the link to the full code:
https://wetransfer.com/downloads/f05fa22faa0e0803a0b6fa13348d7d9520200920195230/52fc67
The js code is in transforms.js.
Thank you!
This line
mat4.perspective(app.projMatrix, Math.PI / 4.0 /*45 degrees*/, 1, 0.1, farChanged());
makes no sense. farChanged does not return anything. It just sets the label's innerHTML
You need to update the project matrix when the event gets called
function farChanged(id, value) {
var label2 = document.getElementById('output-far');
label2.innerHTML = value;
glMatrix.mat4.perspective(app.projMatrix, Math.PI / 4.0 /*45 degrees*/ , 1, 0.1, parseFloat(value));
}
// Application info.
var app = app || {};
function initGL() {
var gl = app.gl;
gl.enable(gl.DEPTH_TEST);
gl.depthFunc(gl.LEQUAL);
gl.viewport(0, 0, app.can.width, app.can.height);
gl.clearColor(0., 0., 0., 1.0);
gl.clear(app.gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
var vs = createShaderFromElement(app.gl, "vs");
var fs = createShaderFromElement(app.gl, "fs");
app.progObject = buildProgram(app.gl, vs, fs);
gl.useProgram(app.progObject);
}
function initScene() {
var gl = app.gl;
// Creer le buffer de geometrie (vertex)
//
var positions = [
// Front face
-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, -1.0, 1.0,
// Back face
-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, -1.0, -1.0,
// Top face
-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, 1.0, -1.0,
// Bottom face
-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, -1.0, -1.0,
// Right face
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, -1.0, -1.0,
// Left face
-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, -1.0, -1.0
];
app.nPoints = positions.length / 3;
var colors = [
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0,
0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0,
0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1,
1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0,
1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1
];
var loc, buffer;
// Create and copy position buffer.
loc = gl.getAttribLocation(app.progObject, "pos");
buffer = gl.createBuffer();
gl.enableVertexAttribArray(loc);
gl.bindBuffer(gl.ARRAY_BUFFER, buffer);
gl.vertexAttribPointer(loc, 3, gl.FLOAT, false /*no normalization*/ , 0 /*stride*/ , 0 /*offset*/ );
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(positions), gl.STATIC_DRAW);
// Create and copy color buffer.
loc = gl.getAttribLocation(app.progObject, "color");
buffer = gl.createBuffer();
gl.enableVertexAttribArray(loc);
gl.bindBuffer(gl.ARRAY_BUFFER, buffer);
gl.vertexAttribPointer(loc, 3, gl.FLOAT, false /*no normalization*/ , 0 /*stride*/ , 0 /*offset*/ );
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(colors), gl.STATIC_DRAW);
// Look for uniforms.
app.pmLocation = gl.getUniformLocation(app.progObject, "projMatrix");
app.mmLocation = gl.getUniformLocation(app.progObject, "modelMatrix");
app.vmLocation = gl.getUniformLocation(app.progObject, "viewMatrix");
var mat4 = glMatrix.mat4;
app.projMatrix = mat4.create();
app.modelMatrix = mat4.create();
app.viewMatrix = mat4.create();
app.pcam = mat4.create();
app.mvpMatrix = mat4.create();
var near = 0.1;
mat4.perspective(app.projMatrix, Math.PI / 4.0 /*45 degrees*/ , 1, 0.1, 10);
mat4.lookAt(app.viewMatrix, [15, 0, -10], [-5, 0, 0], [0, 1, 0]);
}
function animate(time) {
var gl = app.gl;
var mat4 = glMatrix.mat4;
// converts to seconds.
var seconds = time * 1E-3;
var dtime = time - app.oldTime;
var mm1 = mat4.create();
mat4.translate(mm1, app.modelMatrix, [2, 0, 0]);
var angle = time * 0.001;
mat4.rotateY(mm1, mm1, angle);
app.oldTime = time;
gl.clear(app.gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
gl.uniformMatrix4fv(app.pmLocation, false, app.projMatrix);
gl.uniformMatrix4fv(app.mmLocation, false, mm1);
gl.uniformMatrix4fv(app.vmLocation, false, app.viewMatrix);
gl.drawArrays(gl.TRIANGLES, 0, app.nPoints);
// Pour dessiner autre cube, calculer autre model matrix
// et redessiner ... gl.drawArrays(gl.TRIANGLES, 0, app.nPoints);
var mm2 = mat4.create();
mat4.translate(mm2, app.modelMatrix, [-2, 0, 0]);
mat4.rotateY(mm2, mm2, angle)
gl.uniformMatrix4fv(app.pmLocation, false, app.projMatrix);
gl.uniformMatrix4fv(app.mmLocation, false, mm2);
gl.uniformMatrix4fv(app.vmLocation, false, app.viewMatrix);
gl.drawArrays(gl.TRIANGLES, 0, app.nPoints);
//for the camera
var moveInAndOut = 0.1 * Math.cos(angle);
var moveLeftAndRight = 0.1 * Math.sin(angle);
app.pcam = ([moveInAndOut, 0, moveLeftAndRight]);
// app.viewMatrix = lookAt(app.pcam, [0, 0, 0], [0, 1, 0]);
// mat4.lookAt(app.viewMatrix, app.pcam , [0, 0, 0], [0, 1, 0]);
mat4.translate(app.viewMatrix, app.viewMatrix, app.pcam);
mat4.rotateY(app.viewMatrix, app.viewMatrix, 0.05);
window.requestAnimationFrame(animate);
}
function fovChanged(id, value) {
console.log("FOV Angle: ", value);
var label = document.getElementById('output-fov');
label.innerHTML = value;
}
function farChanged(id, value) {
//console.log("Far Plane: ", value);
var label2 = document.getElementById('output-far');
label2.innerHTML = value;
glMatrix.mat4.perspective(app.projMatrix, Math.PI / 4.0 /*45 degrees*/ , 1, 0.1, parseFloat(value));
}
function init() {
[app.can, app.gl] = getContextGL('can');
if (app.can == null || app.gl == null) {
alert("Can't init canvas or context");
return;
}
app.can.width = app.can.height * (app.can.clientWidth / app.can.clientHeight);
var rect = app.can.getBoundingClientRect();
app.scaleX = app.can.width / rect.width;
app.scaleY = app.can.height / rect.height;
initGL();
initScene();
app.oldTime = 0;
animate(0);
}
init();
// -----
function getContextGL(id) {
const can = document.getElementById(id);
const gl = can.getContext('webgl');
return [can, gl];
}
function createShaderFromElement(gl, id) {
const e = document.getElementById(id);
const s = gl.createShader(e.type.indexOf('vertex') >= 0 ? gl.VERTEX_SHADER : gl.FRAGMENT_SHADER);
gl.shaderSource(s, e.text);
gl.compileShader(s);
return s;
}
function buildProgram(gl, vs, fs) {
const p = gl.createProgram();
gl.attachShader(p, vs);
gl.attachShader(p, fs);
gl.linkProgram(p);
return p;
}
div {}
#main-div {
display: inline-block;
}
#viewport,
#manager {
float: left;
margin: auto;
}
.color {
width: 100px;
height: 50px;
}
.blue {
background: #0f0;
}
#viewport {
width: 600px;
height: 700px;
}
#can {
width: 600px;
height: 500px;
border: 1px solid orange;
}
#manager {
width: 200px;
height: 300px;
padding: 0 0 0 5px;
}
#obj-list {
width: 200px;
}
<div id="main-div">
<div id="viewport">
<canvas id="can">Your browser doesn't seem to support canvas!</canvas>
<div class="slider">
<span class="slider-label">FOV cam</span>
<input id="fov" class="slider-input" type="range" min="20" max="90" step="1" value="0" onClick="fovChanged(this.id, this.value)" />
<span id="output-fov" class="slider-value">20</span>
</div>
<div class="slider">
<span class="slider-label">Far Plane</span>
<input id="far" class="slider-input" type="range" min="5" max="30" step="0.1" value="0" onClick="farChanged(this.id, this.value)" />
<span id="output-far" class="slider-value">5</span>
</div>
</div>
</div>
<script id="vs" type="x-shader/x-vertex">
precision mediump float;
uniform mat4 projMatrix;
uniform mat4 viewMatrix;
uniform mat4 modelMatrix;
attribute vec3 pos;
attribute vec3 color;
varying vec3 fColor;
void main()
{
fColor = color;
vec4 pt = vec4(pos, 1.0);
gl_Position = projMatrix * viewMatrix * modelMatrix * pt;
}
</script>
<script id="fs" type="x-shader/x-fragment">
precision mediump float;
varying vec3 fColor;
void main()
{
gl_FragColor = vec4(fColor,1);
}
</script>
<script src="https://cdn.jsdelivr.net/npm/gl-matrix#3.3.0/gl-matrix-min.js"></script>
Some other things.
Don't use body onload: https://stackoverflow.com/a/48256189/128511
Use addEventListener(eventName, function) instead of onClick="string"
Use the input event instead of the click event
don't set innerHTML, instead set textContent
Learn to use the snippets so they work (see the snippet above). In particular you didn't need <html>, <head>, links to <scripts> that don't exist and you needed to link to a cdn for glMatrix.

Trying to use 'attribute mat4' and not 'attribute uniform'

I have this variable 'xformMatrix' and each element holds an array of 16 values:
var xformMatrix =
[[0.9238795325112867, 0.3826834323650898, 0.0,
-0.3826834323650898, 0.9238795325112867, 0.0, 0.0,
0.0, 0.0, 1.0, 0.0,
0.0, 0.0, 0.0, 1.0],
[0.7071067811865476, 0.7071067811865475, 0.0, 0.0,
-0.7071067811865475, 0.7071067811865476, 0.0, 0.0,
0.0, 0.0, 1.0, 0.0,
0.0, 0.0, 0.0, 1.0],
[0.38268343236508984, 0.9238795325112867, 0.0, 0.0,
-0.9238795325112867, 0.38268343236508984, 0.0, 0.0,
0.0, 0.0, 1.0, 0.0,
0.0, 0.0, 0.0, 1.0],
[6.123233995736766e-17, 1, 0.0, 0.0,
-1, 6.123233995736766e-17, 0.0, 0.0,
0.0, 0.0, 1.0, 0.0,
0.0, 0.0, 0.0, 1.0]]
I am trying to use a 4x4 matrix as an attribute to rotate my triangles without having to fill in my vertices array anymore than what it is. I believe I am getting confused where gl.vertexAttribPointer is asking for a size:
gl.vertexAttribPointer(a_xformMatrix, 1, gl.FLOAT, false, 6 *
Float32Array.BYTES_PER_ELEMENT, 5 * Float32Array.BYTES_PER_ELEMENT);
This is how my shaders are setup:
var VSHADER_SOURCE =
'attribute vec4 a_Position;\n' +
'attribute mat4 a_xformMatrix;\n' +
'attribute vec3 a_Color;\n' +
'varying vec3 v_Color;\n' +
'void main() {\n' +
' v_Color = a_Color;\n' +
' gl_Position = a_xformMatrix * a_Position;\n' +
'}\n';
// Fragment shader program
var FSHADER_SOURCE =
'precision mediump float;\n' +
'varying vec3 v_Color;\n' +
'void main() {\n' +
' gl_FragColor = vec4(v_Color, 1.0);\n' +
'}\n';
An example of my function:
function initVertexBuffers(gl) {
// Triangle Verticies
var vertices = new Float32Array(
[ // x, y r, g, b rotate matrix
0.0, 0.5, 1.0, 0.0, 0.0, xformMatrix[0],
-0.5, -0.5, 1.0, 0.0, 0.0, xformMatrix[0],
0.5, -0.5, 1.0, 0.0, 0.0, xformMatrix[0],
0.0, 0.5, 0.0, 1.0, 0.0, xformMatrix[1],
-0.5, -0.5, 0.0, 1.0, 0.0, xformMatrix[1],
0.5, -0.5, 0.0, 1.0, 0.0, xformMatrix[1],
0.0, 0.5, 0.0, 0.0, 1.0, xformMatrix[2],
-0.5, -0.5, 0.0, 0.0, 1.0, xformMatrix[2],
0.5, -0.5, 0.0, 0.0, 1.0, xformMatrix[2],
0.0, 0.5, 1.0, 0.0, 1.0, xformMatrix[3],
-0.5, -0.5, 1.0, 0.0, 1.0, xformMatrix[3],
0.5, -0.5, 1.0, 0.0, 1.0, xformMatrix[3]
]);
var n = 12; // The number of vertices
// Create a buffer object
var vertexBuffer = gl.createBuffer();
if (!vertexBuffer) {
console.log('Failed to create the buffer object');
return false;
}
// Bind the buffer object to target
gl.bindBuffer(gl.ARRAY_BUFFER, vertexBuffer);
// Write date into the buffer object
gl.bufferData(gl.ARRAY_BUFFER, vertices, gl.STATIC_DRAW);
// Assign the buffer object to the position attribute variable
var a_Position = gl.getAttribLocation(gl.program, 'a_Position');
if (a_Position < 0) {
console.log('Failed to get the storage location of a_Position');
return -1;
}
// Assign the buffer object to the color attribute variable
var a_Color = gl.getAttribLocation(gl.program, 'a_Color');
if (a_Color < 0) {
console.log('Failed to get the storage location of a_Color');
return -1;
}
// Assign the buffer object to the rotation matrix attribute variable
var a_xformMatrix = gl.getAttribLocation(gl.program, 'a_xformMatrix');
if (a_xformMatrix < 0) {
console.log('Failed to get the storage location of a_xformMatrix');
return -1;
}
// Set Pointers
gl.vertexAttribPointer(a_Position, 2, gl.FLOAT, false, 6 * Float32Array.BYTES_PER_ELEMENT, 0);
gl.vertexAttribPointer(a_Color, 3, gl.FLOAT, false, 6 * Float32Array.BYTES_PER_ELEMENT, 2 * Float32Array.BYTES_PER_ELEMENT);
gl.vertexAttribPointer(a_xformMatrix, 1, gl.FLOAT, false, 6 * Float32Array.BYTES_PER_ELEMENT, 5 * Float32Array.BYTES_PER_ELEMENT);
// Enable the assignment to a_Position variable
gl.enableVertexAttribArray(a_Position);
gl.enableVertexAttribArray(a_Color);
gl.enableVertexAttribArray(a_xformMatrix);
return n;
}
The final output should look something like this:
Is there a trick to doing it this way or am I just going in the wrong direction?
What you're doing is not common
You've got x, y, r, g, b, m[0], m[1], m[2], m[3], m[4], m[5], m[6], m[7], m[8], m[9], m[10], m[11], m[12], m[13], m[14], m[15] per vertex so your stride, since you're trying to put all the data in the same buffer, would be
21 * Float32Array.BYTES_PER_ELEMENT
Then you need to set 4 attributes for the mat4 and the size for each is 4 (4 attributes, size 4 each = 16 values of the matrix)
// Set Pointers
const stride = 21 * Float32Array.BYTES_PER_ELEMENT;
gl.vertexAttribPointer(a_Position, 2, gl.FLOAT, false, stride, 0);
gl.vertexAttribPointer(a_Color, 3, gl.FLOAT, false, stride, 2 * Float32Array.BYTES_PER_ELEMENT);
gl.vertexAttribPointer(a_xformMatrix + 0, 4, gl.FLOAT, false, stride, 5 * Float32Array.BYTES_PER_ELEMENT);
gl.vertexAttribPointer(a_xformMatrix + 1, 4, gl.FLOAT, false, stride, 9 * Float32Array.BYTES_PER_ELEMENT);
gl.vertexAttribPointer(a_xformMatrix + 2, 4, gl.FLOAT, false, stride, 13 * Float32Array.BYTES_PER_ELEMENT);
gl.vertexAttribPointer(a_xformMatrix + 3, 4, gl.FLOAT, false, stride, 17 * Float32Array.BYTES_PER_ELEMENT);
And further this code isn't putting the matrix data into the Float32Array. You could write some code to merge the data or if you want to do it by hand
var vertices = new Float32Array(
[ // x, y r, g, b rotate matrix
0.0, 0.5, 1.0, 0.0, 0.0,
xformMatrix[0][0], xformMatrix[0][1], xformMatrix[0][2], xformMatrix[0][3],
xformMatrix[0][4], xformMatrix[0][5], xformMatrix[0][6], xformMatrix[0][7],
xformMatrix[0][8], xformMatrix[0][9], xformMatrix[0][10], xformMatrix[0][11],
xformMatrix[0][12], xformMatrix[0][13], xformMatrix[0][14], xformMatrix[0][15],
... repeat for the next vertex ..
]);
Note I didn't bother to check if you make all those changes if your code will work. Only that based on what you said you are trying to do those are the most obvious issues.
You can probably see that if you go this way you have a ton of data to update as you try to rotate the triangles so you probably want to find a different approach.
Check out these tutorials. The first article draws a bunch of rectangles in different colors. It uses one draw call per color which is the most common way. It then builds up to using matrices in the following articles.
You could also just pass in a rotation per triangle and compute the rotation in the shader. You'd then have just one rotation value per triangle per vertex instead of a matrix per triangle per vertex. You'd have to update 3 rotations per triangle to animate them.
You could also indirect through a texture so instead of a rotation per triangle per vertex you have a triangle ID per triangle per vertex. So the first 3 vertices get ID = 0, the next 3 ID = 1, etc. You pass that in as an attribute then you could use that to generate rotation as in rotation = ID * constant or you could use that id to look up a rotation in a texture as in rotation = texture2D(textureWithRotationData, vec2((id + .5) / textureWidth, 0.5).r. The advantage to this method is you'd only have to update 1 rotation per triangle. The one in the texture.

WebGL drawElements out of range?

I am playing around with WebGL trying to learn it, so I took some code from a webgl tutorial, and tried to add my own lines, but whenever I run it, it gives me this error:
.WebGLRenderingContext: GL ERROR :GL_INVALID_OPERATION : glDrawElements: attempt to access out of range vertices in attribute 0
Note: attribute 0 is my vertex buffer
My buffer initialization code is (obviously assume global vars if there is no definition)
cubeVertexPositionBuffer = gl.createBuffer(); // create a buffer
gl.bindBuffer(gl.ARRAY_BUFFER, cubeVertexPositionBuffer);
//for laziness
var _f=1.0/3.0;
vertices = [ // this is code from the tutorial
// Front face
-1.0, -1.0, 1.0, 1.0, -1.0, 1.0, 1.0, 1.0, 1.0, -1.0, 1.0, 1.0,
// Back face
-1.0, -1.0, -1.0, -1.0, 1.0, -1.0, 1.0, 1.0, -1.0, 1.0, -1.0, -1.0,
// Top face
-1.0, 1.0, -1.0, -1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, -1.0,
// Bottom face
-1.0, -1.0, -1.0, 1.0, -1.0, -1.0, 1.0, -1.0, 1.0, -1.0, -1.0, 1.0,
// Right face
1.0, -1.0, -1.0, 1.0, 1.0, -1.0, 1.0, 1.0, 1.0, 1.0, -1.0, 1.0,
// Left face
-1.0, -1.0, -1.0, -1.0, -1.0, 1.0, -1.0, 1.0, 1.0, -1.0, 1.0, -1.0,
// this is my own code
-1.0+ _f, -1.0, 1.0, -1.0+ _f, -1.0, -1.0,
-1.0+2*_f, -1.0, 1.0, -1.0+2*_f, -1.0, -1.0,
-1.0+3*_f, -1.0, 1.0, -1.0+3*_f, -1.0, -1.0,
-1.0+4*_f, -1.0, 1.0, -1.0+4*_f, -1.0, -1.0,
-1.0+5*_f, -1.0, 1.0, -1.0+5*_f, -1.0, -1.0
];
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(vertices),
gl.STATIC_DRAW);
cubeVertexPositionBuffer.itemSize = 3;
cubeVertexPositionBuffer.numItems = 34;
// color buffer code is irrelevant because my color buffer is attribute 1 not 0
// plus it's all copied from the tutorial
// index buffer
// note I changed some code so the cube drawn is wireframe instead of solid
// I tested that without extra vertex or index buffer data and it worked
cubeVertexIndexBuffer = gl.createBuffer(); // this modified a bit from the tutorial
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, cubeVertexIndexBuffer);
cubeVertexIndices =
[ 0, 1, 2, 3, 0, // Front face
4, 5, 6, 7, 4, // Back face
8, 9, 10, 11, 8, // Top face
12, 13, 14, 15, 12, // Bottom face
16, 17, 18, 19, 16, // Right face
20, 21, 22, 23, 20, // Left face
// this is my code
24, 25, 26, 27, 28, 29, 30, 31, 32, 33
];
gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, new Uint16Array(
cubeVertexIndices), gl.STATIC_DRAW);
cubeVertexIndexBuffer.itemSize = 1;
cubeVertexIndexBuffer.numItems = 40;
And here's my drawing code
// set up perspective and stuff
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.ELEMENT_ARRAY_BUFFER, cubeVertexIndexBuffer);
gl.uniformMatrix4fv(shaderProgram.pMatrixUniform, false, pMatrix); // perspective matrix
gl.uniformMatrix4fv(shaderProgram.mvMatrixUniform, false, mvMatrix); // model view matrix
gl.lineWidth(1.0);
gl.drawElements(gl.LINES, cubeVertexIndexBuffer.numItems,
gl.UNSIGNED_SHORT, 0);
Actually I figured it out. Being the WebGL newb that I am, I managed to forget to add the indexes for each vertex in my color buffer. It just never came across my mind that my shaders required each vertex to have a color. (It would have been nice though, if WebGL had said it was an error in attribute 1 (my color attribute) and not attribute 0 (my vertex position attribute).
The problem can also occur if the bufferData you have allocated in:
gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, new Uint16Array(
cubeVertexIndices), gl.STATIC_DRAW);
is too short (See second parameter).
This can happen if the creation of vertices happen programmatically. At least, that's the way it happened to me.

Multiple Programs in WebGL doesn't work

I'm trying to use multiple shader programs in webgl but continue to get issues.
It seems that if i have a different number of vertex shader attributes in programs then i get nothing rendered with no errors. Is there some constraint that means that programs have to have the same number of attributes? Do i need to disable/enable attribute locations when changing programs?
It seems that just creating multiple programs causes the issue ( i dont even have to use the second shader, just the fact that it is created causes the issue).
To create the attributes i'm using the following snippet (works fine when number of attributes in all programs is the same):
for ( var i=0 ; i<vertexAttributes.length ; i++ )
{
shaderProgram[vertexAttributes[i].name] = gl.getAttribLocation(shaderProgram,vertexAttributes[i].name);
gl.enableVertexAttribArray(shaderProgram[vertexAttributes[i].name]);
}
Sorry for posting such a large chunk of code, but this best illustrates the issue. this is code from one of the learningwebgl.com tutorials, all that i have done is add a second pair of shaders ("shader2-fs" and "shader2-vs") and a new function called initShaders2. if initShaders2 is called in webGLStart() (as it is in the code) then nothing is drawn??? i'm baffled!
<script type="text/javascript" src="two_files/glMatrix-0.js"></script>
<script type="text/javascript" src="two_files/webgl-utils.js"></script>
<script id="shader-fs" type="x-shader/x-fragment">
precision mediump float;
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 id="shader2-fs" type="x-shader/x-fragment">
precision mediump float;
varying vec2 vTextureCoord;
varying vec3 vLightWeighting;
uniform sampler2D uSampler;
void main(void) {
vec4 textureColor = texture2D(uSampler, vec2(vTextureCoord.s, vTextureCoord.t));
gl_FragColor = vec4(textureColor.rgb * vLightWeighting, textureColor.a);
}
</script>
<script id="shader2-vs" type="x-shader/x-vertex">
attribute vec3 aVertexPosition;
attribute vec3 aVertexNormal;
attribute vec2 aTextureCoord;
uniform mat4 uMVMatrix;
uniform mat4 uPMatrix;
uniform mat3 uNMatrix;
uniform vec3 uAmbientColor;
uniform vec3 uLightingDirection;
uniform vec3 uDirectionalColor;
uniform bool uUseLighting;
varying vec2 vTextureCoord;
varying vec3 vLightWeighting;
void main(void) {
gl_Position = uPMatrix * uMVMatrix * vec4(aVertexPosition, 1.0);
vTextureCoord = aTextureCoord;
if (!uUseLighting) {
vLightWeighting = vec3(1.0, 1.0, 1.0);
} else {
vec3 transformedNormal = uNMatrix * aVertexNormal;
float directionalLightWeighting = max(dot(transformedNormal, uLightingDirection), 0.0);
vLightWeighting = uAmbientColor + uDirectionalColor * directionalLightWeighting;
}
}
</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 shaderProgram2;
function initShaders2() {
var fragmentShader = getShader(gl, "shader2-fs");
var vertexShader = getShader(gl, "shader2-vs");
shaderProgram2 = gl.createProgram();
gl.attachShader(shaderProgram2, vertexShader);
gl.attachShader(shaderProgram2, fragmentShader);
gl.linkProgram(shaderProgram2);
if (!gl.getProgramParameter(shaderProgram2, gl.LINK_STATUS)) {
alert("Could not initialise shaders");
}
gl.useProgram(shaderProgram2);
shaderProgram2.vertexPositionAttribute = gl.getAttribLocation(shaderProgram2, "aVertexPosition");
gl.enableVertexAttribArray(shaderProgram2.vertexPositionAttribute);
shaderProgram2.vertexNormalAttribute = gl.getAttribLocation(shaderProgram2, "aVertexNormal");
gl.enableVertexAttribArray(shaderProgram2.vertexNormalAttribute);
shaderProgram2.textureCoordAttribute = gl.getAttribLocation(shaderProgram2, "aTextureCoord");
gl.enableVertexAttribArray(shaderProgram2.textureCoordAttribute);
shaderProgram2.pMatrixUniform = gl.getUniformLocation(shaderProgram2, "uPMatrix");
shaderProgram2.mvMatrixUniform = gl.getUniformLocation(shaderProgram2, "uMVMatrix");
shaderProgram2.nMatrixUniform = gl.getUniformLocation(shaderProgram2, "uNMatrix");
shaderProgram2.samplerUniform = gl.getUniformLocation(shaderProgram2, "uSampler");
shaderProgram2.useLightingUniform = gl.getUniformLocation(shaderProgram2, "uUseLighting");
shaderProgram2.ambientColorUniform = gl.getUniformLocation(shaderProgram2, "uAmbientColor");
shaderProgram2.lightingDirectionUniform = gl.getUniformLocation(shaderProgram2, "uLightingDirection");
shaderProgram2.directionalColorUniform = gl.getUniformLocation(shaderProgram2, "uDirectionalColor");
}
var mvMatrix = mat4.create();
var mvMatrixStack = [];
var pMatrix = mat4.create();
function mvPushMatrix() {
var copy = mat4.create();
mat4.set(mvMatrix, copy);
mvMatrixStack.push(copy);
}
function mvPopMatrix() {
if (mvMatrixStack.length == 0) {
throw "Invalid popMatrix!";
}
mvMatrix = mvMatrixStack.pop();
}
function setMatrixUniforms() {
gl.uniformMatrix4fv(shaderProgram.pMatrixUniform, false, pMatrix);
gl.uniformMatrix4fv(shaderProgram.mvMatrixUniform, false, mvMatrix);
}
function degToRad(degrees) {
return degrees * Math.PI / 180;
}
var pyramidVertexPositionBuffer;
var pyramidVertexColorBuffer;
var cubeVertexPositionBuffer;
var cubeVertexColorBuffer;
var cubeVertexIndexBuffer;
function initBuffers() {
pyramidVertexPositionBuffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, pyramidVertexPositionBuffer);
var vertices = [
// Front face
0.0, 1.0, 0.0,
-1.0, -1.0, 1.0,
1.0, -1.0, 1.0,
// Right face
0.0, 1.0, 0.0,
1.0, -1.0, 1.0,
1.0, -1.0, -1.0,
// Back face
0.0, 1.0, 0.0,
1.0, -1.0, -1.0,
-1.0, -1.0, -1.0,
// Left face
0.0, 1.0, 0.0,
-1.0, -1.0, -1.0,
-1.0, -1.0, 1.0
];
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(vertices), gl.STATIC_DRAW);
pyramidVertexPositionBuffer.itemSize = 3;
pyramidVertexPositionBuffer.numItems = 12;
pyramidVertexColorBuffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, pyramidVertexColorBuffer);
var colors = [
// Front face
1.0, 0.0, 0.0, 1.0,
0.0, 1.0, 0.0, 1.0,
0.0, 0.0, 1.0, 1.0,
// Right face
1.0, 0.0, 0.0, 1.0,
0.0, 0.0, 1.0, 1.0,
0.0, 1.0, 0.0, 1.0,
// Back face
1.0, 0.0, 0.0, 1.0,
0.0, 1.0, 0.0, 1.0,
0.0, 0.0, 1.0, 1.0,
// Left face
1.0, 0.0, 0.0, 1.0,
0.0, 0.0, 1.0, 1.0,
0.0, 1.0, 0.0, 1.0
];
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(colors), gl.STATIC_DRAW);
pyramidVertexColorBuffer.itemSize = 4;
pyramidVertexColorBuffer.numItems = 12;
cubeVertexPositionBuffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, cubeVertexPositionBuffer);
vertices = [
// Front face
-1.0, -1.0, 1.0,
1.0, -1.0, 1.0,
1.0, 1.0, 1.0,
-1.0, 1.0, 1.0,
// Back face
-1.0, -1.0, -1.0,
-1.0, 1.0, -1.0,
1.0, 1.0, -1.0,
1.0, -1.0, -1.0,
// Top face
-1.0, 1.0, -1.0,
-1.0, 1.0, 1.0,
1.0, 1.0, 1.0,
1.0, 1.0, -1.0,
// Bottom face
-1.0, -1.0, -1.0,
1.0, -1.0, -1.0,
1.0, -1.0, 1.0,
-1.0, -1.0, 1.0,
// Right face
1.0, -1.0, -1.0,
1.0, 1.0, -1.0,
1.0, 1.0, 1.0,
1.0, -1.0, 1.0,
// Left face
-1.0, -1.0, -1.0,
-1.0, -1.0, 1.0,
-1.0, 1.0, 1.0,
-1.0, 1.0, -1.0
];
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(vertices), gl.STATIC_DRAW);
cubeVertexPositionBuffer.itemSize = 3;
cubeVertexPositionBuffer.numItems = 24;
cubeVertexColorBuffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, cubeVertexColorBuffer);
colors = [
[1.0, 0.0, 0.0, 1.0], // Front face
[1.0, 1.0, 0.0, 1.0], // Back face
[0.0, 1.0, 0.0, 1.0], // Top face
[1.0, 0.5, 0.5, 1.0], // Bottom face
[1.0, 0.0, 1.0, 1.0], // Right face
[0.0, 0.0, 1.0, 1.0] // Left face
];
var unpackedColors = [];
for (var i in colors) {
var color = colors[i];
for (var j=0; j < 4; j++) {
unpackedColors = unpackedColors.concat(color);
}
}
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(unpackedColors), gl.STATIC_DRAW);
cubeVertexColorBuffer.itemSize = 4;
cubeVertexColorBuffer.numItems = 24;
cubeVertexIndexBuffer = gl.createBuffer();
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, cubeVertexIndexBuffer);
var cubeVertexIndices = [
0, 1, 2, 0, 2, 3, // Front face
4, 5, 6, 4, 6, 7, // Back face
8, 9, 10, 8, 10, 11, // Top face
12, 13, 14, 12, 14, 15, // Bottom face
16, 17, 18, 16, 18, 19, // Right face
20, 21, 22, 20, 22, 23 // Left face
];
gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, new Uint16Array(cubeVertexIndices), gl.STATIC_DRAW);
cubeVertexIndexBuffer.itemSize = 1;
cubeVertexIndexBuffer.numItems = 36;
}
var rPyramid = 0;
var rCube = 0;
function drawScene() {
gl.viewport(0, 0, gl.viewportWidth, gl.viewportHeight);
gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
mat4.perspective(45, gl.viewportWidth / gl.viewportHeight, 0.1, 100.0, pMatrix);
mat4.identity(mvMatrix);
mat4.translate(mvMatrix, [-1.5, 0.0, -8.0]);
mvPushMatrix();
mat4.rotate(mvMatrix, degToRad(rPyramid), [0, 1, 0]);
gl.bindBuffer(gl.ARRAY_BUFFER, pyramidVertexPositionBuffer);
gl.vertexAttribPointer(shaderProgram.vertexPositionAttribute, pyramidVertexPositionBuffer.itemSize, gl.FLOAT, false, 0, 0);
gl.bindBuffer(gl.ARRAY_BUFFER, pyramidVertexColorBuffer);
gl.vertexAttribPointer(shaderProgram.vertexColorAttribute, pyramidVertexColorBuffer.itemSize, gl.FLOAT, false, 0, 0);
setMatrixUniforms();
gl.drawArrays(gl.TRIANGLES, 0, pyramidVertexPositionBuffer.numItems);
mvPopMatrix();
mat4.translate(mvMatrix, [3.0, 0.0, 0.0]);
mvPushMatrix();
mat4.rotate(mvMatrix, degToRad(rCube), [1, 1, 1]);
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.ELEMENT_ARRAY_BUFFER, cubeVertexIndexBuffer);
setMatrixUniforms();
gl.drawElements(gl.TRIANGLES, cubeVertexIndexBuffer.numItems, gl.UNSIGNED_SHORT, 0);
mvPopMatrix();
}
var lastTime = 0;
function animate() {
var timeNow = new Date().getTime();
if (lastTime != 0) {
var elapsed = timeNow - lastTime;
rPyramid += (90 * elapsed) / 1000.0;
rCube -= (75 * elapsed) / 1000.0;
}
lastTime = timeNow;
}
function tick() {
requestAnimFrame(tick);
drawScene();
animate();
}
function webGLStart() {
var canvas = document.getElementById("lesson04-canvas");
initGL(canvas);
initShaders2(); // !!!!!!!!!!!!!!!!!!!!!!!!!
initShaders();
initBuffers();
gl.clearColor(0.0, 0.0, 0.0, 1.0);
gl.enable(gl.DEPTH_TEST);
tick();
}
</script>
</head>
<body onload="webGLStart();">
<< Back to Lesson 4<br>
<canvas id="lesson04-canvas" style="border: none;" width="500" height="500"></canvas>
<br>
<< Back to Lesson 4<br>
</body></html>
You'll probably need to give us a little bit more information, but to get you started here's a quick checklist. Any time you change programs, you'll want to:
Enable the appropriate vertex attribute arrays
Bind the vertex attribute pointers, even if they were already bound to the right buffers
Bind any uniforms needed, like texture samplers.
Basically, you want to treat every time you change programs as if it's the first time you're setting up a draw call.

Categories