본문 바로가기
전공/컴퓨터 그래픽스

버퍼 오브젝트 (Buffer Objects) (1)

by 임 낭 만 2023. 5. 29.

Buffer Objects

Buffer Objects

  • Until now, a large number of function calls were needed for drawing graphics primitives (such as polygons, lines, or points)
//...
glColor3f(1.0f, 1.0f, 0.0f);
glBegin(GL_POLYGON);
glTexCoord2f(0.25f, 0.50f);	glVertex3f(-1.0f, 1.0f, 1.0f);
glTexCoord2f(0.25f, 0.25f);	glVertex3f(-1.0f, -1.0f, 1.0f);
glTexCoord2f(0.50f, 0.25f);	glVertex3f(-1.0f, -1.0f, 1.0f);
glTexCoord2f(0.50f, 0.50f);	glVertex3f(1.0f, 1.0f, 1.0f);
glEnd();
//...
  • Each function call is transferring only as small amount of data to the OpenGL pipeline
  • This way of graphics leads to slow processing, because the graphics hardware operations can not be executed in parallel on a large number of data, instead the hardware is constantly waiting for data
  • Therefore, since OpenGL >= 3.1 the corresponding functions (glBegin, glVertex, etc.) are only supported in the "Compatibility Profile"
  • Instead, vertex data is now transferred in larger blocks, which are stored and managed on the graphics card using Buffer Objects
더보기

지금까지는 그래픽 프리미티브(다각형, 선 또는 점)를 그리기 위해 많은 수의 함수 호출이 필요했음.

각 함수 호출은 OpenGL 파이프라인으로 소량의 데이터만 전송함.

이러한 그래픽 방식으로는 처리 속도가 저하됨.이는 다수의 데이터에 대해 그래픽 하드웨어 처리를 병렬로 수행할 수 없고 하드웨어가 항상 데이터를 대기하고 있기 때문임.

따라서 OpenGL >= 3.1 이후 대응하는 함수(glBegin, glVertex 등)는 "호환성 프로파일"에서만 지원됨.

대신 정점 데이터는 더 큰 블록으로 전송되고 버퍼 객체를 사용하여 그래픽 카드에 저장 및 관리.


Vertex Buffer Objects (VBO)

Creating VBOs in OpenGL

  • Creating VBOs is usually done before the actual rendering, e.g., in the function init( )
  • The procedure is analogous to creating textures and involves three steps:
    • Step 1: The function glGenBuffers(1, &bufID) is generating a unique identifier
    • Step 2: With glBindBuffer(GL_ARRAY_BUFFER, bufID) a new VBO is created. Possible targets are GL_ARRAY_BUFFER or GL_ELEMENT_ARRAY_BUFFER
    • Step 3: The function glBufferData(GL_ARRAY_BUFFER, size, inData, GL_STATIC_DRAW) transfers the vertex data
더보기

VBO 생성은 일반적으로 init() 함수와 같이 실제 렌더링 전에 수행됨.

이 절차는 텍스처를 만드는 것과 유사하며 다음 세 단계를 포함함.

1단계 : 함수 glGenBuffers(1, &bufID)가 고유 식별자를 생성함.

2단계 : glBindBuffer(GL_ARRAY_BUFFER, bufID) 를 사용하여 새로운 VBO 가 생성됨. GL_ARRAY_BUFFER 또는 GL_ELEMENT_ARRAY_BUFFER

3단계 : 함수 glBufferData(GL_ARRAY_BUFFER, size, inData, GL_STATIC_DRAW)는 정점 데이터를 전송함.

Activating VBOs in OpenGL

  • In order to use a VBO for rendering, it must be activated. This also involves three steps:
    • Step 1: Binding the buffer with glBindBuffer(GL_ARRAY_BUFFER, bufID)
    • Step 2: Defining a stride and a start address for the memory access
    • Step 3: Activating the buffer for a certain purpose
      glEnableClientState(GL_VERTEX_ARRAY).
      Possible parameters are: GL_VERTEX_ARRAY, GL_COLOR_ARRAY, GL_SECONDARY_COLOR_ARRAY, GL_INDEX_ARRAY, GL_NORMAL_ARRAY, GL_TEXTURE_COORD_ARRAY
더보기

렌더링에 VBO를 사용하려면 VBO를 활성화해야 함

1단계 : glBindBuffer(GL_ARRAY_BUFFER, bufID)를 이용하여 버퍼 바인딩

2단계 : 메모리 액세스에 대한 시작 주소 정의

3단계 : 특정 목적을 위해 버퍼 활성화

Rendering using VBOs in OpenGL

  • After activating a VBO the rendering of all containing data is initiated with a single function call:
    Possible parameters are the same as those of glBegin(…), such as GL_POLYGON, GL_LINE_LOOP, GL_LINES, GL_POINTS, etc.
glDrawArrays(GL_POLYGONS, first, count);
  • If the VBO is no longer used, the memory can be released by glDeleteBuffers(1, &bufID)

Example: Rendering a Sphere with a Large Number of Points

// This code example is created for educational purpose
// by Thorsten Thormaehlen (contact: www.thormae.de).
// It is distributed without any warranty.
#include <GL/glew.h>
#include <GL/freeglut.h> // we use glut here as window manager
#define _USE_MATH_DEFINES
#include <math.h>
#include <iostream>
#include <string>
#include <sstream>
#include <vector>
using namespace std;
class Renderer {
public:
    float t;
    int mode;
    unsigned ayimutSegs, polarSegs;
private:
    GLuint bufID;
    int bufSize;
public:
    // constructor
    Renderer() : t(0.0), mode(0), bufID(0), bufSize(0), ayimutSegs(1000), polarSegs(1000) {}
    //destructor
    ~Renderer() {
        if (bufID != 0) glDeleteBuffers(1, &bufID);
    }
public:
    void init() {
        glEnable(GL_DEPTH_TEST);

        // generating VBO input data
        std::vector<float> dataIn;
        float ayimutStep = 2.0f * float(M_PI) / float(ayimutSegs);
        float polarStep = float(M_PI) / float(polarSegs);
        float r = 1.0;
        bufSize = 0;
        for (unsigned m = 0; m < ayimutSegs; m++) {
            for (unsigned n = 0; n < polarSegs; n++) {
                float phi = ayimutStep * m;
                float theta = polarStep * n;
                // compute xyz from spherical coordinates
                float x = r * sin(theta) * cos(phi);
                float y = r * sin(theta) * sin(phi);
                float z = r * cos(theta);
                dataIn.push_back(x);
                dataIn.push_back(y);
                dataIn.push_back(z);
                bufSize++;
            }
        }
        // generating VBO
        glGenBuffers(1, &bufID);
        glBindBuffer(GL_ARRAY_BUFFER, bufID);
        glBufferData(GL_ARRAY_BUFFER, dataIn.size() * sizeof(float), &dataIn[0], GL_STATIC_DRAW);
    }
    void resize(int w, int h) {
        glViewport(0, 0, w, h);
        glMatrixMode(GL_PROJECTION);
        glLoadIdentity();
        gluPerspective(30.0, (float)w / (float)h, 0.1, 50.0);
    }
    void display() {
        glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
        glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
        glMatrixMode(GL_MODELVIEW);
        glLoadIdentity();
        // set camera
        gluLookAt(3.5, -1.0, 3.5, 0.0, 0.0, 0.0, 0.0, 0.0, 1.0);
        // draw scene
        glRotatef(t, 0.0f, 0.0f, 1.0f);

        // activating VBO
        glBindBuffer(GL_ARRAY_BUFFER, bufID);
        int stride = 0;
        glVertexPointer(3, GL_FLOAT, stride, NULL);
        glEnableClientState(GL_VERTEX_ARRAY);
        if (mode == 0) {
            glColor3f(1.0f, 1.0f, 1.0f);
        }
        else {
            glColorPointer(3, GL_FLOAT, stride, NULL);
            glEnableClientState(GL_COLOR_ARRAY);
        }
        
        //render VBO
        glDrawArrays(GL_POINTS, 0, bufSize);
        glDisableClientState(GL_VERTEX_ARRAY);
        glDisableClientState(GL_COLOR_ARRAY);
    }
};
//this is a static pointer to a Renderer used in the glut callback functions
static Renderer* renderer;
//glut static callbacks start
static void glutResize(int w, int h)
{
    renderer->resize(w, h);
}
static void glutDisplay()
{
    renderer->display();
    glutSwapBuffers();
    glutReportErrors();
}
static void timer(int v)
{
    float offset = 0.25f;
    renderer->t += offset;
    glutDisplay();
    glutTimerFunc(unsigned(20), timer, ++v);
}
static void glutKeyboard(unsigned char key, int x, int y) {
    bool redraw = false;
    std::string modeStr;
    switch (key) {
    case '1':
        if (renderer->mode == 1) renderer->mode = 0;
        else renderer->mode = 1;
        redraw = true;
        break;
    case '2':
        if (renderer->ayimutSegs < 1000) {
            renderer->ayimutSegs *= 10;
            renderer->polarSegs *= 10;
        }
        else {
            renderer->ayimutSegs = 10;
            renderer->polarSegs = 10;
        }
        renderer->init();
        redraw = true;
        break;
    }
    if (redraw) {
        glutDisplay();
    }
}
int main(int argc, char** argv)
{
    glutInit(&argc, argv);
    glutInitDisplayMode(GLUT_DEPTH | GLUT_DOUBLE | GLUT_RGBA);
    glutInitWindowPosition(100, 100);
    glutInitWindowSize(320, 320);
    glutCreateWindow("Press 1 to change color, 2 to increase vertex count");
    GLenum err = glewInit();
    if (GLEW_OK != err) {
        fprintf(stderr, "Glew error: %s\n", glewGetErrorString(err));
    }
    glutDisplayFunc(glutDisplay);
    //glutIdleFunc(glutDisplay);
    glutReshapeFunc(glutResize);
    glutKeyboardFunc(glutKeyboard);

    renderer = new Renderer;
    renderer->init();
    glutTimerFunc(unsigned(20), timer, 0);
    glutMainLoop();
}


Using Vertex Indices

Using Vertex Indices

  • When drawing polygonal surfaces, it often happens that vertices are used several times for adjacent polygons
  • If the vertices shall be stored only once, polygons can be defined using the indices of the vertices

  • To use vertex indices with VBOs, usually two VBOs are defined: one for vertex data and another for index data
  • A VBO with index data is generated and activated with glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, bufID)
  • The rendering is then carried out with the function
glDrawElements(GL_POLYGON, count, GL_UNSIGNED_INT);

Example: Rendering a Spiky Sphere with Vertex Indices

// This code example is created for educational purpose
// by Thorsten Thormaehlen (contact: www.thormae.de).
// It is distributed without any warranty.
#include <GL/glew.h>
#include <GL/freeglut.h> // we use glut here as window manager
#define _USE_MATH_DEFINES
#include <math.h>
#include <iostream>
#include <string>
#include <sstream>
#include <vector>
using namespace std;
class Renderer {
public:
    float t;
    int mode;
    unsigned ayimutSegs;
    unsigned polarSegs;
private:
    GLuint vertBufID, indicesBufID;
    int vertSize;
public:
    // constructor
    Renderer() : t(0.0), mode(1), ayimutSegs(10), polarSegs(10),
        vertBufID(0), indicesBufID(0), vertSize(0) {}
    //destructor
    ~Renderer() {
        if (vertBufID != 0) glDeleteBuffers(1, &vertBufID);
        if (indicesBufID != 0) glDeleteBuffers(1, &indicesBufID);
    }
public:
    void init() {
        glEnable(GL_DEPTH_TEST);
        recreateVBOs();
    }
    void recreateVBOs() {
        if (vertBufID != 0) glDeleteBuffers(1, &vertBufID);
        if (indicesBufID != 0) glDeleteBuffers(1, &indicesBufID);

        // generating VBO input data
        std::vector<float> vertexData;
        std::vector<int> indicesData;
        int totalSegs = ayimutSegs * polarSegs;
        float ayimutStep = 2.0f * M_PI / float(ayimutSegs);
        float polarStep = M_PI / float(polarSegs);
        float r = 1.0;
        vertSize = 0;
        for (unsigned m = 0; m < ayimutSegs; m++) {
            for (unsigned n = 0; n < polarSegs; n++) {
                float phi = ayimutStep * m;
                float theta = polarStep * n;
                // random radius
                int rval = rand();
                r = 1.0f - 0.3f * (fabs(float(rval)) / float(RAND_MAX));

                // create xyz from spherical coordinates
                float x = r * sin(theta) * cos(phi);
                float y = r * sin(theta) * sin(phi);
                float z = r * cos(theta);
                vertexData.push_back(x);
                vertexData.push_back(y);
                vertexData.push_back(z);

                // create vertex indices
                indicesData.push_back(vertSize % totalSegs);
                indicesData.push_back((vertSize + ayimutSegs) % totalSegs);
                indicesData.push_back((vertSize + ayimutSegs + 1) % totalSegs);
                indicesData.push_back((vertSize + 1) % totalSegs);
                vertSize++;
            }
        }
        // generating vertex VBO
        glGenBuffers(1, &vertBufID);
        glBindBuffer(GL_ARRAY_BUFFER, vertBufID);
        glBufferData(GL_ARRAY_BUFFER, vertexData.size() * sizeof(float), &vertexData[0], GL_STATIC_DRAW);
        // generating index VBO
        glGenBuffers(1, &indicesBufID);
        glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, indicesBufID);
        glBufferData(GL_ELEMENT_ARRAY_BUFFER, indicesData.size() * sizeof(int), &indicesData[0], GL_STATIC_DRAW);
    }
    void resize(int w, int h) {
        glViewport(0, 0, w, h);
        glMatrixMode(GL_PROJECTION);
        glLoadIdentity();
        gluPerspective(30.0, (float)w / (float)h, 0.1, 50.0);
    }
    void display() {
        glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
        glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
        glMatrixMode(GL_MODELVIEW);
        glLoadIdentity();
        // set camera
        gluLookAt(3.5, -1.0, 3.5, 0.0, 0.0, 0.0, 0.0, 0.0, 1.0);
        // draw scene
        glRotatef(t, 0.0f, 0.0f, 1.0f);

        // activating VBO
        glBindBuffer(GL_ARRAY_BUFFER, vertBufID);
        int stride = 0;
        glVertexPointer(3, GL_FLOAT, stride, NULL);
        glEnableClientState(GL_VERTEX_ARRAY);
        if (mode == 0) {
            glColor3f(1.0f, 1.0f, 1.0f);
        }
        else {
            glColorPointer(3, GL_FLOAT, stride, NULL);
            glEnableClientState(GL_COLOR_ARRAY);
        }
        glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, indicesBufID);
        glDrawElements(GL_QUADS, vertSize * 4, GL_UNSIGNED_INT, NULL);
        glDisableClientState(GL_VERTEX_ARRAY);
        glDisableClientState(GL_COLOR_ARRAY);
    }
};
//this is a static pointer to a Renderer used in the glut callback functions
static Renderer* renderer;
//glut static callbacks start
static void glutResize(int w, int h)
{
    renderer->resize(w, h);
}
static void glutDisplay()
{
    renderer->display();
    glutSwapBuffers();
    glutReportErrors();
}
static void timer(int v)
{
    float offset = 0.25f;
    renderer->t += offset;
    glutDisplay();
    glutTimerFunc(unsigned(20), timer, ++v);
}
static void glutKeyboard(unsigned char key, int x, int y) {
    bool redraw = false;
    std::string modeStr;
    switch (key) {
    case '1':
        if (renderer->mode == 1) renderer->mode = 0;
        else renderer->mode = 1;
        redraw = true;
        break;
    case '2':
        if (renderer->ayimutSegs < 1000) {
            renderer->ayimutSegs *= 10;
            renderer->polarSegs *= 10;
        }
        else {
            renderer->ayimutSegs = 10;
            renderer->polarSegs = 10;
        }
        renderer->recreateVBOs();
        redraw = true;
        break;
    }
    if (redraw) {
        glutDisplay();
    }
}
int main(int argc, char** argv)
{
    glutInit(&argc, argv);
    glutInitDisplayMode(GLUT_DEPTH | GLUT_DOUBLE | GLUT_RGBA);
    glutInitWindowPosition(100, 100);
    glutInitWindowSize(320, 320);
    glutCreateWindow("Press 1 to change color, 2 to increase vertex count");
    GLenum err = glewInit();
    if (GLEW_OK != err) {
        fprintf(stderr, "Glew error: %s\n", glewGetErrorString(err));
    }
    glutDisplayFunc(glutDisplay);
    //glutIdleFunc(glutDisplay);
    glutReshapeFunc(glutResize);
    glutKeyboardFunc(glutKeyboard);

    renderer = new Renderer;
    renderer->init();

    glutTimerFunc(unsigned(20), timer, 0);
    glutMainLoop();
}


Dynamic Update of VBOs

Dynamic Uodate of VBOs

  • If VBOs should be used in a dynamic scene, it is best for fast display when the vertices are static in the VBOs and the motion is realized only by changing the modelview matrix
    • In this case, no new vertex data must be transferred to the GPU
    • This is possible, for example, for the animation of a car
  • However, it often happens that the vertex data must be changed
    • For example, to realize a muscle deformation during the animation of a runner
    • In this case, new data must be transferred to the GPU
  • Complete :
    • An option for a dynamic update is to transfer the complete data again with (아래 코드)
    • If this happens often, instead of GL_STATIC_DRAW the parameters GL_DYNAMIC_DRWA should be used
    • When the content of the VBOs changes with each new frame, the parameter GL_STREAM_DRAW can be employed
glBufferData(GL_ARRAY_BUFFER, size, data, GL_STATIC_DRAW);
  • Partial :
    • If only a few data within the VBO is affected, it is probably worth to update the new VBO only partially
    • This is done by:
glBufferSubData(GL_ARRAY_BUFFER, offset, size, data);

Example: Dynamic Update of the Spiky Sphere

// This code example is created for educational purpose
// by Thorsten Thormaehlen (contact: www.thormae.de).
// It is distributed without any warranty.
#include <GL/glew.h>
#include <GL/freeglut.h> // we use glut here as window manager
#define _USE_MATH_DEFINES
#include <math.h>
#include <iostream>
#include <string>
#include <sstream>
#include <vector>
using namespace std;
class Renderer {
public:
    float t;
    int mode;
    unsigned ayimutSegs;
    unsigned polarSegs;
    int frameCount;
    int currentTime;
    int previousTime;
private:
    GLuint vertBufID, indicesBufID;
    int vertSize;
public:
    // constructor
    Renderer() : t(0.0), mode(1), ayimutSegs(8), polarSegs(8),
        vertBufID(0), indicesBufID(0), vertSize(0), frameCount(0), previousTime(0) {}
    //destructor
    ~Renderer() {
        if (vertBufID != 0) glDeleteBuffers(1, &vertBufID);
        if (indicesBufID != 0) glDeleteBuffers(1, &indicesBufID);
    }
public:
    void init() {
        glEnable(GL_DEPTH_TEST);
        recreateVBOs();
    }
    void recreateVBOs() {
        if (vertBufID != 0) glDeleteBuffers(1, &vertBufID);
        if (indicesBufID != 0) glDeleteBuffers(1, &indicesBufID);

        // generating VBO input data
        std::vector<float> vertexData;
        std::vector<int> indicesData;
        int totalSegs = ayimutSegs * polarSegs;
        float ayimutStep = 2.0f * float(M_PI) / float(ayimutSegs);
        float polarStep = float(M_PI) / float(polarSegs);
        float r = 1.0;
        vertSize = 0;
        for (unsigned m = 0; m < ayimutSegs; m++) {
            for (unsigned n = 0; n < polarSegs; n++) {
                float phi = ayimutStep * m;
                float theta = polarStep * n;
                // random radius
                int rval = rand();
                r = 1.0f - 0.3f * (fabs(float(rval)) / float(RAND_MAX));

                // create xyz from spherical coordinates
                float x = r * sin(theta) * cos(phi);
                float y = r * sin(theta) * sin(phi);
                float z = r * cos(theta);

                vertexData.push_back(x);
                vertexData.push_back(y);
                vertexData.push_back(z);

                // create vertex indices
                indicesData.push_back(vertSize % totalSegs);
                indicesData.push_back((vertSize + ayimutSegs) % totalSegs);
                indicesData.push_back((vertSize + ayimutSegs + 1) % totalSegs);
                indicesData.push_back((vertSize + 1) % totalSegs);
                vertSize++;
            }
        }
        // generating vertex VBO
        glGenBuffers(1, &vertBufID);
        glBindBuffer(GL_ARRAY_BUFFER, vertBufID);
        glBufferData(GL_ARRAY_BUFFER, vertexData.size() * sizeof(float), &vertexData[0], GL_STATIC_DRAW);
        // generating index VBO
        glGenBuffers(1, &indicesBufID);
        glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, indicesBufID);
        glBufferData(GL_ELEMENT_ARRAY_BUFFER, indicesData.size() * sizeof(int), &indicesData[0], GL_STATIC_DRAW);
    }
    void updateRandomVBOVertex() {
        float ayimutStep = 2.0f * float(M_PI) / float(ayimutSegs);
        float polarStep = float(M_PI) / float(polarSegs);
        float r = 1.0;
        // randomly select point
        int mval = rand();
        int m = int((ayimutSegs - 1) * (fabs(float(mval)) / float(RAND_MAX)));
        int nval = rand();
        int n = int((polarSegs - 1) * (fabs(float(nval)) / float(RAND_MAX)));

        float phi = ayimutStep * m;
        float theta = polarStep * n;

        // random radius
        int rval = rand();
        r = 1.0f - 0.3f * (fabs(float(rval)) / float(RAND_MAX));
        // create xyz from spherical coordinates
        float x = r * sin(theta) * cos(phi);
        float y = r * sin(theta) * sin(phi);
        float z = r * cos(theta);

        int vertPos = (m * polarSegs + n) * 3;

        float newVertexData[3];
        newVertexData[0] = x;
        newVertexData[1] = y;
        newVertexData[2] = z;
        // update vertex in VBO
        glBindBuffer(GL_ARRAY_BUFFER, vertBufID);
        glBufferSubData(GL_ARRAY_BUFFER, vertPos * sizeof(float), 3 * sizeof(float), newVertexData);
    }
    void resize(int w, int h) {
        glViewport(0, 0, w, h);
        glMatrixMode(GL_PROJECTION);
        glLoadIdentity();
        gluPerspective(30.0, (float)w / (float)h, 0.1, 50.0);
    }
    void calculateFPS() {
        frameCount++;
        currentTime = glutGet(GLUT_ELAPSED_TIME);
        int timeInterval = currentTime - previousTime;
        if (timeInterval > 1000) {
            float fps = frameCount / (timeInterval / 1000.0f);
            cout << "rendering " << ayimutSegs * polarSegs << " quads at fps=" << fps << " (targetFps = 60)" << endl;
            previousTime = currentTime;
            frameCount = 0;
        }
    }
    void display() {
        // update up to 100 random vertices
        for (unsigned i = 0; i < 100 && i < (ayimutSegs / 2); i++) {
            updateRandomVBOVertex();
        }
        glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
        glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
        glMatrixMode(GL_MODELVIEW);
        glLoadIdentity();
        // set camera
        gluLookAt(3.5, -1.0, 3.5, 0.0, 0.0, 0.0, 0.0, 0.0, 1.0);
        // draw scene
        glRotatef(t, 0.0f, 0.0f, 1.0f);
        // activating VBO
        glBindBuffer(GL_ARRAY_BUFFER, vertBufID);
        int stride = 0;
        glVertexPointer(3, GL_FLOAT, stride, NULL);
        glEnableClientState(GL_VERTEX_ARRAY);
        if (mode == 0) {
            glColor3f(1.0f, 1.0f, 1.0f);
        }
        else {
            glColorPointer(3, GL_FLOAT, stride, NULL);
            glEnableClientState(GL_COLOR_ARRAY);
        }
        glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, indicesBufID);
        glDrawElements(GL_QUADS, vertSize * 4, GL_UNSIGNED_INT, NULL);

        glDisableClientState(GL_VERTEX_ARRAY);
        glDisableClientState(GL_COLOR_ARRAY);
        calculateFPS();
    }
};
//this is a static pointer to a Renderer used in the glut callback functions
static Renderer* renderer;
//glut static callbacks start
static void glutResize(int w, int h)
{
    renderer->resize(w, h);
}
static void glutDisplay()
{
    renderer->display();
    glutSwapBuffers();
    glutReportErrors();
}
static void timer(int v)
{
    float offset = 0.25f;
    renderer->t += offset;
    glutDisplay();
    glutTimerFunc(unsigned(16), timer, ++v);
}
static void glutKeyboard(unsigned char key, int x, int y) {
    bool redraw = false;
    std::string modeStr;
    switch (key) {
    case '1':
        if (renderer->mode == 1) renderer->mode = 0;
        else renderer->mode = 1;
        redraw = true;
        break;
    case '2':
        if (renderer->ayimutSegs < 512) {
            renderer->ayimutSegs *= 4;
            renderer->polarSegs *= 4;
        }
        else {
            renderer->ayimutSegs = 8;
            renderer->polarSegs = 8;
        }
        renderer->recreateVBOs();
        redraw = true;
        break;
    }
    if (redraw) {
        glutDisplay();
    }
}
int main(int argc, char** argv)
{
    glutInit(&argc, argv);
    glutInitDisplayMode(GLUT_DEPTH | GLUT_DOUBLE | GLUT_RGBA);
    glutInitWindowPosition(100, 100);
    glutInitWindowSize(640, 480);
    glutCreateWindow("Press 1 to change color, 2 to increase vertex count");
    GLenum err = glewInit();
    if (GLEW_OK != err) {
        fprintf(stderr, "Glew error: %s\n", glewGetErrorString(err));
    }
    glutDisplayFunc(glutDisplay);
    //glutIdleFunc(glutDisplay);
    glutReshapeFunc(glutResize);
    glutKeyboardFunc(glutKeyboard);

    renderer = new Renderer;
    renderer->init();

    glutTimerFunc(unsigned(20), timer, 0);
    glutMainLoop();
}

댓글