Эффективно рисовать большое количество полигонов с помощью OpenGL? - PullRequest
4 голосов
/ 22 марта 2012

В настоящее время я использую opengl для рисования нескольких тысяч полигонов, и он работает очень медленно. Для рисования 8000 полигонов требуется около 100 мс.

Вот некоторая информация о моей настройке:

  • У меня есть каждый объект в моем трехмерном поле в виде набора двухмерных плоскостей, поэтому квадрат будет состоять из 6, 4-вершинных плоскостей. Так что у меня есть доступ к каждому отдельному самолету.
  • Я не могу гарантировать, что у каждого многоугольника будет одинаковое количество вершин
  • Сейчас я рисую следующее:

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

Это намного медленнее, чем я ожидал, и вместо этого я решил использовать glDrawElements () и разбить плоскости многоугольника на треугольники, а также использовать треугольные веера или полосы для их рисования.

Я просто ищу совет относительно наиболее эффективного способа сделать это или любую критику в отношении того, как я подхожу к чертежу.

Ответы [ 3 ]

9 голосов
/ 22 марта 2012

Триангуляция все и бросить треугольники в большой VA / VBO .

РЕДАКТИРОВАТЬ : GLUtesselator wrapper:

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;
}

Использует Собственный , но не для чего-либо интересного.

6 голосов
/ 22 марта 2012

glDrawArrays () или glDrawElements () (или glDrawRangeElements () ) являются предпочтительным способом, а также единственным неотрицательным.Непосредственный режим (ваш пример) - самый медленный и наименее предпочтительный метод, полезный в основном для уроков по OpenGL и (по моему опыту) отладки.Есть также display lists, «макросы» для OpenGL, которые всего на один шаг выше при использовании непосредственного режима рисования, и vertex buffer objects (VBOs).

Любой, кроме непосредственного режима, должен быть достаточно быстрым для ваших нужд.,

2 голосов
/ 24 марта 2012

Да, использование триангуляции (что является особым случаем тесселяции) для вычисления точек, а затем их рисование является предпочтительным способом (вы использовали немедленный режим, который не рекомендуется)

В текущей графической архитектуре используются шейдеры.

Таким образом, вместо того, чтобы каждый раз отправлять небольшой набор вершин между процессором и графическим процессором, вы должны

  • Обрабатывает все вершины одновременно, сохраняет их в структуре данных и отправляет это в gpu

  • Нарисуйте его сразу (используя функции glDraw * ()),

Это быстрее, потому что

  • Весь массив рассчитывается только один раз и сохраняется в структуре данных, чтобы его можно было повторно использовать

  • Затем данные полностью отправляются в память графического процессора, где можно выполнять дальнейшие операции без каких-либо дополнительных узких мест при передаче данных и связанных с ними служебных данных (с помощью программируемых шейдеров)

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...