Domanda

Ho un po 'di tempo e ho letto di VBO ed è quello che ho ottenuto:

http://img64.imageshack.us/img64/5733/fps8.jpg

Ok, è molto meglio di prima. È compilato in rilascio. Uso VBO (probabilmente, se va tutto bene) e glDrawArrays per disegnare.

Ecco il codice del disegno. Per favore dammi consigli su come ottimizzarlo. Volevo con il terreno ... uh poche migliaia di FPS, è reale?

void DrawVBO (void)
{
    int i;

    CVert*      m_pVertices; 
    CTexCoord*  m_pTexCoords;

    unsigned int    m_nTextureId;

    m_pVertices = NULL;
    m_pTexCoords = NULL;
    m_nVertexCount = 0;
    m_nVBOVertices = m_nVBOTexCoords = m_nTextureId = 0;


    if( IsExtensionSupported( "GL_ARB_vertex_buffer_object" ) )
    {
        // Pobierz wskaźniki na funkcje OpenGL 
        glGenBuffersARB = (PFNGLGENBUFFERSARBPROC) wglGetProcAddress("glGenBuffersARB");
        glBindBufferARB = (PFNGLBINDBUFFERARBPROC) wglGetProcAddress("glBindBufferARB");
        glBufferDataARB = (PFNGLBUFFERDATAARBPROC) wglGetProcAddress("glBufferDataARB");
        glDeleteBuffersARB = (PFNGLDELETEBUFFERSARBPROC) 
        wglGetProcAddress("glDeleteBuffersARB"); 
    }

    todrawquads=0;
    nIndex=0;


// my function counting how many quads I will draw


    for (i=0;i<MAX_CHUNKS_LOADED;i++)
    {
        if (chunks_loaded[i].created==1)
        {
            countquads(i);
        }
    }

    m_nVertexCount=4*todrawquads;

    m_pVertices = new CVec[m_nVertexCount];
    m_pTexCoords = new CTexCoord[m_nVertexCount];

// another my function adding every quad which i'm going to draw (its verticles) to array

    for (i=0;i<MAX_CHUNKS_LOADED;i++)
    {
        if (chunks_loaded[i].created==1)
        {
            addchunktodraw(i,m_pVertices,m_pTexCoords);
        }
    }


    glClearColor (1,1,1, 0.0);
    glColor3f(1,1,1);

    glClear (GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);        // Clear Screen And Depth Buffer
    glLoadIdentity ();                                          // Reset The Modelview Matrix

    fps++;

    // Camera settings.

    //gluLookAt (zom, zom, zom, 0.0, 0.0, 0.0, 0, 0, 1);

    gluLookAt (zoom, zoom, zoom, 0.0, 0.0, 0.0, 0, 0, 1);
    glRotatef((rot_x / 180 * 3.141592654f),1,0,0);  
    glRotatef((rot_y / 180 * 3.141592654f),0,1,0);
    glRotatef((rot_z / 180 * 3.141592654f),0,0,1);

    //m_nTextureId = t_terrain;

    // Generate And Bind The Vertex Buffer
    glGenBuffersARB( 1, &m_nVBOVertices );                          // Get A Valid Name
    glBindBufferARB( GL_ARRAY_BUFFER_ARB, m_nVBOVertices );         // Bind The Buffer
    // Load The Data
    glBufferDataARB( GL_ARRAY_BUFFER_ARB, m_nVertexCount*3*sizeof(float), m_pVertices, GL_STATIC_DRAW_ARB );

    // Generate And Bind The Texture Coordinate Buffer
    glGenBuffersARB( 1, &m_nVBOTexCoords );                         // Get A Valid Name
    glBindBufferARB( GL_ARRAY_BUFFER_ARB, m_nVBOTexCoords );        // Bind The Buffer
    // Load The Data
    glBufferDataARB( GL_ARRAY_BUFFER_ARB, m_nVertexCount*2*sizeof(float), m_pTexCoords, GL_STATIC_DRAW_ARB );

    // Enable Pointers
    glEnableClientState( GL_VERTEX_ARRAY );                     // Enable Vertex Arrays
    glEnableClientState( GL_TEXTURE_COORD_ARRAY );              // Enable Texture Coord Arrays

    glBindBufferARB( GL_ARRAY_BUFFER_ARB, m_nVBOVertices );
    glVertexPointer( 3, GL_FLOAT, 0, (char *) NULL );       // Set The Vertex Pointer To The Vertex Buffer
    glBindBufferARB( GL_ARRAY_BUFFER_ARB, m_nVBOTexCoords );
    glTexCoordPointer( 2, GL_FLOAT, 0, (char *) NULL );     // Set The TexCoord Pointer To The TexCoord Buffe

    glDrawArrays( GL_QUADS, 0, m_nVertexCount); // Draw All Of The Triangles At Once

    glDisableClientState( GL_VERTEX_ARRAY );                    // Disable Vertex Arrays
    glDisableClientState( GL_TEXTURE_COORD_ARRAY );             // Disable Texture Coord Arrays

    liniergb();

    glutSwapBuffers();

    delete [] m_pVertices; m_pVertices = NULL;
    delete [] m_pTexCoords; m_pTexCoords = NULL;
}

Allora cosa posso farci? (Il codice sopra è la funzione di disegno principale)

modifica

Ho spostato questo:

if( IsExtensionSupported( "GL_ARB_vertex_buffer_object" ) )
{
    // Pobierz wskaźniki na funkcje OpenGL 
    glGenBuffersARB = (PFNGLGENBUFFERSARBPROC) wglGetProcAddress("glGenBuffersARB");
    glBindBufferARB = (PFNGLBINDBUFFERARBPROC) wglGetProcAddress("glBindBufferARB");
    glBufferDataARB = (PFNGLBUFFERDATAARBPROC) wglGetProcAddress("glBufferDataARB");
    glDeleteBuffersARB = (PFNGLDELETEBUFFERSARBPROC) 
    wglGetProcAddress("glDeleteBuffersARB"); 
}

alla mia funzione principale. Probabilmente nessun miglioramento.

modifica2

Ora, posso anche vedere che sta mangiando la memoria. Ogni pochi secondi l'utilizzo della memoria del programma aumenta sempre di più ... Cosa c'è che non va? Cosa non sto eliminando?

modifica3

Ok, grazie mille. Ho spostato del codice fuori dalla funzione di disegno e ... molti più fps! Grazie mille!

http://img197.imageshack.us/img197/5193/fpsfinal.jpg

È una mappa di 640x640 blocchi (quindi 40 volte più grande) con 650'000 quad (circa 70 volte di più) e ancora ~ 170 fps. Grande ! E nessuna perdita di memoria. Grazie ancora!

È stato utile?

Soluzione

La tua funzione DrawVBO dovrebbe contenere solo:

glClearColor (1,1,1, 0.0);
glColor3f(1,1,1);

glClear (GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);        // Clear Screen And Depth Buffer
glLoadIdentity ();                                          // Reset The Modelview Matrix

fps++;

// Camera settings.

//gluLookAt (zom, zom, zom, 0.0, 0.0, 0.0, 0, 0, 1);

gluLookAt (zoom, zoom, zoom, 0.0, 0.0, 0.0, 0, 0, 1);
glRotatef((rot_x / 180 * 3.141592654f),1,0,0);  
glRotatef((rot_y / 180 * 3.141592654f),0,1,0);
glRotatef((rot_z / 180 * 3.141592654f),0,0,1);

// Enable Pointers
glEnableClientState( GL_VERTEX_ARRAY );                     // Enable Vertex Arrays
glEnableClientState( GL_TEXTURE_COORD_ARRAY );              // Enable Texture Coord Arrays

glBindBufferARB( GL_ARRAY_BUFFER_ARB, m_nVBOVertices );
glVertexPointer( 3, GL_FLOAT, 0, (char *) NULL );       // Set The Vertex Pointer To The Vertex Buffer
glBindBufferARB( GL_ARRAY_BUFFER_ARB, m_nVBOTexCoords );
glTexCoordPointer( 2, GL_FLOAT, 0, (char *) NULL );     // Set The TexCoord Pointer To The TexCoord Buffe

glDrawArrays( GL_QUADS, 0, m_nVertexCount); // Draw All Of The Triangles At Once

glDisableClientState( GL_VERTEX_ARRAY );                    // Disable Vertex Arrays
glDisableClientState( GL_TEXTURE_COORD_ARRAY );             // Disable Texture Coord Arrays

liniergb();

glutSwapBuffers();

È necessario spostare il resto in una funzione separata chiamata solo una volta all'avvio (o quando il terreno cambia).

Altri suggerimenti

Stai ricaricando tutti i tuoi buffer e liberandoli di nuovo in ogni singolo frame?Smetti di farlo e il tuo frame rate aumenterà.

Tieni presente che il tuo codice corrente finirà per esaurire gli identificatori VBO, poiché non elimini mai i VBO che crei.

Anche il collegamento di funzioni di estensione sicuramente non deve essere eseguito a ogni frame.

Alcune cose risaltano:

Nella tua funzione di disegno stai allocando la memoria per i tuoi dati geometrici

    m_pVertices = new CVec[m_nVertexCount];
    m_pTexCoords = new CTexCoord[m_nVertexCount];

L'allocazione della memoria è un'operazione estremamente costosa, questa è una di quelle cose che dovrebbero essere fatte solo una volta. OpenGL non è pensato per essere "inizializzato", ma le strutture dati che gli passerai lo sono!

Qui stai copiando i buffer appena allocati in OpenGL, ancora e ancora con ogni frame. Questo è esattamente l'opposto di cosa fare.

// Generate And Bind The Vertex Buffer
glGenBuffersARB( 1, &m_nVBOVertices );                          // Get A Valid Name
glBindBufferARB( GL_ARRAY_BUFFER_ARB, m_nVBOVertices );         // Bind The Buffer
// Load The Data
glBufferDataARB( GL_ARRAY_BUFFER_ARB, m_nVertexCount*3*sizeof(float), m_pVertices, GL_STATIC_DRAW_ARB );

// Generate And Bind The Texture Coordinate Buffer
glGenBuffersARB( 1, &m_nVBOTexCoords );                         // Get A Valid Name
glBindBufferARB( GL_ARRAY_BUFFER_ARB, m_nVBOTexCoords );        // Bind The Buffer
// Load The Data
glBufferDataARB( GL_ARRAY_BUFFER_ARB, m_nVertexCount*2*sizeof(float), m_pTexCoords, GL_STATIC_DRAW_ARB );

L'intera idea dei VBO è, caricare i dati una sola volta, copiarli in OpenGL e poi non riallocarli mai più. Nota che questa non è un'inizializzazione OpenGL, è un'inizializzazione dei dati, qualcosa di assolutamente ragionevole. Vedo che hai chiamato le tue variabili m_pVertices e m_pTexCoords indicando che quelle sono variabili membro di classe. Quindi la soluzione è semplice: sposta l'intero codice di inizializzazione in una funzione di caricamento. Inoltre, invece degli array C ++ nudi, suggerisco caldamente di utilizzare std::vector.

Quindi risolviamo questo problema:

// Load Extensions only once. Well, once per context actually, however
// why don't you just use an extension wrapper and forget about those
// gritty details? Google GLEW or GLee
void init_extensions()
{
    if( IsExtensionSupported( "GL_ARB_vertex_buffer_object" ) )
    {
    // Pobierz wska\u017aniki na funkcje OpenGL 
    glGenBuffersARB = (PFNGLGENBUFFERSARBPROC) wglGetProcAddress("glGenBuffersARB");
    glBindBufferARB = (PFNGLBINDBUFFERARBPROC) wglGetProcAddress("glBindBufferARB");
    glBufferDataARB = (PFNGLBUFFERDATAARBPROC) wglGetProcAddress("glBufferDataARB");
    glDeleteBuffersARB = (PFNGLDELETEBUFFERSARBPROC) wglGetProcAddress("glDeleteBuffersARB"); 
    }
}

class Block0r
{
protected:
    GLuint m_nTextureId;
    GLuint m_nVBOVertices;
    GLuint m_nVBOTexCoords;
    GLuint m_nVertexCount;

    // Call this one time to load the data    
    void LoadVBO()
    {
        std::vector<CVert> vertices;
        std::vector<CTexCoord> texCoords;
        // my function counting how many quads I will draw

        todrawquads = 0;

        for(int i=0; i < MAX_CHUNKS_LOADED; i++) {
            if( chunks_loaded[i].created == 1 ) {
                countquads(i);
            }
        }

        m_nVertexCount = 4*todrawquads;

        vertices.resize(vertexcount);
        texcoords.resize(vertexcount);


        for (i=0;i<MAX_CHUNKS_LOADED;i++) {
            if (chunks_loaded[i].created==1) {
                addchunktodraw(i, &vertices[0], &texcoords[0]);
            }
        }

        glGenBuffersARB( 1, &m_nVBOVertices );
            glBindBufferARB( GL_ARRAY_BUFFER_ARB, m_nVBOVertices );
            glBufferDataARB( GL_ARRAY_BUFFER_ARB, vertices.size()*sizeof(CVert), &vertices[0], GL_STATIC_DRAW_ARB );

        glGenBuffersARB( 1, &m_nVBOTexCoords );
            glBindBufferARB( GL_ARRAY_BUFFER_ARB, m_nVBOTexCoords );
        glBufferDataARB( GL_ARRAY_BUFFER_ARB, texCoords.size()*sizeof(CTexCoord), &texCoords[0], GL_STATIC_DRAW_ARB );
    }

    void DrawVBO()
    {
        glClearColor (1,1,1, 0.0);
        glColor3f(1,1,1);

        glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

        glMatrixMode(GL_PROJECTION);
            setup_projection(); // this should really be done in the drawing handler

        glMatrixMode(GL_MODELVIEW); // don't asssume a certain matrix being active!
        glLoadIdentity();

        fps++;

        gluLookAt (zoom, zoom, zoom, 0.0, 0.0, 0.0, 0, 0, 1);
        glRotatef((rot_x / 180 * 3.141592654f),1,0,0);  
        glRotatef((rot_y / 180 * 3.141592654f),0,1,0);
        glRotatef((rot_z / 180 * 3.141592654f),0,0,1);

        glEnableClientState( GL_VERTEX_ARRAY );
        glEnableClientState( GL_TEXTURE_COORD_ARRAY );

        glBindBufferARB( GL_ARRAY_BUFFER_ARB, m_nVBOVertices );
        glVertexPointer( 3, GL_FLOAT, 0, (GLvoid *) 0 );

        glBindBufferARB( GL_ARRAY_BUFFER_ARB, m_nVBOTexCoords );
        glTexCoordPointer( 2, GL_FLOAT, 0, (GLvoid *) 0 );

        glDrawArrays( GL_QUADS, 0, m_nVertexCount);
        glDisableClientState( GL_VERTEX_ARRAY );
        glDisableClientState( GL_TEXTURE_COORD_ARRAY );
        liniergb();

        glutSwapBuffers();
    }
}

Nota a margine: commenti come // Generate And Bind The Vertex Buffer o // Set The Vertex Pointer To The Vertex Buffer non sono molto utili. Quei commenti dicono solo in modo ridondante, cosa si può leggere comunque dal codice. I commenti dovrebbero essere aggiunti al codice, il cui funzionamento interno non è immediatamente comprensibile, o se dovessi fare un qualche tipo di hack per aggiustare qualcosa e questo hack potrebbe sconcertare qualcun altro, o te stesso nel giro di pochi mesi.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top