Panel użytkownika
Nazwa użytkownika:
Hasło:
Nie masz jeszcze konta?
Autor: Janusz Ganczarski
Biblioteki C++

Obiekty buforowe

[lekcja] Rozdział 29. Obiekty buforowe wierzchołków (VBO) i obiekty buforowe pikseli (PBO); dwa programy przykładowe.
Obecne karty graficzne posiadają bardzo duże ilości pamięci. Jedynie stosunkowo niewielka część z tej pamięci przeznaczona jest na bufor ramki. Pozostałą pamięć mogą wykorzystywać tekstury (mechanizm obiektów tekstur) lub wprowadzone w wersji 1.5 biblioteki OpenGL obiekty buforowe wierzchołków VBO (ang. vertex buffer object), wcześniej dostępne w rozszerzeniu ARB vertex buffer object. W wersji 2.1 biblioteki dodano obsługę obiektów buforowych pikseli PBO (ang. pixel buffer object), które wcześniej obsługiwały rozszerzenia ARB pixel buffer object i EXT pixel buffer - object. Obiekty buforowe PBO i VBO korzystają z tego samego interfejsu, funkcjonalnością zbliżonego do mechanizmów obiektów tekstur.

Generowanie identyfikatorów obiektów

Utworzenie wskazanej w parametrze n ilości unikatowych identyfikatorów (nazw) obiektów buforowych realizuje funkcja:
C/C++
void glGenBuffers( GLsizei n, GLuint * buffers )
Numery identyfikatorów zwracane sa w tablicy wskazywanej w parametrze buffers. Oczywiście program musi zapewnić odpowiednią ilość miejsca w tablicy. Identyfikator o wartości zero nie jest związany z żadnym obiektem buforowym.
Sprawdzenie czy wskazany identyfikator jest identyfikatorem obiektu buforowego umożliwia funkcja:
C/C++
GLboolean glIsBuffer( GLuint buffer )
która zwraca stosowne wartości logiczne (GL_TRUE lub GL_FALSE).

Dowiązanie obiektów buforowych

Utworzony identyfikator trzeba dowiązać do obiektu buforowego wywołując funkcję:
C/C++
void glBindBuffer( GLenum target, GLuint buffer )
której parametr buffer zawiera unikatowy identyfikator obiektu, a parametr target określa rodzaj obiektu buforowanego. Dostępne są następujące rodzaje obiektów buforowych:
  • GL_ARRAY_BUFFER - obiekt buforowy tablic wierzchołków,
  • GL_ELEMENT_ARRAY_BUFFER - obiekt buforowy indeksowych tablic wierzchołków,
  • GL_PIXEL_UNPACK_BUFFER - obiekt buforowy odczytu (rozpakowania) danych pikseli,
  • GL_PIXEL_PACK_BUFFER - obiekt buforowy zapisu (spakowania) danych pikseli.
Pierwsze dwa rodzaje obiektów buforowych należą do grupy obiektów buforowych wierzchołków (VBO), dwa pozostałe to obiekty buforowe pikseli (PBO).
Pierwsze wywołanie funkcji glBindBuffer tworzy obiekt buforowy, a następne wywołania służą do przełączania się pomiędzy wybranymi obiektami. Opisane dalej operacje na obiektach buforowych wykonywane są zawsze na bieżącym obiekcie, wybranym przy użyciu funkcji glBindBuffer.

Usuwanie obiektów

Usuwanie wybranej grupy obiektów buforowych realizuje funkcja:
C/C++
void glDeleteBuffers( GLsizei n, const GLuint * buffers )
Parametr n określa ilość usuwanych obiektów buforowych, których identyfikatowy zawiera tablica buffers.

Ładowanie danych do obiektu

Ładowanie danych do obiektu buforowego można zrealizować na dwa sposoby. Pierwszy polega na skorzystaniu z tradycyjnego modelu aplikacji OpenGL, tj. kopiowaniu danych do obiektu bufora z tablicy (lub innej struktury danych) zawartej w pamięci operacyjnej komputera. Taki model ładowania danych wykorzystują obiekty tekstur. Druga metoda korzysta z tzw. odwzorowania obiektu buforowego (ang. mapping buffer object) i pozwala na bezpośrednie operowanie na pamięci obiektu.
Zauważmy, że pierwsza z wymienionych metod wymaga zarezerwowania odpowiedniej ilości pamięci operacyjnej komputera, choć może to być alokacja tymczasowa (dynamiczna). Drugi sposób nie wymaga przydzielania dodatkowych zasobów systemowych.
Niezależnie od przyjętej metody ładowanie danych do obiektu buforowego wymaga wywołania funkcji:
C/C++
void glBufferData( GLenum target, GLsizeiptr size, const GLvoid * data, GLenum usage )
Parametr target określa rodzaj obiektu buforowego, którego dane tworzymy. Możliwe są cztery opisane wcześniej wartości: GL_ARRAY_BUFFER, GL - ELEMENT ARRAY BUFFER, GL_PIXEL_UNPACK_BUFFER i GL_PIXEL_PACK_BUFFER.
Drugi parametr size wskazuje rozmiar danych bufora obiektu buforowego. Wielkość ta jest określana w BMU - podstawowe jednostkach maszynowych (ang. basic machine units), czyli w bajtach. Jedynym sposobem na zmianę wielkości bufora jest usunięcie obiektu i ponowne jego utworzenie z żądanym rozmiarem.
Kolejny parametr data zawiera wskaźnik na dane ładowane do obiektu buforowego. Podanie wartości różnej od NULL powoduje skopiowanie danych z bufora zawartego w pamięci operacyjnego do bufora zawartego w pamięci karty graficznej. Natomiast w przypadku podania wartości NULL pamięć bufora obiektu buforowego jest jedynie rezerwowana, a jej zawartość jest nieokreślona. Możliwość tę wykorzystujemy w szczególności w przypadku, gdy zamierzamy bezpośrednio operować na pamięci obiektu buforowego.
Ostatni parametr usage zawiera wskazówkę dla biblioteki OpenGL dotyczącą przewidywanej metody dostępu do danych zawartych w obiekcie buforowym. Możliwe są następujące wartości:
  • GL_STREAM_DRAW - jednokrotne lub sporadyczne pobieranie danych i przez większość czasu wykorzystanie ich do zapisu do obiektu OpenGL,
  • GL_STREAM_READ - jednokrotne lub sporadyczne pobieranie danych i przez większość czasu wykorzystanie ich do odczytu z obiektu OpenGL,
  • GL_STREAM_COPY - jednokrotne lub sporadyczne pobieranie danych i przez większość czasu wykorzystanie ich zarówno do odczytu z obiektu OpenGL jak i do zapisu,
  • GL_STATIC_DRAW - jednokrotne lub sporadyczne pobieranie danych i wielokrotne ich wykorzystanie do zapisu do obiektu OpenGL,
  • GL_STATIC_READ - jednokrotne lub sporadyczne pobieranie danych i wielokrotne ich wykorzystanie do odczytu z obiektu OpenGL,
  • GL_STATIC_COPY - jednokrotne lub sporadyczne pobieranie danych i wielokrotne wykorzystanie ich zarówno do odczytu z obiektu OpenGL jak i do zapisu,
  • GL_DYNAMIC_DRAW - wielokrotne pobieranie danych i wielokrotne ich wykorzystanie do zapisu do obiektu OpenGL,
  • GL_DYNAMIC_READ - wielokrotne pobieranie danych i wielokrotne ich wykorzystanie do odczytu z obiektu OpenGL,
  • GL_DYNAMIC_COPY - wielokrotne pobieranie danych i wielokrotne wykorzystanie ich zarówno do odczytu z obiektu OpenGL jak i do zapisu. Parametr usage jest jedynie wskazówką umożliwiającą optymalizację implementacji biblioteki OpenGL. Wybrana metoda dostępu do danych obiektu nie ogranicza wykonywanych na nim operacji.
W przypadku braku możliwości przydzielenia wymaganej ilości pamięci funkcja glBufferData generuje błąd GL_OUT_OF_MEMORY.
Zmianę całości lub części danych zawartych w obiekcie buforowym umożliwia funkcja:
C/C++
void glBufferSubData( GLenum target, GLintptr offset, GLsizeiptr size, const GLvoid * data )
Parametr target określa oczywiście rodzaj obiektu buforowego i przyjmuje jedną z czterech znanych już wartości. Zakres modyfikowanych danych określają parametry offset i size, gdzie offset jest numerem pierwszego zmienianego elementu danych bufora obiektu (numeracja elementów zaczyna się od zera i jest prowadzona w bajtach), a size to ilość modyfikowanych elementów bufora. Nowe dane zawarte są w tablicy wskazywanej w parametrze data.
Wspomniana wcześniej metoda odwzorowania obiektu buforowego wymaga uzyskania wskaźnika do danych obiektu buforowego. Wymaga to wywołania funkcji:
C/C++
GLvoid * glMapBuffer( GLenum target, GLenum access )
której parametr target określa rodzaj obiektu buforowego i przyjmuje znane już wartości: GL_ARRAY_BUFFER, GL_ELEMENT_ARRAY_BUFFER, GL_PIXEL_UNPACK_BUFFER i GL_PIXEL_PACK_BUFFER.
Drugi parametr access wskazuje do jakich operacji będzie wykorzystany pobierany wskaźnik na dane obiektu buforowego:
  • GL_READ_ONLY - odczyt danych,
  • GL_WRITE_ONLY - zapis danych,
  • GL_READ_WRITE - odczyt i zapis danych.
Wykorzystanie wskaźnika niezgodnie z planowanym przeznaczeniem nie powoduje wprawdzie zgłoszenia błędu przez bibliotekę OpenGL, ale efekt działania jest nieokreślony.
Jeżeli odwzorowywany obiekt jest już w tym trybie funkcja glMapBuffer zwraca wartość NULL oraz dodatkowo generowany jest błąd GL_INVALID - OPERATION.
Gdy obiekt buforowy znajduje się w trybie nie można na min wykonywać innych operacji niż związanych z pobieraniem (lub ładowaniem) danych. W szczególności wywołanie funkcji glBufferSubData spowoduje wystąpienie błędu GL_INVALID_OPERATION. Efekt działania innych operacji związanych z obiektem buforowym jest nieokreślony. Zezwolenie na takie zachowanie obiektów buforowych specyfikacji biblioteki OpenGL tłumaczy możliwością uzyskania wyższej wydajności.
Po zakończeniu operacji w trybie odwzorowania trzeba zwolnić pobrany wskaźnik do danych obiektu buforowego wywołując funkcję:
C/C++
GLboolean glUnmapBuffer( GLenum target )
której parametr target określa oczywiście rodzaj obiektu buforowego.
W przypadku poprawnego wyjścia obiektu buforowego z trybu odwzorowania funkcja glUnmapBuffer zwraca wartość GL_TRUE. Wartość GL_FALSE zostanie zwrócona, gdy obiekt nie znajduje się w trybie odwzorowania oraz w każdym przypadku wystąpienia zdarzenia wpływającego na zmianę stanu pamięci karty graficznej. Takim zdarzeniami mogą być w szczególności: zmiana rozdzielczości ekranu, przejście karty graficznej w tryb oszczędzania energii. Wystąpienie takich sytuacji wymaga ponownego załadowania danych do pamięci bufora obiektu.

Pobieranie właściwości obiektu

Pobranie właściwości bieżącego obiektu buforowego umożliwia funkcja:
C/C++
void glGetBufferParameteriv( GLenum target, GLenum pname, GLint * params )
Parametr target określa rodzaj obiektu buforowego i przyjmuje jedną z dobrze znanych wartości: GL_ARRAY_BUFFER, GL_ELEMENT_ARRAY_BUFFER, GL_PIXEL_UNPACK_BUFFER i GL_PIXEL_PACK_BUFFER.
Drugi parametr pname zawiera rodzaj pobieranej właściwości obiektu buforowego, który zostaje zwrócony w parametrze params. Możliwe są następujące wartości:
  • GL_BUFFER_SIZE - rozmiar danych obiektu (w bajtach),
  • GL_BUFFER_USAGE - wskazówka dostępu do danych obiektu,
  • GL_BUFFER_ACCESS - przeznaczenie wskaźnika do danych obiektu,
  • GL_BUFFER_MAPPEDznacznik czy obiekt znajduje się w trybie odwzorowania.
Jeżeli obiekt buforowy znajduje się w trybie odwzorowania możliwe jest pobranie wskaźnika na dane obiektu. Wymaga to wywołania funkcji:
C/C++
void glGetBufferPointerv( GLenum target, GLenum pname, GLvoid ** params )
której parametr target określa rodzaj obiektu buforowego (cztery znane wartości: GL_ARRAY_BUFFER, GL_ELEMENT_ARRAY_BUFFER, GL_PIXEL_UNPACK - BUFFER i GL_PIXEL_PACK_BUFFER), a drugi parametr pname może przyjąć

Tabela 1: Zestawienie właściwości obiektów buforowych

parametrwartość początkowadopuszczalne wartości
GL_BUFFER_SIZE0nieujemna liczba całkowita
GL_BUFFER_USAGEGL_STATIC_DRAWGL_STREAM_DRAW, GL_STREAM_READ,
GL_STREAM_COPY, GL_STATIC_DRAW,
GL_STATIC_READ, GL_STATIC_COPY,
GL_DYNAMIC_DRAW, GL_DYNAMIC_READ,
GL_DYNAMIC_COPY
GL_BUFFER_ACCESSGL_READ_WRITEGL_READ_ONLY, GL_WRITE_ONLY,
GL_READ_WRITE
GL_BUFFER_MAPPEDGL_FALSEGL_TRUE, GL_FALSE
GL_BUFFER_MAP_POINTERNULLwskaźnik
jedynie wartość GL_BUFFER_MAP_POINTER, oznaczającą właśnie pobieranie wskaźnika na dane obiektu. Wskaźnik jest zwracany w ostatnim parametrze params.
W przypadku, gdy wybrany obiekt buforowy nie jest w trybie odwzorowania w parametrze params zostanie zwrócona wartość NULL.
Zestawienie wszystkich właściwości obiektów buforowych wraz z dopuszczalnymi wartościami oraz wartościami początkowymi przedstawia tabela 1.

Pobieranie danych obiektu

Biblioteka OpenGL umożliwia pobranie wybranych danych zawartych w obiekcie buforowym przy użyciu funkcji:
C/C++
void glGetBufferSubData( GLenum target, GLintptr offset, GLsizeiptr size, GLvoid * data )
której parametr target określa rodzaj obiektu buforowego, a parametry offset i size wyznaczają odpowiednio pierwszy pobierany element oraz ilość kopiowanych elementów. Numeracja elementów obiektu buforowego zaczyna się od zera i jest wyznaczana w bajtach. Bufor, do którego kopiowane są wybrane elementy, wskazywany jest w parametrze data.

Obiekty buforowe tablic wierzchołków

Dane przechowywane w obiektach buforowych tablic wierzchołków są zgodne z formatem używanym w tablicach wierzchołków. Umożliwia to bezpośrednie korzystanie z tablic wierzchołków przy użyciu funkcji: glArrayElement, glDrawArrays, glMultiDrawArrays, glDrawElements, glDrawRangeElements i glMultiDrawElements. Jedyna różnica polega na sposobie dostarczenia danych tablic wierzchołków do funkcji: glVertexPointer, glNormalPointer, glColorPointer, glSecondaryColorPointer, glIndexPointer, glEdgeFlagPointer, glFogCoordPointer i glTexCoordPointer. Zamiast wskaźnika na dane tablicy (parametr pointer powyższych funkcji) należy podać położenie początku danych w bieżącym obiekcie buforowym tablicy wierzchołków. W praktyce jeden obiekt buforowy tablic wierzchołków może zatem służyć do przechowywania danych wielu tablic wierzchołków.

Obiekty buforowe indeksowych tablic wierzchołków

Obiekty buforowe indeksowych tablic wierzchołków są bezpośrednio przystosowane do współpracy z funkcjami: glDrawElements, glDrawRangeElements i glMultiDrawElements obsługującymi indeksowe tablice wierzchołków. Jedyna różnica polega na sposobie dostarczenia danych tablicy indeksów. Zamiast wskaźnika na dane tablicy z indeksami (parametr indices dwóch pierwszych funkcji) podaje się położenie początku danych w bieżącym obiekcie buforowym indeksowej tablicy wierzchołków. W przypadku funkcji glMultiDrawElements parametr indices zawiera adresy położenia danych kolejnych tablic indeksów w bieżącym obiekcie buforowym.

Obiekty buforowe odczytu (rozpakowania) danych pikseli

Obiekty buforowe odczytu danych pikseli można wykorzystać jako źródło danych pikseli dla następujących funkcji biblioteki OpenGL: glBitmap, glColorSubTable, glColorTable, glCompressedTexImage1D, glCompressedTexImage2D, glCompressedTexImage3D, glCompressedTexSubImage1D, glCompressedTexSubImage2D, glCompressedTexSubImage3D, glConvolutionFilter1D, glConvolutionFilter2D, glDrawPixels, glPixelMapfv, glPixelMapuiv, glPixelMapusv, glPolygonStipple, glSeparableFilter2D, glTexImage1D, glTexImage2D, glTexImage3D, glTexSubImage1D, glTexSubImage2D i glTexSubImage3D.
W parametrach powyższych funkcji wskazujących źródło danych pikseli zamiast adresu tablicy z danymi wskazujemy położenie danych w obiekcie buforowym.

Obiekty buforowe zapisu (spakowania) danych pikseli

Obiekty buforowe zapisu danych pikseli można wykorzystać jako bufor danych pikseli dla następujących funkcji biblioteki OpenGL: glGetCompressedTexImage, glGetConvolutionFilter, glGetHistogram, glGetMinmax, glGetPixelMapfv, glGetPixelMapuiv, glGetPixelMapusv, glGetPolygonStipple, glGetSeparableFilter, glGetTexImage i glReadPixels.
W parametrach powyższych funkcji wskazujących tablicę na dane pikseli zamiast adresu tablicy na dane wskazujemy położenie danych w obiekcie buforowym.

Programy przykładowe

Pierwszy program przykładowy (plik vbo.cpp) jest kolejnym rozwinięciem programu służącego wcześniej do testów list wyświetlania oraz tablic wierzchołków. W porównaniu do poprzednika trzykrotnie zwiększono ilość wyświetlanych prymitywów. Zastosowanie obiektów buforowych wyraźnie zwiększyło szybkość wyświetlania prymitywów, przy czym największy wzrost szybkości dotyczy wyświetlania punktów (23%), nieco mniej zyskują odcinki (8%) i trójkąty (6%). Pamiętajmy jednak, że program faktycznie testuje jedynie szybkość renderingu, a VBO pozwala także na optymalizację zarządzania pamięcią, co umożliwia jeszcze większe przyspieszenia działania programów.
W programie zastosowano obie opisane wyżej metody ładowania danych do obiektu buforowego, jednak nie został wyeliminowany bufor zawarty w pamięci operacyjnej z uwagi na konieczność jego obecności do porównawczego testu tablic wierzchołków. Dane tablicy ze składowymi kolorów wierzchołków prymitywów i tablicy ze współrzędnymi tych wierzchołków przechowywane są w dwóch odrębnych obiektach buforowych, choć możliwe jest także wykorzystanie jednego obiektu do przechowywania danych obu tablic.
Wyniki wszystkich testów dostępnych w programie przedstawiono na rysunkach 1 - 6.
Rysunek 1. Program VBO - punkty z obiektami buforowymi
Rysunek 1. Program VBO - punkty z obiektami buforowymi
Rysunek 2. Program VBO - punkty z tablicami wierzchołków
Rysunek 2. Program VBO - punkty z tablicami wierzchołków

Plik vbo.cpp

Rysunek 3. Program VBO - odcinki z obiektami buforowymi
Rysunek 3. Program VBO - odcinki z obiektami buforowymi
Rysunek 4. Program VBO - odcinki z tablicami wierzchołków
Rysunek 4. Program VBO - odcinki z tablicami wierzchołków
Rysunek 5. Program VBO - trójkąty z obiektami buforowymi
Rysunek 5. Program VBO - trójkąty z obiektami buforowymi
Rysunek 6. Program VBO - trójkąty z tablicami wierzchołków
Rysunek 6. Program VBO - trójkąty z tablicami wierzchołków
C/C++
/*
(c) Janusz Ganczarski
http://www.januszg.hg.pl
JanuszG@enter.net.pl
*/

#include <GL/glut.h>
#include <GL/glext.h>
#ifndef WIN32
#define GLX_GLXEXT_LEGACY
#include <GL/glx.h>
#define wglGetProcAddress glXGetProcAddressARB
#endif
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <time.h>
#include "colors.h"

// wskaźnik na funkcję glGenBuffers

PFNGLGENBUFFERSPROC glGenBuffers = NULL;

// wskaźnik na funkcję glBindBuffer

PFNGLBINDBUFFERPROC glBindBuffer = NULL;

// wskaźnik na funkcję glBufferData

PFNGLBUFFERDATAPROC glBufferData = NULL;

// wskaźnik na funkcję glMapBuffer

PFNGLMAPBUFFERPROC glMapBuffer = NULL;

// wskaźnik na funkcję glUnmapBuffer

PFNGLUNMAPBUFFERPROC glUnmapBuffer = NULL;

// stałe do obsługi menu podręcznego

enum
{
    // rodzaj testu
    POINTS_VBO, // punkty z VBO
    POINTS_VA, // punkty z tablicami wierzchołków
    LINES_VBO, // odcinki z VBO
    LINES_VA, // odcinki z tablicami wierzchołków
    TRIANGLES_VBO, // trójkąty z VBO
    TRIANGLES_VA, // trójkąty z tablicami wierzchołków
   
    // obszar renderingu
    FULL_WINDOW, // aspekt obrazu - całe okno
    ASPECT_1_1, // aspekt obrazu 1:1
    EXIT // wyjście
};

// aspekt obrazu

int aspect = FULL_WINDOW;

// usunięcie definicji makr near i far

#ifdef near
#undef near
#endif
#ifdef far
#undef far
#endif

// rozmiary bryły obcinania

const GLdouble left = - 2.0;
const GLdouble right = 2.0;
const GLdouble bottom = - 2.0;
const GLdouble top = 2.0;
const GLdouble near = 3.0;
const GLdouble far = 7.0;

// rodzaj testu - domyślnie punkty z tablicami wierzchołków

int test = POINTS_VA;

// identyfikatory obiektów buforowych wierzchołków

GLuint vbo_id[ 2 ];

// ilość generowanych współrzędnych prymitywów

const int size = 90000;

// współrzędne prymitywów

GLfloat vertex_xyz[ 3 * size ];

// kolory prymitywów

GLfloat color_rgb[ 3 * size ];

// licznik ramek (FPS)

int frames = 0;

// licznik czasu

long start_time = 0;

// tablica znaków ze wartością FPS

char time_string[ 100 ] = "FPS:";

// funkcja rysująca napis w wybranym miejscu

void DrawString( GLfloat x, GLfloat y, char * string )
{
    // położenie napisu
    glRasterPos2f( x, y );
   
    // wyświetlenie napisu
    int len = strlen( string );
    for( int i = 0; i < len; i++ )
         glutBitmapCharacter( GLUT_BITMAP_9_BY_15, string[ i ] );
   
}

// funkcja generująca scenę 3D

void DisplayScene()
{
    // licznik czasu
    if( !frames++ )
         start_time = clock();
   
    // kolor tła - zawartość bufora koloru
    glClearColor( 1.0, 1.0, 1.0, 1.0 );
   
    // czyszczenie bufora koloru i bufora głębokości
    glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT );
   
    // wybór macierzy modelowania
    glMatrixMode( GL_MODELVIEW );
   
    // macierz modelowania = macierz jednostkowa
    glLoadIdentity();
   
    // przesunięcie układu współrzędnych obiektu do środka bryły odcinania
    glTranslatef( 0, 0, -( near + far ) / 2 );
   
    // włączenie testu bufora głębokości
    glEnable( GL_DEPTH_TEST );
   
    // wykonanie wybranego testu
    switch( test )
    {
        // prymitywy w VBO
    case POINTS_VBO:
    case LINES_VBO:
    case TRIANGLES_VBO:
       
        // dowiązanie pierwszego obiektu buforowego
        glBindBuffer( GL_ARRAY_BUFFER, vbo_id[ 0 ] );
       
        // zdefiniowanie danych tablicy wierzchołków
        glVertexPointer( 3, GL_FLOAT, 0, 0 );
       
        // dowiązanie drugiego obiektu buforowego
        glBindBuffer( GL_ARRAY_BUFFER, vbo_id[ 1 ] );
       
        // zdefiniowanie danych tablicy wierzchołków
        glColorPointer( 3, GL_FLOAT, 0, 0 );
       
        // włączenie tablic wierzchołków
        glEnableClientState( GL_VERTEX_ARRAY );
        glEnableClientState( GL_COLOR_ARRAY );
       
        // narysowanie danych zawartych w tablicach
        if( test == POINTS_VBO )
             glDrawArrays( GL_POINTS, 0, size );
        else
        if( test == LINES_VBO )
             glDrawArrays( GL_LINES, 0, size );
        else
             glDrawArrays( GL_TRIANGLES, 0, size );
       
        // wyłączenie tablic wierzchołków
        glDisableClientState( GL_COLOR_ARRAY );
        glDisableClientState( GL_VERTEX_ARRAY );
       
        // wyłączenie dowiązania obiektów buforowych
        glBindBuffer( GL_ARRAY_BUFFER, 0 );
        break;
       
        // prymitywy z tablicami wierzchołków
    case POINTS_VA:
    case LINES_VA:
    case TRIANGLES_VA:
       
        // zdefiniowanie danych tablic wierzchołków
        glVertexPointer( 3, GL_FLOAT, 0, vertex_xyz );
        glColorPointer( 3, GL_FLOAT, 0, color_rgb );
       
        // włączenie tablic wierzchołków
        glEnableClientState( GL_VERTEX_ARRAY );
        glEnableClientState( GL_COLOR_ARRAY );
       
        // narysowanie danych zawartych w tablicach
        if( test == POINTS_VA )
             glDrawArrays( GL_POINTS, 0, size );
        else
        if( test == LINES_VA )
             glDrawArrays( GL_LINES, 0, size );
        else
             glDrawArrays( GL_TRIANGLES, 0, size );
       
        // wyłączenie tablic wierzchołków
        glDisableClientState( GL_VERTEX_ARRAY );
        glDisableClientState( GL_COLOR_ARRAY );
        break;
    };
   
    // trzeba odpowiednio przekształcić układ współrzędnych
    // aby napis znajdował się na samej "górze" bryły obcinania
    glLoadIdentity();
    glTranslatef( 0, 0, - near );
   
    // komunikat o ilości ramek rysowanych na sekundę (FPS)
    glColor3fv( Black );
    if( frames == 100 )
    {
        frames = 0;
        sprintf( time_string, "FPS: %i",( int )( 100 * CLOCKS_PER_SEC /( float )( clock() - start_time ) ) );
    }
   
    // narysowanie napisu
    DrawString( left, bottom, time_string );
   
    // skierowanie poleceń do wykonania
    glFlush();
   
    // zamiana buforów koloru
    glutSwapBuffers();
}

// zmiana wielkości okna

void Reshape( int width, int height )
{
    // obszar renderingu - całe okno
    glViewport( 0, 0, width, height );
   
    // wybór macierzy rzutowania
    glMatrixMode( GL_PROJECTION );
   
    // macierz rzutowania = macierz jednostkowa
    glLoadIdentity();
   
    // parametry bryły obcinania
    if( aspect == ASPECT_1_1 )
    {
        // wysokość okna większa od wysokości okna
        if( width < height && width > 0 )
             glFrustum( left, right, bottom * height / width, top * height / width, near, far );
        else
       
        // szerokość okna większa lub równa wysokości okna
        if( width >= height && height > 0 )
             glFrustum( left * width / height, right * width / height, bottom, top, near, far );
       
    }
    else
         glFrustum( left, right, bottom, top, near, far );
   
    // generowanie sceny 3D
    DisplayScene();
}

// obsługa menu podręcznego

void Menu( int value )
{
    switch( value )
    {
        // wybór rodzaju testu
    case POINTS_VBO:
    case POINTS_VA:
    case LINES_VBO:
    case LINES_VA:
    case TRIANGLES_VBO:
    case TRIANGLES_VA:
        test = value;
        DisplayScene();
        break;
       
        // obszar renderingu - całe okno
    case FULL_WINDOW:
        aspect = FULL_WINDOW;
        Reshape( glutGet( GLUT_WINDOW_WIDTH ), glutGet( GLUT_WINDOW_HEIGHT ) );
        break;
       
        // obszar renderingu - aspekt 1:1
    case ASPECT_1_1:
        aspect = ASPECT_1_1;
        Reshape( glutGet( GLUT_WINDOW_WIDTH ), glutGet( GLUT_WINDOW_HEIGHT ) );
        break;
       
        // wyjście
    case EXIT:
        exit( 0 );
    }
}

// utworzenie obiektów buforowych wierzchołków

void GenerateVBO()
{
    // określenie zarodka dla  ciągu liczb pseudolosowych
    srand( time( NULL ) );
   
    // generowanie położenia punktów (wierzchołków) figur oraz ich kolorów
    for( int i = 0; i < size; i++ )
    {
        vertex_xyz[ 3 * i + 0 ] =( rand() /( float ) RAND_MAX ) * 4 - 2;
        vertex_xyz[ 3 * i + 1 ] =( rand() /( float ) RAND_MAX ) * 4 - 2;
        vertex_xyz[ 3 * i + 2 ] =( rand() /( float ) RAND_MAX ) * 4 - 2;
        color_rgb[ 3 * i + 0 ] =( rand() /( float ) RAND_MAX );
        color_rgb[ 3 * i + 1 ] =( rand() /( float ) RAND_MAX );
        color_rgb[ 3 * i + 2 ] =( rand() /( float ) RAND_MAX );
    }
   
    // wygenerowanie dwóch identyfikatorów obiektów buforowych
    glGenBuffers( 2, vbo_id );
   
    // dowiązanie pierwszego obiektu buforowego
    glBindBuffer( GL_ARRAY_BUFFER, vbo_id[ 0 ] );
   
    // załadowanie danych pierwszego obiektu buforowego
    glBufferData( GL_ARRAY_BUFFER, sizeof( GLfloat ) * size * 3, vertex_xyz, GL_STATIC_DRAW );
   
    // dowiązanie drugiego obiektu buforowego
    glBindBuffer( GL_ARRAY_BUFFER, vbo_id[ 1 ] );
   
    // utworzenie danych drugiego obiektu buforowego
    glBufferData( GL_ARRAY_BUFFER, sizeof( GLfloat ) * size * 3, NULL, GL_STATIC_DRAW );
   
    // pobranie wskaźnika na dane drugiego obiektu buforowego
    GLvoid * buf = glMapBuffer( GL_ARRAY_BUFFER, GL_WRITE_ONLY );
   
    // skopiowanie danych do drugiego obiektu buforowego
    memcpy( buf, color_rgb, sizeof( GLfloat ) * size * 3 );
   
    // zwolnienie wskaźnika na dane drugiego obiektu buforowego
    if( glUnmapBuffer( GL_ARRAY_BUFFER ) == GL_FALSE )
    {
        printf( "Niepoprawne odwzorowanie obiektu buforowego\n" );
        exit( 0 );
    }
   
    // wyłączenie dowiązania obiektów buforowych
    glBindBuffer( GL_ARRAY_BUFFER, 0 );
}

// sprawdzenie i przygotowanie obsługi wybranych rozszerzeń

void ExtensionSetup()
{
    // pobranie numeru wersji biblioteki OpenGL
    const char * version =( char * ) glGetString( GL_VERSION );
   
    // odczyt wersji OpenGL
    int major = 0, minor = 0;
    if( sscanf( version, "%d.%d", & major, & minor ) != 2 )
    {
        #ifdef WIN32
        printf( "Błędny format wersji OpenGL\n" );
        #else
       
        printf( "Bledny format wersji OpenGL\n" );
        #endif
       
        exit( 0 );
    }
   
    // sprawdzenie czy jest co najmniej wersja 1.5
    if( major > 1 || minor >= 5 )
    {
        // pobranie wskaźnika wybranych funkcji OpenGL 1.5
        glGenBuffers =( PFNGLGENBUFFERSPROC ) wglGetProcAddress( "glGenBuffers" );
        glBindBuffer =( PFNGLBINDBUFFERPROC ) wglGetProcAddress( "glBindBuffer" );
        glBufferData =( PFNGLBUFFERDATAPROC ) wglGetProcAddress( "glBufferData" );
        glMapBuffer =( PFNGLMAPBUFFERPROC ) wglGetProcAddress( "glMapBuffer" );
        glUnmapBuffer =( PFNGLUNMAPBUFFERPROC ) wglGetProcAddress( "glUnmapBuffer" );
    }
    else
    // sprawdzenie czy jest obsługiwane rozszerzenie ARB_vertex_buffer_object
    if( glutExtensionSupported( "GL_ARB_vertex_buffer_object" ) )
    {
        // pobranie wskaźnika wybranych funkcji rozszerzenia ARB_vertex_buffer_object
        glGenBuffers =( PFNGLGENBUFFERSPROC ) wglGetProcAddress( "glGenBuffersARB" );
        glBindBuffer =( PFNGLBINDBUFFERPROC ) wglGetProcAddress( "glBindBufferARB" );
        glBufferData =( PFNGLBUFFERDATAPROC ) wglGetProcAddress( "glBufferDataARB" );
        glMapBuffer =( PFNGLMAPBUFFERPROC ) wglGetProcAddress( "glMapBufferARB" );
        glUnmapBuffer =( PFNGLUNMAPBUFFERPROC ) wglGetProcAddress( "glUnmapBufferARB" );
    }
    else
    {
        printf( "Brak rozszerzenia ARB_vertex_buffer_object!\n" );
        exit( 0 );
    }
}

int main( int argc, char * argv[] )
{
    // inicjalizacja biblioteki GLUT
    glutInit( & argc, argv );
   
    // inicjalizacja bufora ramki
    glutInitDisplayMode( GLUT_DOUBLE | GLUT_RGB | GLUT_DEPTH );
   
    // rozmiary głównego okna programu
    glutInitWindowSize( 500, 500 );
   
    // utworzenie głównego okna programu
    glutCreateWindow( "VBO" );
   
    // dołączenie funkcji generującej scenę 3D
    glutDisplayFunc( DisplayScene );
   
    // dołączenie funkcji wywoływanej przy zmianie rozmiaru okna
    glutReshapeFunc( Reshape );
   
    // utworzenie podmenu - Test
    int MenuTest = glutCreateMenu( Menu );
    #ifdef WIN32
   
    glutAddMenuEntry( "Punkty z VBO", POINTS_VBO );
    glutAddMenuEntry( "Punkty z tablicami wierzchołków", POINTS_VA );
    glutAddMenuEntry( "Odcinki z VBO", LINES_VBO );
    glutAddMenuEntry( "Odcinki z tablicami wierzchołków", LINES_VA );
    glutAddMenuEntry( "Trójkąty z VBO", TRIANGLES_VBO );
    glutAddMenuEntry( "Trójkąty z tablicami wierzchołków", TRIANGLES_VA );
    #else
   
    glutAddMenuEntry( "Punkty z VBO", POINTS_VBO );
    glutAddMenuEntry( "Punkty z tablicami wierzcholkow", POINTS_VA );
    glutAddMenuEntry( "Odcinki z VBO", LINES_VBO );
    glutAddMenuEntry( "Odcinki z tablicami wierzcholkow", LINES_VA );
    glutAddMenuEntry( "Trojkaty z VBO", TRIANGLES_VBO );
    glutAddMenuEntry( "Trojkaty z tablicami wierzcholkow", TRIANGLES_VA );
    #endif
   
    // utworzenie podmenu - Aspekt obrazu
    int MenuAspect = glutCreateMenu( Menu );
    #ifdef WIN32
   
    glutAddMenuEntry( "Aspekt obrazu - całe okno", FULL_WINDOW );
    #else
   
    glutAddMenuEntry( "Aspekt obrazu - cale okno", FULL_WINDOW );
    #endif
   
    glutAddMenuEntry( "Aspekt obrazu 1:1", ASPECT_1_1 );
   
    // menu główne
    glutCreateMenu( Menu );
    glutAddSubMenu( "Test", MenuTest );
    glutAddSubMenu( "Aspekt obrazu", MenuAspect );
    #ifdef WIN32
   
    glutAddMenuEntry( "Wyjście", EXIT );
    #else
   
    glutAddMenuEntry( "Wyjscie", EXIT );
    #endif
   
    // określenie przycisku myszki obsługującego menu podręczne
    glutAttachMenu( GLUT_RIGHT_BUTTON );
   
    // sprawdzenie i przygotowanie obsługi wybranych rozszerzeń
    ExtensionSetup();
   
    // utworzenie obiektów buforowych wierzchołków
    GenerateVBO();
   
    // funkcja bezczynności
    glutIdleFunc( DisplayScene );
   
    // wprowadzenie programu do obsługi pętli komunikatów
    glutMainLoop();
    return 0;
}
Drugi przykładowy program (plik pbo.cpp) testuje obiekty buforowe pikseli przy renderingu tekstur. Wyniki uzyskane na komputerze Autora nie wskazują na przewagę szybkości w renderingu tekstur przy pomocy obiektów buforowych, ale też nie odnotowano spadku tej szybkości. Prawdopodobnie użycie większej ilości tekstur wykazałoby przewagę obiektów buforowych, która ma bezpośredni związek z większą przepustowością pamięci karty graficznej w stosunku do przepustowości pamięci operacyjnej komputera.
Przykładowy efekt działania programu przedstawiono na rysunku 7.

Plik pbo.cpp

Rysunek 7. Program PBO - test wyświetlania tekstury „Lena”
Rysunek 7. Program PBO - test wyświetlania tekstury „Lena”
C/C++
/*
(c) Janusz Ganczarski
http://www.januszg.hg.pl
JanuszG@enter.net.pl
*/

#include <GL/glut.h>
#include <GL/glext.h>
#ifndef WIN32
#define GLX_GLXEXT_LEGACY
#include <GL/glx.h>
#define wglGetProcAddress glXGetProcAddressARB
#endif
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <time.h>
#include "targa.h"
#include "colors.h"

// wskaźnik na funkcję glGenBuffers

PFNGLGENBUFFERSPROC glGenBuffers = NULL;

// wskaźnik na funkcję glBindBuffer

PFNGLBINDBUFFERPROC glBindBuffer = NULL;

// wskaźnik na funkcję glBufferData

PFNGLBUFFERDATAPROC glBufferData = NULL;

// wskaźnik na funkcję glGetBufferParameteriv

PFNGLGETBUFFERPARAMETERIVPROC glGetBufferParameteriv = NULL;

// stałe do obsługi menu podręcznego

enum
{
    TEST_PBO, // test z PBO
    TEST_WPBO, // test bez PBO
    EXIT // wyjście
};

// rodzaj testu - domyślnie punkty bez PBO

int test = TEST_WPBO;

// identyfikator obiektu buforowego pikseli

GLuint pbo_id;

// identyfikator obiektu tekstury

GLuint texture_id;

// dane opisujące obraz odczytany z pliku TARGA

GLsizei width; // szerokość obrazu
GLsizei height; // wysokość obrazu
GLenum format; // format danych obrazu
GLenum type; // format danych pikseli obrazu
GLvoid * pixels; // wskaźnik na tablicę z danymi obrazu

// licznik ramek (FPS)

int frames = 0;

// licznik czasu

long start_time = 0;

// tablica znaków ze wartością FPS

char time_string[ 100 ] = "FPS:";

// funkcja rysująca napis w wybranym miejscu

void DrawString( GLfloat x, GLfloat y, char * string )
{
    // położenie napisu
    glRasterPos2f( x, y );
   
    // wyświetlenie napisu
    int len = strlen( string );
    for( int i = 0; i < len; i++ )
         glutBitmapCharacter( GLUT_BITMAP_9_BY_15, string[ i ] );
   
}

// funkcja generująca scenę 3D

void DisplayScene()
{
    // licznik czasu
    if( !frames++ )
         start_time = clock();
   
    // współrzędne położenia prostokąta z teksturą
    float x = glutGet( GLUT_WINDOW_WIDTH ) * rand() /( float ) RAND_MAX;
    float y = 20 + glutGet( GLUT_WINDOW_HEIGHT ) * rand() /( float ) RAND_MAX;
   
    // włączenie teksturowania 2D
    glEnable( GL_TEXTURE_2D );
   
    // filtr pomniejszający
    glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST );
   
    // filtr powiększający
    glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST );
   
    // wyświetlenie tekstury z danych z pamięci operacyjnej
    if( test == TEST_WPBO )
    {
        // tekstura 2D
        glTexImage2D( GL_TEXTURE_2D, 0, GL_RGB, width, height, 0, format, type, pixels );
       
        // czworokąt
        glColor3fv( White );
        glBegin( GL_QUADS );
        glTexCoord2f( 0.0, 0.0 );
        glVertex2f( x, y );
        glTexCoord2f( 1.0, 0.0 );
        glVertex2f( x + 64, y );
        glTexCoord2f( 1.0, 1.0 );
        glVertex2f( x + 64, y + 64 );
        glTexCoord2f( 0.0, 1.0 );
        glVertex2f( x, y + 64 );
        glEnd();
    }
   
    // wyświetlenie tekstury z danych obiektu buforowego pikseli
    if( test == TEST_PBO )
    {
        // dowiązanie obiektu buforowego pikseli
        glBindBuffer( GL_PIXEL_UNPACK_BUFFER, pbo_id );
       
        // tekstura 2D
        glTexImage2D( GL_TEXTURE_2D, 0, GL_RGB, width, height, 0, format, type, NULL );
       
        // czworokąt
        glColor3fv( White );
        glBegin( GL_QUADS );
        glTexCoord2f( 0.0, 0.0 );
        glVertex2f( x, y );
        glTexCoord2f( 1.0, 0.0 );
        glVertex2f( x + 64, y );
        glTexCoord2f( 1.0, 1.0 );
        glVertex2f( x + 64, y + 64 );
        glTexCoord2f( 0.0, 1.0 );
        glVertex2f( x, y + 64 );
        glEnd();
       
        // wyłączenie obiektu buforowania pikseli poprzez
        // dowiązanie obiektu obiektu o identyfikatorze 0
        glBindBuffer( GL_PIXEL_UNPACK_BUFFER, 0 );
    }
   
    // wyłączenie teksturowania 2D
    glDisable( GL_TEXTURE_2D );
   
    // komunikat o ilości ramek rysowanych na sekundę (FPS)
    if( frames == 100 )
    {
        frames = 0;
        sprintf( time_string, "FPS: %i",( int )( 100 * CLOCKS_PER_SEC /( float )( clock() - start_time ) ) );
    }
   
    // zamazanie poprzedniego napisu
    glColor3fv( White );
    glRectf( 0.0, 0.0, 200.0, 20.0 );
   
    // narysowanie napisu
    glColor3fv( Black );
    DrawString( 1, 2, time_string );
   
    // skierowanie poleceń do wykonania
    glFlush();
   
    // zamiana buforów koloru
    glutSwapBuffers();
}

// zmiana wielkości okna

void Reshape( int width, int height )
{
    // obszar renderingu - całe okno
    glViewport( 0, 0, width, height );
   
    // wybór macierzy rzutowania
    glMatrixMode( GL_PROJECTION );
   
    // macierz rzutowania = macierz jednostkowa
    glLoadIdentity();
   
    // parametry bryły obcinania
    gluOrtho2D( 0.0, width, 0.0, height );
   
    // kolor tła - zawartość bufora koloru
    glClearColor( 1.0, 1.0, 1.0, 1.0 );
   
    // czyszczenie bufora koloru
    glClear( GL_COLOR_BUFFER_BIT );
   
    // generowanie sceny 3D
    DisplayScene();
}

// obsługa menu podręcznego

void Menu( int value )
{
    switch( value )
    {
        // wybór rodzaju testu
    case TEST_PBO:
    case TEST_WPBO:
        test = value;
        glClear( GL_COLOR_BUFFER_BIT );
        glutPostRedisplay();
        break;
       
        // wyjście
    case EXIT:
        exit( 0 );
    }
}

// utworzenie obiektów buforowych pikseli

void GeneratePBO()
{
    // wygenerowanie identyfikatora obiektu buforowego
    glGenBuffers( 1, & pbo_id );
   
    // wyrównywanie wiersza mapy pikselowej do pojedyńczego bajta
    glPixelStorei( GL_UNPACK_ALIGNMENT, 1 );
   
    // dowiązanie obiektu buforowego
    glBindBuffer( GL_PIXEL_UNPACK_BUFFER, pbo_id );
   
    // obliczenie rozmiaru bufora na dane
    GLint size = width * height;
    if( format == GL_BGRA )
         size *= 4;
    else
    if( format == GL_BGR )
         size *= 3;
   
    // załadowanie danych obiektu buforowego
    glBufferData( GL_PIXEL_UNPACK_BUFFER, size, pixels, GL_STREAM_DRAW );
   
    // pobranie rozmiaru danych obiektu buforowego
    GLint buf_size;
    glGetBufferParameteriv( GL_PIXEL_UNPACK_BUFFER, GL_BUFFER_SIZE, & buf_size );
   
    // sprawdzenie poprawności rozmiaru danych obiektu buforowego
    if( buf_size != size )
    {
        printf( "Niepoprawny zapis danych do obiektu buforowego\n" );
        exit( 0 );
    }
   
    // wyłączenie dowiązania obiektu buforowego
    glBindBuffer( GL_PIXEL_UNPACK_BUFFER, 0 );
   
    // określenie zarodka dla  ciągu liczb pseudolosowych
    srand( time( NULL ) );
}

// sprawdzenie i przygotowanie obsługi wybranych rozszerzeń

void ExtensionSetup()
{
    // pobranie numeru wersji biblioteki OpenGL
    const char * version =( char * ) glGetString( GL_VERSION );
   
    // odczyt wersji OpenGL
    int major = 0, minor = 0;
    if( sscanf( version, "%d.%d", & major, & minor ) != 2 )
    {
        #ifdef WIN32
        printf( "Błędny format wersji OpenGL\n" );
        #else
       
        printf( "Bledny format wersji OpenGL\n" );
        #endif
       
        exit( 0 );
    }
   
    // sprawdzenie czy jest co najmniej wersja 2.1
    if( 10 * major + minor >= 21 )
    {
        // pobranie wskaźnika wybranych funkcji OpenGL 2.1 (a właściwie 1.5)
        glGenBuffers =( PFNGLGENBUFFERSPROC ) wglGetProcAddress( "glGenBuffers" );
        glBindBuffer =( PFNGLBINDBUFFERPROC ) wglGetProcAddress( "glBindBuffer" );
        glBufferData =( PFNGLBUFFERDATAPROC ) wglGetProcAddress( "glBufferData" );
        glGetBufferParameteriv =( PFNGLGETBUFFERPARAMETERIVPROC )
        wglGetProcAddress( "glGetBufferParameteriv" );
    }
    else
    // sprawdzenie czy jest obsługiwane rozszerzenie ARB_pixel_buffer_object
    if( glutExtensionSupported( "GL_ARB_pixel_buffer_object" ) )
    {
        // pobranie wskaźnika wybranych funkcji rozszerzenia ARB_pixel_buffer_object
        // a właciwie ARB_vertex_buffer_object
        glGenBuffers =( PFNGLGENBUFFERSPROC ) wglGetProcAddress( "glGenBuffersARB" );
        glBindBuffer =( PFNGLBINDBUFFERPROC ) wglGetProcAddress( "glBindBufferARB" );
        glBufferData =( PFNGLBUFFERDATAPROC ) wglGetProcAddress( "glBufferDataARB" );
        glGetBufferParameteriv =( PFNGLGETBUFFERPARAMETERIVPROC )
        wglGetProcAddress( "glGetBufferParameterivARB" );
    }
    else
    {
        printf( "Brak rozszerzenia ARB_vertex_buffer_object!\n" );
        exit( 0 );
    }
}

// wczytanie pliku graficznego w formacie TARGA

void LoadTARGA( int argc, char * argv[] )
{
    // sprawdzenie czy jest parametr
    if( argc < 2 )
    {
        printf( "Brak nazwy pliku TARGA\n" );
        exit( 1 );
    }
   
    // odczyt pliku TARGA i ewentualny komunikat o błędzie
    if( !load_targa( argv[ 1 ], width, height, format, type, pixels ) )
    {
        #ifdef WIN32
        printf( "Błąd odczytu lub błędny format pliku: %s\n", argv[ 1 ] );
        #else
       
        printf( "Blad odczytu lub bledny format pliku: %s\n", argv[ 1 ] );
        #endif
       
        exit( 1 );
    }
}

int main( int argc, char * argv[] )
{
    // odczyt pliku graficznego TARGA
    LoadTARGA( argc, argv );
   
    // inicjalizacja biblioteki GLUT
    glutInit( & argc, argv );
   
    // inicjalizacja bufora ramki
    glutInitDisplayMode( GLUT_DOUBLE | GLUT_RGB );
   
    // rozmiary głównego okna programu
    glutInitWindowSize( 500, 500 );
   
    // utworzenie głównego okna programu
    glutCreateWindow( "PBO" );
   
    // dołączenie funkcji generującej scenę 3D
    glutDisplayFunc( DisplayScene );
   
    // dołączenie funkcji wywoływanej przy zmianie rozmiaru okna
    glutReshapeFunc( Reshape );
   
    // menu główne
    glutCreateMenu( Menu );
    #ifdef WIN32
   
    glutAddMenuEntry( "Test z PBO", TEST_PBO );
    glutAddMenuEntry( "Test bez PBO", TEST_WPBO );
    glutAddMenuEntry( "Wyjście", EXIT );
    #else
   
    glutAddMenuEntry( "Wyjscie", EXIT );
    #endif
   
    // określenie przycisku myszki obsługującego menu podręczne
    glutAttachMenu( GLUT_RIGHT_BUTTON );
   
    // sprawdzenie i przygotowanie obsługi wybranych rozszerzeń
    ExtensionSetup();
   
    // utworzenie obiektów buforowych pikseli
    GeneratePBO();
   
    // funkcja bezczynności
    glutIdleFunc( DisplayScene );
   
    // wprowadzenie programu do obsługi pętli komunikatów
    glutMainLoop();
    return 0;
}
Poprzedni dokument Następny dokument
Test zasłaniania GLSL