Related
I am trying to make a webGL application for rendering random generated terrains. The rendering of the terrain works fine (almost), but when I try to render a simple quad to simulate water, the triangles of the water are not in the right place.
The red parts in the image are the messed up triangles, which should only be two triangles forming a square as big as the terrain. I found out that if the terrain size is 33x33 points (like in the image), the water buffers size makes up 1089 triangles instead of two, which is kind of weird. The same principle applies for other terrain sizes, i.e. 65x65, 129x129, etc.
My water code is something like this with size set to 50:
height: 0,
rotation: [0, 0, 0],
scale: [1, 1, 1],
ver: [
-size, 0, size,
-size, 0, -size,
size, 0, -size,
-size, 0, size,
size, 0, -size,
size, 0, size
],
vao: undefined,
setup_buffer: function(){
this.vao = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, this.vao);
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(this.ver), gl.STATIC_DRAW);
gl.vertexAttribPointer(
water_shader.position_attrib_location, // Attribute location
3, // Number of elements per attribute
gl.FLOAT, // Type of elements
gl.FALSE,
3 * Float32Array.BYTES_PER_ELEMENT, // Size of an individual vertex
0 // Offset from the beginning of a single vertex to this attribute
);
gl.bindBuffer(gl.ARRAY_BUFFER, null);
}
So all I am doing is creating and binding a buffer, storing 6 vertices in it and specifiying them via vertexAttribPointer before unbinding the buffer.
The terrain.setup_buffer() function is almost the same except that it uses an index buffer and that one vertex contains 9 coordinates (position, color, normal) instead of 3. Note that the terrain generation and the variables of the terrain are not in this code, but I can assure that all functions are working and all variables existing and initialized.
this.vao = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, this.vao);
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(this.ver), gl.STATIC_DRAW);
this.ibo = gl.createBuffer();
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, this.ibo);
gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, new Uint16Array(this.ind), gl.STATIC_DRAW);
gl.vertexAttribPointer(
terrain_shader.position_attrib_location, // Attribute location
3, // Number of elements per attribute
gl.FLOAT, // Type of elements
gl.FALSE,
9 * Float32Array.BYTES_PER_ELEMENT, // Size of an individual vertex
0 // Offset from the beginning of a single vertex to this attribute
);
gl.vertexAttribPointer(
terrain_shader.color_attrib_location, // Attribute location
3, // Number of elements per attribute
gl.FLOAT, // Type of elements
gl.FALSE,
9 * Float32Array.BYTES_PER_ELEMENT, // Size of an individual vertex
3 * Float32Array.BYTES_PER_ELEMENT // Offset from the beginning of a single vertex to this attribute
);
gl.vertexAttribPointer(
terrain_shader.normal_attrib_location, // Attribute location
3, // Number of elements per attribute
gl.FLOAT, // Type of elements
gl.FALSE,
9 * Float32Array.BYTES_PER_ELEMENT, // Size of an individual vertex
6 * Float32Array.BYTES_PER_ELEMENT // Offset from the beginning of a single vertex to this attribute
);
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, null);
gl.bindBuffer(gl.ARRAY_BUFFER, null);
So this is my main loop with all the initializations.
var canvas = document.getElementById('opengl-surface');
var gl = canvas.getContext('webgl');
if (!gl) {
console.log('WebGL not supported, falling back on experimental-webgl');
gl = canvas.getContext('experimental-webgl');
}
if (!gl) {
alert('Your browser does not support WebGL');
}
gl.clearColor(0.75, 0.85, 0.8, 1.0);
gl.enable(gl.DEPTH_TEST);
//create shader
water_shader.setup_shader();
terrain_shader.setup_shader();
// Create buffers
terrain.generate(5, 0.9, true);
water.setup_buffer();
terrain.setup_buffer();
var projectionMatrix = new Float32Array(16);
mat4.perspective(projectionMatrix, glMatrix.toRadian(45), canvas.width/canvas.height, 0.1, 1000.0);
gl.useProgram(water_shader.program);
gl.uniformMatrix4fv(water_shader.location_projection_matrix, gl.FALSE, projectionMatrix);
gl.uniform4fv(water_shader.location_color, [1, 0, 0, 1]);
gl.useProgram(null);
gl.useProgram(terrain_shader.program);
gl.uniformMatrix4fv(terrain_shader.location_projection_matrix, gl.FALSE, projectionMatrix);
gl.uniform3fv(terrain_shader.location_light_direction, light.direction);
gl.uniform3fv(terrain_shader.location_light_color, light.color);
gl.useProgram(null);
//
// Main render loop
//
var identity = new Float32Array(16);
mat4.identity(identity);
var loop = function(){
camera.rotate();
camera.translate();
gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
//render terrain
{
gl.useProgram(terrain_shader.program);
gl.uniformMatrix4fv(terrain_shader.location_view_matrix, gl.FALSE, camera.view_matrix());
gl.uniformMatrix4fv(terrain_shader.location_model_matrix, gl.FALSE, terrain.model_matrix());
gl.bindBuffer(gl.ARRAY_BUFFER, terrain.vao);
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, terrain.ibo);
gl.enableVertexAttribArray(terrain_shader.position_attrib_location);
gl.enableVertexAttribArray(terrain_shader.color_attrib_location);
gl.enableVertexAttribArray(terrain_shader.normal_attrib_location);
gl.drawElements(gl.TRIANGLES, terrain.ind.length, gl.UNSIGNED_SHORT, 0);
gl.disableVertexAttribArray(terrain_shader.position_attrib_location);
gl.disableVertexAttribArray(terrain_shader.color_attrib_location);
gl.disableVertexAttribArray(terrain_shader.normal_attrib_location);
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, null);
gl.bindBuffer(gl.ARRAY_BUFFER, null);
gl.useProgram(null);
}
//render water_shader
{
gl.useProgram(water_shader.program);
gl.uniformMatrix4fv(water_shader.location_view_matrix, gl.FALSE, camera.view_matrix());
gl.uniformMatrix4fv(water_shader.location_model_matrix, gl.FALSE, water.model_matrix());
gl.bindBuffer(gl.ARRAY_BUFFER, water.vao);
gl.enableVertexAttribArray(water_shader.position_attrib_location);
gl.drawArrays(gl.TRIANGLES, 0, 1089); //here should be 2 istead of 1089
gl.disableVertexAttribArray(water_shader.position_attrib_location);
gl.bindBuffer(gl.ARRAY_BUFFER, null);
gl.useProgram(null);
}
requestAnimationFrame(loop);
};
requestAnimationFrame(loop);
The shaders are pretty much straight forward and do not need much explanation. For the sake of completeness, here is my water shader code
VS:
precision mediump float;
attribute vec3 vertPosition;
uniform mat4 modelMatrix;
uniform mat4 viewMatrix;
uniform mat4 projectionMatrix;
void main()
{
gl_Position = projectionMatrix * viewMatrix * modelMatrix * vec4(vertPosition, 1.0),
}
FS:
precision mediump float;
uniform vec4 color;
void main()
{
gl_FragColor = color;
}
There are also other problems, e.g. if shrink the terrain size to (2^3+1)x(2^3+1) vertices, I get an "GL_INVALID_OPERATION : glDrawArrays: attempt to access out of range vertices in attribute 0" error. This should not happen, since I logged the arrays and got a vertex array of the size 729 (9x9x9), and an index array of the size 384 (8x8x2x3).
Another problem is that if I call water.setup_buffer() after terrain.setup_buffer(), both render calls (terrain and water) throw the same error as above mentioned ("GL_INVALID_OPERATION ").
If it helps, I am working on google chrome and windows 10, but on ms edge the same errors occur.
Unless you're using Vertex Array Objects (which are part of WebGL2 but are only optional in WebGL1 as an extension) the vertex attribute state IS GLOBAL STATE. That is state set by gl.vertexAttribPointer, gl.enableVertexAttribArray, gl.vertexAttribXXX is all global state unless you're using Vertex Array Objects (which you're not)
That means when you call
water.setup_buffer();
The global attribute state is set. You then call
terrain.setup_buffer();
Which overwrites the previous global attribute state.
Here's some answers that describe attribute state
https://stackoverflow.com/a/27164577/128511
https://stackoverflow.com/a/28641368/128511
You should either
(a) use Vertex Array Objects (VAOs) so that attribute state is per VAO
or
(b) separate setting up buffers (init time stuff) from setting up attributes (render time stuff).
Without VAOs the normal way to render is
for each thing you want to draw
gl.useProgram
setup attributes for that thing
bind textures and set uniforms for that thing
call gl.drawElements or gl.drawArrays
I am working on learning WebGL and having a great time! I decided to use glTF as the 3d format for this project. I have it working well, with one weird exception. When the index count is low (say a simple triangulated cube), the index count equals the index buffer size. This can't be right. In every other model I have, the index count is 1/2 the size of the buffer.
These causes render errors like this "Error: WebGL warning: drawElements: Index buffer too small.". Below is the relevant code.
Renderable Constructor:
constructor(type,indexCount,vertBuffer,indexBuffer,uvBuffer,normalBuffer,modelMatrix){
this.type = type;
this.indexCount = indexCount;
this.name = "NONE";
this.vertBuffer = GL.createBuffer();
GL.bindBuffer(GL.ARRAY_BUFFER, this.vertBuffer);
GL.bufferData(GL.ARRAY_BUFFER, vertBuffer, GL.STATIC_DRAW);
GL.bindBuffer(GL.ARRAY_BUFFER, null);
this.uvBuffer = GL.createBuffer();
GL.bindBuffer(GL.ARRAY_BUFFER, this.uvBuffer);
GL.bufferData(GL.ARRAY_BUFFER, uvBuffer, GL.STATIC_DRAW);
GL.bindBuffer(GL.ARRAY_BUFFER, null);
this.indexBuffer = GL.createBuffer();
GL.bindBuffer(GL.ELEMENT_ARRAY_BUFFER, this.indexBuffer);
GL.bufferData(GL.ELEMENT_ARRAY_BUFFER, indexBuffer, GL.STATIC_DRAW);
GL.bindBuffer(GL.ELEMENT_ARRAY_BUFFER, null);
this.normalBuffer = GL.createBuffer();
GL.bindBuffer(GL.ARRAY_BUFFER, this.normalBuffer);
GL.bufferData(GL.ARRAY_BUFFER, normalBuffer, GL.STATIC_DRAW);
GL.bindBuffer(GL.ARRAY_BUFFER, null);
this.matrix = modelMatrix;
this.witMatrix = mat4.create();
this.textures = [];
//Create defaults
this.setTexture(new dTexture(TEX.COLOR,"res/missingno.png"));
this.setTexture(new dTexture(TEX.LIGHT,"res/rawLight.png"));
}
GLTF to "Renderable"
static fromGLTF(type,gltf){
console.log("GLTF: loading "+gltf.nodes[0].name);
return new Renderable(type,
gltf.nodes[0].mesh.primitives[0].indicesLength,
gltf.nodes[0].mesh.primitives[0].attributes.POSITION.bufferView.data,
gltf.accessors[gltf.nodes[0].mesh.primitives[0].indices].bufferView.data,
gltf.nodes[0].mesh.primitives[0].attributes.TEXCOORD_0.bufferView.data,
gltf.nodes[0].mesh.primitives[0].attributes.NORMAL.bufferView.data,
gltf.nodes[0].matrix);
}
Here is the rendering code (It's not very pretty, but for completeness, here it is):
render(){
GL.viewport(0.0,0.0,this.canvas.width,this.canvas.height);
GL.clear(GL.COLOR_BUFFER_BIT | GL.DEPTH_BUFFER_BIT);
this.renderables.forEach(renderable => {
//mat4.identity(renderable.witMatrix);
mat4.invert(renderable.witMatrix,renderable.matrix);
mat4.transpose(renderable.witMatrix,renderable.witMatrix);
GL.useProgram(this.programs[renderable.type].program);
GL.uniformMatrix4fv(this.programs[renderable.type].pMatrix, false, this.projectionMatrix);
GL.uniformMatrix4fv(this.programs[renderable.type].vMatrix, false, this.viewMatrix);
GL.uniformMatrix4fv(this.programs[renderable.type].mMatrix, false, renderable.matrix);
GL.enableVertexAttribArray(this.programs[renderable.type].positon);
GL.bindBuffer(GL.ARRAY_BUFFER,renderable.vertBuffer);
GL.vertexAttribPointer(this.programs[renderable.type].positon, 3, GL.FLOAT, false,0,0);
GL.enableVertexAttribArray(this.programs[renderable.type].uv);
GL.bindBuffer(GL.ARRAY_BUFFER,renderable.uvBuffer);
GL.vertexAttribPointer(this.programs[renderable.type].uv, 2, GL.FLOAT, false,0,0);
if(renderable.type == SHADER.STATIC){
GL.uniform1i(this.programs[renderable.type].colorPos, 0); // texture unit 0
GL.activeTexture(GL.TEXTURE0);
GL.bindTexture(GL.TEXTURE_2D, renderable.textures[TEX.COLOR].data);
GL.uniform1i(this.programs[renderable.type].lightPos, 1); // texture unit 1
GL.activeTexture(GL.TEXTURE1);
GL.bindTexture(GL.TEXTURE_2D, renderable.textures[TEX.LIGHT].data);
}else if(renderable.type == SHADER.DYNAMIC){
GL.uniform1i(this.programs[renderable.type].colorPos, 0); // texture unit 0
GL.activeTexture(GL.TEXTURE0);
GL.bindTexture(GL.TEXTURE_2D, renderable.textures[TEX.COLOR].data);
GL.enableVertexAttribArray(this.programs[renderable.type].normalPos);
GL.bindBuffer(GL.ARRAY_BUFFER,renderable.normalBuffer);
GL.vertexAttribPointer(this.programs[renderable.type].normalPos, 3, GL.FLOAT, false,0,0);
GL.uniformMatrix4fv(this.programs[renderable.type].witMatrix, false, renderable.witMatrix);
// set the light position
GL.uniform3fv(this.programs[renderable.type].lightPosPos, [
Math.sin(this.counter)*0.75,
Math.cos(this.counter)*0.75+1,
0
]);
this.counter+=this.dt*0.25;
}
GL.bindBuffer(GL.ELEMENT_ARRAY_BUFFER, renderable.indexBuffer);
GL.drawElements(GL.TRIANGLES,renderable.indexCount,GL.UNSIGNED_SHORT,0);
GL.activeTexture(GL.TEXTURE1);
GL.bindTexture(GL.TEXTURE_2D,this.nullLightmap.data);
});
GL.flush();
}
Any ideas?
When the index count is low (say a simple triangulated cube), the index count equals the index buffer size. This can't be right. In every other model I have, the index count is 1/2 the size of the buffer.
The size of the index buffer depends on the number of indices and the componentType.
See Accessor Element Size:
componentType Size in bytes
5120 (BYTE) 1
5121 (UNSIGNED_BYTE) 1
5122 (SHORT) 2
5123 (UNSIGNED_SHORT) 2
5125 (UNSIGNED_INT) 4
5126 (FLOAT) 4
The componentType specifies the data type of a single index. When the number of indices is low (<= 256), then the data type UNSIGNED_BYTE is used, while the type of the index buffer is UNSIGNED_SHORT or even UNSIGNED_INT, if there are more indices. If the type is UNSIGNED_BYTE, then of course the number of indices is equal the size of the buffer in bytes.
Dependent on the type of the element indices you have to adept the draw call, e.g. GL.UNSIGNED_BYTE:
GL.drawElements(GL.TRIANGLES,renderable.indexCount,GL.UNSIGNED_BYTE,0);
Note, the values of the componentType (5120, 5121, ...) which seems to be arbitrary, are the values of the OpenGL enumerator constants GL.BYTE, GL.UNSIGNED_BYTE, ...
I suggest to pass the componentType to the constructor as you do it with the number of indices (indexCount)
constructor(
type,indexCount,componentType,
vertBuffer,indexBuffer,uvBuffer,normalBuffer,modelMatrix){
this.indexCount = indexCount;
this.componentType = componentType;
and to use it in when drawing the geometry:
GL.drawElements(
GL.TRIANGLES,
renderable.indexCount,
renderable.componentType,
0);
I am currently working on project in HTML and js where I have a simple canvas and two text fields (x and y) with a button called "Draw".
Trying to get it to draw a line via the user's entered x and y intercepts onto the canvas with onclick.
The function I am calling with onclick is generateImage().
I can tell that the text fields are in fact receiving the users info. I tested this by using a window.alert to show a pop up with the info (i have it commented out for now).
At first I thought it had to do with how i set up my button, but after asking around I think this cannot be true:
<button type="button" onclick="generateImage()"> DRAW LINE!</button>
I have no more errors on the console.
I even had my professor look at it and he said that it looked alright. I am beginning to think that this is a problem somewhere else within the program. I am sure I am either a) being stupid/frustrated or b) seriously over looking something.
Here is some of my code.
One last thing. var x0=... and var y0 =... I am aware that I will get a slope of zero and a line length of zero because they are the same..
My professor has told us to use this algorithm even thought it doesn't make sense to me. I would like to mention that I have also tried to hard code the x and y values and it still does not work.
Thanks
HTML
<body>
<form action="demo_form.asp">
X = <input name="X Value" type="text" id="Xvalue"><br>
Y = <input name="Y Value" type="text" id="Yvalue"><br>
</form>
<button type="button" onclick="generateImage()"> DRAW LINE!</button>
<p>Click "DRAW LINE" button to draw the line with custom X and Y coords</p>
<canvas id="gl-canvas" width="512", height="512">
<pre>No Support... :(</pre>
</canvas>
</body>
***JS***
function generateImage()
{
var x0 = document.getElementById('Xvalue').value;
var y0 = document.getElementById('Yvalue').value;
var x1 = document.getElementById('Xvalue').value;
var y1 = document.getElementById('Yvalue').value;
// window.alert(x0);
// window.alert(y0);
var slope = (y1-y0)/(x1-x0);
for (var i=0; i<x1-x0; i++){
var y = (slope*i)+y0;
var x = x0 + i;
myImage.setPixel(x,y, RED);
}
}
function glInit(canvas)
{
//makes our GL instance
gl=WebGLUtils.setupWebGL(canvas);
//gets and initializes our vertex and fragment shaders
program = initShaders(gl, "vertex-shader", "fragment-shader");
//sets the resulting shader program to use
gl.useProgram(program);
}
window.onload=function init()
{
var canvas = document.getElementById("gl-canvas");
glInit(canvas);
myImage=new Image(canvas);
myImage.setImageSize(700, 350);
myImage.clearImage();
generateImage(myImage);
renderImage(myImage);
}
function renderImage(myImage)
{
points=[];
colors=[];
for (var i=0; i<pixels.length; i++)
{
var y=Math.floor(i/myImage.width);
var x=i-(y*myImage.width);
colors.push(myImage.getPixel(x, y));
x/=myImage.width/2;
y/=myImage.height/2;
x-=1.0;
y-=1.0;
points.push(vec3(x, y, 0.0, 0.0));
}
gl.clearColor(0.7, 0.7, 0.7, 1.0);
gl.viewport(0, 0, myImage.width, myImage.height);
var posBufferID=gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, posBufferID);
gl.bufferData(gl.ARRAY_BUFFER, flatten(points), gl.STATIC_DRAW);
program.vPositionA=gl.getAttribLocation(program, "vPosition");
gl.vertexAttribPointer(program.vPosition, 3, gl.FLOAT, false, 0, 0);
gl.enableVertexAttribArray(program.vPositionA);
var colBufferID=gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, colBufferID);
gl.bufferData(gl.ARRAY_BUFFER, flatten(colors), gl.STATIC_DRAW);
program.vColorA=gl.getAttribLocation(program, "vColor");
gl.vertexAttribPointer(program.vColorA, 4, gl.FLOAT, false, 0, 0);
gl.enableVertexAttribArray(program.vColorA);
render(program, colBufferID, posBufferID);
}
function render(program, colBufferID, posBufferID)
{
gl.clear(gl.COLOR_BUFFER_BIT);
gl.bindBuffer(gl.ARRAY_BUFFER, colBufferID);
gl.vertexAttribPointer(program.vColorA, 4, gl.FLOAT, false, 0, 0);
gl.bindBuffer(gl.ARRAY_BUFFER, posBufferID);
gl.vertexAttribPointer(program.vPositionA, 3, gl.FLOAT, false, 0, 0);
gl.drawArrays(gl.POINTS, 0, points.length);
}
Completely new to javascript, attempting to do a webGL project.
Here is what I am working towards in the end, the idea is to have two for loops
// Create the vertex data for a grid to be drawn
function createGrid(gl, x, y) {
var vertexBuffer;
vertexBuffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, vertexBuffer);
var matrix= []
for(var i = 0, i < x, ++i)
{
for(var j = 0, i < y, ++j)
{
matrix.push(createSquare(i,j))
}
}
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(matrix), gl.STATIC_DRAW);
var grid = {buffer:vertexBuffer, vertSize:3, nVerts:matrix.length, primtype:gl.TRIANGLE_STRIP};
return grid;
}
I am trying to dynamically create a wall of squares essentially. I have two functions.
// Create the vertex data for a square to be drawn
function createSquare(gl, x, y) {
var square = [ x+ .5, y+ .5, 0.0,
x+ -.5, y+ .5, 0.0,
x+ .5,y+ -.5, 0.0,
x+ -.5,y+ -.5, 0.0];
return square;
}
// Create the vertex data for a grid to be drawn
function createGrid(gl, x, y) {
var vertexBuffer;
vertexBuffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, vertexBuffer);
var matrix= []
matrix.push(createSquare(gl,x,y ));
var numVerts = matrix.length;
alert(matrix[0]);
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(matrix), gl.STATIC_DRAW);
var grid = {buffer:vertexBuffer, vertSize:3, nVerts:numVerts, primtype:gl.TRIANGLE_STRIP};
return grid;
}
What is messing me up is the line
matrix.push(createSquare(gl,x,y ));
I was expecting to get matrixpopulated with the same array that was created in createSquare. However, all the the items in my square object are in the first index of matrix, assuming in string format. Searched and all I saw were people not having their array returned at all.
matrix[0] = 1.5,0.5,0,0.5,0.5,0,1.5,-0.5,0,0.5,-0.5,0 and
matrix[1] = undefined
EDIT: I get it now, matrix[0] contains the array object. This is also why I tried array.concat() earlier but then I was in the same situation as others where the array didn't return at all. array.push() was the only function that returned me anything.
Instead of pushing createSquare(gl,x,y) into grid just set grid equal to it so:
grid = createSquare(gl, x, y);
You are defining your grid variable as an array, and then pushing a square into it, as the way it is, grid will be always an array with length === 1, if You just replace:
grid.push(createSquare(gl,x,y ));
with:
grid = createSquare(gl,x,y);
It should work as you want.
Also note that you are redefining grid in the following line:
var grid = {buffer:vertexBuffer, vertSize:3, nVerts:numVerts, primtype:gl.TRIANGLE_STRIP};
Maybe thats intended, but I comment it to you so that you know.
So the answer was.....
matrix = matrix.concat(createSquare(x,y));
When I was goofing around earlier I was just using
matrix.concat(createSquare(x,y));
...and it didn't complain.
The issue in your original code is createSquare returns an array. So the line
matrix.push(createSquare(i,j))
is making an array of arrays. But new Float32Array(matrix) from the line
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(matrix), gl.STATIC_DRAW);
expects a single array, not an array of arrays. There are multiple ways to fix this. The simplest is to change this like
matrix.push(createSquare(i,j))
to this
matrix = matrix.concat(createSquare(i,j))
The concat function takes the array you have and the arrays you pass it and concatenates them into a new array. In other words, a single line matrix.concat(someArray) will do nothing because it will make a new array but because that array is not assigned to a variable it's lost immediately.
But note that is a slow solution. Maybe it's fast enough for your needs?
The reason it's slow is because every time you concat a bunch of work needs to be done. Every element from the previous 2 arrays needs to copied to a new array. So for example lets say you made a 1000 squares. The code would be doing
newArray = arrayOf0Elements + arrayOf12Elements;
newArray = arrayOf12Elements + arrayOf12Elements;
newArray = arrayOf24Elements + arrayOf12Elements;
newArray = arrayOf36Elements + arrayOf12Elements;
newArray = arrayOf48Elements + arrayOf12Elements;
...
newArray = arrayof11964Elements + arrayOf12Elements;
newArray = arrayof11976Elements + arrayOf12Elements;
newArray = arrayof11988Elements + arrayOf12Elements;
Hopefully you can see why that's slow. That's 6006000 copies that have to happen!
One simple way to make it fast-er is to concat at the end like this. Leave the code exactly as was but add this line
var combinedData = Array.prototype.concat.apply([], matrix);
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(combinedData), gl.STATIC_DRAW);
This way, in the 1000 square case, you make 1000 squares of 12 elements each. You then concat them all at once. That's only 12000 copies vs the other way which was 6006000 copies.
Array.prototype.concat is the bare JavaScript function for concatting arrays. Calling apply lets you pass it an object to operate on, in this case [] and then the arguments to it in an array. It's the same as if you had done this
var combineData = [].concat(matrix[0], matrix[1], matrix[2], ... matrix[999]);
One of the fastest ways would be to create the Float32Array at the beginning and pass it into createSquare with an offset like this
// Create the vertex data for a square to be drawn
function createSquare(dst, offset, x, y) {
dst[offset++] = x+ .5;
dst[offset++] = y+ .5;
dst[offset++] = 0.0;
dst[offset++] = x+ -.5;
dst[offset++] = y+ .5;
dst[offset++] = 0.0;
dst[offset++] = x+ .5;
dst[offset++] = y+ -.5;
dst[offset++] = 0.0;
dst[offset++] = x+ -.5;
dst[offset++] = y+ -.5;
dst[offset++] = 0.0;
return offset;
}
// Create the vertex data for a grid to be drawn
function createGrid(gl, x, y) {
var vertexBuffer;
vertexBuffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, vertexBuffer);
// allocate space for all vertices up front
var matrix = new Float32Array(x * y * 12);
var offset = 0;
for(var i = 0, i < x, ++i)
{
for(var j = 0, i < y, ++j)
{
offset = createSquare(matrix, offset, i, j))
}
}
gl.bufferData(gl.ARRAY_BUFFER, matrix, gl.STATIC_DRAW);
var grid = {buffer:vertexBuffer, vertSize:3, nVerts:matrix.length, primtype:gl.TRIANGLE_STRIP};
return grid;
}
Now there's no concatenation at all and no extra arrays being created.
Which way you choose is up to you. I'd probably pick the middle concat version as it's probably the most flexible because you don't have to compute the number of vertices up front so if you change the number of vertices per square you don't have to change any other code.
I am trying to render a model under multiple light sources using multiple drawElement calls with different light sources. The gist of the code is as follows.
this.setUniform("u_matrix", "Matrix4fv", false, matrix);
this.setUniform("u_nMatrix", "Matrix3fv", false, nMatrix);
this.setUniform("u_kSpecular", "3fv", material.specular);
this.setUniform("u_kDiffuse", "3fv", material.diffuse);
this.setUniform("u_kAmbient", "3fv", material.ambient);
this.setUniform("u_shininess", "1f", material.shininess);
this.setVertexAttribArray("a_position", new Float32Array(mesh.vertices), 3);
this.setVertexAttribArray("a_normal", new Float32Array(mesh.vertexNormals), 3);
this.setElementArray(new Uint16Array(mesh.indices));
for(var i = 0;i < lights.length;i++) {
if(i == 1) {
gl.enable(gl.BLEND);
gl.blendFunc(gl.ONE, gl.ONE);
gl.blendEquation(gl.FUNC_ADD);
}
var light = lights[i];
console.dir(light);
this.setUniform("u_lightType", "1i", light.type);
this.setUniform("u_lightVector", "3fv", light.vector);
this.setUniform("u_lightColor", "3fv", light.color);
gl.drawElements(gl.TRIANGLES, mesh.indices.length, gl.UNSIGNED_SHORT, 0);
}
gl.disable(gl.BLEND);
The issue here is that only the first lighting is rendered. Subsequent drawElements do not render anything. I do not think the issue is with my shader code, because the issue persists even if I strip the shader down to just set fragment color equal to the light type or some such parameter.
setUniform setVertexAttribArray and setElementArray are some helpers that i have written. They basically look like this:
setElementArray: function(array) {
var gl = this.gl;
var buffer = this._arrBuffers.__indexBuffer;
if(_.isUndefined(buffer)) {
buffer = gl.createBuffer();
this._arrBuffers.__indexBuffer = buffer;
}
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, buffer);
gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, array, gl.STATIC_DRAW);
}
setVertexAttribArray: function(name, array, size, type, normalized, stride, offset) {
var gl = this.gl;
size = size || 2;
type = type || gl.FLOAT;
normalized = normalized || false;
stride = stride || 0;
offset = offset || 0;
var buffer = this._arrBuffers[name];
if(_.isUndefined(buffer)) {
buffer = gl.createBuffer();
this._arrBuffers[name] = buffer;
}
var location = this.getLocation(name, true);
gl.bindBuffer(gl.ARRAY_BUFFER, buffer);
gl.bufferData(gl.ARRAY_BUFFER, array, gl.STATIC_DRAW);
gl.enableVertexAttribArray(location);
gl.vertexAttribPointer(location, size, type, normalized, stride, offset);
}
I have found the solution. I had set gl.depthFunc(gl.LESS), so subsequent renderings failed the depth test. Setting gl.depthFunc(gl.LEQUAL) solved the problem.