使用OpenGL有效地绘制大量多边形?

时间:2012-03-22 19:16:21

标签: opengl

我目前正在使用opengl来绘制几千个多边形并且它的运行速度非常慢。绘制8000个多边形需要大约100毫秒。

以下是有关我的设置的一些信息:

  • 我将3d场中的每个对象设置为2d多边形平面的集合,因此正方形将由6个4顶点平面组成。所以我可以访问每个单独的飞机。
  • 我不能保证每个多边形都有相同数量的顶点
  • 我目前的绘图如下:

    for(allPlanes){
      glBegin(GL_POLYGON);
       for(allPointsInThePlane){
           glVertex(pointX,pointY,pointZ);
        }
      glEnd();
    }
    

这比我想象的慢得多,我已经研究过使用glDrawElements()代替并将多边形平面分成三角形并使用三角形或条带来绘制它们。

我只想找到一些关于最有效的方法的建议,或者对我接近绘图方式的批评。

3 个答案:

答案 0 :(得分:9)

Triangulate所有内容并将三角形投入大VA / VBO

编辑:GLUtesselator包装器:

struct TessContext
{
    ~TessContext()
    {
        for( size_t i = 0; i < combined.size(); ++i )
        {
            delete[] combined[i];
        }
    }

    vector< Eigen::Vector2d > pts;
    vector< GLdouble* > combined;
};

#define APIENTRY __stdcall

void APIENTRY tess_begin( GLenum type ) {}
void APIENTRY tess_edgeFlag( GLboolean flag ) {}
void APIENTRY tess_end() {}

void APIENTRY tess_vertex( void *data, TessContext* ctx )
{
    GLdouble* coord = (GLdouble*)data;
    ctx->pts.push_back( Eigen::Vector2d( coord[0], coord[1] ) );
}

void APIENTRY tess_combine( GLdouble coords[3], void *vertex_data[4], GLfloat weight[4], void **outData, TessContext* ctx )
{
    GLdouble* newVert = new GLdouble[3];
    ctx->combined.push_back( newVert );

    newVert[0] = coords[0];
    newVert[1] = coords[1];
    newVert[2] = coords[2];
    *outData = newVert;
}

template< typename Vec >
vector< Vec > Triangulate
    ( 
    const vector< Vec >& aSimplePolygon
    )
{
    vector< GLdouble > coords;
    for( size_t i = 0; i < aSimplePolygon.size(); ++i )
    {
        coords.push_back( aSimplePolygon[i].x() );
        coords.push_back( aSimplePolygon[i].y() );
        coords.push_back( 0 );
    }

    GLUtesselator* tess = gluNewTess();
    //gluTessProperty( tess, GLU_TESS_WINDING_RULE, GLU_TESS_WINDING_ODD );
    //gluTessProperty( tess, GLU_TESS_WINDING_RULE, GLU_TESS_WINDING_NONZERO );

    gluTessCallback( tess, GLU_TESS_BEGIN,          (GLvoid (APIENTRY *)())    tess_begin      );
    gluTessCallback( tess, GLU_TESS_EDGE_FLAG,      (GLvoid (APIENTRY *)())    tess_edgeFlag   );
    gluTessCallback( tess, GLU_TESS_VERTEX_DATA,    (GLvoid (APIENTRY *)())    tess_vertex     );
    gluTessCallback( tess, GLU_TESS_END,            (GLvoid (APIENTRY *)())    tess_end        );
    gluTessCallback( tess, GLU_TESS_COMBINE_DATA,   (GLvoid (APIENTRY *)())    tess_combine    );
    gluTessNormal( tess, 0.0, 0.0, 1.0 );

    TessContext ctx;

    gluTessBeginPolygon( tess, &ctx );
    gluTessBeginContour( tess );

    for( size_t i = 0; i < aSimplePolygon.size(); ++i )
    {
        gluTessVertex( tess, &coords[i*3], &coords[i*3] );
    }

    gluTessEndContour( tess );
    gluTessEndPolygon( tess );

    gluDeleteTess(tess);

    vector< Vec > ret( ctx.pts.size() );
    for( size_t i = 0; i < ret.size(); ++i )
    {
        ret[i].x() = ctx.pts[i].x();
        ret[i].y() = ctx.pts[i].y();
    }

    return ret;
}

使用Eigen但不是有趣的事情。

答案 1 :(得分:6)

glDrawArrays()glDrawElements()(或glDrawRangeElements())是首选方式,也是唯一不推荐使用的方法。立即模式(您的示例)是最慢和最不优选的方法,主要用于OpenGL教程和(根据我自己的经验)调试。 OpenGL还有display lists,“宏”,使用立即模式进行绘制只有一步,vertex buffer objects (VBOs)

除了立即模式之外,任何一个都应该足够快,以满足您的需求。

答案 2 :(得分:2)

是的,使用三角测量(这是曲面细分的一种特殊情况)计算点然后绘制它们是首选方式(您使用的是立即模式,不推荐使用)

当前的图形架构使用shaders

因此,不应每次在处理器和GPU之间发送一小组顶点,

  • 立即处理所有顶点,将其保存到数据结构并将其发送到gpu

  • 立即绘制(使用glDraw *()函数),

这更快,因为

  • 整个数组只计算一次,并保留在数据结构中,以便可以重复使用

  • 然后将数据完全发送到GPU内存,在那里可以执行进一步的操作,而不会产生任何额外的数据传输瓶颈和相关的开销(使用可编程着色器)