WebGL如何绘制多个立方体

3
我正在尝试使用WebGL绘制5161个立方体。问题在于,并非所有的立方体都被绘制出来。经过一些搜索,我认为这是因为我在一个VBO调用中传递了太多的顶点。您可以在此处查看jsfiddle:http://jsfiddle.net/n5fjhe21/。您可以使用 QWERASDF 和箭头键进行移动,但现在实现不太好。

我的绘图调用以前是这样的:

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

我会这样做:只需执行一次 data.pushData(),然后根据需要进行渲染。这样很快。glObject 是一个 Cube 数组。

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

但问题在于,我认为我一次传入了太多的顶点。因此,我试图合并pushData和render:

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

但这样速度不够快,因为我学到了,传入新的BufferData是很慢的。所以我的问题是,在这种情况下应该怎么做?我找不到任何处理此问题的WebGL资源。我的感觉倾向于创建多个VBO对象,但我想先确保我朝着正确的方向前进。作为跟进问题,假设需要绘制许多立方体,每个立方体都有唯一的位置 (x,y,z) 和方向 (rX,rY,rZ),那么要如何实现呢?谢谢。
1个回答

3

好的,我解决了我的问题,现在我把解决方法留在这里给需要的人看:

基本上,我的想法是正确的,因为我需要使用多个绘制调用,因为每个索引绘制(drawElements)只能引用VBO中2^16个元素。我第一次实现的缺陷是,我实际上在每次渲染调用时尝试重新构建一个由多个立方体顶点组成的新的大型typedArray。不用说,那非常慢。所以,我真正应该做的是只创建一次typedArray/buffer。为了克服2^16元素引用限制,我所要做的就是将这一个大型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;
}; 

然后进行渲染很简单:
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);
    });
};

我改用gl.bufferSubData来替代gl.bufferData,从而避免构建新缓冲的开销。

有了这个改变,我现在可以绘制至少60000个立方体: http://jsfiddle.net/n5fjhe21/1/


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接