|
Главная
Статьи
Ссылки
Скачать
Скриншоты
Юмор
Почитать
Tools
Проекты
Обо мне
Гостевая
Форум
|
Расширение 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.