Использование библиотеки GLew для работы с расширениями OpenGL

Не секрет, что для серьезного использования OpenGL сейчас необходимо пользоваться различными расширениями (по крайней мере под форточками, где до сих пор OpenGL 1.1).

Использование расширений дает как доступ к таким стандартным уже возможностям, как шейдеры, так и к довольно специфическим возможностям конкретным видеокарточек.

Существует довольно много различных библиотек, предназначенных для работы с расширениями OpenGL (одна из них - мою libExt - постоянно используется в примерах с данного сайта).

В этой статье я хочу рассмотреть одну из самых популярных таких библиотек - (вошедшую в состав OpenGL SDK) - библиотеку OpenGL Extension Wrangler Library - GLew.

Это простая в использовании кроссплатформенная (в числе поддерживаемых платформ - M$ Windoze, Linux, Mac OS X, Solaris и другие ) библиотека обеспечивает удобство в использовании расширений OpenGL.

Для M$ Windoze (как и многих других платформ) можно скачать уже откомпилированную версию, состоящую из заголовочного файла glew.h, динамической библиотеки glew32.dll и двух библиотек glew32.lib и glew32s.lib.

При этом вы можете для компиляции своих программ использовать как статическую версию (т.е. не требующую динамической библиотеки glew32.dll, библиотечный файл glew32s.lib), так и динамическую (библиотека glew32.lib). В последнем случае для выполнения потребуется glew32.dll, но размер выполняемого файла будет меньше.

Для использования библиотеки необходимо подключить ее заголовочный файл glew.h вместо файла gl.h (он сам подключает все, что надо).

Далее для инициализации библиотеки необходимо вызвать функцию glewInit, возвращающую значение GL_TRUE при успешной инициализации библиотеки.

Обратите внимание, что данная функция должна быть вызвана уже после того, как будет создан и настроен контекст OpenGL, например в программах, использующих GLUT, после вызова функции glutCreateWindow.

Самый простой способ проверить поддержку расширения с заданным именем заключается в проверке значения глобальной переменной с именем вида GLEW_имя-расширения (т.е. в названии расширения GL заменяется на GLEW).

Так расширению GL_ARB_vertex_program соответствует глобальная переменная GLEW_ARB_vertex_program. Если она отлична от нуля, то данное расширение поддерживается и Вы можете напрямую обращаться к функциям, вводимым этим расширением.

Также можно проверить поддержку расширения по строке, содержащей его имя, при помощи функции glewIsSupported.

if ( glewInit () != GLEW_OK )
{
    printf ( "Error in glewInit\n" );

    return 1;
}

if ( !GLEW_ARB_shading_language_100 )
{
    printf ( "GL_ARB_shading_language_100 NOT supported.\n" );

    return 1;
}

if ( !GLEW_ARB_shader_objects )
{
    printf ( "GL_ARB_shader_objects NOT supported" );

    return 2;
}

Для работы с расширениями, специфичными для определенной платформы дополнительно включите заголовочный файл wglew.h (для платформы M$ Windoze) или glxew.h (для Linux).

Аналогичным способом можно проверить поддержку заданной версии OpenGL - каждой версии соответствует глобальная переменная вида GLEW_VERSION_n_m (через n и m обозначены старший и младший номера версии, например 1.4).

Так следующий фрагмент кода проверяет поддержку OpenGL 1.4.

if ( GLEW_VERSION_1_4 )
{
   ...
}

Этого же можно добиться и используя функцию glewIsSupported:

if ( glewIsSupported ( "VERSION_1_4" ) )
{
   ...
}

Для поддержки ряда экспериментальных расширений следует перед вызовом glewInit установить значение глобальной переменной glewExperimental в GL_TRUE.

Ниже приводится исходный текст простого примера, использующего библиотеку GLew для работы с шейдерами в OpenGL (обратите внимание, что данной программе кроме библиотек GLUT и GLew больше ничего не нужно).

#include    <GL/glew.h>
#include    <GL/gl.h>
#include    <GL/glu.h>

#ifdef  _WIN32
    #include    <GL/wglew.h>
#else
    #include    <GL/glxew.h>
#endif

#include    <glut.h>
#include    <stdio.h>
#include    <stdlib.h>
#include    <string.h>
#include    <ctype.h>
#include    <math.h>

float   eye   [] = { 7, 5, 7, 1 };      // camera position
float   light [] = { 5, 0, 4, 1 };      // light position
float   rot   [] = { 0, 0, 0 };
float   angle = 0;
int     mouseOldX = 0;
int     mouseOldY = 0;

GLhandleARB program        = 0;         // program handles
GLhandleARB vertexShader   = 0;
GLhandleARB fragmentShader = 0;

bool    checkOpenGLError ()
{
    bool    retCode = true;

    for ( ; ; )
    {
        GLenum  glErr = glGetError ();

        if ( glErr == GL_NO_ERROR )
            return retCode;

        printf ( "glError: %s\n", gluErrorString ( glErr ) );

        retCode = false;
        glErr   = glGetError ();
    }

    return retCode;
}

void printInfoLog ( GLhandleARB object )
{
    int         logLength     = 0;
    int         charsWritten  = 0;
    GLcharARB * infoLog;

    checkOpenGLError ();                    // check for OpenGL errors

    glGetObjectParameterivARB ( object, GL_OBJECT_INFO_LOG_LENGTH_ARB, &logLength );

    if ( !checkOpenGLError() )              // check for OpenGL errors
        exit ( 1 );

    if ( logLength > 0 )
    {
        infoLog = (GLcharARB*) malloc ( logLength );

        if ( infoLog == NULL )
        {
            printf("ERROR: Could not allocate InfoLog buffer\n");

            exit ( 1 );
        }

        glGetInfoLogARB ( object, logLength, &charsWritten, infoLog );

        printf ( "InfoLog:\n%s\n\n", infoLog );
        free   ( infoLog );
    }

    if ( !checkOpenGLError () )             // check for OpenGL errors
        exit ( 1 );
}

bool    loadShader ( GLhandleARB shader, const char * fileName )
{
    printf ( "Loading %s\n", fileName );

    FILE * file = fopen ( fileName, "rb" );
    
    if ( file == NULL )
    {
        printf ( "Error opening %s\n", fileName );
        exit   ( 1 );
    }

    fseek ( file, 0, SEEK_END );
    
    int size = ftell ( file );
    
    if ( size < 1 )
    {
        fclose ( file );
        printf ( "Error loading file %s\n", fileName );
        exit   ( 1 );
    }
    
    char * buf = (char *) malloc ( size );
    
    fseek ( file, 0, SEEK_SET );
    
    if ( fread ( buf, 1, size, file ) != size )
    {
        fclose ( file );
        printf ( "Error loading file %s\n", fileName );
        exit   ( 1 );
    }

    fclose ( file );
    
    GLint   compileStatus;

    glShaderSourceARB ( shader, 1, (const char **) &buf, &size );

    free ( buf );
                                        // compile the particle vertex shader, and print out
    glCompileShaderARB ( shader );

    if ( !checkOpenGLError() )          // check for OpenGL errors
        return false;

    glGetObjectParameterivARB ( shader, GL_OBJECT_COMPILE_STATUS_ARB, &compileStatus );
    printInfoLog              ( shader );

    return compileStatus != 0;
}

bool    setUniformVector ( GLhandleARB program, const char * name, const float * value )
{
    int loc = glGetUniformLocationARB ( program, name );

    if ( loc < 0 )
        return false;

    glUniform4fvARB ( loc, 1, value );

    return true;
}

void init ()
{
    glClearColor ( 0.5, 0.5, 0.5, 1.0 );
    glEnable     ( GL_DEPTH_TEST );
    glDepthFunc  ( GL_LEQUAL );

    glHint ( GL_POLYGON_SMOOTH_HINT,         GL_NICEST );
    glHint ( GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST );
}

void display ()
{
    glClear ( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT );

                                                // draw the light
    glMatrixMode ( GL_MODELVIEW );
    glPushMatrix ();

    glTranslatef       ( light [0], light [1], light [2] );
    glColor4f          ( 1, 1, 1, 1 );
    glutSolidSphere    ( 0.1f, 15, 15 );
    glPopMatrix        ();

    glMatrixMode   ( GL_MODELVIEW );
    glPushMatrix   ();

    glRotatef    ( rot [0], 1, 0, 0 );
    glRotatef    ( rot [1], 0, 1, 0 );
    glRotatef    ( rot [2], 0, 0, 1 );

    glUseProgramObjectARB ( program );
    
    glutSolidTeapot ( 2.5 );
    
    glUseProgramObjectARB ( 0 );

    glPopMatrix ();

    glutSwapBuffers ();
}

void reshape ( int w, int h )
{
   glViewport     ( 0, 0, (GLsizei)w, (GLsizei)h );
   glMatrixMode   ( GL_PROJECTION );
                                            // factor all camera ops into projection matrix
   glLoadIdentity ();
   gluPerspective ( 60.0, (GLfloat)w/(GLfloat)h, 1.0, 60.0 );
   gluLookAt      ( eye [0], eye [1], eye [2],  // eye
                    0, 0, 0,                    // center
                    0.0, 0.0, 1.0 );            // up

   glMatrixMode   ( GL_MODELVIEW );
   glLoadIdentity ();
}

void motion ( int x, int y )
{
    rot [1] -= ((mouseOldY - y) * 180.0f) / 200.0f;
    rot [2] -= ((mouseOldX - x) * 180.0f) / 200.0f;
    rot [0]  = 0;

    for ( int i = 0; i < 3; i++ )
        if ( rot [i] > 360 )
            rot [i] -= 360;
        else
        if ( rot [i] < -360 )
            rot [i] += 360;

    mouseOldX = x;
    mouseOldY = y;

    glutPostRedisplay ();
}

void mouse ( int button, int state, int x, int y )
{
    if ( state == GLUT_DOWN )
    {
        mouseOldX = x;
        mouseOldY = y;
    }
}

void key ( unsigned char key, int x, int y )
{
    if ( key == 27 || key == 'q' || key == 'Q' )    //  quit requested
        exit ( 0 );
}

void    animate ()
{
    angle  = 0.004f * glutGet ( GLUT_ELAPSED_TIME );

    light [0] = 2*cos ( angle );
    light [1] = 3*sin ( 1.4 * angle );
    light [2] = 3 + 0.5 * sin ( angle / 3 );

    glUseProgramObjectARB ( program );
    setUniformVector      ( program, "eyePos",   eye   );
    setUniformVector      ( program, "lightPos", light );
    glUseProgramObjectARB ( 0 );

    glutPostRedisplay ();
}

int main ( int argc, char * argv [] )
{
                                // initialize glut
    glutInit            ( &argc, argv );
    glutInitDisplayMode ( GLUT_DOUBLE | GLUT_RGB | GLUT_DEPTH );
    glutInitWindowSize  ( 500, 500 );


                                // create window
    glutCreateWindow ( "Example of GLSL Blinn shader" );

                                // register handlers
    glutDisplayFunc  ( display );
    glutReshapeFunc  ( reshape );
    glutKeyboardFunc ( key     );
    glutMouseFunc    ( mouse   );
    glutMotionFunc   ( motion  );
    glutIdleFunc     ( animate );

    init ();

    if ( glewInit () != GLEW_OK )
    {
        printf ( "Error in glewInit\n" );
        
        return 1;
    }
    
    if ( !GLEW_ARB_shading_language_100 )
    {
        printf ( "GL_ARB_shading_language_100 NOT supported.\n" );

        return 1;
    }

    if ( !GLEW_ARB_shader_objects )
    {
        printf ( "GL_ARB_shader_objects NOT supported" );

        return 2;
    }

    GLint       linked;

                                        // create a vertex shader object and a fragment shader object
    vertexShader   = glCreateShaderObjectARB ( GL_VERTEX_SHADER_ARB   );
    fragmentShader = glCreateShaderObjectARB ( GL_FRAGMENT_SHADER_ARB );

                                        // load source code strings into shaders
    if ( !loadShader ( vertexShader, "blinn.vsh" ) )
        exit ( 1 );

    if ( !loadShader ( fragmentShader, "blinn.fsh" ) )
        exit ( 1 );

                                        // create a program object and attach the
                                        // two compiled shaders
    program = glCreateProgramObjectARB ();

    glAttachObjectARB ( program, vertexShader   );
    glAttachObjectARB ( program, fragmentShader );

                                        // link the program object and print out the info log
    glLinkProgramARB ( program );

    if ( !checkOpenGLError() )          // check for OpenGL errors
        exit ( 1 );

    glGetObjectParameterivARB ( program, GL_OBJECT_LINK_STATUS_ARB, &linked );

    if ( !linked )
        return 0;
        
    glutMainLoop ();

    return 0;
}

По этой ссылке можно скачать весь исходный код к этой статье. Также доступны для скачивания откомпилированные версии для M$ Windows, Linux и Mac OS X.

Используются технологии uCoz