Can't parse webgl object to my object - javascript

I'm trying to make an object in javascript to contain a 3dObject and everyting related to it:vertex positions, normals, texture coordonates etc + the methods needed to handle it: loading the object, drawing etc.
The problem is that when I try to parse the WebGL context object, in my case gl, wich is declared global, in order to use specific methods like createBuffer(), bindBuffer(), javascript give's me the following error: "Uncaught TypeError: Cannot call method 'createBuffer' of undefined"
This is the object code:
function object3D(gl){
this.vertexPositionBuffer = gl.createBuffer();
this.textureCoordBuffer = gl.createBuffer();
this.normalDirectionBuffer = gl.createBuffer();
this.indexBuffer = gl.createBuffer();
this.loadVertices = function (vertices, itemSize, numItems){
gl.bindBuffer(gl.ARRAY_BUFFER, this.vertexPositionBuffer);
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(vertices), gl.STATIC_DRAW);
this.vertexPositionBuffer.numItems = numItems;
this.vertexPositionBuffer.itemSize = itemSize;
}
this.loadNormals = function (normals, itemSize, numItems){
gl.bindBuffer(gl.ARRAY_BUFFER, this.normalDirectionBuffer);
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(normals), gl.STATIC_DRAW);
this.normalDirectionBuffer.numItems = numItems;
this.normalDirectionBuffer.itemSize = itemSize;
}
this.loadIndex = function (indexArray, itemSize, numItems){
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, this.indexBuffer);
gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, new Uint16Array, gl.STATIC_DRAW);
this.indexBuffer.numItems = numItems;
this.indexBuffer.itemSize = itemSize;
}
this.loadTexture = function (textureCoord, itemSize, numItems){
gl.bindBuffer(gl.ARRAY_BUFFER, this.textureCoordBuffer);
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(textureCoord), gl.STATIC_DRAW);
this.textureCoordBuffer.numItems = numItems;
this.textureCoordBuffer.itemSize = itemSize;
}
}
This is how I try to create an instance of this object:
var cube1 = new object3D(gl);
WebGL context assign:
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");
}
}
Outside any function and inside my object definition console.log(gl) says is undefined, but in other places it works just fine, altough it is declared global.
I have to mention that i have a solid background in c++ but i'm pretty new to javascript and handling objects in it.

Related

drawElements with multiple attributes

I am trying to render multiple attribute buffers with drawElements:
var verteces = [1,1, 0,-1, -1,0];
var vBuffer = gl.createBuffer(gl.ARRAY_BUFFER);
gl.bindBuffer(gl.ARRAY_BUFFER, vBuffer);
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(verteces), gl.DYNAMIC_DRAW);
gl.enableVertexAttribArray(0);
gl.vertexAttribPointer(0, 2, gl.FLOAT, false, 0, 0);
var colors = [1,0,0,1, 0,1,0,1, 0,0,1,1];
var cBuffer = gl.createBuffer(gl.ARRAY_BUFFER);
gl.bindBuffer(gl.ARRAY_BUFFER, cBuffer);
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(colors), gl.DYNAMIC_DRAW);
gl.enableVertexAttribArray(1);
gl.vertexAttribPointer(1, 4, gl.FLOAT, false, 0, 0);
var indices = [0,3,2];
var elBuffer = gl.createBuffer(gl.ELEMENT_ARRAY_BUFFER);
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, elBuffer);
gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, new Uint8Array(indices), gl.STATIC_DRAW);
If I run this code with gl.drawArrays(gl.TRIANGLES, 0, 3); it renders a beautiful triangle with rbg fill. But if I call gl.drawElements(gl.TRIANGLES, 3, gl.UNSIGNED_BYTE, 0); I get
[.CommandBufferContext.Offscreen-MainThread-0AB44528]GL ERROR :GL_INVALID_OPERATION : glDrawElements: attempt to access out of range vertices in attribute 1
Can’t figure out what is wrong here. drawElements without setting up colors buffer works alright.
Appreciate any help.
YOu proivide vertex data for 3 vertices, so the only valid vertex indices are 0,1,2. Yet, you try to render with an index array of 0,3,2, which will try to acces a fourth vertex, and would result in an out-of-bounds access to the vertex buffers.

WebGL Error - Error: WebGL: bindBuffer: buffer already contains element data

I am trying to draw a simple circle using WebGL but am getting a few errors. I am very new to writing WebGL code and would love if anyone could explain this to me and what the problem is.
I can create a simple square using the same code but with 5 vertices and this works perfectly. But when I try to create an array using this method, it doesn't seem to like it. I am sorry if it is a trivial mistake but an explanation would be very helpful.
Thank you in advance.
Error: WebGL: bindBuffer: buffer already contains element data.
webgl-debug.js:232:20 Error: WebGL: vertexAttribPointer: invalid
element size webgl-debug.js:232:20 TypeError: value is undefined
These are shown in the console. Here is the code I am using.
function setupBuffers() {
//Setup the circle vertices
circleVertexBuffer = gl.createBuffer();
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, circleVertexBuffer);
var r = 0.2;
var centre = 0;
var circleVertices = [];
var z = 0;
theta = 178;
circleVertices.push(centre);
circleVertices.push(r);
circleVertices.push(z);
for(var i = 0; i<theta; i++){
var rads2deg = i * (Math.PI/180);
var x = r * Math.cos(rads2deg);
var y = r * Math.sin(rads2deg);
circleVertices.push(x);
circleVertices.push(y);
circleVertices.push(z);
}
circleVertices.push(centre);
circleVertices.push(r);
circleVertices.push(z);
console.log(circleVertices);
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(circleVertices), gl.STATIC_DRAW);
circleVertices.itemSize = 3;
circleVertices.numberOfItems = circleVertices.length/circleVertices.itemSize;
}
function draw() {
//set up a viewport that is the same as the canvas using function viewport (int x, int y, sizei w, sizei h) where x and y give the x and y window coordinates of the viewports width and height.
gl.viewport(0, 0, gl.viewportWidth, gl.viewportHeight);
//fill the canvas with solid colour. Default is black. If other color is desiarible using function gl.clearColor (r,g,b,a)
gl.clear(gl.COLOR_BUFFER_BIT);
gl.bindBuffer(gl.ARRAY_BUFFER, circleVertexBuffer);
gl.vertexAttrib4f(shaderProgram.vertexColorAttribute, 1.0, 1.0, 1.0, 1.0);
gl.vertexAttribPointer(shaderProgram.vertexPositionAttribute, circleVertexBuffer.itemSize, gl.FLOAT, false, 0, 0);
gl.drawArrays(gl.LINE_STRIP, 0, circleVertexBuffer.numberOfItems);
}
The problem is this line
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, circleVertexBuffer);
There are 2 types of buffers in WebGL.
ELEMENT_ARRAY_BUFFER buffers
These buffers hold indices for gl.drawElements
ARRAY_BUFFER buffers.
These buffers hold attribute data (positions,normals,texcoords, etc)
When you create a buffer with gl.createBuffer it doesn't have a buffer type yet. The first time you bind that buffer with gl.bindBuffer it becomes whatever type of buffer you bound it to. If you bind it to ARRAY_BUFFER it's now an ARRAY_BUFFER buffer. If you bind it to ELEMENT_ARRAY_BUFFER it's now an ELEMENT_ARRAY_BUFFER buffer. Once it comes one of those types you can not change it's type or use it for the other type.
So, in your code you do this
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, circleVertexBuffer);
Which makes circleVertexBuffer an ELEMENT_ARRAY_BUFFER type buffer. But then in draw you have this
gl.bindBuffer(gl.ARRAY_BUFFER, circleVertexBuffer);
The buffer can't be both types. Change the first one in setupBuffers to
gl.bindBuffer(gl.ARRAY_BUFFER, circleVertexBuffer);
You might find this answer helpful
https://stackoverflow.com/a/27164577/128511

Unable to render to framebuffer (texture)

I'm trying to implement shadows using shadow maps, so I need to render a scene to a separate framebuffer (texture). I cannot get it to work properly, so after stripping down my codebase I'm left with a relatively simple set of instructions which should render a scene to a texture, and then simply render the texture.
The program consists of two programs:
Ground program
Teapot program
The first should render a rectangle, with a certain texture. The second one should render a teapot (with colors based on its position). Eech render step does the following (well, that's the idea anyway):
Switch to framebuffer
Render teapot
Switch to normal buffer
Render teapot
Render ground
Now, the ground fragment shader looks like:
gl_FragColor = texture2D(shadowMap, fTexCoord);
'shadowMap' is the texture I render to in step 2. I expect to see a floating teapot with a rectangle drawn under it. That indeed works. Now, I also expect to have the 'ground' to contain a teapot. After all, we rendered the scene we are looking at without the ground to the framebuffer/texture.
Code
var UNSIGNED_SHORT_SIZE = 2;
// Variables filled by setup()
var glCanvas;
var gl, teapotProgram, groundProgram;
var vBuffer, iBuffer, fBuffer;
var vertices, indices, textures;
var teapot = null;
var model;
var view;
var light;
var projection;
var BASE_URL = "https://hmbastiaan.nl/martijn/webgl/W08P02_SO/";
var WIDTH = 150, HEIGHT = 150;
function makeTeapot(){
var drawingInfo = teapot.getDrawingInfoObjects();
var indices = drawingInfo.indices;
for(var i=0; i < indices.length; i++){
indices[i] += 4; // Add offset for 'ground'
}
return {
indices: drawingInfo.indices,
vertices: drawingInfo.vertices
}
}
function makeRectangle(x1, x2, y1, y2, z1, z2){
var x1 = -2,
x2 = 2,
y1 = -1,
y2 = -1,
z1 = -1,
z2 = -5;
var vertices = [
vec4(x1, y2, z1, 1),
vec4(x2, y1, z1, 1),
vec4(x2, y1, z2, 1),
vec4(x1, y2, z2, 1)
];
var textures = [
vec2(-1.0, -1.0),
vec2( 1.0, -1.0),
vec2( 1.0, 1.0),
vec2(-1.0, 1.0)
];
var indices = [
0, 1, 2,
0, 2, 3
];
return {
indices: indices,
vertices: vertices,
textures: textures
}
}
function resetBuffers(){
vertices = [];
indices = [];
textures = [];
// Add rectangle
var rectangle = makeRectangle();
Array.prototype.push.apply(vertices, rectangle.vertices);
Array.prototype.push.apply(indices, rectangle.indices);
Array.prototype.push.apply(textures, rectangle.textures);
// Add teapot
var teapot = makeTeapot();
Array.prototype.push.apply(vertices, teapot.vertices);
Array.prototype.push.apply(indices, teapot.indices);
console.log(vertices);
console.log(indices);
console.log(textures);
// Send to GPU
gl.bindBuffer(gl.ARRAY_BUFFER, vBuffer);
gl.bufferData(gl.ARRAY_BUFFER, flatten(vertices), gl.STATIC_DRAW);
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, iBuffer);
gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, new Uint16Array(indices), gl.STATIC_DRAW);
}
function setup(){
$.get(BASE_URL + "teapot.obj", function(teapot_obj_data){
teapot = new OBJDoc(BASE_URL + "teapot.obj");
if(!teapot.parse(teapot_obj_data, 1)){
alert("Parsing teapot.obj failed.");
return;
}
setup2();
}).fail(function(){
alert("Getting teapot.obj failed.");
});
}
function setup2(){
glCanvas = document.getElementById("gl-canvas");
gl = WebGLUtils.setupWebGL(glCanvas, {stencil: true, alpha: false});
gl.viewport(0, 0, WIDTH, HEIGHT);
teapotProgram = initShaders(gl, BASE_URL + "vshader-teapot.glsl", BASE_URL + "fshader-teapot.glsl");
groundProgram = initShaders(gl, BASE_URL + "vshader-ground.glsl", BASE_URL + "fshader-ground.glsl");
light = vec3(0.0, 2.0, -2.0);
view = lookAt(vec3(0, 0, 3), vec3(0,0,0), vec3(0,1,0));
projection = perspective(45, 1.0, 1, 100.0);
// Get teapot uniforms
gl.useProgram(teapotProgram);
teapotProgram.modelLoc = gl.getUniformLocation(teapotProgram, "Model");
teapotProgram.viewLoc = gl.getUniformLocation(teapotProgram, "View");
teapotProgram.projectionLoc = gl.getUniformLocation(teapotProgram, "Projection");
// Upload uniforms
gl.uniformMatrix4fv(teapotProgram.projectionLoc, false, flatten(projection));
gl.uniformMatrix4fv(teapotProgram.viewLoc, false, flatten(view));
gl.uniformMatrix4fv(teapotProgram.modelLoc, false, flatten(scalem(0.25, 0.25, 0.25)));
// Get teapot attributes
teapotProgram.vPosition = gl.getAttribLocation(teapotProgram, "vPosition");
// Get ground uniforms
gl.useProgram(groundProgram);
groundProgram.modelLoc = gl.getUniformLocation(groundProgram, "Model");
groundProgram.viewLoc = gl.getUniformLocation(groundProgram, "View");
groundProgram.projectionLoc = gl.getUniformLocation(groundProgram, "Projection");
groundProgram.shadowMap = gl.getUniformLocation(groundProgram, "shadowMap");
// Get ground attributes
groundProgram.vTexCoord = gl.getAttribLocation(groundProgram, "vTexCoord");
groundProgram.vPosition = gl.getAttribLocation(groundProgram, "vPosition");
// Allocate and fill vertices buffer
vBuffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, vBuffer);
gl.vertexAttribPointer(teapotProgram.vPosition, 4, gl.FLOAT, false, 0, 0);
gl.enableVertexAttribArray(teapotProgram.vPosition);
gl.vertexAttribPointer(groundProgram.vPosition, 4, gl.FLOAT, false, 0, 0);
gl.enableVertexAttribArray(groundProgram.vPosition);
// Allocate indices buffer
iBuffer = gl.createBuffer();
// Setup FBO
fBuffer = gl.createFramebuffer();
gl.bindFramebuffer(gl.FRAMEBUFFER, fBuffer);
fBuffer.renderbuffer = gl.createRenderbuffer();
gl.bindRenderbuffer(gl.RENDERBUFFER, fBuffer.renderbuffer);
gl.renderbufferStorage(gl.RENDERBUFFER, gl.DEPTH_COMPONENT16, 512, 512);
fBuffer.texture = gl.createTexture();
gl.activeTexture(gl.TEXTURE0);
gl.bindTexture(gl.TEXTURE_2D, fBuffer.texture);
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, 512, 512, 0, gl.RGBA, gl.UNSIGNED_BYTE, null);
gl.generateMipmap(gl.TEXTURE_2D);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST_MIPMAP_LINEAR);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST);
gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, fBuffer.texture, 0);
gl.framebufferRenderbuffer(gl.FRAMEBUFFER, gl.DEPTH_ATTACHMENT, gl.RENDERBUFFER, fBuffer.renderbuffer);
// Sanity checking: framebuffer seems to throw now errors
if (!gl.isFramebuffer(fBuffer)) {
throw("Invalid framebuffer");
}
var status = gl.checkFramebufferStatus(gl.FRAMEBUFFER);
switch (status) {
case gl.FRAMEBUFFER_COMPLETE:
break;
case gl.FRAMEBUFFER_INCOMPLETE_ATTACHMENT:
throw("Incomplete framebuffer: FRAMEBUFFER_INCOMPLETE_ATTACHMENT");
break;
case gl.FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT:
throw("Incomplete framebuffer: FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT");
break;
case gl.FRAMEBUFFER_INCOMPLETE_DIMENSIONS:
throw("Incomplete framebuffer: FRAMEBUFFER_INCOMPLETE_DIMENSIONS");
break;
case gl.FRAMEBUFFER_UNSUPPORTED:
throw("Incomplete framebuffer: FRAMEBUFFER_UNSUPPORTED");
break;
default:
throw("Incomplete framebuffer: " + status);
}
// Set ground textures
gl.uniform1i(groundProgram.shadowMap, 0);
// Upload uniforms
gl.uniformMatrix4fv(groundProgram.projectionLoc, false, flatten(projection));
gl.uniformMatrix4fv(groundProgram.viewLoc, false, flatten(view));
gl.uniformMatrix4fv(groundProgram.modelLoc, false, flatten(mat4()));
// Restore default buffers
gl.bindTexture(gl.TEXTURE_2D, null);
gl.bindRenderbuffer(gl.RENDERBUFFER, null);
gl.bindFramebuffer(gl.FRAMEBUFFER, null);
// Set background colour
gl.clearColor(0.3921, 0.5843, 0.9294, 1.0);
gl.enable(gl.DEPTH_TEST);
gl.enable(gl.CULL_FACE);
resetBuffers();
window.requestAnimationFrame(render);
}
function render(){
var teapot = makeTeapot();
gl.useProgram(teapotProgram);
gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT | gl.STENCIL_BUFFER_BIT);
// Switch to framebuffer
gl.bindFramebuffer(gl.FRAMEBUFFER, fBuffer);
// Draw teapot
teapot = makeTeapot();
gl.drawElements(gl.TRIANGLES, teapot.indices.length, gl.UNSIGNED_SHORT, 6 * UNSIGNED_SHORT_SIZE);
// Set framebuffer to defualt buffer (in-browser output)
gl.bindFramebuffer(gl.FRAMEBUFFER, null);
// Draw ground
gl.useProgram(groundProgram);
gl.drawElements(gl.TRIANGLES, 6, gl.UNSIGNED_SHORT, 0);
// Render teapot
gl.useProgram(teapotProgram);
gl.drawElements(gl.TRIANGLES, teapot.indices.length, gl.UNSIGNED_SHORT, 6 * UNSIGNED_SHORT_SIZE);
}
setup();
<div>
<br/>
<canvas width="150" height="150" id="gl-canvas">Sorry :|</canvas>
</div>
<script type='text/javascript' src="https://code.jquery.com/jquery-2.1.4.min.js"></script>
<script type='text/javascript' src="https://hmbastiaan.nl/martijn/webgl/angel/webgl-utils.js"></script>
<script type='text/javascript' src="https://hmbastiaan.nl/martijn/webgl/angel/initShaders2.js"></script>
<script type='text/javascript' src="https://hmbastiaan.nl/martijn/webgl/angel/MV.js"></script>
<script type='text/javascript' src="https://hmbastiaan.nl/martijn/webgl/angel/objParser.js"></script>
Functions of interest:
setup2(): sets up all the buffers and uniforms.
render(): renders the scene.
Disclaimer: this is for an assignment, although this code is simplified enough to not look like the original assignment at all :).
At a glance there are several issues.
Texture bindings are global. Since in setup2 you unbind the 1 texture that means it's never used.
You need to bind whatever textures are needed before each draw call. In other words when you draw the ground you need to bind the teapot texture as in
gl.bindTexture(gl.TEXTURE_2D, fBuffer.texture);
Note: This is an over simplification of what's really needed. You really need to
Choose a texture unit to bind the texture to
var unit = 5;
gl.activeTexture(gl.TEXTURE0 + unit);
Bind the texture to that unit.
gl.bindTexture(gl.TEXTURE_2D, fBuffer.texture);
Set the uniform sampler to that texture unit
gl.uniform1i(groundProgram.shadowMap, unit);
The reason you don't need those extra steps is because (a) you only
have 1 texture so you're using texture unit #0, the default and (b) because
uniforms default to 0 so shadowMap is looking at texture unit #0.
Because you've made a mipmapped texture just rendering to level 0 will not update the mips.
In other words after you render the teapot you'll have a teapot in mip level 0 but mip levels 1, 2, 3, 4, 5 etc will still have nothing in them. You need to call
gl.generateMipmap(gl.TEXTURE_2D)
For that texture after you've rendered the teapot to it. Either that or stop using mips
You need to set the viewport every time you call gl.bindFramebuffer.
gl.bindFramebuffer should almost always be followed by a call to gl.viewport to make the viewport match the size of the thing you're rendering to
gl.bindFramebuffer(gl.FRAMEBUFFER, fb);
// set to size of fb
gl.viewport(0, 0, widthOfFb, heightOfFb);
renderSomething();
gl.bindFramebuffer(gl.FRAMEBUFFER, null);
// set to size of canvas's drawingBuffer
gl.viewport(0, 0, gl.drawingBufferWidth, gl.drawingBufferHeight);
Attributes settings are global
You setup the teapot attributes. Then you draw a teapot to the texture. You then draw ground, but you're still using the teapot attributes.
Just like textures you need to setup attributes before each draw call.
I'm also guessing you really should not be calling makeTeapot in your render function but instead it should be called in setup.
You might find this article useful
You should also consider not putting properties on WebGL objects as it's arguably an anti-pattern.
Also synchronous XHR requests are not cool. You're getting this message in the JavaScript console
Synchronous XMLHttpRequest on the main thread is deprecated because
of its detrimental effects to the end user's experience. For more
help, check http://xhr.spec.whatwg.org/.

Webgl throws different error in browsers

Im am learning webgl I made an application to read PLY files and render them in with webgl. The application works fine in Chrome, but when I run it in safari or in Firefox it doesn't work. I tested it in 2 more computers and it just worked in one. Each the browsers throw different errors. This is the error in safari:
[Warning] WebGL: INVALID_OPERATION: drawElements: attempt to access out of bounds arrays
In Firefox I get the following error:
Error: WebGL: drawElements: no VBO bound to enabled vertex attrib index 2!
The same code in Chrome works perfectly. Using the debugger in the different browsers I could not find a difference in the buffers and the arrays used for drawing.
Here is the code:
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.textureCoordAttribute = gl.getAttribLocation(shaderProgram, "aTextureCoord");
gl.enableVertexAttribArray(shaderProgram.textureCoordAttribute);
shaderProgram.normalCoordAttribute = gl.getAttribLocation(shaderProgram, "aVertexNormal");
gl.enableVertexAttribArray(shaderProgram.normalCoordAttribute);
shaderProgram.pMatrixUniform = gl.getUniformLocation(shaderProgram, "uPMatrix");
shaderProgram.mvMatrixUniform = gl.getUniformLocation(shaderProgram, "uMVMatrix");
shaderProgram.nMatrixUniform = gl.getUniformLocation(shaderProgram, "uNMatrix");
shaderProgram.samplerUniform = gl.getUniformLocation(shaderProgram, "uSampler");
shaderProgram.ambientColorUniform = gl.getUniformLocation(shaderProgram, "uAmbientColor");
shaderProgram.lightingDirectionUniform = gl.getUniformLocation(shaderProgram, "uLightingDirection");
shaderProgram.directionalColorUniform = gl.getUniformLocation(shaderProgram, "uDirectionalColor");
}
function initPLYObjectBuffers (ply_obj) {
ply_obj.vertexBuffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, ply_obj.vertexBuffer);
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(ply_obj.vertexArray), gl.STATIC_DRAW);
if(ply_obj.hasNormal){
ply_obj.normalBuffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, ply_obj.normalBuffer);
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(ply_obj.normalArray), gl.STATIC_DRAW);
}
if(ply_obj.hasTexture){
ply_obj.textureBuffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, ply_obj.textureBuffer);
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(ply_obj.textureArray), gl.STATIC_DRAW);
}
// if(ply_obj.hasColor){
// ply_obj.colorBuffer = gl.createBuffer();
// gl.bindBuffer(gl.ARRAY_BUFFER, ply_obj.colorBuffer);
// gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(colors), gl.STATIC_DRAW);
// }
ply_obj.indexBuffer = gl.createBuffer();
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, ply_obj.indexBuffer);
gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, new Uint16Array(ply_obj.indexArray), gl.STATIC_DRAW);
}
function drawPLYObject(obj){
gl.bindBuffer(gl.ARRAY_BUFFER, obj.vertexBuffer);
gl.vertexAttribPointer(shaderProgram.vertexPositionAttribute, obj.vertexItemSize, gl.FLOAT, false, 0, 0);
gl.bindBuffer(gl.ARRAY_BUFFER, obj.normalBuffer);
gl.vertexAttribPointer(shaderProgram.vertexNormalAttribute, obj.normalItemSize, gl.FLOAT, false, 0, 0);
gl.bindBuffer(gl.ARRAY_BUFFER, obj.textureBuffer);
gl.vertexAttribPointer(shaderProgram.textureCoordAttribute, obj.textureItemSize, gl.FLOAT, false, 0, 0);
gl.activeTexture(gl.TEXTURE0);
gl.bindTexture(gl.TEXTURE_2D, myTexture);
gl.uniform1i(shaderProgram.samplerUniform, 0);
gl.uniform3f(
shaderProgram.ambientColorUniform,
parseFloat(document.getElementById("ambientR").value),
parseFloat(document.getElementById("ambientG").value),
parseFloat(document.getElementById("ambientB").value)
);
var lightingDirection = [
parseFloat(document.getElementById("lightDirectionX").value),
parseFloat(document.getElementById("lightDirectionY").value),
parseFloat(document.getElementById("lightDirectionZ").value)
];
var adjustedLD = vec3.create();
vec3.normalize(lightingDirection, adjustedLD);
vec3.scale(adjustedLD, -1);
gl.uniform3fv(shaderProgram.lightingDirectionUniform, adjustedLD);
gl.uniform3f(
shaderProgram.directionalColorUniform,
parseFloat(document.getElementById("directionalR").value),
parseFloat(document.getElementById("directionalG").value),
parseFloat(document.getElementById("directionalB").value)
);
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, obj.indexBuffer);
setMatrixUniforms();
gl.drawElements(gl.TRIANGLES, obj.getIndexCount(), gl.UNSIGNED_SHORT, 0);
}
Most of the code is from a tutorial. The only addition is the ply_obj, which contains the arrays for the vertex, normals, and texture of the obj in the file. I also save there the reference for the different buffers to use later in the drawPLYObject function. The code that generates the ply_obj is from this page http://threejs.org/examples/webgl_loader_ply.html. I just changed it to work with plain webgl without three.js.
The error always appears in this line:
gl.drawElements(gl.TRIANGLES, obj.getIndexCount(), gl.UNSIGNED_SHORT, 0);
I checked the arrays and they are correct and are the same in all browsers. I don't know what else could be the problem. Thank you in advance. If you need more information i would gladly provide it.
Ok it turned out to be really dumb. I was referencing a variable with another name. Somehow chrome managed to run the program with this error.

webgl how to draw many cubes

I am trying to draw 5161 cubes using webGL. The problem is not all cubes are drawn. Upon some searching, I think its because I am passing too many vertices in one VBO call. You can take a look at jsfiddle here: http://jsfiddle.net/n5fjhe21/. You can move around with QWERASDF and arrows keys but it isnt well implemented right now.
My draw call used to look like this:
function render(){
gl.uniformMatrix4fv(u_matrixLoc, false, new Float32Array(pMatrix));
gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
gl.drawElements(gl.TRIANGLES, data.triangles.length, gl.UNSIGNED_SHORT, 0);
}
So I would do is data.pushData() once and render as needed; It was fast. glObject is an array of Cubes.
data.pushData = function(){
// pushData once then call drawElements on every render call doesnt work as I hit some kind of limit;
// not all cubes are drawn; I think the draw calls must be split up;
data.vertices = [];
data.uv = [];
data.triangles = [];
var vertexOffset = 0;
glObjects.forEach(function pushingObject(o){
data.vertices.push.apply(data.vertices,o.vertices);
data.uv.push.apply(data.uv,o.uv);
o.triangles.forEach(function pushingTriangles(index){
data.triangles.push(index+vertexOffset);
});
vertexOffset += o.vertices.length/3; // change to component length later
});
gl.bindBuffer(gl.ARRAY_BUFFER, vertexBuffer);
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(data.vertices),gl.DYNAMIC_DRAW );
gl.bindBuffer(gl.ARRAY_BUFFER, uvBuffer);
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(data.uv),gl.STATIC_DRAW);
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, triangleBuffer);
gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, new Uint16Array(data.triangles), gl.DYNAMIC_DRAW );
};
But the problem (I think) is that I am passing in too many vertices at once. So I tried to merge pushData and render together:
data.render = function(){
data.vertices = [];
data.uv = [];
data.triangles = [];
var vertexOffset = 0;
glObjects.forEach(function pushingObject(o){
if (vertexOffset + o.vertices.length > 65536){
vertexOffset = 0;
glDraw();
data.vertices.length = 0;
data.uv.length = 0;
data.triangles.length = 0;
}
data.vertices.push.apply(data.vertices,o.vertices);
data.uv.push.apply(data.uv,o.uv);
o.triangles.forEach(function pushingTriangles(index){
data.triangles.push(index+vertexOffset);
});
vertexOffset += o.vertices.length/3; // change to component length later
});
glDraw();
function glDraw(){
gl.bindBuffer(gl.ARRAY_BUFFER, vertexBuffer);
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(data.vertices),gl.STATIC_DRAW);
gl.bindBuffer(gl.ARRAY_BUFFER, uvBuffer);
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(data.uv),gl.STATIC_DRAW);
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, triangleBuffer);
gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, new Uint16Array(data.triangles), gl.STATIC_DRAW);
gl.drawElements(gl.TRIANGLES, data.triangles.length, gl.UNSIGNED_SHORT, 0);
}
};
But this isnt fast enough because as I learnt, passing in new bufferData is slow. So my question is, what does one do in this situation? I was unable to locate any webgl resource that deal with this. My feeling leans towards creating multiple VBO objects but I want to make sure I am going in the right direction first. And as a follow up question, suppose if one need to draw many cubes all with unique position (x,y,z) and orientation (rX,rY,rZ), how does one go about implementing it? Thanks in advance.
Ok I solved my problem and I'll leave this here for stragglers:
Basically, I had the right idea in that I need to use multiple draw calls as each indexed draw (drawElements) can only refer to 2^16 elements in a VBO. The flaw in my first implementation is that I actually tried to reconstruct a new big typedArray made of multiple cube vertices in every render call. Needless to say, that is very slow. So instead of that, I really should have only created the typedArray/buffer once. To overcome the 2^16 element reference limitation, all I have to do is to separate the one bigass typedArray into manageable sizes, and this is exactly what this new version of pushData does:
data.pushData = function(){
// ensure each vertex attribute has less than 2^16 vertices because that is how many that be be referenced each time
// with gl.drawElements call
function newChunk(){
return {
vertices: [],
uv: [],
triangles: []
}
}
var chunk = newChunk();
var vertexOffset = 0;
glObjects.forEach(function pushingVerts(o){
if (vertexOffset + o.vertices.length > 65536){
vertexOffset = 0;
data.chunks.push(chunk);
chunk = newChunk();
}
chunk.vertices.push.apply(chunk.vertices,o.vertices);
chunk.uv.push.apply(chunk.uv,o.uv);
o.triangles.forEach(function pushingTriangles(index){
chunk.triangles.push(index+vertexOffset);
});
vertexOffset += o.vertices.length/3; // change to component length later
});
data.chunks.push(chunk);
data.chunks.forEach(function toTypeArray(c){
c.vertices = new Float32Array(c.vertices);
c.uv = new Float32Array(c.uv);
c.triangles = new Uint16Array(c.triangles);
});
gl.bindBuffer(gl.ARRAY_BUFFER, vertexBuffer);
gl.bufferData(gl.ARRAY_BUFFER, sizeofFloat * 65536*3,gl.DYNAMIC_DRAW);
gl.bindBuffer(gl.ARRAY_BUFFER, uvBuffer);
gl.bufferData(gl.ARRAY_BUFFER, sizeofFloat * 65536*2,gl.DYNAMIC_DRAW);
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, triangleBuffer);
gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, sizeofFloat * 65536, gl.DYNAMIC_DRAW);
// for some reason only allocating sizeofUnsignedShort * 65536 is not enough.
return data.chunks;
};
Then for render its simply:
data.renderChunks = function(){
data.chunks.forEach(function renderChunk(c){
gl.bindBuffer(gl.ARRAY_BUFFER, vertexBuffer);
gl.bufferSubData(gl.ARRAY_BUFFER, 0, c.vertices);
gl.bindBuffer(gl.ARRAY_BUFFER, uvBuffer);
gl.bufferSubData(gl.ARRAY_BUFFER, 0, c.uv);
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, triangleBuffer);
gl.bufferSubData(gl.ELEMENT_ARRAY_BUFFER, 0, c.triangles);
gl.drawElements(gl.TRIANGLES, c.triangles.length, gl.UNSIGNED_SHORT, 0);
});
};
Also I changed from using gl.bufferData to gl.bufferSubData to avoid the overhead of constructing a new buffer.
And with this I can now draw 60,000 cubes (at least):
http://jsfiddle.net/n5fjhe21/1/

Categories

Resources