opengl учебник
Начало работы с opengl
Поиск…
замечания
OpenGL - это открытый стандарт для рендеринга 2D и 3D-графики с использованием графического оборудования. OpenGL реализован в потрясающем массиве платформ, позволяющих приложениям, нацеленным на OpenGL, быть чрезвычайно гибкими.
Версии
Версия | Дата выхода |
---|---|
1,1 | 1997-03-04 |
1.2 | 1998-03-16 |
1.2.1 | 1998-10-14 |
1,3 | 2001-08-14 |
1.4 | 2002-07-24 |
1,5 | 2003-07-29 |
2,0 | 2004-09-07 |
2,1 | 2006-07-02 |
3.0 | 2008-08-11 |
3,1 | 2009-03-24 |
3,2 | 2009-08-03 |
3,3 | 2010-03-11 |
4,0 | 2010-03-11 |
4,1 | 2010-07-26 |
4,2 | 2011-08-08 |
4,3 | 2012-08-06 |
4,4 | 2013-07-22 |
4.5 | 2014-08-11 |
Получение OpenGL
Одно из самых распространенных заблуждений относительно OpenGL заключается в том, что это была библиотека, которую можно было установить из сторонних источников. Это заблуждение приводит ко многим вопросам в форме «Как установить OpenGL» или «Где скачать OpenGL SDK».
Это не то, как OpenGL находит путь в компьютерную систему. OpenGL сам по себе представляет собой всего лишь набор спецификаций для команд, которым должна следовать реализация. Так важно выполнение. И пока реализация OpenGL является частью драйверов GPU. Это может измениться в будущем, когда новый интерфейс программирования GPU позволит по-настоящему реализовать OpenGL в качестве библиотеки, но на данный момент это API программирования для графических драйверов.
Когда OpenGL был впервые выпущен, API как-то нашел свой путь в контракте ABI (Application Binary Interface) Windows, Solaris и Linux (LSB-4 Desktop) в дополнение к его происхождению Sun Irix. Apple следовала и фактически интегрировала OpenGL так глубоко в MacOS X, что доступная версия OpenGL тесно связана с установленной версией MacOS X. Это имеет заметный эффект, что среды системного программирования для этих операционных систем (т.е. компилятор и компоновщик ссылок, которые ориентированы на эти системы) должны также предоставлять определения API OpenGL. Такого нет необходимости фактически устанавливать SDK для OpenGL. Технически возможно программировать OpenGL в этих операционных системах без необходимости установки выделенного SDK, предполагая, что установлена среда сборки после целевого ABI.
Побочным эффектом этих строгих правил ABI является то, что версия OpenGL, открытая через интерфейс привязки, является самым низким общим знаменателем, который может ожидать, что программы, запущенные на целевой платформе, могут быть доступны. Следовательно, современные функции OpenGL должны быть доступны через механизм расширения, который подробно описывается отдельно.
Linux
В Linux довольно распространено разделение пакетов разработки для различных аспектов системы, чтобы их можно было обновлять индивидуально. В большинстве дистрибутивов Linux файлы разработки для OpenGL содержатся в выделенном пакете, который обычно зависит от мета-пакета разработки приложений для настольных приложений. Поэтому для установки файлов разработки OpenGL для Linux, как правило, позаботятся об установке мета-пакета /
Майкрософт Виндоус
Библиотека привязки API opengl32.dll
(названная так для 32-разрядных и 64-разрядных версий Windows) поставляется по умолчанию с каждой версией Windows с Windows NT-4 и Windows 95B (оба около 1997 года). Однако эта DLL не обеспечивает фактическую реализацию OpenGL (кроме программной резервной копии, единственной целью которой является обеспечение безопасности программ, если другая реализация OpenGL не установлена). Эта DLL принадлежит Windows и не может быть изменена или перемещена! Современные версии OpenGL поставляются как часть так называемого Installable Client Driver (ICD) и доступны через opengl32.dll
по умолчанию, который поставляется с предустановленной версией каждой версии Windows. Однако корпорация Microsoft решила, что графические драйверы, установленные с помощью Центра обновления Windows , не будут устанавливать / обновлять OpenGL ICD. Поскольку в таких новых установках Windows с установленными драйверами автоматически отсутствует поддержка современных функций OpenGL. Чтобы получить OpenGL ICD с современными функциями, графические драйверы должны быть загружены непосредственно с веб-сайта поставщика графического процессора и установлены вручную.
Что касается разработки, никаких дополнительных шагов не следует предпринимать. Все компиляторы C / C ++, следующие спецификациям Windows ABI, поставляются с заголовками и заглушкой компоновщика (opengl32.lib), необходимыми для сборки и связывания исполняемых файлов, которые используют OpenGL.
Ручная настройка OpenGL в Windows
Полный код примера, включенный в конце
Компоненты Windows для OpenGL
WGL
WGL (может быть выражен wiggle ) означает «Windows-GL», как в «интерфейсе между Windows и OpenGL» - набор функций из Windows API для связи с OpenGL. Функции WGL имеют префикс wgl, а его маркеры имеют префикс WGL_ .
Версия OpenGL по умолчанию, поддерживаемая в системах Microsoft, составляет 1,1. Это очень старая версия (последняя - 4.5). Способ получения последних версий - обновить графические драйверы, но ваша видеокарта должна поддерживать эти новые версии.
Полный список функций WGL можно найти здесь .
Интерфейс графического устройства (GDI)
GDI (сегодня обновленный до GDI +) - это 2D-интерфейс рисования, который позволяет рисовать окно в Windows. Вам нужно, чтобы GDI инициализировал OpenGL и разрешил ему взаимодействовать с ним (но фактически не использует GDI).
В GDI каждое окно имеет контекст устройства (DC), который используется для идентификации цели рисования при вызове функций (вы передаете его как параметр). Однако OpenGL использует свой собственный контекст рендеринга (RC) . Таким образом, DC будет использоваться для создания RC.
Основные настройки
Создание окна
Итак, для того, чтобы делать вещи в OpenGL, нам нужен RC, и чтобы получить RC, нам нужен DC, и для получения DC нам нужно окно. Создание окна с использованием Windows API требует нескольких шагов. Это базовая процедура, поэтому для более подробного объяснения вам следует проконсультироваться с другой документацией, потому что речь идет не об использовании Windows API.
Это настройка Windows, поэтому необходимо включить Windows.h
, а точкой входа программы должна быть процедура WinMain
с ее параметрами. Программа также должна быть связана с opengl32.dll
и с gdi32.dll
(независимо от того, находитесь ли вы в 64 или 32-битной системе).
Сначала нам нужно описать наше окно, используя структуру WNDCLASS
. Он содержит информацию о окне, которое мы хотим создать:
/* REGISTER WINDOW */
WNDCLASS window_class;
// Clear all structure fields to zero first
ZeroMemory(&window_class, sizeof(window_class));
// Define fields we need (others will be zero)
window_class.style = CS_OWNDC;
window_class.lpfnWndProc = window_procedure; // To be introduced later
window_class.hInstance = instance_handle;
window_class.lpszClassName = TEXT("OPENGL_WINDOW");
// Give our class to Windows
RegisterClass(&window_class);
/* *************** */
Для точного объяснения значения каждого поля (и для полного списка полей) обратитесь к документации MSDN.
Затем мы можем создать окно с помощью CreateWindowEx
. После создания окна мы можем получить DC:
/* CREATE WINDOW */
HWND window_handle = CreateWindowEx(WS_EX_OVERLAPPEDWINDOW,
TEXT("OPENGL_WINDOW"),
TEXT("OpenGL window"),
WS_OVERLAPPEDWINDOW,
0, 0,
800, 600,
NULL,
NULL,
instance_handle,
NULL);
HDC dc = GetDC(window_handle);
ShowWindow(window_handle, SW_SHOW);
/* ************* */
Наконец, нам нужно создать цикл сообщений, который принимает события окна из ОС:
/* EVENT PUMP */
MSG msg;
while (true) {
if (PeekMessage(&msg, window_handle, 0, 0, PM_REMOVE)) {
if (msg.message == WM_QUIT)
break;
TranslateMessage(&msg);
DispatchMessage(&msg);
}
// draw(); <- there goes your drawing
SwapBuffers(dc); // To be mentioned later
}
/* ********** */
Формат пикселей
OpenGL должен знать некоторую информацию о нашем окне, такую как цветовая битность, метод буферизации и т. Д. Для этого мы используем формат пикселей . Однако мы можем только предложить ОС, какой формат пикселя нам нужен, и ОС будет поставлять наиболее похожие поддерживаемые , мы не имеем прямого контроля над ним. Вот почему он называется только дескриптором .
/* PIXEL FORMAT */
PIXELFORMATDESCRIPTOR descriptor;
// Clear all structure fields to zero first
ZeroMemory(&descriptor, sizeof(descriptor));
// Describe our pixel format
descriptor.nSize = sizeof(descriptor);
descriptor.nVersion = 1;
descriptor.dwFlags = PFD_DRAW_TO_WINDOW | PFD_DRAW_TO_BITMAP | PFD_SUPPORT_OPENGL | PFD_GENERIC_ACCELERATED | PFD_DOUBLEBUFFER | PFD_SWAP_LAYER_BUFFERS;
descriptor.iPixelType = PFD_TYPE_RGBA;
descriptor.cColorBits = 32;
descriptor.cRedBits = 8;
descriptor.cGreenBits = 8;
descriptor.cBlueBits = 8;
descriptor.cAlphaBits = 8;
descriptor.cDepthBits = 32;
descriptor.cStencilBits = 8;
// Ask for a similar supported format and set it
int pixel_format = ChoosePixelFormat(dc, &descriptor);
SetPixelFormat(dc, pixel_format, &descriptor);
/* *********************** */
Мы включили двойную буферизацию в поле dwFlags
, поэтому мы должны вызвать SwapBuffers
, чтобы видеть вещи после рисования.
Рендеринг контекста
После этого мы можем просто создать наш контекст рендеринга:
/* RENDERING CONTEXT */
HGLRC rc = wglCreateContext(dc);
wglMakeCurrent(dc, rc);
/* ***************** */
Обратите внимание, что только один поток может использовать RC за раз. Если вы хотите использовать его из другого потока позже, вы должны вызвать wglMakeCurrent
чтобы снова активировать его (это отключит его в текущем потоке и так далее).
Получение функций OpenGL
Функции OpenGL получаются с помощью указателей функций. Общая процедура:
- Как-то получить типы указателей функций (по существу, прототипы функций)
- Объявите каждую функцию, которую мы хотели бы использовать (с ее указателем на функцию)
- Получить фактическую функцию
Например, рассмотрим glBegin:
// We need to somehow find something that contains something like this,
// as we can't know all the OpenGL function prototypes
typedef void (APIENTRY *PFNGLBEGINPROC)(GLenum);
// After that, we need to declare the function in order to use it
PFNGLBEGINPROC glBegin;
// And finally, we need to somehow make it an actual function
(«PFN» означает «указатель на функцию», затем следует имя функции OpenGL и «PROC» в конце - это обычное имя типа указателя функции OpenGL.)
Вот как это делается в Windows. Как упоминалось ранее, Microsoft только отправляет OpenGL 1.1. Во-первых, типы указателей функций для этой версии можно найти, включив GL/gl.h
После этого мы объявляем все функции, которые мы намерены использовать, как показано выше (выполнение этого в файле заголовка и объявление их «extern» позволит нам использовать их все после их загрузки один раз, просто включив его). Наконец, загрузка функций OpenGL 1.1 выполняется путем открытия библиотеки DLL:
HMODULE gl_module = LoadLibrary(TEXT("opengl32.dll"));
/* Load all the functions here */
glBegin = (PFNGLBEGINPROC)GetProcAddress("glBegin");
// ...
/* *************************** */
FreeLibrary(gl_module);
Однако мы, вероятно, хотим немного больше, чем OpenGL 1.1. Но Windows не дает нам прототипов функций или экспортированных функций для чего-либо выше этого. Прототипы должны быть приобретены из реестра OpenGL . Есть три интересующих нас файла: GL/glext.h
, GL/glcorearb.h
и GL/wglext.h
.
Чтобы завершить GL/gl.h
предоставленный Windows, нам нужен GL/glext.h
. Он содержит (как описано в реестре) «OpenGL 1.2 и выше профили совместимости и интерфейсы расширений» (подробнее о профилях и расширениях позже, где мы увидим, что на самом деле не рекомендуется использовать эти два файла ).
Фактические функции должны быть получены с помощью wglGetProcAddress
(нет необходимости открывать DLL для этого парня, их там нет, просто используйте функцию). С его помощью мы можем получить все функции из OpenGL 1.2 и выше (но не 1.1). Обратите внимание, что для правильной работы контекста рендеринга OpenGL необходимо создать и сделать текущим . Так, например, glClear
:
// Include the header from the OpenGL registry for function pointer types
// Declare the functions, just like before
PFNGLCLEARPROC glClear;
// ...
// Get the function
glClear = (PFNGLCLEARPROC)wglGetProcAddress("glClear");
Мы можем создать процедуру get_proc
для обёртки, которая использует как wglGetProcAddress
и GetProcAddress
:
// Get function pointer
void* get_proc(const char *proc_name)
{
void *proc = (void*)wglGetProcAddress(proc_name);
if (!proc) proc = (void*)GetProcAddress(gl_module, proc_name); // gl_module must be somewhere in reach
return proc;
}
Итак, чтобы завершить, мы создали бы заголовочный файл с объявлениями указателей функций следующим образом:
extern PFNGLCLEARCOLORPROC glClearColor;
extern PFNGLCLEARDEPTHPROC glClearDepth;
extern PFNGLCLEARPROC glClear;
extern PFNGLCLEARBUFFERIVPROC glClearBufferiv;
extern PFNGLCLEARBUFFERFVPROC glClearBufferfv;
// And so on...
Затем мы можем создать такую процедуру, как load_gl_functions
которую мы вызываем только один раз, и работает так:
glClearColor = (PFNGLCLEARCOLORPROC)get_proc("glClearColor");
glClearDepth = (PFNGLCLEARDEPTHPROC)get_proc("glClearDepth");
glClear = (PFNGLCLEARPROC)get_proc("glClear");
glClearBufferiv = (PFNGLCLEARBUFFERIVPROC)get_proc("glClearBufferiv");
glClearBufferfv = (PFNGLCLEARBUFFERFVPROC)get_proc("glClearBufferfv");
И ты все настроен! Просто включите заголовок с указателями функций и GL.
Лучшая настройка
Профили OpenGL
OpenGL находится в разработке более 20 лет, и разработчики всегда были строгими относительно обратной совместимости (BC) . Из-за этого очень сложно добавить новую функцию. Таким образом, в 2008 году он был разделен на два «профиля». Ядро и совместимость . Основной профиль ломает BC в пользу улучшения производительности и некоторых новых функций. Он даже полностью удаляет некоторые устаревшие функции. Профиль совместимости поддерживает BC со всеми версиями до 1.0, а некоторые новые функции недоступны на нем. Он предназначен только для старых, устаревших систем, все новые приложения должны использовать основной профиль.
Из-за этого возникает проблема с нашей базовой настройкой - она предоставляет только контекст, который обратно совместим с OpenGL 1.0. Формат пикселей также ограничен. Существует лучший подход, используя расширения.
Расширения OpenGL
Любое дополнение к исходной функциональности OpenGL называется расширением. Как правило, они могут либо сделать некоторые вещи законными, которые раньше не были, расширить диапазон значений параметров, расширить GLSL и даже добавить совершенно новые функции.
Существуют три основные группы расширений: поставщик, EXT и ARB. Расширения поставщиков исходят от конкретного поставщика, и у них есть определенная производителем марка, например AMD или NV. Расширения EXT производятся несколькими поставщиками, работающими вместе. Через некоторое время они могут стать расширениями ARB, которые являются официально поддерживаемыми и одобрены ARB.
Чтобы получить типы указателей функций и прототипы функций всех расширений и, как упоминалось ранее, все типы указателей функций из OpenGL 1.2 и выше , необходимо загрузить файлы заголовков из реестра OpenGL . Как обсуждалось, для новых приложений лучше использовать основной профиль, поэтому было бы GL/glcorearb.h
включить GL/glcorearb.h
вместо GL/gl.h
и GL/glext.h
(если вы используете GL/glcorearb.h
тогда GL/glcorearb.h
Включить GL/gl.h
).
Существуют также расширения для WGL, в GL/wglext.h
. Например, функция для получения списка всех поддерживаемых расширений фактически является самим расширением wglGetExtensionsStringARB
(она возвращает большую строку с разделенным пробелом списком всех поддерживаемых расширений).
Получение расширений также осуществляется через wglGetProcAddress
, поэтому мы можем просто использовать наш обертку, как раньше.
Расширенный формат пикселей и создание контекста
Расширение WGL_ARB_pixel_format
позволяет нам создавать расширенные форматы пикселей. В отличие от ранее, мы не используем struct. Вместо этого мы передаем список желаемых атрибутов.
int pixel_format_arb;
UINT pixel_formats_found;
int pixel_attributes[] = {
WGL_SUPPORT_OPENGL_ARB, 1,
WGL_DRAW_TO_WINDOW_ARB, 1,
WGL_DRAW_TO_BITMAP_ARB, 1,
WGL_DOUBLE_BUFFER_ARB, 1,
WGL_SWAP_LAYER_BUFFERS_ARB, 1,
WGL_COLOR_BITS_ARB, 32,
WGL_RED_BITS_ARB, 8,
WGL_GREEN_BITS_ARB, 8,
WGL_BLUE_BITS_ARB, 8,
WGL_ALPHA_BITS_ARB, 8,
WGL_DEPTH_BITS_ARB, 32,
WGL_STENCIL_BITS_ARB, 8,
WGL_ACCELERATION_ARB, WGL_FULL_ACCELERATION_ARB,
WGL_PIXEL_TYPE_ARB, WGL_TYPE_RGBA_ARB,
0
};
BOOL result = wglChoosePixelFormatARB(dc, pixel_attributes, NULL, 1, &pixel_format_arb, &pixel_formats_found);
Аналогично, расширение WGL_ARB_create_context
позволяет нам создавать расширенные контексты:
GLint context_attributes[] = {
WGL_CONTEXT_MAJOR_VERSION_ARB, 3,
WGL_CONTEXT_MINOR_VERSION_ARB, 3,
WGL_CONTEXT_PROFILE_MASK_ARB, WGL_CONTEXT_CORE_PROFILE_BIT_ARB,
0
};
HGLRC new_rc = wglCreateContextAttribsARB(dc, 0, context_attributes);
Для точного объяснения параметров и функций обратитесь к спецификации OpenGL.
Почему мы не начали с них? Ну, это потому, что расширения позволяют нам это делать, и для получения расширений нам нужен wglGetProcAddress
, но это работает только с активным допустимым контекстом. Поэтому, по сути, прежде чем мы сможем создать необходимый нам контекст, нам нужно уже иметь некоторый контекст, и его обычно называют фиктивным контекстом .
Однако Windows не позволяет устанавливать формат пикселя окна более одного раза. Из-за этого окно необходимо уничтожить и воссоздать, чтобы применить новые вещи:
wglMakeCurrent(dc, NULL);
wglDeleteContext(rc);
ReleaseDC(window_handle, dc);
DestroyWindow(window_handle);
// Recreate the window...
Полный пример кода:
/* We want the core profile, so we include GL/glcorearb.h. When including that, then
GL/gl.h should not be included.
If using compatibility profile, the GL/gl.h and GL/glext.h need to be included.
GL/wglext.h gives WGL extensions.
Note that Windows.h needs to be included before them. */
#include <cstdio>
#include <Windows.h>
#include <GL/glcorearb.h>
#include <GL/wglext.h>
LRESULT CALLBACK window_procedure(HWND, UINT, WPARAM, LPARAM);
void* get_proc(const char*);
/* gl_module is for opening the DLL, and the quit flag is here to prevent
quitting when recreating the window (see the window_procedure function) */
HMODULE gl_module;
bool quit = false;
/* OpenGL function declarations. In practice, we would put these in a
separate header file and add "extern" in front, so that we can use them
anywhere after loading them only once. */
PFNWGLGETEXTENSIONSSTRINGARBPROC wglGetExtensionsStringARB;
PFNWGLCHOOSEPIXELFORMATARBPROC wglChoosePixelFormatARB;
PFNWGLCREATECONTEXTATTRIBSARBPROC wglCreateContextAttribsARB;
PFNGLGETSTRINGPROC glGetString;
int WINAPI WinMain(HINSTANCE instance_handle, HINSTANCE prev_instance_handle, PSTR cmd_line, int cmd_show) {
/* REGISTER WINDOW */
WNDCLASS window_class;
// Clear all structure fields to zero first
ZeroMemory(&window_class, sizeof(window_class));
// Define fields we need (others will be zero)
window_class.style = CS_HREDRAW | CS_VREDRAW | CS_OWNDC;
window_class.lpfnWndProc = window_procedure;
window_class.hInstance = instance_handle;
window_class.lpszClassName = TEXT("OPENGL_WINDOW");
// Give our class to Windows
RegisterClass(&window_class);
/* *************** */
/* CREATE WINDOW */
HWND window_handle = CreateWindowEx(WS_EX_OVERLAPPEDWINDOW,
TEXT("OPENGL_WINDOW"),
TEXT("OpenGL window"),
WS_OVERLAPPEDWINDOW,
0, 0,
800, 600,
NULL,
NULL,
instance_handle,
NULL);
HDC dc = GetDC(window_handle);
ShowWindow(window_handle, SW_SHOW);
/* ************* */
/* PIXEL FORMAT */
PIXELFORMATDESCRIPTOR descriptor;
// Clear all structure fields to zero first
ZeroMemory(&descriptor, sizeof(descriptor));
// Describe our pixel format
descriptor.nSize = sizeof(descriptor);
descriptor.nVersion = 1;
descriptor.dwFlags = PFD_DRAW_TO_WINDOW | PFD_DRAW_TO_BITMAP | PFD_SUPPORT_OPENGL | PFD_GENERIC_ACCELERATED | PFD_DOUBLEBUFFER | PFD_SWAP_LAYER_BUFFERS;
descriptor.iPixelType = PFD_TYPE_RGBA;
descriptor.cColorBits = 32;
descriptor.cRedBits = 8;
descriptor.cGreenBits = 8;
descriptor.cBlueBits = 8;
descriptor.cAlphaBits = 8;
descriptor.cDepthBits = 32;
descriptor.cStencilBits = 8;
// Ask for a similar supported format and set it
int pixel_format = ChoosePixelFormat(dc, &descriptor);
SetPixelFormat(dc, pixel_format, &descriptor);
/* *********************** */
/* RENDERING CONTEXT */
HGLRC rc = wglCreateContext(dc);
wglMakeCurrent(dc, rc);
/* ***************** */
/* LOAD FUNCTIONS (should probably be put in a separate procedure) */
gl_module = LoadLibrary(TEXT("opengl32.dll"));
wglGetExtensionsStringARB = (PFNWGLGETEXTENSIONSSTRINGARBPROC)get_proc("wglGetExtensionsStringARB");
wglChoosePixelFormatARB = (PFNWGLCHOOSEPIXELFORMATARBPROC)get_proc("wglChoosePixelFormatARB");
wglCreateContextAttribsARB = (PFNWGLCREATECONTEXTATTRIBSARBPROC)get_proc("wglCreateContextAttribsARB");
glGetString = (PFNGLGETSTRINGPROC)get_proc("glGetString");
FreeLibrary(gl_module);
/* ************** */
/* PRINT VERSION */
const GLubyte *version = glGetString(GL_VERSION);
printf("%s\n", version);
fflush(stdout);
/* ******* */
/* NEW PIXEL FORMAT*/
int pixel_format_arb;
UINT pixel_formats_found;
int pixel_attributes[] = {
WGL_SUPPORT_OPENGL_ARB, 1,
WGL_DRAW_TO_WINDOW_ARB, 1,
WGL_DRAW_TO_BITMAP_ARB, 1,
WGL_DOUBLE_BUFFER_ARB, 1,
WGL_SWAP_LAYER_BUFFERS_ARB, 1,
WGL_COLOR_BITS_ARB, 32,
WGL_RED_BITS_ARB, 8,
WGL_GREEN_BITS_ARB, 8,
WGL_BLUE_BITS_ARB, 8,
WGL_ALPHA_BITS_ARB, 8,
WGL_DEPTH_BITS_ARB, 32,
WGL_STENCIL_BITS_ARB, 8,
WGL_ACCELERATION_ARB, WGL_FULL_ACCELERATION_ARB,
WGL_PIXEL_TYPE_ARB, WGL_TYPE_RGBA_ARB,
0
};
BOOL result = wglChoosePixelFormatARB(dc, pixel_attributes, NULL, 1, &pixel_format_arb, &pixel_formats_found);
if (!result) {
printf("Could not find pixel format\n");
fflush(stdout);
return 0;
}
/* **************** */
/* RECREATE WINDOW */
wglMakeCurrent(dc, NULL);
wglDeleteContext(rc);
ReleaseDC(window_handle, dc);
DestroyWindow(window_handle);
window_handle = CreateWindowEx(WS_EX_OVERLAPPEDWINDOW,
TEXT("OPENGL_WINDOW"),
TEXT("OpenGL window"),
WS_OVERLAPPEDWINDOW,
0, 0,
800, 600,
NULL,
NULL,
instance_handle,
NULL);
dc = GetDC(window_handle);
ShowWindow(window_handle, SW_SHOW);
/* *************** */
/* NEW CONTEXT */
GLint context_attributes[] = {
WGL_CONTEXT_MAJOR_VERSION_ARB, 3,
WGL_CONTEXT_MINOR_VERSION_ARB, 3,
WGL_CONTEXT_PROFILE_MASK_ARB, WGL_CONTEXT_CORE_PROFILE_BIT_ARB,
0
};
rc = wglCreateContextAttribsARB(dc, 0, context_attributes);
wglMakeCurrent(dc, rc);
/* *********** */
/* EVENT PUMP */
MSG msg;
while (true) {
if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) {
if (msg.message == WM_QUIT)
break;
TranslateMessage(&msg);
DispatchMessage(&msg);
}
// draw(); <- there goes your drawing
SwapBuffers(dc);
}
/* ********** */
return 0;
}
// Procedure that processes window events
LRESULT CALLBACK window_procedure(HWND window_handle, UINT message, WPARAM param_w, LPARAM param_l)
{
/* When destroying the dummy window, WM_DESTROY message is going to be sent,
but we don't want to quit the application then, and that is controlled by
the quit flag. */
switch(message) {
case WM_DESTROY:
if (!quit) quit = true;
else PostQuitMessage(0);
return 0;
}
return DefWindowProc(window_handle, message, param_w, param_l);
}
/* A procedure for getting OpenGL functions and OpenGL or WGL extensions.
When looking for OpenGL 1.2 and above, or extensions, it uses wglGetProcAddress,
otherwise it falls back to GetProcAddress. */
void* get_proc(const char *proc_name)
{
void *proc = (void*)wglGetProcAddress(proc_name);
if (!proc) proc = (void*)GetProcAddress(gl_module, proc_name);
return proc;
}
Скомпилирован с g++ GLExample.cpp -lopengl32 -lgdi32
с MinGW / Cygwin или cl GLExample.cpp opengl32.lib gdi32.lib user32.lib
с компилятором MSVC. Убедитесь, однако, что заголовки из реестра OpenGL находятся в пути include. Если нет, используйте флаг -I
для g++
или /I
для cl
, чтобы сообщить компилятору, где они находятся.
Создание OpenGL 4.1 с C ++ и Cocoa
Примечание. В этом примере будет некоторый Objective-c. В этом примере мы создадим оболочку для C ++. Поэтому не беспокойтесь об этом.
Сначала запустите Xcode и создайте проект.
Удалите все источники, кроме файла Info.plist (ваше приложение не будет работать без него)
Создайте 4 новых исходных файла: файл и заголовок Objective-c ++ (я назвал мой MacApp). Класс C ++ (я назвал my (Application)
В левом верхнем углу (с именем проекта) щелкните по нему и добавьте связанные фреймворки и библиотеки. Добавить: OpenGL.Framework AppKit.Framework GLKit.Framework
Вероятно, ваш проект будет выглядеть следующим образом:
NSApplication - основной класс, который вы используете при создании приложения MacOS. Это позволяет вам регистрировать окна и захватывать события.
Мы хотим зарегистрировать (наше) окно в NSApplication. Сначала создайте в своем объектно-c ++-заголовке объект-c-класс, который наследует NSWindow и реализует NSApplicationDelegate. NSWindow нуждается в указателе на приложение C ++, OpenGL View и таймер для цикла рисования
//Mac_App_H
#import <Cocoa/Cocoa.h>
#import "Application.hpp"
#import <memory>
NSApplication* application;
@interface MacApp : NSWindow <NSApplicationDelegate>{
std::shared_ptr<Application> appInstance;
}
@property (nonatomic, retain) NSOpenGLView* glView;
-(void) drawLoop:(NSTimer*) timer;
@end
Мы называем это основным
int main(int argc, const char * argv[]) {
MacApp* app;
application = [NSApplication sharedApplication];
[NSApp setActivationPolicy:NSApplicationActivationPolicyRegular];
//create a window with the size of 600 by 600
app = [[MacApp alloc] initWithContentRect:NSMakeRect(0, 0, 600, 600) styleMask:NSTitledWindowMask | NSClosableWindowMask | NSMiniaturizableWindowMask backing:NSBackingStoreBuffered defer:YES];
[application setDelegate:app];
[application run];
}
Реализация нашего окна на самом деле довольно просто. Сначала мы объявляем с синтезом нашего glview и добавляем глобальную цель-c булевым, когда окно должно закрываться.
#import "MacApp.h"
@implementation MacApp
@synthesize glView;
BOOL shouldStop = NO;
Теперь для конструктора. Я предпочитаю использовать initWithContentRect.
-(id)initWithContentRect:(NSRect)contentRect styleMask:(NSUInteger)aStyle backing:(NSBackingStoreType)bufferingType defer:(BOOL)flag{
if(self = [super initWithContentRect:contentRect styleMask:aStyle backing:bufferingType defer:flag]){
//sets the title of the window (Declared in Plist)
[self setTitle:[[NSProcessInfo processInfo] processName]];
//This is pretty important.. OS X starts always with a context that only supports openGL 2.1
//This will ditch the classic OpenGL and initialises openGL 4.1
NSOpenGLPixelFormatAttribute pixelFormatAttributes[] ={
NSOpenGLPFAOpenGLProfile, NSOpenGLProfileVersion3_2Core,
NSOpenGLPFAColorSize , 24 ,
NSOpenGLPFAAlphaSize , 8 ,
NSOpenGLPFADoubleBuffer ,
NSOpenGLPFAAccelerated ,
NSOpenGLPFANoRecovery ,
0
};
NSOpenGLPixelFormat* format = [[NSOpenGLPixelFormat alloc]initWithAttributes:pixelFormatAttributes];
//Initialize the view
glView = [[NSOpenGLView alloc]initWithFrame:contentRect pixelFormat:format];
//Set context and attach it to the window
[[glView openGLContext]makeCurrentContext];
//finishing off
[self setContentView:glView];
[glView prepareOpenGL];
[self makeKeyAndOrderFront:self];
[self setAcceptsMouseMovedEvents:YES];
[self makeKeyWindow];
[self setOpaque:YES];
//Start the c++ code
appInstance = std::shared_ptr<Application>(new Application());
}
return self;
}
Хорошо ... теперь у нас действительно запущенное приложение. Возможно, вы видите черный экран или мерцание.
Давайте начнем рисовать удивительный треугольник. (В c ++)
Мой заголовок приложения
#ifndef Application_hpp
#define Application_hpp
#include <iostream>
#include <OpenGL/gl3.h>
class Application{
private:
GLuint program;
GLuint vao;
public:
Application();
void update();
~Application();
};
#endif /* Application_hpp */
Реализация:
Application::Application(){
static const char * vs_source[] =
{
"#version 410 core \n"
" \n"
"void main(void) \n"
"{ \n"
" const vec4 vertices[] = vec4[](vec4( 0.25, -0.25, 0.5, 1.0), \n"
" vec4(-0.25, -0.25, 0.5, 1.0), \n"
" vec4( 0.25, 0.25, 0.5, 1.0)); \n"
" \n"
" gl_Position = vertices[gl_VertexID]; \n"
"} \n"
};
static const char * fs_source[] =
{
"#version 410 core \n"
" \n"
"out vec4 color; \n"
" \n"
"void main(void) \n"
"{ \n"
" color = vec4(0.0, 0.8, 1.0, 1.0); \n"
"} \n"
};
program = glCreateProgram();
GLuint fs = glCreateShader(GL_FRAGMENT_SHADER);
glShaderSource(fs, 1, fs_source, NULL);
glCompileShader(fs);
GLuint vs = glCreateShader(GL_VERTEX_SHADER);
glShaderSource(vs, 1, vs_source, NULL);
glCompileShader(vs);
glAttachShader(program, vs);
glAttachShader(program, fs);
glLinkProgram(program);
glGenVertexArrays(1, &vao);
glBindVertexArray(vao);
}
void Application::update(){
static const GLfloat green[] = { 0.0f, 0.25f, 0.0f, 1.0f };
glClearBufferfv(GL_COLOR, 0, green);
glUseProgram(program);
glDrawArrays(GL_TRIANGLES, 0, 3);
}
Application::~Application(){
glDeleteVertexArrays(1, &vao);
glDeleteProgram(program);
}
Теперь нам нужно снова и снова вызывать обновление (если вы хотите что-то переместить). Внедрите в свой класс object-c
-(void) drawLoop:(NSTimer*) timer{
if(shouldStop){
[self close];
return;
}
if([self isVisible]){
appInstance->update();
[glView update];
[[glView openGLContext] flushBuffer];
}
}
И добавьте этот метод в реализацию вашего класса object-c:
- (void)applicationDidFinishLaunching:(NSNotification *)notification {
[NSTimer scheduledTimerWithTimeInterval:0.000001 target:self selector:@selector(drawLoop:) userInfo:nil repeats:YES];
}
это вызовет функцию обновления вашего класса c ++ снова и снова (каждые 0,000001 секунд, чтобы быть точным)
Чтобы закончить, мы закрываем окно при нажатии кнопки закрытия:
- (BOOL)applicationShouldTerminateAfterLastWindowClosed:(NSApplication *)theApplication{
return YES;
}
- (void)applicationWillTerminate:(NSNotification *)aNotification{
shouldStop = YES;
}
Поздравляем, теперь у вас есть удивительное окно с треугольником OpenGL без каких-либо сторонних фреймворков.
Создание контекста OpenGL в Cross Platform (с использованием SDL2)
Создание окна с контекстом OpenGL (добавление нагрузки через GLEW ):
#define GLEW_STATIC #include <GL/glew.h> #include <SDL2/SDL.h> int main(int argc, char* argv[]) { SDL_Init(SDL_INIT_VIDEO); /* Initialises Video Subsystem in SDL */ /* Setting up OpenGL version and profile details for context creation */ SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE); SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 3); SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 2); /* A 800x600 window. Pretty! */ SDL_Window* window = SDL_CreateWindow ( "SDL Context", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, 800, 600, SDL_WINDOW_OPENGL ); /* Creating OpenGL Context */ SDL_GLContext gl_context = SDL_GL_CreateContext(window); /* Loading Extensions */ glewExperimental = GL_TRUE; glewInit(); /* The following code is for error checking. * If OpenGL has initialised properly, this should print 1. * Remove it in production code. */ GLuint vertex_buffer; glGenBuffers(1, &vertex_buffer); printf("%u\n", vertex_buffer); /* Error checking ends here */ /* Main Loop */ SDL_Event window_event; while(1) { if (SDL_PollEvent(&window_event)) { if (window_event.type == SDL_QUIT) { /* If user is exiting the application */ break; } } /* Swap the front and back buffer for flicker-free rendering */ SDL_GL_SwapWindow(window); } /* Freeing Memory */ glDeleteBuffers(1, &vertex_buffer); SDL_GL_DeleteContext(gl_context); SDL_Quit(); return 0; }
Настройка современного OpenGL 4.1 на macOS (Xcode, GLFW и GLEW)
1. Установите GLFW
Первый шаг - создать окно OpenGL. GLFW - это библиотека с открытым исходным кодом, многоплатформенная библиотека для создания окон с OpenGL, для установки GLFW сначала загружайте свои файлы с сайта www.glfw.org
Извлеките папку GLFW, и ее содержимое будет выглядеть следующим образом:
Загрузите и установите CMake для сборки GLFW. Перейти на www.cmake.org/download/ , загрузить CMake и установить для MAC OS X
Если Xcode не установлен. Загрузите и установите Xcode из Mac App Store.
Создание новой папки Создание внутри папки GLFW
Откройте CMake, нажмите кнопку « Обзор источника» , чтобы выбрать папку GLFW (убедитесь, что CMakeLists.txt) находится внутри этой папки. После этого нажмите кнопку « Обзор сборки» и выберите новую созданную папку « Создать » на предыдущем шаге.
Теперь нажмите кнопку « Настроить» и выберите «Генератор Xcode как генератор» с параметром « Использовать стандартные компиляторы» и нажмите « Готово» .
Отметьте опцию BUILD_SHARED_LIBS, а затем снова нажмите кнопку « Настроить» и, наконец, нажмите кнопку « Создать» .
После поколения CMake должно выглядеть так
Теперь Open Finder и goto / usr , создайте локальное имя папки, если оно еще не существует. Откройте локальную папку и создать две папки включают в себя и Lib , если уже не там.
Теперь откройте папку GLFW и goto Build (где CMake создал файлы). Откройте файл GLFW.xcodeproj в Xcode.
Выберите « Установить»> «Мой Mac», а затем нажмите « Запустить» (кнопка «Форма игры»).
Теперь он успешно установлен (игнорируйте предупреждения).
Чтобы убедиться, что Open Finder и папка goto / usr / local / lib и три библиотеки библиотеки GLFW уже присутствуют там (если нет, то откройте папку « Сборка » внутри папки GLFW и перейдите в src / Debug, скопируйте все файлы в / usr / local / lib )
Open Finder и goto / usr / local / include и папка GLFW будут присутствовать там с двумя файлами заголовков внутри него по имени glfw3.h и glfw3native.h
2. Установите GLEW
GLEW - это кросс-платформенная библиотека, которая помогает в поиске и загрузке расширений OpenGL. Он предоставляет механизмы времени выполнения для определения, какие расширения OpenGL поддерживаются на целевой платформе. Это только для современного OpenGL (OpenGL версии 3.2 и выше, который требует определения функций во время выполнения). Чтобы установить первую загрузку своих файлов с glew.sourceforge.net
Извлеките папку GLFW, и ее содержимое будет выглядеть так.
Теперь откройте терминал, перейдите в папку GLEW и введите следующие команды
make
sudo make install
make clean
Теперь GLEW успешно установлен. Чтобы убедиться, что его установленный, Open Finder, перейдите в каталог / usr / local / include, и в нем будет присутствовать папка GL с тремя файлами заголовков внутри нее по имени glew.h , glxew.h и wglew.h
Откройте Finder и перейдите в / usr / local / lib, и файлы библиотеки GLEW будут уже присутствовать там
3. Тестирование и запуск
Теперь мы успешно установили GLFW и GLEW. Его время кодировать. Откройте Xcode и создайте новый проект Xcode. Выберите « Инструмент командной строки», затем выполните следующие действия и выберите язык C ++ .
Xcode создаст новый проект командной строки.
Нажмите на название проекта, а на вкладке « Параметры сборки» выберите « Базовый для всех» в разделе « Пути поиска », добавьте / usr / local / include в пути поиска заголовков и добавьте / usr / local / lib в пути поиска библиотеки
Нажмите на название проекта, а на вкладке « Сборка фаз » и в разделе « Связывание с бинарными библиотеками» добавьте OpenGL.framework, а также добавьте недавно созданные библиотеки GLFW и GLEW из / usr / local / lib
Теперь мы готовы кодировать в Modern Open GL 4.1 на macOS, используя C ++ и Xcode. Следующий код создаст окно OpenGL, используя GLFW с выводом пустого экрана.
#include <GL/glew.h>
#include <GLFW/glfw3.h>
// Define main function
int main()
{
// Initialize GLFW
glfwInit();
// Define version and compatibility settings
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 2);
glfwWindowHint(GLFW_OPENGL_PROFILE,GLFW_OPENGL_CORE_PROFILE);
glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE);
glfwWindowHint(GLFW_RESIZABLE, GL_FALSE);
// Create OpenGL window and context
GLFWwindow* window = glfwCreateWindow(800, 600, "OpenGL", NULL, NULL);
glfwMakeContextCurrent(window);
// Check for window creation failure
if (!window)
{
// Terminate GLFW
glfwTerminate();
return 0;
}
// Initialize GLEW
glewExperimental = GL_TRUE; glewInit();
// Event loop
while(!glfwWindowShouldClose(window))
{
// Clear the screen to black
glClearColor(0.0f, 0.0f, 0.0f, 1.0f); glClear(GL_COLOR_BUFFER_BIT);
glfwSwapBuffers(window);
glfwPollEvents();
}
// Terminate GLFW
glfwTerminate(); return 0;
}
Создать контекст Opengl с Java и LWJGL 3.0
В этом примере кода мы создадим пустое окно Opengl с использованием LWJGL 3.0+, это не содержит шагов для создания проекта в вашей среде IDE
- Создайте имя класса WindowManager, который будет содержать весь код плиты котла для создания окна контекста opengl на экране
WindowManager.java
import org.lwjgl.glfw.*;
import static org.lwjgl.glfw.Callbacks.*;
import static org.lwjgl.glfw.GLFW.*;
import static org.lwjgl.opengl.GL11.*;
import static org.lwjgl.system.MemoryUtil.*;
/**
* Class Containing code related to inflating Opengl Window
*/
public class Displaymanager {
private static long window;
public static void createDisplay(){
// Setup an error callback. The default implementation
// will print the error message in System.err.
GLFWErrorCallback.createPrint(System.err).set();
// Initialize GLFW. Most GLFW functions will not work before doing this.
if ( !glfwInit() )
throw new IllegalStateException("Unable to initialize GLFW");
// Configure our window
glfwDefaultWindowHints(); // optional, the current window hints are already the default
glfwWindowHint(GLFW_VISIBLE, GLFW_FALSE); // the window will stay hidden after creation
glfwWindowHint(GLFW_RESIZABLE, GLFW_TRUE); // the window will be resizable
int WIDTH = 300;
int HEIGHT = 300;
// Create the window
window = glfwCreateWindow(WIDTH, HEIGHT, "Hello World!", NULL, NULL);
if ( window == NULL )
throw new RuntimeException("Failed to create the GLFW window");
// Setup a key callback. It will be called every time a key is pressed, repeated or released.
glfwSetKeyCallback(window, (window, key, scancode, action, mods) -> {
if ( key == GLFW_KEY_ESCAPE && action == GLFW_RELEASE )
glfwSetWindowShouldClose(window, true); // We will detect this in our rendering loop
});
// Get the resolution of the primary monitor
GLFWVidMode vidmode = glfwGetVideoMode(glfwGetPrimaryMonitor());
// Center our window
glfwSetWindowPos(
window,
(vidmode.width() - WIDTH) / 2,
(vidmode.height() - HEIGHT) / 2
);
// Make the OpenGL context current
glfwMakeContextCurrent(window);
// Enable v-sync
glfwSwapInterval(1);
// Make the window visible
glfwShowWindow(window);
}
public static boolean isCloseRequested(){
return glfwWindowShouldClose(window);
}
public static void updateDisplay(){
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); // clear the framebuffer
glfwSwapBuffers(window); // swap the color buffers
// Poll for window events. The key callback above will only be
// invoked during this call.
glfwPollEvents();
}
public static void destroyDisplay(){
// Terminate GLFW and free the error callback
cleanUp();
glfwTerminate();
glfwSetErrorCallback(null).free();
}
private static void cleanUp() {
// Free the window callbacks and destroy the window
glfwFreeCallbacks(window);
glfwDestroyWindow(window);
}
}
- Затем создайте класс, содержащий основной цикл рендеринга, который вызовет все указанные выше функции
OpenGlMain.java
import org.lwjgl.opengl.GL;
import renderEngine.Displaymanager;
import static org.lwjgl.opengl.GL11.glClearColor;
/**
* Class to test the opengl Window
*/
public class OpenGlMain {
public static void main(String[] args) {
Displaymanager.createDisplay();
// This line is critical for LWJGL's interoperation with GLFW's
// OpenGL context, or any context that is managed externally.
// LWJGL detects the context that is current in the current thread,
// creates the GLCapabilities instance and makes the OpenGL
// bindings available for use.
GL.createCapabilities();
while (!Displaymanager.isCloseRequested()){
// Set the clear color
glClearColor(1.0f, 0.0f, 0.0f, 0.0f);
Displaymanager.updateDisplay();
}
Displaymanager.destroyDisplay();
}
}
Для получения дополнительной информации официальный представитель LWJGL Guide