Use vbos что это
Что-то про VAO/VBO, шейдеры
CumingSoon
Местный стендапер
Также, VBO представляет из себя массив ака буфер данных, находящийся в видеопамяти.
Q: Что такое VAO?
А: Всё очень просто, это массив, содержащий буферы с данными(те самые VBO), а также последовательность индексов(о них позже).
Допустим, рисуется параллелограмм(выше картинка). Поскольку ОГЛ рисует треугольниками, то наш параллелограмм распадается на 2 треугольника. Каждая из 4 вершин может иметь: позицию(3 координаты), UV-координаты(2 координаты). Значит, VAO будет следующим(тут вершины повторяются, поскольку мы описываем треугольники, а каждому из них нужно указать три вершины в опр. порядке):
-VAO
—Pos0
—UV0
//второй треугольник
—Pos2
—UV2
Итого, 6 * (3 + 2) = 30 чисел.
Вместо того, чтобы загружать каждый раз данные о вершине, мы загрузим разово все вершины и последовательность, в которой эти вершины будут рисоваться(индексы). Тогда VAO примет вид:
—indices[0, 1, 2, 2, 3, 1]
Итого, 4 * (3 + 2) + 6 = 26 чисел. Экономия в 4 числа.
— Всего?!
Да, всего. Но мы ведь рисуем лишь два треугольника, а какова будет экономия, если нарисовать 2-3к треугольников?
Local Space: локальное пространство фигуры. Думаю, всё понятно, да?
World(global) Space: мировое пространство фигуры. Тут тоже всё ясно, так?
View(camera) Space: пространство в камере. В мире компьютерной графике вращается не камера, а вершины фигур, смещаются также вершины. Камеры нет, это всё ложь.
Clip Space: если вы используете перспективную проекцию, то более далёкие фигуры будут меньше, чем ближние; параллельные прямые начинают сходится. Но можно задать ортогональную проекцию. Тогда все фигуры будут одинаковых размеров, будто б не удалены; параллельные не сходятся. О построении матриц проекции:
http://ogldev.atspace.co.uk/www/tutorial12/tutorial12.html
http://www.songho.ca/opengl/gl_projectionmatrix.html
What is the «Use VBOs» setting?
In the 14w29b snapshot there is a new option in the «Video Settings» menu called «Use VBOs»:
The snapshot update says that enabling «Vertex Buffer Objects» should increase your FPS by 5% to 10% on average.
I am looking for a simple explanation of what VBOs do visually and how they work.
3 Answers 3
The answer provided by Flaunting is correct, but in case anyone is interested why it might be more efficient, here is an explanation.
In immediate mode (I think this is the default case in minecraft) when you want to render say a square:
You would issue the following commands each frame (in pseudo code)
For one square, this is not a lot, but there could be millions of vertices in a scene, and they may have more attributes (colour, normal etc.). This is a lot of data to send to the GPU each frame.
Using VBOS, you would load all of the vertex data into GPU memory at the start. Pseudo code might look like this:
The OpenGL code will give you back a ‘name’ for this VBO (a non-zero unsigned integer iirc). You can then reference this when you want to draw the square. So each frame, you only need to issue one draw command:
You may have to set up the drawing state so that it uses pairs of vertices for lines, but for each additional VBO, you only require one extra draw call. In fact, for static level geometry (probably not applicable in the case of minecraft) you can combine all of these vertices into one massive VBO, if you have enough GPU memory.
I’m surprised that the speed-up is only 5-10%. This is probably because of the dynamic level geometry.
Лучший способ использовать VBOs
Каковы самые быстрые и самые гибкие (применимые в большинстве ситуаций) способы использования VBOs?
Я разрабатываю приложение openGL, и я хочу, чтобы он достиг максимальной производительности, поэтому мне нужно, чтобы кто-то ответил на эти вопросы. Я прочитал много вопросов и ответов, но я догадываюсь, что мне не нужна информация, которая мне не нужна, что забивает мой мозг.
ОТВЕТЫ
Ответ 1
Как можно меньше. Переключение VBO осуществляется с небольшой, но измеримой стоимостью. В общем, вы попытаетесь сгруппировать аналогичные данные в VBOs. Например, в игре FPS все различные виды мусора, лежащие на улице, небольшие реквизиты и т.д., Обычно будут расположены в одном и том же или только небольшом количестве VBOs.
Это также сводится к размеру партии рисунка. glDraw… вызовы, которые составляют менее 100 примитивов, являются субоптимальными (это всегда было так, даже 15 лет назад). Таким образом, вы хотите, по возможности, выпустить по меньшей мере 100 примитивов. Но если имеется только одна сетка, скажем, 20 треугольников (низкопоставленные реквизиты для инстанса или такие), каждый в своем собственном VBO вы больше не можете делать больше.
glGenBuffers → glBindBuffer → glBufferDatap >
UPDATE Вы можете передать нулевой указатель на параметр data glBufferData для инициализации объекта буфера без установки данных.
Создайте VBO с более крупной размерностью, чем размер ваших данных. Ваша операционная система делает это в любом случае для ваших данных на стороне хоста, это называется пейджинг. Также, если вы хотите использовать glMapBuffer, создавая объект буфера, кратный размеру главной страницы, очень хорош для всей системы.
Обновите данные с помощью glBufferSubData или сопоставьте его с изменением glMapBuffer в сопоставленной памяти на стороне хоста, затем glUnmapBuffer.
Если данные перерастают буферный объект, создайте новый, более крупный и скопируйте с помощью glCopyBufferSubData. См. Параграф lase.
Если данные потребляют только часть VBO и разделяют ее с другими данными, и у вас не хватает памяти, тогда просто не получайте доступ к ней. В идеале вы держите вокруг какой-то индекс, в котором вы отслеживаете, какой VBO имеет, какие части его доступны для какой задачи. Это очень похоже на управление памятью, а именно на схему, известную как стек объектов (obstacks).
Однако в конечном итоге может иметь смысл уплотнить существующий объект буфера. Для этого вы создадите новый объект буфера, привяжите его как пишущий объект, а старый объект буфера будет выбран как цель чтения. Затем используйте glCopyBufferSubData, чтобы скопировать содержимое в новый, затянутый буферный объект. Конечно, вам придется обновить все ссылки на имя объекта буфера (= OpenGL ID) и смещения.
По этой причине имеет смысл написать тонкий слой абстракции поверх объектов буфера OpenGL, который отслеживает фактические типизированные данные в бесструктурных блоках объектов OpenGL-буфера.
Ответ 2
Сколько vbo я должен использовать?
Сколько вам нужно, звучит глупо, но хорошо, так оно и есть
Как обновить данные vbos, если размер данных не исправлен?
Перезаписать и отобразить один и тот же VBO с разными данными и длиной.
Как создать vbos?
Как визуализировать vbos?
Как мне обращаться с данными в vbos, которые я больше не хочу отображать?
создать новое vbo и скопировать данные в него или отобразить только те части этого vbo, которые находятся в памяти.
Чтобы отобразить только части, см. glVertexBuffer glTexCoordPointer (просто вычислите новый указатель и новый размер на основе смещения)
Изменить 1:
Использование одного VBO для всего, что не так, потому что вам нужно управлять алликацией новых координат вершин/текстурных координат, которые действительно становятся очень грязными.
Лучше группировать небольшие реквизиты в VBO и выполнять команды рисования.
Могу ли я добавить данные в буфер с помощью glBufferSubData (добавив 100 элементов в буфер с размером x)?
Изменить 2
Хорошее учебное пособие также Изучение современного 3D-графического программирования, но его не специфический VBO.
¿Deberías mantener los VBO encendidos o apagados en Minecraft? (12.11.21)
Cada videojuego ofrece diferentes opciones y configuraciones que tienen un efecto sobre cómo se ve el juego y cómo se desarrolla realmente. Por ejemplo, establecer la configuración de textura demasiado alta permite al jugador cargar texturas de alta calidad en su juego. Del mismo modo, reducir algunas configuraciones le da al jugador un aumento de rendimiento.
VBOs activados o desactivados en Minecraft
Al acceder a la configuración de Minecraft, es posible que haya notado una opción llamada «VBO». Es una de las muchas opciones que puedes encontrar dentro de tu configuración de gráficos / video en los videojuegos. La mayoría de los jugadores no tienen mucha idea de lo que son los VBO, por lo que preguntan si deberían encenderlos o apagarlos.
Lecciones populares de Minecraft
Hoy, Discutiremos esto en detalle para ayudarlo a saber si debe mantenerlo encendido o apagado. Entonces, ¡vayamos directo a eso!
¿De qué se trata la configuración de VBO?
Para aquellos de ustedes que no lo saben, las VBO son básicamente una configuración de video que son las siglas de Vertical Buffer Object. Es una función de OpenGL que es responsable del tipo de procedimiento o método que utiliza el juego para cargar los datos de los vértices (incluido el posicionamiento, el vector y el color).
Tiene un efecto sustancial en tu rendimiento real del juego. Ofrece una forma más inteligente de cómo el juego representa algunas escenas en el juego. Esta es también la razón por la que puedes notar que el juego funciona mejor en algunas situaciones en comparación con el renderizado inmediato.
Si simplificáramos aún más las cosas, cuando tienes la opción desactivada, todo el mundo en Minecraft será cargado incluso cuando no puede verlo. Por otro lado, cuando los VBO están activados, partes innecesarias del mundo que no puedes cargar.
¿Deberías activarlo?
Si ha entendido qué son los VBO, debería quedar bastante claro que activar esta configuración para algunos usuarios les daría un gran impulso en FPS. Una cosa importante a tener en cuenta es que cuando lo tengas encendido, cambiarás la mayor parte de la carga del juego a la GPU en lugar de a la CPU.
Por lo tanto, también puede depender de su sistema si puede aprovechar al máximo la opción activada. Para algunos usuarios, es posible que ni siquiera tenga ningún efecto en su rendimiento. Todo lo que tienes que hacer es intentar activar y desactivar la opción y comparar si notas una diferencia en el rendimiento de tu juego. Si es así, definitivamente debería mantenerlo encendido.
Conclusión
Aquí encontrará todo lo que necesita saber sobre si debe mantener las VBO encendidas o apagadas. Asegúrese de leer bien el artículo para comprenderlo completamente.
Video de Youtube: ¿Deberías mantener los VBO encendidos o apagados en Minecraft?
Записки программиста
Продолжаем изучение OpenGL: VBO, VAO и шейдеры
В прошлый раз мы сделали заготовку кроссплатформенного проекта на C++, в котором используется OpenGL. Мы определились с тем, как будем управлять зависимостями, какую систему сборки и IDE будем использовать. Мы даже научились рисовать окошки при помощи GLFW и перемножать матрички, используя GLM. Как оказалось, в этом нет большой науки. Сегодня же мы поговорим о самом OpenGL, так как, напомню, в предыдущем примере мы использовали устаревшие процедуры glBegin, glEnd и подобные, и в наши дни так делать не нужно.
Для начала нам потребуется подключить к проекту еще одну библиотеку, GLXW. Делается это аналогично тому, как мы это делали для GLFW и GLM — через сабмодули Git. GLXW является лоадером OpenGL. Дело в том, что доступ к функциям OpenGL осуществляется через подгрузку динамических библиотек. На разных платформах эти динамические библиотеки загружаются по-разному, да и находятся в разных местах. Собственно, лоадеры предназначены для того, чтобы скрыть эти различия от программиста. Таких лоадеров написано очень много. Например, во многих туториалах используется GLEW. Мной был выбран GLXW, так как он использовался в примерах, по которым я начинал разбираться. Также на FreeNode зависает автор GLXW (ник exDM69), а также человек (ник dav1d), который хостит статическую версию GLXW по адресу http://glxw.dav1d.de/glxw.git. GLXW на GitHub использует Python для генерации заголовочных файлов. Статическая версия от dav1d делает это по cron, и потому не требует установки Python. В общем и целом, хотя бы понятно, куда обращаться за помощью в случае чего. Наконец, с GLXW пока не было никаких проблем. Раз он просто работает, почему бы на нем и не остановиться?
if ( glxwInit ( ) ) <
std :: cerr «Failed to init GLXW» std :: endl ;
glfwDestroyWindow ( window ) ;
glfwTerminate ( ) ;
return 1 ;
>
Ранее многократно отмечалось, что glBegin, glEnd и прочие функции нынче признаны устаревшими (так называемый immediate mode). Но как же прикажете что-то без них рисовать? Чтобы ответить на этот вопрос, придется для начала понять, что такое VBO и VAO. Противоположность immediate mode, о которой далее пойдет речь, называется retained mode.
Vertex Buffer Object (VBO) — это такое средство OpenGL, позволяющее загружать определенные данные в память GPU. Например, если вы хотите сообщить GPU координаты вершин, цвета или нормали, нужно создать VBO и положить эти данные в него.
Пример загрузки координат треугольника:
Процедура glGenBuffers возвращает заданное количество неиспользуемых в настоящее время айдишников VBO. В терминологии OpenGL идентификаторы принято называть именами, поэтому далее будем стараться вместо «айдишники» говорить «имена». Заметьте, что никакого создания буферов здесь пока не происходит.
Далее процедурой glBindBuffer мы связываем (bind) полученное на предыдущем шаге имя с определенной точкой связывания (binding point). Мы хотим сохранить в буфере координаты вершин, и должны использовать для этого точку связывания GL_ARRAY_BUFFER. Обратите внимание, здесь снова ни о каком создании буферов речи пока не идет. Мне лично проще всего думать о glBindBuffer, как о команде «ок, сейчас мы будем что-то делать вот с этим VBO».
Наконец, при вызове glBufferData происходит выделение памяти и загрузка в нее наших данных. Последний аргумент процедуры называется usage и задает предполагаемый паттерн использования буфера. Часть STATIC говорит о том, что мы не собираемся модифицировать данные, а часть DRAW — о том, что данные будут использованы для отрисовки чего-то. Этот аргумент не приводит к каким-то действительным ограничениям на использование буфера. Но он является подсказкой для реализации OpenGL, и может существенно влиять на производительность приложения. Так что, лучше указывать в нем что-то правдоподобное. Заметьте, что первым аргументом указывается binding point, а не конкретный VBO. То есть, выбор самого VBO был сделан на предыдущем шаге.
Когда VBO больше не нужен, его можно удалить следующим образом:
Итак, мы сохранили данные в буфере. Зачем, спрашивается, нужны еще какие-то VAO?
Vertex Arrays Object (VAO) — это такая штука, которая говорит OpenGL, какую часть VBO следует использовать в последующих командах. Чтобы было понятнее, представьте, что VAO представляет собой массив, в элементах которого хранится информация о том, какую часть некого VBO использовать, и как эти данные нужно интерпретировать. Таким образом, один VAO по разным индексам может хранить координаты вершин, их цвета, нормали и прочие данные. Переключившись на нужный VAO мы можем эффективно обращаться к данным, на которые он «указывает», используя только индексы.
Насколько я смог разобраться, то, что лежит в VAO по каким-то индексам, правильно называется «vertex attribute array». Название часто сокращают до «vertex attributes» или просто «attributes». Соответственно, отдельный элемент массива называется атрибутом (attribute, без s на конце). При этом каждый атрибут состоит из компонетов (components). Например, координаты вершины (один атрибут) задаются тремя координатами (три компонента типа float). Чтобы нарисовать треугольник, нужно задать три вершины, поэтому нужен массив из трех атрибутов. Усложняется ситуация тем, что авторы туториалов могут использоваться свою собственную терминологию, вроде attribute list или подобную. Еще мне встречались просто опечатки, когда в тексте написано attribute, когда по контексту явно должно быть attributes. Стоит ли говорить, что такой бардак несколько усложняет изучение OpenGL? 🙂
Если написанное выше не очень понятно, попробуйте посмотреть первые пару минут этого видео. Возможно, станет яснее.
Создание и удаление VAO происходит по аналогии с VBO:
Используются VAO примерно следующим образом:
glBindVertexArray ( vao ) ;
glBindBuffer ( GL_ARRAY_BUFFER, vbo ) ;
glBindBuffer ( GL_ARRAY_BUFFER, 0 ) ; // unbind VBO
glBindVertexArray ( 0 ) ; // unbind VAO
C bind и unbind, надеюсь, все понятно.
Процедура glVertexAttribPointer говорит, откуда брать данные для массива атрибутов, а также в каком формате эти данные находятся. Номер массива атрибутов, переданный первым аргументом этой процедуре, еще понадобится нам чуть ниже, а также когда мы дойдем до работы с шейдерами. Второй аргумент задает размер компонента. В данном случае — три float’а на одну вершину. Третий аргумент задает тип компонента. Оставшиеся аргументы называются normalized, stride и pointer. Сейчас они нам не очень интересны.
Основной же цикл нашей программы будет выглядеть следующим образом:
while ( glfwWindowShouldClose ( window ) == GL_FALSE ) <
glClear ( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT ) ;
glUseProgram ( programId ) ; // на это пока не обращайте внимания
glfwSwapBuffers ( window ) ;
glfwPollEvents ( ) ;
>
Как видите, в нем выбирается интересующий нас VAO при помощи уже знакомой нам процедуры glBindVertexArray.
Вызываемая далее процедура glEnableVertexAttribArray «включает» указанный vertex attribute array. На предыдущем шаге мы решили, что у него будет номер ноль, поэтому здесь используем тот же номер. Как можно без труда догадаться, процедура glDisableVertexAttribArray совершает обратное действие. Нужно это включение и выключение, как мне объяснили в IRC, для оптимизации. Отключая ненужные атрибуты, мы не передаем лишних данных шейдерам (о них ниже). По умолчанию все атрибуты находятся в выключенном состоянии.
Наконец, glDrawArrays отвечает за рисование примитивов. Первым аргументом передается тип примитива. В данном случае рисуется треугольник. Второй аргумент определяет, с вершины под каким номером нужно начать. Последний аргумент задает количество вершин, которое следует использовать.
Вот так замысловато происходит передача координат вершин в OpenGL! Однако остается открытым вопрос, что же делает glUseProgram?
Шейдеры
Шейдер — это программа, предназначенная для выполнения на определенном этапе rendering pipeline. Шейдеры пишутся на специальном языке GLSL (OpenGL Shading Language). Как у всех программ, у шейдеров есть какой-то вход и какой-то выход. Какие именно — зависит от типа шейдера. Существует много типов шейдеров, но в рамках данной заметки нам потребуется только два. Это vertex shader и fragment shader.
Vertex shader работает с отдельными вершинами. На вход данный шейдер получает координаты вершин и их же он должен отдавать на выход. Наш vertex shader на языке GLSL будет выглядеть так:
layout ( location = 0 ) in vec3 vertexPos ;
В части location = 0 используется тот самый номер, который мы передавали первым аргументом при вызове glVertexAttribPointer. То есть, это номер массива атрибутов, в котором мы решили хранить вершины примитива. Шейдер говорит, что входную вершину он будет брать оттуда. Соответственно, атрибуты должны быть включены вызовом glEnableVertexAttribArray. Выход же мы должны записать в переменную gl_Position. Тут есть небольшое неудобство, связанное с тем, что в программе мы использовали координаты xyz, а в шейдере на выход должны отдавать уже xyzw. Поэтому выходному w присваиваем значение 1 вручную.
Fragment shader работает с фрагментами. С ним все немного сложнее. Пока нам достаточно знать, что, помимо прочего, на выход он должен отдавать цвета пикселей. В самом простом варианте fragment shader выглядит так:
Здесь просто для любого входа возвращается один и тот же цвет.
Прежде, чем использовать шейдеры, их нужно скомпилировать. Делается это прямо в программе. Берутся исходники шейдеров в GLSL в обычных сишных строках с нулем на конце, и из них при помощи функций OpenGL получаются скомпилированные. Код этот довольно скучный, поэтому в посте мы его рассматривать не будем. Кому интересно, тот может ознакомиться с ним в качестве домашнего задания.
Поскольку шейдеры предназначены для различных этапов rendering pipeline, они также объединяются в program object. Делается это как-то так:
GLuint prepareProgram ( bool * errorFlagPtr ) <
* errorFlagPtr = false ;
GLuint programId = glCreateProgram ( ) ;
glAttachShader ( programId, vertexShaderId ) ;
glAttachShader ( programId, fragmentShaderId ) ;
glLinkProgram ( programId ) ;
* errorFlagPtr = checkProgramLinkStatus ( programId ) ;
if ( * errorFlagPtr ) return 0 ;
glDeleteShader ( vertexShaderId ) ;
glDeleteShader ( fragmentShaderId ) ;
Удалять шейдеры после их присоединения к программе совершенно легально. Затем, когда в основном цикле мы говорим:
… подцепляются соответствующие шейдеры. Vertex shader никак не меняет переданные ему координаты, а fragment shader окрашивает примитив в заданный цвет:
Приведенный скриншот как бы намекает нам, что код работает как под Linux, так и под Windows. Кроме того, код также был проверен на работоспособность под MacOS. Для разнообразия я решил раскрасить треугольники в разные цвета. Заметьте также, что теперь размер треугольника меняется вместе с изменением размера окна. В предыдущей заметке это было не так. В качестве упражнения можете разобраться, как это работает. Там нет ничего сложного.
В IRC мне настоятельно советовали проверять, работает ли код с GPU от NVidia. Но, к сожалению, графических карт этого производителя у меня под рукой не имеется. Думается, на таком простом коде проблем возникнуть не должно.
Как видите, все оказалось несколько запутанно, но не так, чтобы прям очень сложно. Полную версию исходного кода к этой заметке вы найдете в этом репозитории. Как обычно, буду рад любым вашим комментариям.