bufferobject.c

来自「OpeNGL超级宝典源代码. OpeNGL超级宝典源代码.」· C语言 代码 · 共 470 行

C
470
字号
// BufferObject.c
// OpenGL SuperBible, Chapter 16
// Demonstrates vertex buffer objects
// Program by Benjamin Lipchak

#include "../../Common/OpenGLSB.h"	// System and OpenGL Stuff
#include "../../Common/GLTools.h"	// OpenGL toolkit
#include <stdio.h>

PFNGLBINDBUFFERPROC    glBindBuffer;
PFNGLBUFFERDATAPROC    glBufferData;
PFNGLBUFFERSUBDATAPROC glBufferSubData;
PFNGLDELETEBUFFERSPROC glDeleteBuffers;
PFNGLGENBUFFERSPROC    glGenBuffers;
PFNGLMAPBUFFERPROC     glMapBuffer;
PFNGLUNMAPBUFFERPROC   glUnmapBuffer;

GLint windowWidth = 512;                // window size
GLint windowHeight = 512;

GLuint bufferID;

GLboolean useBufferObject = GL_TRUE;
GLboolean mapBufferObject = GL_TRUE;
GLboolean animating =       GL_TRUE;

GLint numSphereVertices = 30000;
GLfloat *sphereVertexArray = NULL;

GLfloat ambientLight[] = { 0.4f, 0.4f, 0.4f, 1.0f};
GLfloat diffuseLight[] = { 0.9f, 0.9f, 0.9f, 1.0f};
GLfloat cameraPos[]    = { 400.0f, 400.0f, 400.0f, 1.0f};

// Called to regenerate points on the sphere
void RegenerateSphere(void)
{
    GLint i;
    
    if (mapBufferObject && useBufferObject)
    {
        // Delete old vertex array memory
        if (sphereVertexArray)
            free(sphereVertexArray);

        glBindBuffer(GL_ARRAY_BUFFER, bufferID);

        // Avoid pipeline flush during glMapBuffer by
        // marking data store as empty
        glBufferData(GL_ARRAY_BUFFER, sizeof(GLfloat) * 
                     numSphereVertices * 3, NULL, 
                     animating ? GL_STREAM_DRAW : GL_STATIC_DRAW);

        sphereVertexArray = (GLfloat *)glMapBuffer(GL_ARRAY_BUFFER, 
                                                   GL_WRITE_ONLY);
        if (!sphereVertexArray)
        {
            fprintf(stderr, "Unable to map buffer object!\n");
            Sleep(2000);
            exit(0);
        }
    }
    else if (!sphereVertexArray)
    {
        // We need our old vertex array memory back
        sphereVertexArray = (GLfloat *)malloc(sizeof(GLfloat) * 
                                              numSphereVertices * 3);
        if (!sphereVertexArray)
        {
            fprintf(stderr, "Unable to allocate memory for vertex arrays!\n");
            Sleep(2000);
            exit(0);
        }
    }

    for (i = 0; i < numSphereVertices; i++)
    {
        GLfloat r1, r2, r3, scaleFactor;

        // pick a random vector
        r1 = (GLfloat)(rand() - (RAND_MAX/2));
        r2 = (GLfloat)(rand() - (RAND_MAX/2));
        r3 = (GLfloat)(rand() - (RAND_MAX/2));

        // determine normalizing scale factor
        scaleFactor = 1.0f / sqrt(r1*r1 + r2*r2 + r3*r3);

        sphereVertexArray[(i*3)+0] = r1 * scaleFactor;
        sphereVertexArray[(i*3)+1] = r2 * scaleFactor;
        sphereVertexArray[(i*3)+2] = r3 * scaleFactor;
    }

    if (mapBufferObject && useBufferObject)
    {
        if (!glUnmapBuffer(GL_ARRAY_BUFFER))
        {
            // Some window system event has trashed our data...
            // Try, try again!
            RegenerateSphere();
        }
        sphereVertexArray = NULL;
    }
}

// Switch between buffer objects and plain old vertex arrays
void SetRenderingMethod(void)
{
    if (useBufferObject)
    {
        glBindBuffer(GL_ARRAY_BUFFER, bufferID);
        // No stride, no offset
        glNormalPointer(GL_FLOAT, 0, 0);
        glVertexPointer(3, GL_FLOAT, 0, 0);

        if (!mapBufferObject)
        {
            if (animating)
            {
                glBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(GLfloat) * 
                                numSphereVertices * 3, sphereVertexArray);
            }
            else
            {
                // If not animating, this gets called once
                // to establish new static buffer object
                glBufferData(GL_ARRAY_BUFFER, sizeof(GLfloat) * 
                             numSphereVertices * 3, sphereVertexArray, 
                             GL_STATIC_DRAW);
            }
        }
    }
    else
    {
        glBindBuffer(GL_ARRAY_BUFFER, 0);
        glNormalPointer(GL_FLOAT, 0, sphereVertexArray);
        glVertexPointer(3, GL_FLOAT, 0, sphereVertexArray);
    }
}

// Called to draw scene objects
void DrawModels(void)
{
    GLint r, g, b;

    // Draw 27 spheres in a color cube
    for (r = 0; r < 3; r++)
    {
        for (g = 0; g < 3; g++)
        {
            for (b = 0; b < 3; b++)
            {
                glColor3f(r * 0.5f, g * 0.5f, b * 0.5f);

                glPushMatrix();
                glTranslatef(100.0f * r - 100.0f, 
                             100.0f * g - 100.0f, 
                             100.0f * b - 100.0f);
                glScalef(50.0f, 50.0f, 50.0f);
                glDrawArrays(GL_POINTS, 0, numSphereVertices);
                glPopMatrix();
            }
        }
    }
}

// Called to draw scene
void RenderScene(void)
{
    static GLTStopwatch stopWatch;
    static int frameCounter = 0;
    
    // Get initial time
    if (frameCounter == 0)
        gltStopwatchReset(&stopWatch);


    frameCounter++;
    if (frameCounter == 100)
    {
        frameCounter = 0;
        fprintf(stdout, "FPS: %f\n", 100.0f / gltStopwatchRead(&stopWatch));
        gltStopwatchReset(&stopWatch);
    }

    // Track camera angle
    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
    gluPerspective(45.0f, 1.0f, 10.0f, 10000.0f);
    glMatrixMode(GL_MODELVIEW);
    glLoadIdentity();
    gluLookAt(cameraPos[0], cameraPos[1], cameraPos[2], 
              0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f);

    // Clear the window with current clearing color
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

    if (animating)
    {
        RegenerateSphere();
        SetRenderingMethod();
    }

    // Draw objects in the scene
    DrawModels();

    // Flush drawing commands
    glutSwapBuffers();

    glutPostRedisplay();
}

// This function does any needed initialization on the rendering
// context. 
void SetupRC()
{
    const GLubyte *version;
    GLboolean glVersion15 = GL_FALSE;

    fprintf(stdout, "Buffer Object Demo\n\n");

    // Make sure required functionality is available!
    version = glGetString(GL_VERSION);
    if ((version[0] == '1') && (version[1] == '.') &&
        (version[2] >= '5') && (version[2] <= '9'))
    {
        glVersion15 = GL_TRUE;
    }

    if (!glVersion15 && !gltIsExtSupported("GL_ARB_vertex_buffer_object"))
    {
        fprintf(stderr, "Neither OpenGL 1.5 nor GL_ARB_vertex_buffer_object"
                        " extension is available!\n");
        Sleep(2000);
        exit(0);
    }

    // Load the function pointers
    if (glVersion15)
    {
        glBindBuffer = gltGetExtensionPointer("glBindBuffer");
        glBufferData = gltGetExtensionPointer("glBufferData");
        glBufferSubData = gltGetExtensionPointer("glBufferSubData");
        glDeleteBuffers = gltGetExtensionPointer("glDeleteBuffers");
        glGenBuffers = gltGetExtensionPointer("glGenBuffers");
        glMapBuffer = gltGetExtensionPointer("glMapBuffer");
        glUnmapBuffer = gltGetExtensionPointer("glUnmapBuffer");
    }
    else
    {
        glBindBuffer = gltGetExtensionPointer("glBindBufferARB");
        glBufferData = gltGetExtensionPointer("glBufferDataARB");
        glBufferSubData = gltGetExtensionPointer("glBufferSubDataARB");
        glDeleteBuffers = gltGetExtensionPointer("glDeleteBuffersARB");
        glGenBuffers = gltGetExtensionPointer("glGenBuffersARB");
        glMapBuffer = gltGetExtensionPointer("glMapBufferARB");
        glUnmapBuffer = gltGetExtensionPointer("glUnmapBufferARB");
    }

    if (!glBindBuffer || !glBufferData || !glBufferSubData || 
        !glDeleteBuffers || !glGenBuffers || !glMapBuffer || !glUnmapBuffer)
    {
        fprintf(stderr, "Not all entrypoints were available!\n");
        Sleep(2000);
        exit(0);
    }

    sphereVertexArray = (GLfloat *)malloc(sizeof(GLfloat) * 
                                          numSphereVertices * 3);
    if (!sphereVertexArray)
    {
        fprintf(stderr, "Unable to allocate system memory for vertex arrays!");
        Sleep(2000);
        exit(0);
    }

    fprintf(stdout, "Controls:\n");
    fprintf(stdout, "\tRight-click for menu\n\n");
    fprintf(stdout, "\tx/X\t\tMove +/- in x direction\n");
    fprintf(stdout, "\ty/Y\t\tMove +/- in y direction\n");
    fprintf(stdout, "\tz/Z\t\tMove +/- in z direction\n\n");
    fprintf(stdout, "\tq\t\tExit demo\n\n");
    
    // Generate a buffer object
    glGenBuffers(1, &bufferID);
    // Create the data store 
    glBindBuffer(GL_ARRAY_BUFFER, bufferID);
    glBufferData(GL_ARRAY_BUFFER, sizeof(GLfloat) * 
                 numSphereVertices * 3, NULL, 
                 GL_STATIC_DRAW);

    // Set up vertex arrays
    RegenerateSphere();
    SetRenderingMethod();
    glEnableClientState(GL_NORMAL_ARRAY);
    glEnableClientState(GL_VERTEX_ARRAY);

    // Dark background
    glClearColor(0.3f, 0.3f, 0.3f, 1.0f );

    // Hidden surface removal
    glEnable(GL_DEPTH_TEST);
    glDepthFunc(GL_LESS);

    // Set up some lighting state that never changes
    glShadeModel(GL_SMOOTH);
    glEnable(GL_LIGHTING);
    glEnable(GL_COLOR_MATERIAL);
    glEnable(GL_NORMALIZE);
    glEnable(GL_LIGHT0);
    glLightfv(GL_LIGHT0, GL_AMBIENT, ambientLight);
    glLightfv(GL_LIGHT0, GL_DIFFUSE, diffuseLight);
}

void ProcessMenu(int value)
{
    switch(value)
    {
    case 1:
        useBufferObject = !useBufferObject;
        RegenerateSphere();
        SetRenderingMethod();
        if (useBufferObject)
        {
            glutChangeToMenuEntry(1, "Toggle buffer object usage (currently ON)", 1);
        }
        else
        {
            glutChangeToMenuEntry(1, "Toggle buffer object usage (currently OFF)", 1);
        }
        break;

    case 2:
        mapBufferObject = !mapBufferObject;
        RegenerateSphere();
        SetRenderingMethod();
        if (mapBufferObject)
        {
            glutChangeToMenuEntry(2, "Toggle mapped buffer object (currently ON)", 2);
        }
        else
        {
            glutChangeToMenuEntry(2, "Toggle mapped buffer object (currently OFF)", 2);
        }
        break;

    case 3:
        animating = !animating;
        if (animating)
        {
            glutChangeToMenuEntry(3, "Toggle animation (currently ON)", 3);
            // Establish streaming buffer object
            // Data will be loaded with subsequent calls to glBufferSubData
            glBindBuffer(GL_ARRAY_BUFFER, bufferID);
            glBufferData(GL_ARRAY_BUFFER, sizeof(GLfloat) * 
                         numSphereVertices * 3, NULL, GL_STREAM_DRAW);
        } 
        else
        {
            glutChangeToMenuEntry(3, "Toggle animation (currently OFF)", 3);
            // Establish static buffer object
            // Data will be loaded with subsequent calls to glBufferSubData
            glBindBuffer(GL_ARRAY_BUFFER, bufferID);
            glBufferData(GL_ARRAY_BUFFER, sizeof(GLfloat) * 
                         numSphereVertices * 3, NULL, GL_STATIC_DRAW);
            RegenerateSphere();
            SetRenderingMethod();
        }
        break;

    default:
        break;
    }

    // Refresh the Window
    glutPostRedisplay();
}

void KeyPressFunc(unsigned char key, int x, int y)
{
    switch (key)
    {
    case 'x':
        cameraPos[0] += 5.0f;
        break;
    case 'X':
        cameraPos[0] -= 5.0f;
        break;
    case 'y':
        cameraPos[1] += 5.0f;
        break;
    case 'Y':
        cameraPos[1] -= 5.0f;
        break;
    case 'z':
        cameraPos[2] += 5.0f;
        break;
    case 'Z':
        cameraPos[2] -= 5.0f;
        break;
    case 'q':
    case 'Q':
    case 27 : /* ESC */
        exit(0);
    }

    // Refresh the Window
    glutPostRedisplay();
}

void SpecialKeys(int key, int x, int y)
{
    switch (key)
    {
    case GLUT_KEY_LEFT:
        cameraPos[0] += 5.0f;
        break;
    case GLUT_KEY_RIGHT:
        cameraPos[0] -= 5.0f;
        break;
    case GLUT_KEY_UP:
        cameraPos[1] += 5.0f;
        break;
    case GLUT_KEY_DOWN:
        cameraPos[1] -= 5.0f;
        break;
    default:
        break;
    }

    // Refresh the Window
    glutPostRedisplay();
}

void ChangeSize(int w, int h)
{
    windowWidth = w;
    windowHeight = h;

    glViewport(0, 0, windowWidth, windowHeight);
}

int main(int argc, char* argv[])
{
    glutInit(&argc, argv);
    glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB | GLUT_DEPTH);
    glutInitWindowSize(windowWidth, windowHeight);
    glutCreateWindow("Buffer Object Demo");
    glutReshapeFunc(ChangeSize);
    glutKeyboardFunc(KeyPressFunc);
    glutSpecialFunc(SpecialKeys);
    glutDisplayFunc(RenderScene);

    // Create the Menu
    glutCreateMenu(ProcessMenu);
    glutAddMenuEntry("Toggle buffer object usage (currently ON)", 1);
    glutAddMenuEntry("Toggle mapped buffer object (currently ON)", 2);
    glutAddMenuEntry("Toggle animation (currently ON)", 3);
    glutAttachMenu(GLUT_RIGHT_BUTTON);

    SetupRC();

    glutMainLoop();

    if (sphereVertexArray)
        free(sphereVertexArray);
    if (glDeleteBuffers)
        glDeleteBuffers(1, &bufferID);

    return 0;
}

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?