简体   繁体   中英

Three js how to add triangle to BufferGeometry manually

I've been trying to find the fastest way to change a mesh's vertices with three.js. I found that if I change parts of mesh.geometry.attributes.position.array, then set mesh.geometry.attributes.position.needsUpdate=true, it works well and doesn't have to rebuild arrays or recreate opengl buffers. I found that needsUpdate=true changes the version number of the attribute and that makes it resend the attributes vertices array to the opengl buffer.

So I tried doing that myself instead by calling gl.bindBuffer() then gl.bufferData() but then after doing that every loop for a while it crashes on my call to new Float32Array(). Which is weird because when I check my memory usage I'm only using 4MB right before it crashes. I realize it's not the best way to be deallocating/reallocating the array every loop just to make it slightly bigger when I could be doubling the size of the array when it gets full, but I want to understand why it's crashing when done this way.

https://jsfiddle.net/q1txL19c/3/ Crashes in 20 seconds. But if I change the if(0) to if(1) it works.

What is three.js doing differently that makes it not crash? Why does new Float32Array() fail when not much javascript memory has been used up according to the profiler?

<!doctype html>
<html>
   <body style='margin:0;padding:0'>
        <script src="https://threejs.org/build/three.js"></script>
        <script>

var camera, scene, renderer, mesh
var triangles = 1
init()

function init()
{
    scene = new THREE.Scene()

    camera = new THREE.PerspectiveCamera(50, window.innerWidth / window.innerHeight, .1, 10000)
    camera.position.z = 15
    scene.add(camera)

    var geometry = new THREE.BufferGeometry()

    var material = new THREE.MeshBasicMaterial( {side: THREE.FrontSide, transparent:false, vertexColors: THREE.VertexColors} )
    mesh = new THREE.Mesh(geometry, material)

    var positions = new Float32Array([1,1,0, 0,1,0, 0,0,0])
    geometry.addAttribute('position', new THREE.BufferAttribute(positions,3))

    var colors = new Float32Array([0,0,1, 0,0,0, 0,0,0])
    geometry.addAttribute('color', new THREE.BufferAttribute(colors,3))

    scene.add(mesh)

    renderer = new THREE.WebGLRenderer()
    renderer.setSize(window.innerWidth, window.innerHeight)
    renderer.setClearColor( 0x6699DD )

    document.body.appendChild(renderer.domElement)

    loop()
}

function addTriangle(geometry)
{
    // Make 3 new vertices, each with x,y,z. 9 total positions.
    var newVertices = []
    for(var i=0; i<9; i++)
        newVertices[i] = Math.random()*10-5

    appendArrayToAttribute(geometry.attributes.position, newVertices)


    // Make 3 new colors, 1 for each new vertex, each with r,g,b. 9 total slots.
    var newColors = []
    for(var i=0; i<9; i++)
        newColors[i] = Math.random()

    appendArrayToAttribute(geometry.attributes.color, newColors)
}

function appendArrayToAttribute(attribute, arrayToAppend)
{
    // Make a new array for the geometry to fit the 9 extra positions at the end, since you can't resize Float32Array
    try
    {
        var newArray = new Float32Array(attribute.array.length + arrayToAppend.length)
    }
    catch(e)
    {
        console.log(e)
        if(!window.alerted)
        {
            alert("out of memory!? can't allocate array size="+(attribute.array.length + arrayToAppend.length))
            window.alerted = true
        }
        return false
    }
    newArray.set(attribute.array)
    newArray.set(arrayToAppend, attribute.array.length)


    attribute.setArray(newArray)

    if(0)
    {
        attribute.needsUpdate = true
    }
    else
    {
        // Have the geometry use the new array and send it to opengl.
        var gl = renderer.context
        gl.bindBuffer(gl.ARRAY_BUFFER, renderer.properties.get(attribute).__webglBuffer)
        gl.bufferData(gl.ARRAY_BUFFER, attribute.array, gl.STATIC_DRAW)
    }

}

function loop()
{
    requestAnimationFrame(loop)

    mesh.rotation.x += 0.01
    mesh.rotation.y += 0.02

    renderer.render(scene, camera)

    for(var i=0;i<10;i++)
    {
        addTriangle(mesh.geometry)
        triangles++
    }
    if(Math.random()<.03)
    {
        console.log("triangles="+triangles)
        var gl = renderer.context
        console.log("gl buffer size="+gl.getBufferParameter(gl.ARRAY_BUFFER, gl.BUFFER_SIZE))
    }
}

      </script>

   </body>
</html>

You can add faces to BufferGeometry after the first render, but you must pre-allocate your geometry attribute buffers to be large enough, as they can't be resized.

Also, you will be updating array values, not instantiating new arrays.

You can update the number of faces to render like so:

geometry.setDrawRange( 0, 3 * numFacesToDraw ); // 3 vertices for each face

See this related answer and demo .

three.js r.84

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM