webgl如何绘制许多立方体

时间:2021-09-13 13:34:26

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.

我正在尝试使用webGL绘制5161个多维数据集。问题不是所有立方体都被绘制出来。经过一些搜索,我认为它是因为我在一次VBO调用中传递了太多顶点。你可以在这里看看jsfiddle:http://jsfiddle.net/n5fjhe21/。您可以使用QWERASDF和箭头键移动,但它现在还没有很好地实现。

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()并根据需要进行渲染;它很快。 glObject是一个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:

但问题(我认为)是我一次传递太多顶点。所以我尝试合并pushData并一起渲染:

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.

但这还不够快,因为据我所知,传入新的bufferData很慢。所以我的问题是,在这种情况下做了什么?我无法找到任何处理此问题的webgl资源。我的感觉倾向于创建多个VBO对象,但我想确保我首先朝着正确的方向前进。作为一个后续问题,假设如果需要绘制多个具有唯一位置(x,y,z)和方向(rX,rY,rZ)的立方体,那么如何实现它呢?提前致谢。

1 个解决方案

#1


2  

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:

基本上,我有正确的想法,因为我需要使用多个绘制调用,因为每个索引绘制(drawElements)只能引用VBO中的2 ^ 16个元素。我的第一个实现中的缺陷是我实际上尝试在每个渲染调用中重建由多个立方体顶点组成的新的大型类型。不用说,这很慢。所以不是那样,我真的应该只创建一次typedArray / buffer。为了克服2 ^ 16元素引用限制,我所要做的就是将一个bigass typedArray分成可管理的大小,这正是这个新版本的pushData所做的:

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.

此外,我从使用gl.bufferData更改为gl.bufferSubData,以避免构造新缓冲区的开销。

And with this I can now draw 60,000 cubes (at least): http://jsfiddle.net/n5fjhe21/1/

有了这个,我现在可以画出60,000个立方体(至少):http://jsfiddle.net/n5fjhe21/1/

#1


2  

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:

基本上,我有正确的想法,因为我需要使用多个绘制调用,因为每个索引绘制(drawElements)只能引用VBO中的2 ^ 16个元素。我的第一个实现中的缺陷是我实际上尝试在每个渲染调用中重建由多个立方体顶点组成的新的大型类型。不用说,这很慢。所以不是那样,我真的应该只创建一次typedArray / buffer。为了克服2 ^ 16元素引用限制,我所要做的就是将一个bigass typedArray分成可管理的大小,这正是这个新版本的pushData所做的:

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.

此外,我从使用gl.bufferData更改为gl.bufferSubData,以避免构造新缓冲区的开销。

And with this I can now draw 60,000 cubes (at least): http://jsfiddle.net/n5fjhe21/1/

有了这个,我现在可以画出60,000个立方体(至少):http://jsfiddle.net/n5fjhe21/1/