Не удалось установить формат пикселя arb
Более подробную информацию относительно сглаживания, также как о том, что я собираюсь рассказать, можно найти по этим адресам:
Вот краткий обзор того, как наш метод будет работать, с учетом вышесказанного. В отличие от других расширений, касающихся визуализации OpenGL, расширение ARB_MULTISAMPLE включается в работу при создании окна визуализации.
Наш процесс выглядит следующим образом:
· Создается обычное окно
· Собираем возможные значения форматов пикселей для последующего сглаживания ( InitMultisample )
· Если сглаживание возможно, то уничтожаем окно и создаем его заново, с новым форматом пикселя.
· Для частей, которые мы хотим сгладить, просто вызываем функциюglEnable(GL_ARB_MULTISAMPLE).
Начнем с начала и поговорим о нашем исходном файле – arbMultiSample.cpp. Начинаем со стандартного включения заголовочных файлов gl.h и glu.h, а также windows.h. О arb_multisample.h мы поговорим позже.
Две строчки ниже определяют точки входа в список строк WGL. Мы будем их использовать при доступе к атрибутам формата пикселя для определения формата нашего типа. Две другие переменные нужны для доступа к нашим данным.
// Объявления, которые мы будем использовать
bool arbMultisampleSupported = false;
int arbMultisampleFormat = 0;
Следующей функцией, о которой мы поговорим, является WGLisExtensionSupported , которая будет использована для сбора информации о WGL расширениях для определения поддерживаемого формата на нашей системе. Мы будем давать описания кода по мере продвижения по нему, так как это легче чем прыгать по странице туда сюда.
Примечание: Код внизу написан Генри Гоффином. Его изменения внесли: Улучшенный разбор расширений GL и решили проблему с выпаданием кода, если первая проверка была не успешной.
bool WGLisExtensionSupported(const char *extension)
const size_t extlen = strlen(extension);
const char *supported = NULL;
// попытка использовать wglGetExtensionStringARB на текущем контексте, если возможно
PROC wglGetExtString = wglGetProcAddress("wglGetExtensionsStringARB");
// Если проверка не пройдена, то попытаемся использовать стандартное расширение OpenGL
if (supported == NULL)
// Если и это не поддерживается, тогда работаем без расширений
if (supported == NULL)
// Начинаем проверку с начала строки, увеличиваем на 1, при false совпадение
for (const char* p = supported; ; p++)
// Продвигаем p до следующего возможного совпадения
p = strstr(p, extension);
return false; // Совпадения нет
//Убедимся, что есть совпадение в начале строки,
//или первый символ – пробел, или может быть случайное
//совпадение "wglFunkywglExtension" с "wglExtension"
// Также убедимся, что текущий символ пустой или пробел
// или еще "wglExtensionTwo" может совпасть с "wglExtension"
if ((p==supported || p[-1]==' ') && (p[extlen]=='\0' || p[extlen]==' '))
return true; // Совпадение
Примечание переводчика:
Работа с wglGetProcAddress описана в уроке 22. Функция const char *wglGetExtensionsStringARB(HDC hdc) возвращает строку с перечнем расширений, hdc – контекст устройства.
Следующая функция – собственно суть всей программы. В сущности, мы собираемся выяснить поддерживается ли наше arb расширение на нашей системе. По сему мы будем запрашивать контекст устройства с целью выяснить наличие метода множественной выборки. Опять… давайте просто перейдем к коду.
bool InitMultisample(HINSTANCE hInstance,HWND hWnd,PIXELFORMATDESCRIPTOR pfd)
// посмотрим, есть ли строка в WGL!
// Возьмем наш формат пикселя
// Мы не нашли поддержки для метода множественной выборки, выставим наш флаг и выйдем.
// Получаем контекст нашего устройства. Нам это необходимо для того, что
// спросить у OpenGL окна, какие атрибуты у нас есть
HDC hDC = GetDC(hWnd);
// Эти атрибуты – биты, которые мы хотим протестировать в нашем типе
// Все довольно стандартно, только одно на чем мы хотим
// действительно сфокусироваться - это SAMPLE BUFFERS ARB и WGL SAMPLES
// Они выполнят главную проверку на предмет: есть или нет
WGL_DRAW_TO_WINDOW_ARB,GL_TRUE, // Истинна, если формат пикселя может быть использован в окне
WGL_SUPPORT_OPENGL_ARB,GL_TRUE, // Истинна, если поддерживается OpenGL
WGL_ACCELERATION_ARB,WGL_FULL_ACCELERATION_ARB, // Полная аппаратная поддержка
WGL_ALPHA_BITS_ARB,8, // Размерность альфа-канала
WGL_DEPTH_BITS_ARB,16, // Глубина буфера глубины
WGL_STENCIL_BITS_ARB,0, // Глубина буфера шаблона
WGL_DOUBLE_BUFFER_ARB,GL_TRUE, // Истина, если используется двойная буферизация
WGL_SAMPLE_BUFFERS_ARB,GL_TRUE, // Что мы и хотим
WGL_SAMPLES_ARB, 4 , // проверка на 4x тип
// Сначала посмотрим, сможем ли мы получить формат пикселя для 4x типа
// Если вернулось True, и наш счетчик форматов больше 1
if (valid && numFormats >= 1)
// Формат пикселя с 4x выборкой отсутствует, проверяем на 2x тип
if (valid && numFormats >= 1)
// возвращаем годный формат
Примечание переводчика:
BOOL wglChoosePixelFormatARB(HDC hdc,
const GLint *piAttribIList,
const GLfloat *pfAttribFList,
Выбирает форматы пикселя согласно запрашиваемым атрибутам. hdc – контекст устройства, piAttribIList или pfAttribFList – список желаемых атрибутов (пары формате целого числа или в формате с плавающей запятой, в конце списка , значения типов атрибутов задаются объявлениями define выше или взяты из wglext.h, значение зависит от типа). nMaxFormats – максимальное число форматов, которое будет возвращено. piFormats – массив индексов форматов пикселов, которые совпадают с запрашиваемым. Наилучший формат будет первым. nNumFormats – сколько форматов найдено при запросе.
Теперь, когда у нас есть готовый код запроса, мы должны изменить процесс создания окна. Сперва, мы должны включить наш заголовочный файл arb_multisample.h и поместить прототипы DestroyWindow и CreateWindow в начало файла.
// ЗДЕСЬ ПРОБЕЖАЛ ТАРАКАН
BOOL DestroyWindowGL (GL_Window* window);
BOOL CreateWindowGL (GL_Window* window);
Следующий кусок кода должен быть добавлен в функцию CreateWindowGL, код функции был оставлен без изменений, дабы вы могли вносить свои модификации. В общем, мы делаем часть «уничтожения» до конца работы. Мы не можем запросить формат пикселя, для определения типа множественной выборки, пока мы не создадим окно. Но мы не можем создать окно, пока не знаем формат пикселя, который оно поддерживает. Сродни вечному вопросу о курице и яйце. Итак, все, что я сделал – это маленькая двухпроходная система; мы создаем окно, определяем формат пикселя, затем уничтожаем (пересоздаем) окно, если метод множественной выборки поддерживается. Немного круто…
window->hDC = GetDC (window->hWnd); // Забираем контекст данного окна
if (window->hDC == 0) // Мы получили контекст устройства?
DestroyWindow (window->hWnd); // Уничтожаем окно
window->hWnd = 0; // Обнуляем указатель
return FALSE; // возвращаем False
// ЗДЕСЬ ПРОБЕЖАЛ ТАРАКАН
// Наш первый проход, множественная выборка пока не подключена, так что мы создаем обычное окно
// Если поддержка есть, тогда мы идем на второй проход
// это значит, что мы хотим использовать наш формат пикселя для выборки
// и так, установим PixelFormat в arbMultiSampleformat вместо текущего
PixelFormat = ChoosePixelFormat (window->hDC, &pfd); // найдем совместимый формат пикселя
if (PixelFormat == 0) // мы нашли его?
ReleaseDC (window->hWnd, window->hDC); // Освобождаем контекст устройства
window->hDC = 0; // Обнуляем контекст
DestroyWindow (window->hWnd); // Уничтожаем окно
window->hWnd = 0; // Обнуляем указатель окна
return FALSE; // возвращаем False
// пытаемся установить формат пикселя
if (SetPixelFormat (window->hDC, PixelFormat, &pfd) == FALSE)
ReleaseDC (window->hWnd, window->hDC); // Освобождаем контекст устройства
window->hDC = 0; // Обнуляем контекст
DestroyWindow (window->hWnd); // Уничтожаем окно
window->hWnd = 0; // Обнуляем указатель окна
return FALSE; // возвращаем False
Теперь окно создано и у нас есть правильный указатель, чтобы запросить поддержку множественной выборки. Если поддержка есть, то мы уничтожаем окно и опять вызываем CreateWindowGL с новым форматом пикселя.
// Сделаем контекст визуализации нашим текущим контекстом
if (wglMakeCurrent (window->hDC, window->hRC) == FALSE)
wglDeleteContext (window->hRC); // Уничтожаем контекст визуализации
window->hRC = 0; // Обнуляем контекст визуализации
ReleaseDC (window->hWnd, window->hDC); // Освобождаем контекст устройства
window->hDC = 0; // Обнуляем его
DestroyWindow (window->hWnd); // Уничтожаем окно
window->hWnd = 0; // Обнуляем указатель окна
return FALSE; // возвращаем False
// ЗДЕСЬ ПРОБЕЖАЛ ТАРАКАН
// Теперь, когда наше окно создано, мы хотим узнать какие типы доступны.
// Мы вызываем нашу функцию InitMultiSample для создания окна
// если вернулся правильный контекст, то мы уничтожаем текущее окно
// и создаем новой, используя интерфейс множественной выборки.
ShowWindow (window->hWnd, SW_NORMAL); // Сделаем окно видимым
Ок, и так настройка теперь завершена! Мы настроили наше окно для работы с методом множественной выборки. Теперь повеселимся, собственно делая это! К счастью, ARB решила сделать поддержку метода множественной выборки динамической, это позволяет нам включать и выключать ее вызовами glEnable / glDisable.
Вот собственно и все! Далее идет код примера, в котором простые квадраты вращаются, чтобы вы могли увидеть, как хорошо метод множественной выборки работает. НАСЛАЖДАЙТЕСЬ!
Урок X1. Улучшенная обработка ввода с использованием DirectInput и Windows
Вы должны использовать самые современные технологии, чтобы конкурировать с такими играми как Quake и Unreal. В этом уроке я научу вас, как подключить и использовать DirectInput и как использовать мышь в OpenGL под Windows. Код этого урока базируется на коде урока 10. Начнем.
Первое, что нам понадобиться, это переменная для хранения X и Y позиции мыши.
typedef struct tagSECTOR
SECTOR sector1; // Наша модель
POINT mpos; // Позиция мыши (Новое)
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM); // Объявление WndProc
Отлично, как вы видите, мы добавили новую переменную mpos. Структура POINT состоит из двух переменных – x и y, мы будем использовать их для того, чтобы вычислить вращение сцены. Далее мы изменим, часть функции CreateGLWindow() так, как показано ниже.
ShowCursor(FALSE); // Убрать указатель мыши (Изменено)
if (fullscreen) // Если полноэкранный режим?
Выше мы переместили вызов ShowCursor(FALSE) так, чтобы курсора мыши не было видно не только в полноэкранном режиме, но и в оконном тоже. Теперь нам нужно получить и установить координаты мыши каждый кадр, поэтому измените функцию WinMain() так как показано ниже:
SwapBuffers(hDC); // Смена буферов (двойная буферизация)
GetCursorPos(&mpos); // Получить текущую позицию мыши (Новое)
SetCursorPos(320,240); // Установить мышь в центр окна (Новое)
heading += (float)(320 - mpos.x)/100 * 5;//Обновить направление движения (Новое)
yrot = heading; // Обновить вращение вокруг оси Y (Новое)
lookupdown -= (float)(240 - mpos.y)/100 * 5;//Обновить вращение вокруг X (Новое)
Сначала мы получили позицию мыши при помощи функции GetCursorPos(POINT p). Смещение от центра окна даст нам информацию о том, куда и насколько нужно вращать камеру. Затем мы устанавливаем мышь в центр окна для следующего прохода используя SetCursorPos(int X, int Y).
Замечание: Не устанавливайте позицию мыши в 0,0! Если вы сделаете это, то не сможете обработать перемещение мыши вверх и влево, потому что 0,0 это левый верхний угол окна. 320, 240 – это центр окна для режима 640х480.
После того как мы позаботились о мыши, нужно изменить часть кода для выполнения перемещения.
float = (P - CX) / U * S;
CX – текущая позиция мыши
S – скорость мыши (будучи истинным квакером я люблю в этом месте значение 12).
По этой формуле вычисляются значения переменных heading и lookupdown.
С мышью вроде как разобрались. Идем дальше.
Клавиатура (DirectX 7)
Теперь мы можем при помощи мыши вращать камеру в нашем мире. Следующий шаг использовать клавиатуру для перемещения вперед, атаки и приседания. Довольно болтовни, начнем кодировать.
Сначала я расскажу, как использовать DirectX7. Первый шаг – настройка компилятора. Я покажу, как сделать это на примере Visual C++, настройка других компиляторов может отличаться от предложенного способа.
Если у вас еще нет DirectX SDK, то вам придется его заиметь, например, скачать с сайта MicroSoft, и проинсталлировать его.
После этого, в VisualStudio зайдите в меню Project->Settings. Выберите закладку Link и в строке Object/libraty modules в начало строки добавьте dinput.lib dxguid.lib winmm.lib. Библиотеки DirectInput, DirectX GUID и Windows Multimediaсоответственно, последняя необходима для таймера. Возможно, вам также понадобиться войти в меню Tools->Options и на закладке Directories добавить пути (Include files и Library files) к DirectX SDK и переместить их наверх списка.
Теперь DirectInput готов к использованию, можно начинать программировать!
Мы нуждаемся в подключении заголовочных файлов DirectInput, для того чтобы мы могли использовать некоторые его функции. Также мы нуждаемся в создании и добавлении новых переменных для DirectInput и для устройства DirectInput клавиатуры. Сделаем мы это следующим образом:
LPDIRECTINPUT7 g_DI; // DirectInput (Новое)
LPDIRECTINPUTDEVICE7 g_KDIDev; // Устройство клавиатуры (Новое)
В последних две строчках объявляются переменные для DirectInput (g_DI) и для устройства клавиатуры (g_KDIDev), последнее будет получать данные и обрабатывать их. Константы DirectInput не сильно отличаются от стандартных констант Windows.
Основное отличие в замене VK на DIK. Хотя некоторые названия изменили существенно. Все DIK константы объявлены в файле dinput.h.
Теперь нужно написать функцию инициализации DirectInput’а и устройства клавиатуры. Под CreateGLWindow() добавьте следующее:
// Инициализация DirectInput (Новое)
if ( DirectInputCreateEx( hInstance, // Instance окна
DIRECTINPUT_VERSION, // Версия DirectInput
NULL ) ) // NULL параметр
return(false); // Не создался DirectInput
// Создание устройства клавиатуры
if ( g_DI->CreateDeviceEx( GUID_SysKeyboard,
// Какое устройство создается (клавиатура, мышь или джойстик)
(void**)&g_KDIDev, // Устройство клавиатуры
NULL ) ) // NULL параметр
return(false); // Не создалось устройство клавиатуры
// Установка формата данных для клавиатуры
return(false); // Не удалось установить формат данных
// Установка уровня кооперации
if ( g_KDIDev->SetCooperativeLevel(hWnd, DISCL_FOREGROUND | DISCL_EXCLUSIVE) )
return(false); // Не удалось установить режим
// здесь не хватает функций уничтожения устройства клавиатуры и DirectInput
if (g_KDIDev) // Создано устройство клавиатуры? (лишняя проверка)
g_KDIDev->Acquire(); // Взять его под контроль
return(false); // возвращаем false
return(true); // все отлично
Этот код в достаточной мере снабжен комментариями и должен быть понятен. Первое – мы инициализируем DirectInput и при помощи него создаем устройство клавиатуры, которое затем берем под контроль. Можно также использовать DirectInput для мыши, но на данном этапе средств Windows вполне достаточно.
Теперь нужно заменить старый код обработки ввода с клавиатуры на новый, использующий DirectInput. Изменить предстоит много, приступим.
В настоящее время в программах, работающих с трёхмерной графикой, как правило, используются 32-х битные текстуры высокого разрешения (512x512 и выше). Платой за высокое качество текстур являются повышенные требования, как к объёму видеопамяти, так и к её пропускной способности. Казалось бы - есть шина AGP, которая позволяет хранить текстуры большого размера в ОЗУ компьютера. Но на самом деле это не так - быстродействие видеопамяти видеокарты и ОЗУ компьютера различаются почти на порядок, поэтому шина AGP фактически только упрощает программирование, позволяя загружать больший объём текстур при инициализации программы. Если же все текстуры текущего кадра не поместятся в видеопамяти, производительность видеокарты очень сильно упадаёт.
В результате производителям видеокарт пришлось решать "неразрешимую" задачу: как увеличить качество текстур без увеличения объёма видеопамяти. Выход был найден довольно быстро - фирмы S3 и 3dfx предложили форматы сжатия текстур S3TC и FXT1, которые позволяют сжимать текстуры в 6 и 8 раз соответственно (имеется в виду максимальная степень сжатия). Оба этих метода реализуют сжатие с потерей информации. Но в большинстве случаев качество сжатых текстур с увеличенной детализацией лучше качества несжатых текстур того же объёма.
Перед тем как перейти к работе со сжатыми текстурами, мы сначала рассмотрим два небольших раздела, которые значительно облегчат нам программирование.
Классы семейства tex_object_XXXX для работы с текстурами.
Во время работы с текстурами нам постоянно необходимо выполнять ряд одних и тех же операций: запросить имя для текстуры командой glGenTextures(), создать поименованную текстуру командой glBindTexture(), удалить текстуру командой glDeleteTexture(). При этом приходится постоянно "помнить" имя текстуры и её тип (GL_TEXTURE_1D и т.д.).
После создания нескольких приложений программист начинает ощущать потребность в классе, который бы умел самостоятельно создавать текстуру при первом обращении к ней, уничтожать её при выходе за пределы видимости и "помнить свои параметры".
Поэтому в состав NVIDIA OpenGL включён набор классов tex_object_XXXX, которые предназначены для работы с текстурами различных видов. Сейчас для нас интересны только классы tex_object_1D, tex_object_2D и tex_object_rectangle, которые работают с текстурами GL_TEXTURE_1D, GL_TEXTURE_2D и GL_NV_texture_rectangle соответственно. Если мы откроем файл " \OpenGLSDK \include\glh\glh_obs.h", то увидим, что все эти классы являются производными от tex_object и различаются только конструктором, который заносит в поле target (тип класса) соответствующий идентификатор. Помимо этого поля в классе определены ещё поля texture (имя текстуры) и valid (показывает, ссылается ли класс на реальную текстуру). Основные методы класса приведены в таблице 1.
Метод | Аналогичная команда OpenGL | Назначение |
Bind | glBindTexture(target, texture) | Устанавливает текстуру в качестве текущей. Если текстура ещё не существует (valid=false), то перед этим получает её имя командой glGenTextures. |
Del | glDeleteTextures(1, &texture) | Удаляет текстуру |
Parameter | glTexParameter[ i][f][iv][fv] | Устанавливает параметры текстуры |
Enable | glEnable(target) | Включает режим наложения текстуры |
Disable | glDisable(target) | Выключает режим наложения текстуры |
Таблица 1: основные методы класса tex_object
Для демонстрации использования этого класса ниже приведены фрагменты программы, выводящей на экран плоскость с нанесённой на неё текстурой (Ex01) (рисунок 1).
Рисунок 1
Ниже приведены фрагменты кода, отвечающие за работу с текстурами:
Как видно, применение этого класса практически не изменяет структуру программы. Но пользователю теперь не надо следить за загрузкой/выгрузкой текстур, а так же постоянно указывать тип текстуры (GL_TEXTURE_2D) в командах OpenGL. Это позволяет очень легко модифицировать в программу. Например, для изменения типа текстуры на GL_NV_texture_rectangle достаточно заменить класса tex_object_2D на tex_object_rectangle (и пересчитать координаты текстур).
Этот урок является вводным к циклу уроков по OpenGL 3.3. Цикл уроков задуман мной как ознакомительный и, в то же время, как справочный материал для всех кто интересуется OpenGL, особенно новой версией 3.3.
В этом и последующих уроках я планирую использовать платформу Windows как базовую, однако, в дальнейшем, возможно портирование исходных кодов на другие платформы, в частности меня интересуют платформы Linux и Mac OS.
Я полагаю, что вы уже знакомы с основами программирование на языке С++, также надеюсь что у вас есть базовые навыки работы с WinAPI, т.к. в текущем уроке используется WinAPI для создания и управления окном, к которому будет привязан контекст OpenGL. Если у вас возникают трудности с освоением материала - не расстраивайтесь, лучше обратитесь к справочной литературе и Интернету за выяснением подробностей по незнакомым конструкциям в коде. Когда мне что-то не понятно я именно так и поступаю, в этом ничего зазорного, знать абсолютно все невозможно :)
Для полноценной работы с функционалом представленным в OpenGL 3.3 нам необходимо, для начала, создать контекст с поддержкой OpenGL 3.3. Далее будут рассмотрены основные этапы, через которые надо пройти, для достижения этой цели.
В первую очередь после создания окна нам необходимо определиться с форматом пикселей, который мы будем использовать, поможет нам в этом функция ChoosePixelFormat, она определит какой из системных форматов больше всего подходит к интересующему нас формату пикселей:
Выбор формата пикселей нужен в первую очередь для того, чтобы установить правильные настройки окна, в котором будет отображаться все, что мы будем выводить на экран в дальнейшем. По хорошему мы должны также проверить, что ключевые для нас настройки формата присутствуют в том формате пикселей, который вернула функция ChoosePixelFormat. Однако, судя по моему опыту, ситуация когда система не может предоставить используемый в уроке формат маловероятна. Но все же стоит знать, что ситуация, когда система вернет не запрошенный формат, возможна - например не получится установить точность буфера глубины в 64 бита, система может предоставить 24 бита и сказать об успехе. Учтите это и старайтесь задавать корректные форматы пикселей.
Отдельного внимания заслуживает точность буфера глубины. Для всех уроков которые я собираюсь вам представить хватит точности в 16 бит, т.к. на экране не предвидится больших обозримых пространств или огромных объектов. Но вообще этот параметр выбирается исходя из задачи, если разница между ближней (z-near) и дальней (z-far) плоскостью отсечения небольшая, к примеру вращающийся куб, top-down игра или 2D, то хватит и точности в 16 бит. Ставлю здесь 24 бита, чтобы если кто-то скопирует код не удивлялся потом, почему что-то криво рисуется в его случае :)
После того как мы выбрали подходящий формат пикселей, мы должны установить его для нашего окна, делается это с помощью функции SetPixelFormat:
В Windows для создания контекста OpenGL 3.3 нам понадобится функция-расширение wglCreateContextAttribsARB, доступ к которой мы можем получить посредством специальной функции wglGetProcAddress, но для этого нам надо иметь уже готовый контекст OpenGL более старой версии. Поэтому для начала нам надо создать временный контекст OpenGL посредством вызова функции wglCreateContext:
После чего мы уже можем получить доступ к функции wglCreateContextAttribsARB:
Функция wglGetProcAddress очень похожа на стандартную функцию GetProcAddress, позволяющую получать указатели на функции из DLL и делает примерно то же самое, т.е. позволяет получить указатель на функцию из драйвера видеокарты, который подгружает и драйвера OpenGL. Если драйвер видеокарты не содержит такой функции - будет возвращен NULL, это может значить что, либо необходимо обновить драйвер видеокарты, либо функция вообще не поддерживается текущей видеокартой и поэтому в драйвере не реализована.
Если все прошло удачно и нам удалось получить адрес функции wglCreateContextAttribsARB, то мы можем попробовать создать контекст OpenGL 3.3:
В итоге мы имеем на руках полнофункциональный контекст с поддержкой OpenGL 3.3, который мы можем использовать для дальнейшей работы.
На этом текущий урок заканчивается, в следующем уроке будет рассмотрен пример загрузки и работы с расширениями OpenGL 3.3.
Привет всем, дружелюбный соседский таракан (the Friendly Neighborhood Roach) здесь с интересным примером, который поможет вашим приложениям достигнуть небывалых вершин. Мы все сталкиваемся с большой проблемой, когда хотим чтобы наши программы на OpenGL лучше выглядели. Этот эффект называется «пикселизация» (или зубчатость или ступенчатость - aliasing), и представляет из себя квадратные зубцы на диагональных гранях относительно квадратных пикселей на вашем экране. Решение проблемы – Anti-Aliasing (Сглаживание граней) используется для размытия таких зубцов, что позволяет создавать более гладкие грани объектов. Один из способов, позволяющих получить сглаживание, называется “Multisampling” (мультисэмплинг, или мультиопрос, или мультивыборка, или далее переводится как метод множественной выборки). Идея состоит в том, чтобы для каждого пикселя выбрать окружающие его пиксели (или субпиксели) с целью определить, нуждается ли данная грань в сглаживании (если пиксель не принадлежит грани, то сглаживать его не надо), но обычно мы избавляемся от ступенчатости при помощи "размазывания" самого пикселя.
Fullscreen AntiAliasing (полноэкранное сглаживание) – это то, в чем программы визуализации, не в масштабах реального времени, всегда имели преимущество. Однако с сегодняшним аппаратным обеспечением мы способны добиться схожего эффекта в реальном времени. Расширение ARB_MULTISAMPLE позволяет нам это сделать. По существу, каждый пиксель представлен своими соседями для определения оптимального сглаживания. Как бы то ни было этот процесс дорого обходится и может замедлить производительность. Например, требуется больше видеопамяти:
num_samples * (sizeof(Front_buffer) +sizeof(Z_buffer))
Более подробную информацию относительно сглаживания, также как о том, что я собираюсь рассказать, можно найти по этим адресам:
Вот краткий обзор того, как наш метод будет работать, с учетом вышесказанного. В отличие от других расширений, касающихся визуализации OpenGL, расширение ARB_MULTISAMPLE включается в работу при создании окна визуализации.
Наш процесс выглядит следующим образом:
· Создается обычное окно
· Собираем возможные значения форматов пикселей для последующего сглаживания (InitMultisample)
· Если сглаживание возможно, то уничтожаем окно и создаем его заново, с новым форматом пикселя.
· Для частей, которые мы хотим сгладить, просто вызываем функцию glEnable(GL_ARB_MULTISAMPLE).
Начнем с начала и поговорим о нашем исходном файле – arbMultiSample.cpp. Начинаем со стандартного включения заголовочных файлов gl.h и glu.h, а также windows.h. О arb_multisample.h мы поговорим позже.
Две строчки ниже определяют точки входа в список строк WGL. Мы будем их использовать при доступе к атрибутам формата пикселя для определения формата нашего типа. Две другие переменные нужны для доступа к нашим данным.
// Объявления, которые мы будем использовать
bool arbMultisampleSupported = false;
int arbMultisampleFormat = 0;
Следующей функцией, о которой мы поговорим, является WGLisExtensionSupported, которая будет использована для сбора информации о WGL расширениях для определения поддерживаемого формата на нашей системе. Мы будем давать описания кода по мере продвижения по нему, так как это легче чем прыгать по странице туда сюда.
Примечание: Код внизу написан Генри Гоффином. Его изменения внесли: Улучшенный разбор расширений GL и решили проблему с выпаданием кода, если первая проверка была не успешной.
bool WGLisExtensionSupported(const char *extension)
const size_t extlen = strlen(extension);
const char *supported = NULL;
// попытка использовать wglGetExtensionStringARB на текущем контексте, если возможно
PROC wglGetExtString = wglGetProcAddress("wglGetExtensionsStringARB");
// Если проверка не пройдена, то попытаемся использовать стандартное расширение OpenGL
if (supported == NULL)
// Если и это не поддерживается, тогда работаем без расширений
if (supported == NULL)
// Начинаем проверку с начала строки, увеличиваем на 1, при false совпадение
for (const char* p = supported; ; p++)
// Продвигаем p до следующего возможного совпадения
p = strstr(p, extension);
return false; // Совпадения нет
//Убедимся, что есть совпадение в начале строки,
//или первый символ – пробел, или может быть случайное
//совпадение "wglFunkywglExtension" с "wglExtension"
// Также убедимся, что текущий символ пустой или пробел
// или еще "wglExtensionTwo" может совпасть с "wglExtension"
return true; // Совпадение
Примечание переводчика:
Работа с wglGetProcAddress описана в уроке 22. Функция const char *wglGetExtensionsStringARB(HDC hdc) возвращает строку с перечнем расширений, hdc – контекст устройства.
Следующая функция – собственно суть всей программы. В сущности, мы собираемся выяснить поддерживается ли наше arb расширение на нашей системе. По сему мы будем запрашивать контекст устройства с целью выяснить наличие метода множественной выборки. Опять… давайте просто перейдем к коду.
bool InitMultisample(HINSTANCE hInstance,HWND hWnd,PIXELFORMATDESCRIPTOR pfd)
// посмотрим, есть ли строка в WGL!
// Возьмем наш формат пикселя
// Мы не нашли поддержки для метода множественной выборки, выставим наш флаг и выйдем.
// Получаем контекст нашего устройства. Нам это необходимо для того, что
// спросить у OpenGL окна, какие атрибуты у нас есть
HDC hDC = GetDC(hWnd);
// Эти атрибуты – биты, которые мы хотим протестировать в нашем типе
// Все довольно стандартно, только одно на чем мы хотим
// действительно сфокусироваться - это SAMPLE BUFFERS ARB и WGL SAMPLES
// Они выполнят главную проверку на предмет: есть или нет
// у нас поддержка множественной выборки
WGL_DRAW_TO_WINDOW_ARB,GL_TRUE, // Истинна, если формат пикселя может быть использован в окне
WGL_SUPPORT_OPENGL_ARB,GL_TRUE, // Истинна, если поддерживается OpenGL
WGL_ACCELERATION_ARB,WGL_FULL_ACCELERATION_ARB, // Полная аппаратная поддержка
WGL_ALPHA_BITS_ARB,8, // Размерность альфа-канала
WGL_DEPTH_BITS_ARB,16, // Глубина буфера глубины
WGL_STENCIL_BITS_ARB,0, // Глубина буфера шаблона
WGL_DOUBLE_BUFFER_ARB,GL_TRUE, // Истина, если используется двойная буферизация
WGL_SAMPLE_BUFFERS_ARB,GL_TRUE, // Что мы и хотим
WGL_SAMPLES_ARB, 4 , // проверка на 4x тип
// Сначала посмотрим, сможем ли мы получить формат пикселя для 4x типа
// Если вернулось True, и наш счетчик форматов больше 1
if (valid && numFormats >= 1)
// Формат пикселя с 4x выборкой отсутствует, проверяем на 2x тип
if (valid && numFormats >= 1)
// возвращаем годный формат
Примечание переводчика:
BOOL wglChoosePixelFormatARB(HDC hdc,
const GLint *piAttribIList,
const GLfloat *pfAttribFList,
Выбирает форматы пикселя согласно запрашиваемым атрибутам. hdc – контекст устройства, piAttribIList или pfAttribFList – список желаемых атрибутов (пары формате целого числа или в формате с плавающей запятой, в конце списка , значения типов атрибутов задаются объявлениями define выше или взяты из wglext.h, значение зависит от типа). nMaxFormats – максимальное число форматов, которое будет возвращено. piFormats – массив индексов форматов пикселов, которые совпадают с запрашиваемым. Наилучший формат будет первым. nNumFormats – сколько форматов найдено при запросе.
Теперь, когда у нас есть готовый код запроса, мы должны изменить процесс создания окна. Сперва, мы должны включить наш заголовочный файл arb_multisample.h и поместить прототипы DestroyWindow и CreateWindow в начало файла.
// ЗДЕСЬ ПРОБЕЖАЛ ТАРАКАН
BOOL DestroyWindowGL (GL_Window* window);
BOOL CreateWindowGL (GL_Window* window);
Следующий кусок кода должен быть добавлен в функцию CreateWindowGL, код функции был оставлен без изменений, дабы вы могли вносить свои модификации. В общем, мы делаем часть «уничтожения» до конца работы. Мы не можем запросить формат пикселя, для определения типа множественной выборки, пока мы не создадим окно. Но мы не можем создать окно, пока не знаем формат пикселя, который оно поддерживает. Сродни вечному вопросу о курице и яйце. Итак, все, что я сделал – это маленькая двухпроходная система; мы создаем окно, определяем формат пикселя, затем уничтожаем (пересоздаем) окно, если метод множественной выборки поддерживается. Немного круто…
window->hDC = GetDC (window->hWnd); // Забираем контекст данного окна
if (window->hDC == 0) // Мы получили контекст устройства?
DestroyWindow (window->hWnd); // Уничтожаем окно
window->hWnd = 0; // Обнуляем указатель
return FALSE; // возвращаем False
// ЗДЕСЬ ПРОБЕЖАЛ ТАРАКАН
// Наш первый проход, множественная выборка пока не подключена, так что мы создаем обычное окно
// Если поддержка есть, тогда мы идем на второй проход
// это значит, что мы хотим использовать наш формат пикселя для выборки
// и так, установим PixelFormat в arbMultiSampleformat вместо текущего
PixelFormat = ChoosePixelFormat (window->hDC, &pfd); // найдем совместимый формат пикселя
if (PixelFormat == 0) // мы нашли его?
ReleaseDC (window->hWnd, window->hDC); // Освобождаем контекст устройства
window->hDC = 0; // Обнуляем контекст
DestroyWindow (window->hWnd); // Уничтожаем окно
window->hWnd = 0; // Обнуляем указатель окна
return FALSE; // возвращаем False
// пытаемся установить формат пикселя
if (SetPixelFormat (window->hDC, PixelFormat, &pfd) == FALSE)
ReleaseDC (window->hWnd, window->hDC); // Освобождаем контекст устройства
window->hDC = 0; // Обнуляем контекст
DestroyWindow (window->hWnd); // Уничтожаем окно
window->hWnd = 0; // Обнуляем указатель окна
return FALSE; // возвращаем False
Теперь окно создано и у нас есть правильный указатель, чтобы запросить поддержку множественной выборки. Если поддержка есть, то мы уничтожаем окно и опять вызываем CreateWindowGL с новым форматом пикселя.
// Сделаем контекст визуализации нашим текущим контекстом
if (wglMakeCurrent (window->hDC, window->hRC) == FALSE)
wglDeleteContext (window->hRC); // Уничтожаем контекст визуализации
window->hRC = 0; // Обнуляем контекст визуализации
ReleaseDC (window->hWnd, window->hDC); // Освобождаем контекст устройства
window->hDC = 0; // Обнуляем его
DestroyWindow (window->hWnd); // Уничтожаем окно
window->hWnd = 0; // Обнуляем указатель окна
return FALSE; // возвращаем False
// ЗДЕСЬ ПРОБЕЖАЛ ТАРАКАН
// Теперь, когда наше окно создано, мы хотим узнать какие типы доступны.
// Мы вызываем нашу функцию InitMultiSample для создания окна
// если вернулся правильный контекст, то мы уничтожаем текущее окно
// и создаем новой, используя интерфейс множественной выборки.
ShowWindow (window->hWnd, SW_NORMAL); // Сделаем окно видимым
Ок, и так настройка теперь завершена! Мы настроили наше окно для работы с методом множественной выборки. Теперь повеселимся, собственно делая это! К счастью, ARB решила сделать поддержку метода множественной выборки динамической, это позволяет нам включать и выключать ее вызовами glEnable / glDisable.
Читайте также: