Работа с расширениями OpenGL с использованием NVIDIA OpenGL SDK 5.1. (Часть 1)
Автор: Сергей Гайдуков
Как известно, OpenGL в чистом виде обладает довольно ограниченными возможностями по сравнению, например, с DirectX. Так, в нем нет средств для работы с внеэкранными поверхностями, шейдерами и многого другого. Но зато с самого начала OpenGL обладал открытой архитектурой, которая заключается в поддержке расширений.
Что такое расширение OpenGL
Инициализация WGL-расширений
Инициализация OpenGL расширений
Расширение NV_texture_rectangle
Что такое расширение OpenGL
Если производитель обнаруживал, что OpenGL не может использовать все аппаратные возможности его "железа", он выпускал расширение для OpenGL с поддержкой новых возможностей. Причем это расширение, как правило, появлялось почти на год раньше, чем новая версия DirectX, поддерживающая новые возможности. Так, поддержка вершинных и пиксельных шейдеров для GeForce2 в OpenGL появилась намного раньше 8-й версии DirectX. А когда DirectX 8 все-таки вышел, то выяснилось, что про пиксельные шейдеры GeForce2 он ничего не знает:
Наряду с достоинствами этот подход имеет и недостатки — расширения OpenGL разных производителей часто несовместимы друг с другом, даже когда они выполняют одинаковые функции. Это становится настоящей головной болью, когда программа должна поддерживать большое количество разных видеокарт. С другой стороны, существует множество стандартных расширений, которые поддерживают практически все видеокарты (разумеется, при наличии аппаратной поддержки). Названия этих расширений обычно начинаются с GL_ARB, GL_EXT и GL_SGIS.
В этой статье я собираюсь рассмотреть использование OpenGL-расширений корпорации NVIDIA, но везде, где будет выбор между расширениями NVIDIA (GL_NV) и стандартными расширениями, выполняющими одинаковые функции, предпочтение будет отдаваться последним.
Для выполнения всех примеров вам потребуются Microsoft Visual C++ 6 и NVIDIA OpenGL SDK, который можно скачать с www.nvidia.com. В составе этого SDK имеется NVIDIA OpenGL Extension Specification, в котором описаны большинство расширений, которые поддерживаются NVIDIA.
Инициализация WGL-расширений
Перед тем, как использовать любое OpenGL-расширение, его надо инициализировать. Для начала рассмотрим инициализацию WGL-расширений. Этот класс расширений предназначен для инициализации OpenGL в ОС семейства Windows, а также для выполнения некоторых функций, которые зависят от используемой операционной системы - например, работа со внеэкранными поверхностями (буферами пикселей). В приведенном ниже примере показывается, как проверить поддержку двух расширений WGL_ARB_pixel_format и WGL_ARB_pbuffer и подготовить их к использованию. Этот и все остальные примеры из статьи полностью можно скачать.
Для начала не забудьте настроить Visual C++ для работы с NVSDK (пропишите пути к включаемым файлам и библиотекам). Теперь создайте новый проект и настройте его для работы с OpenGL (подключите библиотеки opengl.lib, glu32.lib, glut32.lib, установите точку входа mainCRTStartup и т.д.)
Напишите простейшее GLUT-приложение, создающее окно:
void main() { glutInitDisplayMode( GLUT_RGBA); glutInitWindowSize( 512 ,384); glutInitWindowSize( 100,100); glutCreateWindow( "wglExtDemo"); }
Не забудьте директивой #include подключить заголовочные файлы glext.h (замена gl.h) и wglext.h. Перед началом работы с WGL-расширениями надо проверить поддержку расширения WGL_ARB_extensions_string, при помощи которого осуществляется инициализация остальных WGL-расширений.
Для это мы объявляем переменную, которая является указателем на функцию wglGetExtensionsStringARB. Обратите внимание, что название типа функции начинается с префикса PFN, далее идет название расширения, а затем окончание ARBPROC (в случае расширений ARB):
PFNWGLGETEXTENSIONSSTRINGARBPROC wglGetExtensionsStringARB=0;
Теперь надо получить адрес этой функции. Для этого используется функция wglGetProcAddress. В качества параметра она принимает строку, содержащую название функции, адрес которой надо получить:
wglGetExtensionsStringARB=
(PFNWGLGETEXTENSIONSSTRINGARBPROC) wglGetProcAddress("wglGetExtensionsStringARB");
В случае удачного вызова она возвращает адрес функции, иначе - NULL.
Теперь при помощи этой функции можно получить список всех WGL-расширений, которые поддерживает данная платформа. Для этого следует вызвать эту функцию, передав ей в качестве параметра контекст текущего устройства WGL. Функция возвращает строку, оканчивающуюся нулем и содержащую список поддерживаемых WGL-расширений, разделенных пробелами. После этого проверим, поддерживаются ли требуемые нам расширения. Для этого можно использовать функцию strstr:
//---------------- ExtensionStr=strstr(WGLExtensionsList, "WGL_ARB_pixel_format"); if ( !ExtensionStr) { MessageBox( 0, "WGL_ARB_PIXEL_FORMAT Extension not found", "Error", MB_OK | MB_ICONSTOP); exit( 1); }
Если все прошло удачно, то, используя функцию wglGetProcAddress, можно получить адреса точек входа нужных нам функций:
wglCreatePbufferARB=(PFNWGLCREATEPBUFFERARBPROC) wglGetProcAddress( "wglCreatePbufferARB"); if ( !wglCreatePbufferARB) { MessageBox( 0, "wglCreatePbufferARB function not found", "Error", MB_OK | MB_ICONSTOP); exit( 1); }
Как видно, инициализировать WGL-расширения непосредственно через OpenGL API довольно неудобно, особенно если надо подключить больше десятка различных расширений, каждое из которых содержит больше десятка функций. Но в NVIDIA OpenGL SDK входит библиотека OpenGL Helper Library, которая заметно упрощает работу с расширениями. Ее использование показано в следующем примере. Для начала вставьте в программу следующие директивы (после #include wglext.h):
#define GLH_EXT_SINGLE_FILE #include <glh_nveb.h> #include <glh_extensions.h> #include "glh_obs.h"
В этом случае, инициализировать все необходимые расширения можно с помощью одной функции glh_init_extensions, которой передается список необходимых расширений, разделенных пробелами. В случае успешного завершения функция возвращает True. В противном случае, используя функцию glh_get_unsupported_extensions(), можно получить список неподдерживаемых расширений: