使用Stage3D渲染不同对象时,使用顶点缓冲区的最佳方法是什么

时间:2012-04-08 10:59:59

标签: actionscript-3 flash stage3d

我有一组简单的2D对象,主要由两个三角形组成。 虽然对象非常简单,但每个对象都是使用模板操作绘制的 所以每个对象都需要它自己的drawTriangles()调用。

存储和处理这些对象及其顶点和索引缓冲区的最佳方法是什么?

我可以想到几种不同的方法:

  1. 在启动时创建一个大的顶点和索引缓冲区并将每个对象添加到它,例如:

    public function initialize():void{
        for each(object in objectList){
            vertexData.push(object.vertices);
            indexData.push(triangle1, triangle2);
        }
    
        indices  = context3D.createIndexBuffer( numTriangles * 3 );
        vertices = context3D.createVertexBuffer( numVertices, dataPerVertex );
        indices.uploadFromVector( indexData, 0, numIndices );
        vertices.uploadFromVector( vertexData, 0, numVertices );
    }
    

    在渲染循环遍历所有对象的过程中,检查哪些对象在屏幕上可见并更新其顶点。然后重新上传整个顶点缓冲区。 然后循环遍历可见对象,并通过单独调用drawTriangles()

    绘制每个对象的三角形
    public function onRender():void{
        for each(object in objectList){
            if(object.visibleOnScreen)
                object.updateVertexData();
        }
    
        vertices.uploadFromVector( vertexData, 0, numVertices );
    
        for each(object in objectsOnScreen){
            drawTriangles(indices, object.vertexDataOffset, object.numTriangles);
        }
    }
    
  2. 除了只在需要时重新上传每个对象的顶点数据,您还可以执行与数字1类似的操作:

    public function onRender():void{
        for each(object in objectList){
            if(object.visibleOnScreen){
                if(object.hasChanged)
                    vertices.uploadFromVector( vertexData, object.vertexDataOffset, object.numTriangles );
                drawTriangles(indices, object.vertexDataOffset, object.numTriangles);
            }
        }
    }
    
  3. 您还可以创建一个新的顶点缓冲区,每个帧上只包含可见对象:

    public function onRender():void{
        for each(object in objectList){
            if(object.visibleOnScreen){
                vertexData.push(object.vertices);
                indexData.push(triangle1, triangle2);
            }
        }
    
        indices  = context3D.createIndexBuffer( numTriangles * 3 );
        vertices = context3D.createVertexBuffer( numVertices, dataPerVertex );
        indices.uploadFromVector( indexData, 0, numIndices );
        vertices.uploadFromVector( vertexData, 0, numVertices );
    
        for each(object in objectsOnScreen){
            drawTriangles(indices, object.vertexDataOffset, object.numTriangles);
        }
    }
    
  4. 另一种选择是为每个对象创建单独的顶点缓冲区:

    public function initialize():void{
        for each(object in objectList){
            object.indices  = context3D.createIndexBuffer( object.numTriangles * 3 );
            object.vertices = context3D.createVertexBuffer( object.numVertices, dataPerVertex );
        }
    }
    
    public function onRender():void{
        for each(object in objectList){
            if(object.visibleOnScreen){
                if(object.hasChanged)
                    object.vertices.uploadFromVector( object.vertexData, 0, object.numTriangles );
                drawTriangles(indices, 0, object.numTriangles);
            }
        }
    }
    
  5. 我可以想象选项1会很慢,因为每个帧都需要上传整个顶点缓冲区 选项2的优势在于,它只需要上传已更改但可能会多次调用uploadFromVector的顶点数据。

    更新

    我已经看过Starling框架源代码,特别是QuadBatch类:
    http://gamua.com/starling/
    https://github.com/PrimaryFeather/Starling-Framework

    似乎所有顶点都是手工转换的,并且每帧都重建并上传整个顶点缓冲区。

1 个答案:

答案 0 :(得分:0)

无论如何,您都需要在每个帧上传数据,因为Stage3D需要知道在哪里绘制。最终通过减少绘制调用开始优化。您可以使用byteArray而不是未更改的数据以及对已更改的数据使用Vector来加速数据上载。上传向量较慢但设置向量数据较快,上传bytearray更快但设置bytearray数据较慢(因此仅用于缓存数据)。您也不需要每次都创建索引和顶点缓冲区。使用舒适的长度创建一次,只有当长度变得太小时才创建新的。所有这些都可以很好地加速一切,但仍然会减少绘制调用的速度(在25次以上的绘制调用后你应该开始看到下行)。