Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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?

like image 867
Daniel Jackson Avatar asked Jul 29 '26 01:07

Daniel Jackson


1 Answers

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);
like image 117
Rabbid76 Avatar answered Jul 31 '26 15:07

Rabbid76



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!