Multiple objects in webgl - javascript

I'm trying to render a few objects to a canvas and I'm having a bit of trouble understanding what's not working.
I'm building two objects at the moment that represent the two meshes that I want to render. If I create one mesh the code works fine so the problem, I think, is that the data gets screwed up when I'm building two or more.
Here's an example of the mesh data:
"name":"cone",
"buffers":{
"vertexPosition":{}, // Buffer
"vertexIndex":{} // Buffer
},
"mesh":{
"vertices":[], // emptied it to fit on page
"faces":[] // emptied it to fit on page
},
"mvMatrix": Float32Array[16],
"itemSize":3,
"numItems":12,
"program":{
"vertexPosAttrib":0,
"mvMatrixUniform":{},
"pMatrixUniform":{}
}
This is build from this function:
buildMeshData: function(){
this.mvMatrix = mat4.create();
this.buffers.vertexPosition = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, this.buffers.vertexPosition);
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(this.mesh.vertices), gl.STATIC_DRAW);
this.buffers.vertexIndex = gl.createBuffer();
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, this.buffers.vertexIndex);
gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, new Uint16Array(this.mesh.faces), gl.STATIC_DRAW);
this.itemSize = 3;
this.numItems = this.mesh.faces.length;
var vertexProps = {
attributes: ['vec3', 'VertexPosition'],
uniforms: ['mat4', 'MVMatrix', 'mat4', 'PMatrix'],
varyings: ['vec3', 'TexCoord']
}
var vertexShaderFunction = 'vTexCoord = aVertexPosition + 0.5; gl_Position = uPMatrix * uMVMatrix * vec4(aVertexPosition, 1);';
var vshaderInput = utils.buildVertexShader(vertexProps, vertexShaderFunction);
var fragmentProps = {
attributes: [],
uniforms: [],
varyings: ['vec3', 'TexCoord']
}
var fragmentShaderFunction = 'gl_FragColor = vec4(vTexCoord, 1);';
var fshaderInput = utils.buildFragmentShader(fragmentProps, fragmentShaderFunction);
this.program = gl.createProgram();
var vshader = gl.createShader(gl.VERTEX_SHADER);
gl.shaderSource(vshader, vshaderInput);
gl.compileShader(vshader);
var fshader = gl.createShader(gl.FRAGMENT_SHADER);
gl.shaderSource(fshader, fshaderInput);
gl.compileShader(fshader);
gl.attachShader(this.program, vshader);
gl.attachShader(this.program, fshader);
gl.linkProgram(this.program);
gl.useProgram(this.program);
this.program.vertexPosAttrib = gl.getAttribLocation(this.program, 'aVertexPosition');
gl.vertexAttribPointer(this.program.vertexPosAttrib, this.itemSize, gl.FLOAT, false, 0, 0);
gl.enableVertexAttribArray(this.program.vertexPosAttrib);
this.program.mvMatrixUniform = gl.getUniformLocation(this.program, "uMVMatrix");
this.program.pMatrixUniform = gl.getUniformLocation(this.program, "uPMatrix");
scene.add(this);
}
and the render function goes like this:
function render(){
currentTime = new Date().getTime();
deltaTime = (currentTime - initialTime) / 1000; // in seconds
gl.viewport(0, 0, stage.width, stage.height);
gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
for(var i in scene.meshes){
(function(mesh){
mat4.translate(mesh.mvMatrix, mesh.mvMatrix, [0, 2 * i, -10 - (10 * i)]);
gl.useProgram(mesh.program);
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, null);
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, mesh.buffers.vertexIndex);
gl.vertexAttribPointer(mesh.program.vertexPosAttrib, mesh.itemSize, gl.FLOAT, false, 0, 0);
gl.enableVertexAttribArray(mesh.program.vertexPosAttrib);
gl.uniformMatrix4fv(mesh.program.mvMatrixUniform, false, mesh.mvMatrix);
gl.uniformMatrix4fv(mesh.program.pMatrixUniform, false, scene.pMatrix);
gl.drawElements(gl.TRIANGLES, mesh.numItems, gl.UNSIGNED_SHORT, 0);
gl.disableVertexAttribArray(mesh.program.vertexPosAttrib);
})(scene.meshes[i])
}
// requestAnimationFrame(render);
}
The result of this is the second object is drawn correctly but the first causes the error:
[.WebGLRenderingContext]GL ERROR :GL_INVALID_OPERATION : glDrawElements: attempt to access out of range vertices in attribute 0
...and is therefore not drawn.
Any ideas where the problem lies. Hopefully thats enough information from the code, I didn't want to put up too much, but if you need to see anything else I'll update.

This code
gl.vertexAttribPointer(this.program.vertexPosAttrib, this.itemSize, gl.FLOAT, false, 0, 0);
gl.enableVertexAttribArray(this.program.vertexPosAttrib);
Need to be called when drawing each mesh and not where it's called now. Additionally before calling gl.vertexAttribPointer for this.program.vertexPosAttrib you need to call
gl.bindBuffer(gl.ARRAY_BUFFER, mesh.buffers.vertexPosition);
Because gl.vertexAttribPointer binds the buffer currently bound to gl.ARRAY_BUFFER to the specified attribute.
In other words
gl.bindBuffer(gl.ARRAY_BUFFER, mesh.buffers.vertexPosition);
gl.vertexAttribPointer(mesh.program.vertexPosAttrib, mesh.itemSize, gl.FLOAT, false, 0, 0);
gl.enableVertexAttribArray(mesh.program.vertexPosAttrib);

Related

WebGL draw multiple objects

I am working on WebGL and I have made some classes to make rendering easier. The problem is, that only the first class can be rendered and all other classes aren't rendered. I looked for every array buffer and bind and unbind them when needed but it still doesn't work. This is my class for a triangleElementCluster:
function TriangleElementCluster(vertices, uvs, normals, indices, indicesLenght, shader, gl) {
shader.use();
var verticesBuffer = new ArrayBufferFloat(vertices, gl);
var verticesAttribLocation = new VertexAttribPointerFloat(shader.getProgram(), "vertex", 3, 3, 0, gl);
var uvsBuffer = new ArrayBufferFloat(uvs, gl);
var uvsAttribLocation = new VertexAttribPointerFloat(shader.getProgram(), "uv", 2, 2, 0, gl);
var normalsBuffer = new ArrayBufferFloat(normals, gl);
var normalsAttribLocation = new VertexAttribPointerFloat(shader.getProgram(), "normal", 3, 3, 0, gl);
var indicesBuffer = new ElementArrayBuffer16(indices, gl);
verticesBuffer.unbind();
verticesAttribLocation.unbind();
uvsBuffer.unbind();
uvsAttribLocation.unbind();
normalsBuffer.unbind();
normalsAttribLocation.unbind();
indicesBuffer.unbind();
this.setTexture = function(texture) {
this.texture = texture;
}
this.render = function() {
verticesBuffer.bind();
verticesAttribLocation.bind();
uvsBuffer.bind();
uvsAttribLocation.bind();
normalsBuffer.bind();
normalsAttribLocation.bind();
indicesBuffer.bind();
this.texture.activate(gl.TEXTURE0);
gl.drawElements(gl.TRIANGLES, indicesLenght, gl.UNSIGNED_SHORT, 0);
verticesBuffer.unbind();
verticesAttribLocation.unbind();
uvsBuffer.unbind();
uvsAttribLocation.unbind();
normalsBuffer.unbind();
normalsAttribLocation.unbind();
indicesBuffer.unbind();
}
}
These are the classes for ArrayBuffers and VertexAttribPoints:
function ArrayBufferFloat(array, gl) {
this.arrayBuffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, this.arrayBuffer);
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(array), gl.STATIC_DRAW);
this.unbind = function() {
gl.bindBuffer(gl.ARRAY_BUFFER, null);
}
this.bind = function() {
gl.bindBuffer(gl.ARRAY_BUFFER, this.arrayBuffer);
}
}
function VertexAttribPointerFloat(shaderProgram, shaderVariableName, elementLenght, stepSize, offset, gl) {
var attribLocation = gl.getAttribLocation(shaderProgram, shaderVariableName);
gl.vertexAttribPointer(attribLocation, elementLenght, gl.FLOAT, gl.FALSE, stepSize * Float32Array.BYTES_PER_ELEMENT, offset);
gl.enableVertexAttribArray(attribLocation);
console.log(attribLocation);
this.bind = function() {
gl.enableVertexAttribArray(attribLocation);
}
this.unbind = function() {
gl.disableVertexAttribArray(attribLocation);
}
}
You might have noticed that I print the ID of the VertexAttribPointer and I get: 2 0 1 2 0 1 I have two classes and both of them are using the same pointers, this shouldn't happen, what could lead to this?
From my understanding of OpenGL every buffer etc. is deactivated after the triangles have been drawn. Where is the error that leads to only drawing the first class?
This
function VertexAttribPointerFloat(shaderProgram, shaderVariableName, elementLenght, stepSize, offset, gl) {
var attribLocation = gl.getAttribLocation(shaderProgram, shaderVariableName);
gl.vertexAttribPointer(attribLocation, elementLenght, gl.FLOAT, gl.FALSE, stepSize * Float32Array.BYTES_PER_ELEMENT, offset);
gl.enableVertexAttribArray(attribLocation);
console.log(attribLocation);
this.bind = function() {
gl.enableVertexAttribArray(attribLocation);
}
this.unbind = function() {
gl.disableVertexAttribArray(attribLocation);
}
}
Needs to be this
function VertexAttribPointerFloat(shaderProgram, shaderVariableName, elementLenght, stepSize, offset, gl) {
var attribLocation = gl.getAttribLocation(shaderProgram, shaderVariableName);
this.bind = function() {
gl.enableVertexAttribArray(attribLocation);
gl.vertexAttribPointer(attribLocation, elementLenght, gl.FLOAT, gl.FALSE, stepSize * Float32Array.BYTES_PER_ELEMENT, offset);
}
this.unbind = function() {
gl.disableVertexAttribArray(attribLocation);
}
}
And your program usage need to be moved to render
function TriangleElementCluster(vertices, uvs, normals, indices, indicesLenght, shader, gl) {
this.render = function() {
shader.use();
...
}
}
You don't actually need any of the unbinds what-so-ever BTW
You can think of bind as similar to just setting global variables.
const state = {
temp1: 0,
temp2: 0,
temp3: 0,
result: 0,
};
function add() { state.result = state.temp1 + state.temp2; }
function sub() { state.result = state.temp1 - state.temp2; }
function sum() { state.result = state.temp1 + state.temp2 + state.temp3; }
function bind(id, value) { state[id] = value; }
function get(id) { return state[id]; }
bind('temp1', 1);
bind('temp2', 2);
bind('temp3', 3);
sum();
console.log('sum:', get('result'));
bind('temp1', 4);
bind('temp2', 5);
add();
console.log('add:', get('result'));
Notice I don't unbind anything. I just bind the things I need for the next function to run. It's the same in WebGL though the state is more complicated

WebGL triangles not rendered correctly

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

GLTF index count same as buffer size error

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);

Drawing lines in HTML5/JS webGL using user input from text field

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);
}

Only first call works in multiple drawElements calls with same vertex buffers

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.

Categories