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