OpenGL渲染大量动态2D圆形

3

我看到了一个关于这个主题的类似帖子 这里,然而,我的问题有点不同。

我有一个二维图,由不同位置和大小的圆组成。目前,我的渲染方案使用显示列表来存储预先绘制好的圆,用户可以使用glScalef/glTranslatef来调整大小和位置。然而,由于我要渲染成千上万个圆,所以调整大小和绘制变得极为缓慢。每个圆都可以有不同的半径和颜色,因此必须在循环内完成这些操作。

当用户更改圆的大小时,有哪些方法可以尝试提高圆形渲染速度?我已经查阅了与VBO相关的信息,如上面的链接所述,但对于我的应用程序,对象大小不断变化的情况下,我能获得多少性能提升还是不太清楚。


2
你应该研究一下现代OpenGL。在现代OpenGL中已经不再使用glScale/glTranslate等函数族。与您正在使用的传统版本相比,性能提升可能非常明显。如果可以使用OpenGL 3.1或更高版本,请使用。请查看这个页面,了解现代OpenGL的介绍。 - Cornstalks
“成千上万个圆”有多少个圆?10000个吗?“极慢”有多慢?每帧>100ms? - genpfault
3个回答

1
因为我正在渲染成千上万个圆形,所以调整大小和绘制变得极其缓慢

仅使用顶点数组,在Intel HD Graphics 3000上每帧大约需要60毫秒,其中包含10000个圆形:

// g++ -O3 circles.cpp -o circles -lglut -lGL
#include <GL/glut.h>
#include <vector>
#include <iostream>
#include <cmath>
using namespace std;

// returns a GL_TRIANGLE_FAN-able buffer containing a unit circle
vector< float > glCircle( unsigned int subdivs = 20 )
{
    vector< float > buf;

    buf.push_back( 0 );
    buf.push_back( 0 );
    for( unsigned int i = 0; i <= subdivs; ++i )
    {
        float angle = i * ((2.0f * 3.14159f) / subdivs);
        buf.push_back( cos(angle) );
        buf.push_back( sin(angle) );
    }

    return buf;
}

struct Circle
{
    Circle()
    {
        x = ( rand() % 200 ) - 100;
        y = ( rand() % 200 ) - 100;
        scale = ( rand() % 10 ) + 4;
        r = rand() % 255;
        g = rand() % 255;
        b = rand() % 255;
        a = 1;
    }

    float x, y;
    float scale;
    unsigned char r, g, b, a;
};

vector< Circle > circles;
vector< float > circleGeom;
void init()
{
    srand( 0 );
    for( size_t i = 0; i < 10000; ++i )
        circles.push_back( Circle() );
    circleGeom = glCircle( 100 );
}

void display()
{
    int beg = glutGet( GLUT_ELAPSED_TIME );

    glClear( GL_COLOR_BUFFER_BIT );

    glMatrixMode( GL_PROJECTION );
    glLoadIdentity();
    double w = glutGet( GLUT_WINDOW_WIDTH );
    double h = glutGet( GLUT_WINDOW_HEIGHT );
    double ar = w / h;
    glOrtho( -100 * ar, 100 * ar, -100, 100, -1, 1);

    glMatrixMode( GL_MODELVIEW );
    glLoadIdentity();

    glEnableClientState( GL_VERTEX_ARRAY );
    glVertexPointer( 2, GL_FLOAT, 0, &circleGeom[0] );

    for( size_t i = 0; i < circles.size(); ++i )
    {
        Circle& c = circles[i];
        c.scale = ( rand() % 10 ) + 4;

        glPushMatrix();
        glTranslatef( c.x, c.y, 0 );
        glScalef( c.scale, c.scale, 0 );
        glColor3ub( c.r, c.g, c.b );
        glDrawArrays( GL_TRIANGLE_FAN, 0, circleGeom.size() / 2 );
        glPopMatrix();
    }

    glDisableClientState( GL_VERTEX_ARRAY );

    glutSwapBuffers();

    int end = glutGet( GLUT_ELAPSED_TIME );
    double elapsed = (double)( end - beg );
    cout << elapsed << "ms" << endl;
}

void timer(int extra)
{
    glutPostRedisplay();
    glutTimerFunc(16, timer, 0);
}

int main( int argc, char **argv )
{
    glutInit( &argc, argv );
    glutInitDisplayMode( GLUT_RGBA | GLUT_DOUBLE );
    glutInitWindowSize( 600, 600 );
    glutCreateWindow( "Circles" );

    init();

    glutDisplayFunc( display );
    glutTimerFunc(0, timer, 0);
    glutMainLoop();
    return 0;
}

0

ARB_instanced_arrays-基于实例的绘制可能是最简单的方法。

您将拥有一个 M 个顶点的圆,您将绘制 N 次,并将每个圆的 x/y 位置、半径和颜色存储为顶点属性,适当使用 glVertexAttribDivisor()

如果您需要半径自适应 LOD,则会更加棘手。您可能需要深入了解几何着色器。


0

第二种方法是使用实例化数组,使用glDrawElementsInstanced或glDrawArraysInstanced作为一种干净的解决方案,可以很好地转移到其他类型的几何体。

如果您想/需要坚持使用OpenGL 2(例如必须在iThing上运行),并且只需要圆形,请考虑使用点精灵。每个圆的起点是点顶点值。将半径存储为纹理坐标的S值,表面法线的X值等。启用混合,GL_PROGRAM_POINT_SIZE,可能还有点平滑;编写一个顶点着色器,只需将gl_PointSize设置为所需的半径即可。瞬间生成圆形。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接