Расширение EXT_bindable_uniform.

Расширение EXT_bindable_uniform, появившееся в серии GPU GeForce 8xxx (ATI R600 и выше), дает новую и очень удобную возможность - вместо явной загрузки uniform-переменных при помощи команд glUiniform использовать для этого вершинный буфер (VBO).

При этом данные для соответствующей uniform-переменной будут браться прямо из соответствующего буфера и для изменения значения такой переменной (такие переменные называются bound) достаточно просто изменить значение в буфере.

Количество доступных uniform-переменных для каждого типа шейдера (вершинного, фрагментного и геометрического) ограничено, но их должно быть не менее 8 для каждого из этих типов шейдеров (16 для OpenGL 3.0 и выше).

Получить максимальное количество bound uniform-переменных можно при помощи команды glGetIntegerv с параметром GL_MAX_VERTEX_BINDABLE_UNIFORMS_EXT, GL_MAX_FRAGMENT_BINDABLE_UNIFORMS_EXT и GL_MAX_GEOMETRY_BINDABLE_UNIFORMS_EXT.

Подключаемый к шейдеру буфер данных должен иметь минимально требуемый размер, иначе при чтении юниформа в шейдере результат будет не определён. Размер буфера данных не может превышать зависимой от реализации константы GL_MAX_BINDABLE_UNIFORM_SIZE_EXT.

int  num;

glGetIntegerv ( GL_MAX_VERTEX_BINDABLE_UNIFORMS_EXT, &num );

Подключение bound uniform-переменных осуществляется после этапа линковки шейдера. В результате линковки все обычные uniform-переменные получают значение ноль, а для bound uniform-переменных сбрасывается привязка к буферам.

После этого можно привязать к переменную к буферу при помощи следующей команды.

void glUniformBufferEXT ( GLuint program, int location, GLuint bufferId );

Судя по всему использование структур в качестве bound uniform-переменных не поддерживается. Также не поддерживается использование массивов с заранее неизвестной длиной.

Ниже приводятся вершинный и фрагментный шейдеры, реализующие обычное попиксельное освещение по Блинну, только для передачи положение источника света, его цвета и положения наблюдателя используется bound uniform массив из трех элементов.

//
// vertex shader
//

#extension GL_EXT_bindable_uniform: enable

bindable uniform vec4 light [3];

varying vec3 l;
varying vec3 h;
varying vec3 n;
varying vec3 v;

void main(void)
{
    vec3    p = vec3 ( gl_ModelViewMatrix * gl_Vertex );            // transformed point to world space

    l = normalize ( vec3 ( light [0] ) - p );                       // vector to light source
    v = normalize ( vec3 ( light [2] ) - p );                       // vector to the eye
    h = normalize ( l + v );
    n = normalize ( gl_NormalMatrix * gl_Normal );                  // transformed n

    gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex;
}

//
// fragment shader
//

#extension GL_EXT_bindable_uniform: enable

bindable uniform vec4 light [3];

varying vec3 l;
varying vec3 h;
varying vec3 v;
varying vec3 n;

void main (void)
{
    const vec4  diffColor = vec4 ( 0.5, 0.0, 0.0, 1.0 );
    const float specPower = 30.0;

    vec3    n2   = normalize ( n );
    vec3    l2   = normalize ( l );
    vec3    h2   = normalize ( h );
    vec4    diff = diffColor * max ( dot ( n2, l2 ), 0.0 );
    vec4    spec = light [1] * pow ( max ( dot ( n2, h2 ), 0.0 ), specPower );

    gl_FragColor = diff + spec;
}

Полный исходный код на С++ для данного примера приводится на следующем листинге.

//
// Example for EXT_bindable_uniform.
//
// Author: Alex V. Boreskoff <alexboreskoff@mtu-net.ru>, <steps3d@narod.ru>
//

#include    "libExt.h"

#include    <glut.h>
#include    <stdio.h>
#include    <stdlib.h>

#include    "libTexture.h"
#include    "Vector3D.h"
#include    "Vector2D.h"
#include    "Vector4D.h"
#include    "Data.h"
#include    "GlslProgram.h"

Vector3D    eye   ( 7, 5, 7 );          // camera position
Vector3D    light ( 5, 0, 4 );          // light position
float       angle = 0;
Vector3D    rot ( 0, 0, 0 );
int         mouseOldX = 0;
int         mouseOldY = 0;

GlslProgram program;
GLenum      lightBuffer;                // VBO with data for light

struct LightProps
{
    float   pos    [4];
    float   color  [4];
    float   eyePos [4];
} lightProps;

/////////////////////////////////////////////////////////////////////////////////

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
    program.unbind ();

    glMatrixMode ( GL_MODELVIEW );
    glPushMatrix ();

    glTranslatef       ( light.x, light.y, light.z );
    glColor4fv         ( lightProps.color );
    glutSolidSphere    ( 0.1f, 15, 15 );
    glPopMatrix        ();

    glMatrixMode   ( GL_MODELVIEW );
    glPushMatrix   ();

    glRotatef    ( rot.x, 1, 0, 0 );
    glRotatef    ( rot.y, 0, 1, 0 );
    glRotatef    ( rot.z, 0, 0, 1 );

    program.bind   ();
    glutSolidTeapot ( 2.5 );
    
    program.unbind ();

    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.x, eye.y, eye.z,    // eye
                    0, 0, 0,                // center
                    0.0, 0.0, 1.0 );        // up

   glMatrixMode   ( GL_MODELVIEW );
   glLoadIdentity ();
}

void motion ( int x, int y )
{
    rot.y -= ((mouseOldY - y) * 180.0f) / 200.0f;
    rot.z -= ((mouseOldX - x) * 180.0f) / 200.0f;
    rot.x  = 0;

    if ( rot.z > 360 )
        rot.z -= 360;

    if ( rot.z < -360 )
        rot.z += 360;

    if ( rot.y > 360 )
        rot.y -= 360;

    if ( rot.y < -360 )
        rot.y += 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.x = 2*cos ( angle );
    light.y = 3*sin ( 1.4 * angle );
    light.z = 3 + 0.5 * sin ( angle / 3 );
    
    glBindBufferARB    ( GL_UNIFORM_BUFFER_EXT, lightBuffer );

    LightProps * props = (LightProps *) glMapBufferARB ( GL_UNIFORM_BUFFER_EXT, GL_WRITE_ONLY );
    
    lightProps.pos   [0] = light.x;
    lightProps.pos   [1] = light.y;
    lightProps.pos   [2] = light.z;
    lightProps.color [0] = 0.25 * ( 3 + sin ( 1.1 * angle ) );
    lightProps.color [1] = 0.25 * ( 3 + sin ( 1.2 * angle ) );
    lightProps.color [2] = 0.25 * ( 3 + sin ( 1.3 * angle ) );
    
    *props = lightProps;
    
    glUnmapBufferARB ( GL_UNIFORM_BUFFER_EXT );
    
    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 EXT_bindable_uniform" );

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

    init           ();
    initExtensions ();
    printfInfo     ();

    assertExtensionsSupported ( "GL_EXT_bindable_uniform" );

    if ( !program.loadShaders ( "blinn.vsh", "blinn.fsh" ) )
    {
        printf ( "Error loading shaders:\n%s\n", program.getLog ().c_str () );

        return 3;
    }

    lightProps.eyePos [0] = eye.x;
    lightProps.eyePos [1] = eye.y;
    lightProps.eyePos [2] = eye.z;
    lightProps.eyePos [3] = 1;
    lightProps.pos    [0] = light.x;
    lightProps.pos    [1] = light.y;
    lightProps.pos    [2] = light.z;
    lightProps.pos    [3] = 1;
    lightProps.color  [0] = 1;
    lightProps.color  [1] = 1;
    lightProps.color  [2] = 1;
    lightProps.color  [3] = 1;
    
    glGenBuffersARB    ( 1, &lightBuffer );
    glBindBufferARB    ( GL_UNIFORM_BUFFER_EXT, lightBuffer );
    glBufferDataARB    ( GL_UNIFORM_BUFFER_EXT, sizeof ( lightProps ), &lightProps, GL_STREAM_DRAW_ARB );
    glUniformBufferEXT ( program.getProgram (), program.locForUniformName ( "light" ), lightBuffer );
    glBindBufferARB    ( GL_UNIFORM_BUFFER_EXT, 0 );
    
    
    glutMainLoop ();

    return 0;
}

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

Обратите внимание, что для компиляции примера необходима последняя версия библиотеки libExt и последняя реализация класса GlslProgram.

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