2012-03-22 116 views
4

我目前使用opengl来绘制几千个多边形,它的运行速度非常慢。需要apx 100 ms来绘制8000个多边形。用OpenGL高效地绘制大量的多边形?

这里是我的设置了一些信息:

  • 我有我的设置为2D多边形面的集合3D领域的每个对象,所以方将包括6,4顶点的飞机。所以我可以访问每一架飞机。
  • 我不能保证每个多边形将具有相同数量的顶点
  • 我目前正在绘制如下:

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

这比我预期的要慢,我已经调查改为使用glDrawElements(),并将多边形平面分解为三角形,并使用三角形或条来绘制它们。

我只是寻找一些建议,以最有效的方式来做到这一点,或任何批评在我接近绘图的方式。

+5

您正在做大量的OpenGL调用。每次通话都有一些开销,这会让你的工作变慢。您必须将所有数据存储在缓冲区中,并使用几个调用来呈现所有内容。请参阅@genpfault答案。 – lvella 2012-03-22 19:39:13

回答

9

Triangulate everything and toss the triangles into a big 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但不是什么有趣的事。

6

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

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

+0

@aid,我试图使用glDrawArrays()函数时遇到了麻烦。我创建了一个浮点数组来作为我每个飞机的顶点。我启用GL_VERTEX_ARRAY,然后为每个数组分配一个glVerterxPointer(3,GL_FLOAT,0,plane#array)。所以现在我的每个飞机都有一个顶点指针。然后我调用glDrawArrays(GL_TRIANGLE_FAN,0,???)我很困惑如何处理最后一个参数,它会是我所做的顶点指针的总数量吗? – Dark 2012-03-23 18:22:39

+0

第三个参数是要绘制的顶点数。 – aib 2012-03-24 09:04:32

2

是使用三角测量(这是镶嵌的特殊情况)来计算点,然后绘制它们是优选的方式(您使用哪个已被弃用立即模式)

当前图形架构使用shaders

所以不是所有的顶点在每次发送一个小组顶点处理器和GPU,你应该之间,

  • 过程中一旦其保存到一个数据结构,并将其发送到克PU

  • 绘制它在一次(使用glDraw *()函数),

这是更快,因为

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

  • 然后将数据完全发送到GPU内存,以便在没有任何额外瓶的情况下执行进一步的操作(通过使用可编程着色器)的数据传输和相关开销的领域