![](https://habrastorage.org/webt/ui/wu/2k/uiwu2kjzkvznzwubjaqnk7lkoz0.jpeg)
Всем хоршего настроения и температуры за окном пониже. Как и обещал, публикую продолжение статьи по супер-пепер соверменному OpenGL. Кто не читал первую часть — Суперсовременный OpenGL. Часть 1.
Возможно повезет и я смогу весь оставшийся материал впихнуть в эту статью, это не точно…
Array Texture
Текстурные массивы были добавленны еще в OpenGL 3.0, но почему-то мало кто пишет о них (информация надёжно прячется масонами). Все вы знакомы с программированием и знаете что такое масcив, хотя лучше я «подойду» с другой стороны.
Для уменьшения количества переключений между текстурами, а как следствие и снижению операций переключения состояний, люди используют текстурные атласы(текстура которая хранит в себе данные для несколько объектов). Но умные ребята из Khronos разаботали нам альтернативу — Array texture. Теперь мы можем хранить текстуры как слои в этом массиве, то есть это альтернатива алтласам. На OpenGL Wiki немного другое описание, про mipmaps и т.д., но мне оно кажется слишком сложным (ссылка).
Преимущества использования этого подхода по сравнению с атласами в том, что каждый слой рассматривается как отдельная текстура с точки зрения wrapping и mipmapping.
Но вернемся к нашим баранам… Текстурный массив имеет три вида таргета:
- GL_TEXTURE_1D_ARRAY
- GL_TEXTURE_2D_ARRAY
- GL_TEXTURE_CUBE_MAP_ARRAY
Код создания текстурного массива:
GLsizei width = 512;
GLsizei height = 512;
GLsizei layers = 3;
glCreateTextures(GL_TEXTURE_2D_ARRAY, 1, &texture_array);
glTextureStorage3D(texture_array, 0, GL_RGBA8, width, height, layers);
Самые внимательные заметили, что мы создаем хранилище для 2D текстур, но почему-то используем 3D массив, тут нет ошибки или опечатки. Мы храним 2D текстуры, но так как они расположены «слоями» получаем 3D массив (на самом деле, хранятся пиксельные данные, а не тукстуры. 3D массив имеет 2D слои с данными пикселей).
Тут легко понять на примере 1D текстуры. Каждая строчка в 2D массиве пикселей представляет собой отдельный 1D слой. Также автоматически могут создаваться mipmap тукстур.
На этом все сложности заканчиваются и добавление изображения на определенный слой довольно простое:
glTextureSubImage3D(texarray, mipmap_level, offset.x, offset.y, layer, width, height, 1, GL_RGBA, GL_UNSIGNED_BYTE, pixels);
При использование массивов нам надо немного поменять шейдер
#version 450 core
layout (location = 0) out vec4 color;
layout (location = 0) in vec2 texture_0;
uniform sampler2DArray texture_array;
uniform uint diffuse_layer;
float getCoord(uint capacity, uint layer)
{
return max(0, min(float(capacity - 1), floor(float(layer) + 0.5)));
}
void main()
{
color = texture(texture_array, vec3(texture_0, getCoord(3, diffuse_layer)));
}
Самым лучшим вариантом будет расчитывать нужный слой за пределами шейдера, для этого мы можем использовать UBO / SSBO (используется также для передачи матриц, да и многих других данных, но это как-то в другой раз). Если уж кому не терпится тык_1 и тык_2, можете почитать.
Что касается размеров, то есть GL_MAX_ARRAY_TEXTURE_LAYERS который равен 256 в OpenGL 3.3 и 2048 в OpenGL 4.5.
Cтоит рассказать про Sampler Object (не относиться к Array texture, но полезная вещь) — это объект который используется для настройки состояний текстурного юнита, независимо от того, какой объект сейчас привзяан к юниту. Он помогает отделать состояния сэмплера от конкретного текстурного объекта, что улучшает абстракцию.
GLuint sampler_state = 0;
glGenSamplers(1, &sampler_state);
glSamplerParameteri(sampler_state, GL_TEXTURE_WRAP_S, GL_REPEAT);
glSamplerParameteri(sampler_state, GL_TEXTURE_WRAP_T, GL_REPEAT);
glSamplerParameteri(sampler_state, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glSamplerParameteri(sampler_state, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
glSamplerParameterf(sampler_state, GL_TEXTURE_MAX_ANISOTROPY_EXT, 16.0f);
Я только что создал объект сэмплера, включил линейную фильтрацию и 16-кратную анизотропную фильтрацию для любого текстурного юнита.
GLuint texture_unit = 0;
glBindSampler(texture_unit, sampler_state);
Тут мы просто биндим сэмплер к нужному текстурному юниту, а когда он нам перестает быть нужным биндим 0 к данному юниту.
glBindSampler(texture_unit, 0);
Когда мы привязали сэмплер его настройки имеют приоритет над настройками текстурного юнита. Результат: нет необходимости изменять существующую кодовую базу для добавления объектов сэмплера. Вы можете оставить создание текстур как есть (со своими собственными состояниями сэмплера) и просто добавить код для управления и использования объектов сэмплера.
Когда настало время удалить объект, просто вызываем эту функцию:
glDeleteSamplers(1, &sampler_state);
Texture View
Я переведу это как «текстурный указатель(может правильнее ссылки, я хз)», так как не знаю лучшего перевода.
Что же такое указатели в перспективе OpenGL?
Все очень просто, это указатель на данные immutable(именно изменяемой) текстуры, как видим на картинке нижу.
![](https://habrastorage.org/webt/ul/r0/sy/ulr0syubsujlqfshyemnh6sqdoq.jpeg)
По факту это объект, который расшаривает данные текселей определенного текстурного объекта, для аналогии можно привести std::shared_ptr из С++. Пока существует хоть один указатель на текстуру, исходная текстура не будет удалена драйвером.
В wiki более детально описано, а так же стоит почитать о типах текстуры и таргета (они не обязательно должны совпадать)
Для создания указателя нам надо получить дескриптор текстуры вызвав glGenTexture(никаких иницализаций не нужно) и потом glTextureView.
glGenTextures(1, &texture_view);
glTextureView(texture_view, GL_TEXTURE_2D, source_name, internal_format, min_level, level_count, 5, 1);
Текстурные указатели могут указывать на N-й уровень mipmap'a, довольно полезно и удобно. Указатели могут быть как текстурными массивами, частями массивов, определенным слоем в этом массиве, а может быть срезом 3D текстуры как 2D текстура.
Single buffer for index and vertex
Ну, тут все будет быстро и просто. Раньше спецификация OpenGL по Vertex Buffer Object рекомендовала, что б разработчик разделял данные вершин и индексов в разные буфферы, но сейчас это не обязательно (долгая история почему необязательно).
Все, что нам нужно, это сохранить индексы перед вершинами и сообщить где вершины начинаються(точнее смещение), для этого есть команда glVertexArrayVertexBuffer
Вот как бы мы это сделали:
GLint alignment = GL_NONE;
glGetIntegerv(GL_UNIFORM_BUFFER_OFFSET_ALIGNMENT, &alignment);
const GLsizei ind_len = GLsizei(ind_buffer.size() * sizeof(element_t));
const GLsizei vrt_len = GLsizei(vrt_buffer.size() * sizeof(vertex_t));
const GLuint ind_len_aligned = align(ind_len, alignment);
const GLuint vrt_len_aligned = align(vrt_len, alignment);
GLuint buffer = GL_NONE;
glCreateBuffers(1, &buffer);
glNamedBufferStorage(buffer, ind_len_aligned + vrt_len_aligned, nullptr, GL_DYNAMIC_STORAGE_BIT);
glNamedBufferSubData(buffer, 0, ind_len, ind_buffer.data());
glNamedBufferSubData(buffer, ind_len_aligned, vrt_len, vrt_buffer.data());
GLuint vao = GL_NONE;
glCreateVertexArrays(1, &vao);
glVertexArrayVertexBuffer(vao, 0, buffer, ind_len_aligned, sizeof(vertex_t));
glVertexArrayElementBuffer(vao, buffer);
Tessellation and compute shading
Я не буду вам рассказывать про шейдер тесселяции, так как материала в гугле по этому поводу очень много(на русском), сразу приступим к рассказу про шейдер для расчетов(блииин, тоже много материала, расскажу вкратце).
Преимущество видиокарт в очень большом количестве ядер, видеокарты расчитаны на огромное количество мелких задач, которые можно выполнять паралельно. Шейдер расчетов, как понятно из названия, дает возможность решать задачи которые не связаны с графикой(не обязательно).
Картинка, я не знаю как ее назвать(типа потоки груперуются)
![](https://habrastorage.org/webt/tw/3a/x_/tw3ax_rzsrwx5look90ggm5xehw.jpeg)
Для чего можем использовать?
- Обработка изображения
- Блур
- Алгоритмы на основе плиток (отложенное затенение)
- Симуляции
- Частицы
- Вода
Дальше не вижу смысла писать, тоже есть много инфы в гугле, вот простой пример использования:
//биндим пйплайн с расчетным шейдером
glUseProgramStages( pipeline, GL_COMPUTE_SHADER_BIT, cs);
//биндим текстуру, как изображение для чтения/записи
glBindImageTexture( 0, tex, 0, GL_FALSE, 0, GL_WRITE_ONLY,
GL_RGBA8);
//запускаем 80x45 потоковых групп (достаточно для 1280х720)
glDispatchCompute( 80, 45, 1);
Path rendering
Это новое(уже не новое) расширение от NVidia, его основная цель — векторный 2D рендеринг. Мы его можем использовать для текстов или UI, а поскольку графика векторная, она не зависит от разрешение, что несомненно большой плюс и наш UI'чик будет прекрасно смотреться.
Основной концепцией является — трафарет, затем покрытие(cover в оригинале). Устанавливаем трафарет пути, затем визуалезируем пиксели.
Для менеджмента используются стандартный GLuint, а так же функции создания и удаления имеют стандартное именование.
glGenPathsNV // генерация
glDeletePathsNV // удаление
Вот немного о том, как мы можем получить путь:
- SVG или PostScript в string'e
glPathStringNV
- массив команд с соответствующими координатами
glPathCommandsNV
glPathSubCommands, glPathCoords, glPathSubCoords
- шрифты
glPathGlyphsNV, glPathGlyphRangeNV
- линейные комбинации существующих путей (интерполирование одного, двух и более путей)
glCopyPathNV, glInterpolatePathsNV, glCombinePathsNV
- линейное преобразование существующего пути
glTransformPathNV
Список стандартных команд:
- move-to (x, y)
- close-path
- line-to (x, y)
- quadratic-curve (x1, y1, x2, y2)
- cubic-curve (x1, y1, x2, y2, x3, y3)
- smooth-quadratic-curve (x, y)
- smooth-cubic-curve (x1, y1, x2, y2)
- elliptical-arc (rx, ry, x-axis-rotation, large-arc-flag, sweep-flag, x, y)
Вот как выглядит строка пути в PostScript:
"100 180 moveto 40 10 lineto 190 120 lineto 10 120 lineto 160 10 lineto closepath” //звезда
"300 300 moveto 100 400 100 200 300 100 curveto 500 200 500 400 300 300 curveto
closepath” //сердце
А вот в SVG:
"M100,180 L40,10 L190,120 L10,120 L160,10 z” //звезда
"M300 300 C 100 400,100 200,300 100,500 200,500 400,300 300Z” //сердце
Еще есть много всяких плюшек с видами заполнений, краев, изгибов:
Я не буду тут все описывать, так как материала очень много и это зайемет целую статью (если будет интересно, то как-нибудь напишу).
Вот список приметивов для отрисовки
- Cubic curves
- Quadratic curves
- Lines
- Font glyphs
- Arcs
- Dash & Endcap Style
Вот немного кода, а то уж очень много текста:
//Компилирование SVG пути
glPathStringNV( pathObj, GL_PATH_FORMAT_SVG_NV,
strlen(svgPathString), svgPathString);
//заполняем трафарета
glStencilFillPathNV( pathObj, GL_COUNT_UP_NV, 0x1F);
//конфигурация
//покрываем трафарет (визуализируем пиксилями)
glCoverFillPathNV( pathObj, GL_BOUNDING_BOX_NV);
Вот и все.
Мне кажется, что эта статья вышла менее интересной и познавательной, было сложно выделить основное в материале. Если кому-то интересно более подробно узнать, я могу скинуть некоторые материалы NVidia и ссылки на спецификации (если вспомню, куда их сохранил). Также рад за любую помощь в правках по статье.
Хотелось бы попросить написать в коментариях, о чем бы вы хотели почитать и что вам интересно (Cocos2dx, OpenGL, C++, что-то еще).
Всем спасибо за внимание.
Комментариев нет:
Отправить комментарий