- PVSM.RU - https://www.pvsm.ru -
Всем привет. Все кто хоть немного разбирался в теме OpenGL знают, что существует большое количество статей и курсов по этой теме, но многие не затрагивают современный API, а часть из них вообще рассказывают про glBegin [1] и glEnd [2]. Я постараюсь охватить некоторые нюансы нового API начиная с 4-й версии.
На этот раз я попробую написать интересную и познавательную статью, а что получилось — решать добрым хабравчанинам. Прошу простить меня за мою плохую грамматику (буду благодарен за исправления).
Если вам понравится, напишу про оптимизацию OpenGL и уменьшение DrawCall'ов.
Приступим!
Что будет в этой статье — функционал современного OpenGL
Чего не будет в этой статье — современные подходы к рендерингу на OpenGL
Direct State Access — Прямой доступ к состоянию. Средство изменения объектов OpenGL без необходимости привязывать их к контексту. Это позволяет изменять состояние объекта в локальном контексте, не затрагивая глобальное состояние, разделяемое всеми частями приложения. Это также делает API-интерфейс немного более объектно-ориентированным, поскольку функции, которые изменяют состояние объектов, могут быть четко определены. Вот что нам говорит OpenGL Wiki [3].
Как мы знаем, OpenGL — это API-интерфейс с множеством переключателей — glActiveTexture [4], glBindTexture [5] и т.д.
Отсюда у нас возникают некоторые проблемы:
Что же предложили нам Khronos group и как же помогает DSA?
В теории DSA может помочь свести количество операций не относящихся к отрисовки и меняющих состояние к нулю… Но это не точно.
Теперь я вкратце пробегусь по некоторым новым функциям, подробно останавливаться на параметрах не буду, оставлю линки на вики.
glGenTextures(1, &name);
glBindTexture(GL_TEXTURE_2D, name);
Стало:
glCreateTextures(GL_TEXTURE_2D, 1, &name);
glGenTextures(1, &name);
glBindTexture(GL_TEXTURE_2D, name);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexStorage2D(GL_TEXTURE_2D, 1, GL_RGBA8, width, height);
glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, width, height, GL_RGBA, GL_UNSIGNED_BYTE, pixels);
Теперь же мы это напишем так:
glCreateTextures(GL_TEXTURE_2D, 1, &name);
glTextureParameteri(name, GL_TEXTURE_WRAP_S, GL_CLAMP);
glTextureParameteri(name, GL_TEXTURE_WRAP_T, GL_CLAMP);
glTextureParameteri(name, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTextureParameteri(name, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTextureStorage2D(name, 1, GL_RGBA8, width, height);
glTextureSubImage2D(name, 0, 0, 0, width, height, GL_RGBA, GL_UNSIGNED_BYTE, pixels);
glActiveTexture(GL_TEXTURE0 + 3);
glBindTexture(GL_TEXTURE_2D, name);
Теперь:
glBindTextureUnit(3, name);
Так же изменения коснулись glTextureImage [10], он более не используется и вот почему:
glTexImage довольно небезопасная, очень легко получить невалидные текстуры, потому что функция работает без проверки при вызова, а все значения проверяются драйвером во время рисования. Для ее замены была добавлена glTexStorage [11].
glTexStorage предоставляет способ создания текстур с проверками, выполняемыми во время вызова, что сводит количество ошибок к минимуму. Хранилище текстур решает большинство, если не все проблемы, вызываемые изменяемыми текстурами, хотя неизменяемые текстуры — более надёжно.
Изменения затронули и буфер кадров:
Это не все измененные функции. Следующие на очереди — функции для буферов:
Вот список того, что сейчас входит в поддержку DSA:
С версии 4.3 был добавлен новый функционал для дебага, на мой взгляд очень полезный и удобный. Теперь OpenGL будет вызывать наш callback при ошибках и дебаг сообщениях, уровень которых мы сможем настроить.
Нам надо вызвать всего две функции для включения: glEnable [24]& glDebugMessageCallback [25], проще некуда.
glEnable(GL_DEBUG_OUTPUT);
glDebugMessageCallback(message_callback, nullptr);
Теперь напишем callback функцию для получения месседжа:
void callback(GLenum source, GLenum type, GLuint id, GLenum severity, GLsizei length, GLchar const* message, void const* user_param)
{
auto source_str = [source]() -> std::string {
switch (source)
{
case GL_DEBUG_SOURCE_API: return "API";
case GL_DEBUG_SOURCE_WINDOW_SYSTEM: return "WINDOW SYSTEM";
case GL_DEBUG_SOURCE_SHADER_COMPILER: return "SHADER COMPILER";
case GL_DEBUG_SOURCE_THIRD_PARTY: return "THIRD PARTY";
case GL_DEBUG_SOURCE_APPLICATION: return "APPLICATION";
case GL_DEBUG_SOURCE_OTHER: return "OTHER";
default: return "UNKNOWN";
}
}();
auto type_str = [type]() {
switch (type)
{
case GL_DEBUG_TYPE_ERROR: return "ERROR";
case GL_DEBUG_TYPE_DEPRECATED_BEHAVIOR: return "DEPRECATED_BEHAVIOR";
case GL_DEBUG_TYPE_UNDEFINED_BEHAVIOR: return "UNDEFINED_BEHAVIOR";
case GL_DEBUG_TYPE_PORTABILITY: return "PORTABILITY";
case GL_DEBUG_TYPE_PERFORMANCE: return "PERFORMANCE";
case GL_DEBUG_TYPE_MARKER: return "MARKER";
case GL_DEBUG_TYPE_OTHER: return "OTHER";
default: return "UNKNOWN";
}
}();
auto severity_str = [severity]() {
switch (severity) {
case GL_DEBUG_SEVERITY_NOTIFICATION: return "NOTIFICATION";
case GL_DEBUG_SEVERITY_LOW: return "LOW";
case GL_DEBUG_SEVERITY_MEDIUM: return "MEDIUM";
case GL_DEBUG_SEVERITY_HIGH: return "HIGH";
default: return "UNKNOWN";
}
}();
std::cout << source_str << ", "
<< type_str << ", "
<< severity_str << ", "
<< id << ": "
<< message << std::endl;
}
Так же мы можем настроить фильтр при помощи glDebugMessageControl [26]. Фильтр может работать в режиме фильтрации по источнику/типу/важности или набора сообщений с использованием их идентификаторов.
Фильтр сообщений в определенном скоупе:
glPushDebugGroup( GL_DEBUG_SOURCE_APPLICATION, DEPTH_FILL_ID, 11, “Depth Fill”); //Добавляем маркер
Render_Depth_Only_Pass(); //Выполняем рендеринг
glPopDebugGroup(); //Убираем маркер
Очень полезно будет отключить асинхронный вызов, что б мы могли определить порядок вызовов функций, а так же найти место ошибки в стеке при дебаге. Это делается довольно просто:
glEnable(GL_DEBUG_OUTPUT_SYNCHRONOUS);
Стоит помнить, что небезопасно вызывать OpenGL или функции окон в функции обртаного вызова, а так же обратные вызовы небесплатные и не стоит оставлять их включенными в релизной сборке.
Болеее подробно про эти и другие штучки — дрючки, можно почитать здесь [27].
Когда-то OpenGL работал как «фиксированный конвейер» — это означало, что ко всем передаваемым на визуализацию данным применялась заранее запрограммированная обработка. Следующим шагом было «програмируемый конвейер» — где программируемая часть осуществляет шейдеры, написан в GLSL, классический GLSL программа состояла из вершинного и фрагментного шейдера, но в современном OpenGL добавили некоторые новые типы шейдеров, а именно шейдеры геометрии, теселяции и расчетов (о них я расскажу в следующей части).
SSO [28] позволяют нам изменять этапы шейдера на лету, не связывая их заново. Создание и настройка простого программного конвейера без отладки выглядит следующим образом:
GLuint pipe = GL_NONE;
// Create shaders
GLuint fprog = glCreateShaderProgramv( GL_FRAGMENT_SHADER, 1, &text);
GLuint vprog = glCreateShaderProgramv( GL_VERTEX_SHADER, 1, &text);
// Bind pipeline
glGenProgramPipelines( 1, &pipe);
glBindProgramPipelines( pipe);
// Bind shaders
glUseProgramStages( pipe, GL_FRAGMENT_SHADER_BIT, fprog);
glUseProgramStages( pipe, GL_VERTEX_SHADER_BIT, vprog);
Как мы видим glCreateProgramPipelines [29] генерирует дескриптор и инициализирует объект, glCreateShaderProgramv [30] генерирует, инициализирует, компилирует и связывает шейдерную программу с использованием указанных источников, а glUseProgramStages [31] присоединяет этапы программы к объекту конвейера. glBindProgramPipeline [32] — связывает конвейер с контекстом.
Но есть один нюанс, теперь входные и выходные параметры шейдеров должны совпадать. Мы можем объявить входные/выходные параметры в одном и том же порядке, с одинаковыми именами, либо мы делаем их местоположение явно совпадающим с помощью квалификаторов.
Я рекомендую последний вариант, это позволит нам настроить четко определенный интерфейс, а также проявить гибкость в отношении имен и порядка.
В качестве обеспечения более строгого интерфейса нам также нужно объявить встроенные блоки ввода и вывода, которые мы хотим использовать для каждого этапа.
Встроенные интерфейсы блоков определены как (из вики [33]):
Vertex:
out gl_PerVertex
{
vec4 gl_Position;
float gl_PointSize;
float gl_ClipDistance[];
};
Tesselation Control:
out gl_PerVertex
{
vec4 gl_Position;
float gl_PointSize;
float gl_ClipDistance[];
} gl_out[];
Tesselation Evaluation:
out gl_PerVertex {
vec4 gl_Position;
float gl_PointSize;
float gl_ClipDistance[];
};
Geometry:
out gl_PerVertex
{
vec4 gl_Position;
float gl_PointSize;
float gl_ClipDistance[];
};
Пример повторного объявления встроенного модуля и использование attribute location в обычном вершинном шейдере:
#version 450
out gl_PerVertex { vec4 gl_Position; };
layout (location = 0) in vec3 position;
layout (location = 1) in vec3 color;
layout (location = 0) out v_out
{
vec3 color;
} v_out;
void main()
{
v_out.color = color;
gl_Position = vec4(position, 1.0);
}
Если вам понравилось, ждите продолжение :)
Автор: Aleksey
Источник [34]
Сайт-источник PVSM.RU: https://www.pvsm.ru
Путь до страницы источника: https://www.pvsm.ru/opengl/321763
Ссылки в тексте:
[1] glBegin: http://docs.gl/gl2/glBegin
[2] glEnd: http://docs.gl/gl2/glEnd
[3] OpenGL Wiki: https://www.khronos.org/opengl/wiki/Direct_State_Access
[4] glActiveTexture: http://docs.gl/gl4/glActiveTexture
[5] glBindTexture: http://docs.gl/gl4/glBindTexture
[6] glCreateTextures: http://docs.gl/gl4/glCreateTextures
[7] glGenTextures: http://docs.gl/gl4/glGenTextures
[8] glTextureParameterX: http://docs.gl/gl4/glTexParameter
[9] glBindTextureUnit: http://docs.gl/gl4/glBindTextureUnit
[10] glTextureImage: http://docs.gl/gl4/glTextureImage
[11] glTexStorage: http://docs.gl/gl4/glTexStorage
[12] glCreateFramebuffers: http://docs.gl/gl4/glCreateFramebuffers
[13] glGenFramebuffers: http://docs.gl/gl4/glGenFramebuffers
[14] glNamedFramebufferTexture: http://docs.gl/gl4/glNamedFramebufferTexture
[15] glFramebufferTexture: http://docs.gl/gl4/glFramebufferTexture
[16] glCreateBuffers: http://docs.gl/gl4/glCreateBuffers
[17] glGenBuffers: http://docs.gl/gl4/glGenBuffers
[18] glBindBuffer: http://docs.gl/gl4/glBindBuffer
[19] glNamedBufferData: http://docs.gl/gl4/glNamedBufferData
[20] glBufferData: http://docs.gl/gl4/glBufferData
[21] glVertexAttribFormat: http://docs.gl/gl4/glVertexAttribFormat
[22] glBindVertexBuffer: http://docs.gl/gl4/glBindVertexBuffer
[23] glVertexAttribPointer: http://docs.gl/gl4/glVertexAttribPointer
[24] glEnable: http://docs.gl/gl4/glEnable
[25] glDebugMessageCallback: http://docs.gl/gl4/glDebugMessageCallback
[26] glDebugMessageControl: http://docs.gl/gl4/glDebugMessageControl
[27] здесь: https://www.khronos.org/opengl/wiki/Debug_Output
[28] SSO: https://www.khronos.org/registry/OpenGL/extensions/ARB/ARB_separate_shader_objects.txt
[29] glCreateProgramPipelines: http://docs.gl/gl4/glCreateProgramPipelines
[30] glCreateShaderProgramv: http://docs.gl/gl4/glCreateShaderProgramv
[31] glUseProgramStages: http://docs.gl/gl4/glUseProgramStages
[32] glBindProgramPipeline: http://docs.gl/gl4/glBindProgramPipeline
[33] из вики: https://www.khronos.org/opengl/wiki/Built-in_Variable_(GLSL)
[34] Источник: https://habr.com/ru/post/456932/?utm_source=habrahabr&utm_medium=rss&utm_campaign=456932
Нажмите здесь для печати.