2014-11-05 2 views
2

Я пытаюсь нарисовать 5161 кубов с помощью webGL. Проблема в том, что не все кубы рисуются. При некотором поиске я думаю, что это потому, что я пропускаю слишком много вершин в одном вызове VBO. Вы можете посмотреть здесь jsfiddle: http://jsfiddle.net/n5fjhe21/. Вы можете перемещаться с помощью клавиш QWERASDF и стрелок, но это не так хорошо реализовано прямо сейчас.webgl как рисовать много кубов

Мой вызов отрисовки используется выглядеть следующим образом:

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 - это массив кубов.

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 и делают вместе:

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), как это можно реализовать? Заранее спасибо.

ответ

2

Ok я решил мою проблему, и я оставлю это здесь отставших:

В принципе, у меня была правильная идея в том, что мне нужно использовать несколько вызовов отрисовки в каждой индексируемой дро (drawElements) может относиться только к 2^16 элементов в VBO. Недостатком в моей первой реализации является то, что я на самом деле попытался восстановить новый большой массив typedArray, сделанный из нескольких вершин куба в каждом вызове рендеринга. Излишне говорить, что это очень медленно. Поэтому вместо этого я действительно должен был только создать 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; 
}; 

Тогда для визуализации его просто :

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.bufferData к gl.bufferSubData, чтобы избежать накладных расходов на строительство нового буфера.

И с этим я теперь могу рисовать 60 000 кубов (по крайней мере): http://jsfiddle.net/n5fjhe21/1/

Смежные вопросы