- •Что такое OpenGl?
- •Первое знакомство с программой OpenGl
- •Синтаксис OpenGl
- •Конвейер визуализации OpenGl
- •Подготовка данных для отправки в OpenGl
- •Вершинные шейдеры
- •Мозаичные шейдеры
- •Наша первая программа: Подробное обсуждение Ввод main()
- •Запуск OpenGl
- •Распределение объектов буфера вершин
- •Инициализация вершинных и фрагментарных шейдеров
- •Наш первый рендеринг в OpenGl
- •Рисование в OpenGl
- •Включение и выключение команд в OpenGl
- •Шейдеры и OpenGl
- •Настраиваемый конвейер OpenGl
- •Обзор OpenGl Shading Language
- •Создание шейдера при помощи glsl Точка отправления
- •Задача переменных
- •Охват переменных
- •Инициализация переменных
- •Сложные типы
- •Получение доступа к элементам матриц и векторов
- •Структуры
- •Квалификаторы хранилищ
- •Заявления
- •Вычислительная инвариантность
- •Глобальные функции компиляции шейдеров
- •Блоки интерфейса
- •Унифицированные блоки
- •Указание унифицированных блоков в шейдере
- •Доступ к унифицированным блокам из приложения
- •Блоки In/Out
- •Компилирование шейдеров
- •Наша функция LoadShaders()
- •Подпрограммы шейдеров
- •Установка подпрограмм glsl
- •Раздельные объекты шейдеров
Распределение объектов буфера вершин
Массив буферов вершин содержит в себе данные связанные с набором вершин. Эти данные хранятся в объектах буферизации, и ими распоряжается привязанный в данный момент массив буферов вершин. И хотя существует только одна разновидность буферов вершин, существует много видов других буферов, не имеющих отношения к вершинным данным. Как ранее упоминалось, буферный объект - это выделяемый и хранящийся сервером OpenGL объем памяти, и практически все данные передающиеся в OpenGL хранятся в виде объектов буферизации.
Последовательность инициализации буфера вершин схожа с порядком создания массива вершинных объектов, с тем лишь отличием, что они занимают физическое место в буфере.
Для начала, нужно задать несколько наименований для ваших вершинных буферов. Как и стоило ожидать, это делается посредством вызова функции glGen*; в данном случае, glGenBuffers(). В нашем примере NumVBOs( “Vertex-Buffer Objects) были распределены в наш массив buffers. Вот полное описание glGenBuffers().
void glGenBuffers(GLsizei n, GLuint *buffers);
Returns n currently unused names for buffer objects in the array buffers.
The names returned in buffers do not have to be a contiguous set of
integers.
The names returned are marked as used for the purposes of allocating
additional buffer objects, but only acquire a valid state once they have
been bound.
Zero is a reserved buffer object name and is never returned as a buffer
object by glGenBuffers().
Когда вы зададите наименования вашим буферными объектам, вы можете их использовать командой glBindBuffer(). Поскольку в OpenGL много видов объектов буферизации, когда мы привязываем буфер, нам нужно указать, какой вид файла нужно использовать. Поскольку для нашего примера нужны данные вершин графов, мы используем буфер GL_ARRAY_BUFFER. На данный момент есть восемь типов объектов буферизации, каждый из которых используется для своей цели в OpenGL. Мы обсудим каждый тип в соответствующем разделе этой книги. Вот подробности о glBindBuffer():
void glBindBuffer(GLenum target, GLuint buffer);
Specifies the current active buffer object. target must be set to one of
GL_ARRAY_BUFFER, GL_ELEMENT_ARRAY_BUFFER,
GL_PIXEL_PACK_BUFFER, GL_PIXEL_UNPACK_BUFFER,
GL_COPY_READ_BUFFER, GL_COPY_WRITE_BUFFER,
GL_TRANSFORM_FEEDBACK_BUFFER, or GL_UNIFORM_BUFFER. buffer
specifies the buffer object to be bound to.
glBindBuffer() does three things: 1. When using buffer of an unsigned
integer other than zero for the first time, a new buffer object is created
and assigned that name. 2. When binding to a previously created buffer
object, that buffer object becomes the active buffer object. 3. When
binding to a buffer value of zero, OpenGL stops using buffer objects for
that target.
Как и в случае с другими объектами, вы можете удалить буфер командой glDeleteBuffers().
void glDeleteBuffers(GLsizei n, const GLuint *buffers);
Deletes n buffer objects, named by elements in the array buffers. The
freed buffer objects may now be reused (for example, by glGenBuffers()).
If a buffer object is deleted while bound, all bindings to that object are
reset to the default buffer object, as if glBindBuffer() had been called
with zero as the specified buffer object. Attempts to delete nonexistent
buffer objects or the buffer object named zero are ignored without
generating an error.
Вы также можете запросить задано ли наименование объекту буферизации командой gllsBuffer()
GLboolean glIsBuffer(GLuint buffer);
Returns GL_TRUE if buffer is the name of a buffer object that has been
bound, but has not been subsequently deleted. Returns GL_FALSE if
buffer is zero or if buffer is a nonzero value that is not the name of a buffer
object.
Загрузка данных в буферный объект
После инициализации нашего буфер вершин, нам нужно перевести данные с наших вершинных объектов в буферные. Это делается с помощью команды glBufferData(), которая выполняет два функции: выделить область в памяти для хранения вершинных данных и копировать денные массивов в приложение на сервер OpenGL.
Поскольку данная процедура будет часто использоваться в дальнейшем, пожалуй, стоит подробнее её описать тут. Мы обратимся к использованию этой процедуры ещё не раз в нашей книге. Для начала, вот полное описание glBufferData()
void glBufferData(GLenum target, GLsizeiptr size,
const GLvoid *data, GLenum usage);
Allocates size storage units (usually bytes) of OpenGL server memory for
storing data or indices. Any previous data associated with the currently
bound object will be deleted.
target may be either GL_ARRAY_BUFFER for vertex attribute data;
GL_ELEMENT_ARRAY_BUFFER for index data;
GL_PIXEL_UNPACK_BUFFER for pixel data being passed into OpenGL;
GL_PIXEL_PACK_BUFFER for pixel data being retrieved from
OpenGLGL_COPY_READ_BUFFER and GL_COPY_WRITE_BUFFER for
data copied between buffers; GL_TEXTURE_BUFFER for texture data
stored as a texture buffer; GL_TRANSFORM_FEEDBACK_BUFFER for
results from executing a transform feedback shader; or
GL_UNIFORM_BUFFER for uniform variable values.
size is the amount of storage required for storing the respective data. This
value is generally number of elements in the data multiplied by their
respective storage size.
data is either a pointer to a client memory that is used to initialize the
buffer object or NULL. If a valid pointer is passed, size units of storage are
copied from the client to the server. If NULL is passed, size units of
storage are reserved for use but are left uninitialized.
usage provides a hint as to how the data will be read and written after
allocation. Valid values are GL_STREAM_DRAW, GL_STREAM_READ,
GL_STREAM_COPY, GL_STATIC_DRAW, GL_STATIC_READ,
GL_STATIC_COPY, GL_DYNAMIC_DRAW, GL_DYNAMIC_READ,
GL_DYNAMIC_COPY.
glBufferData() will generate a GL_OUT_OF_MEMORY error if the
requested size exceeds what the server is able to allocate. It will generate a
GL_INVALID_VALUE error if usage is not one of the permitted values.
Мы понимаем, что это немаленький объем текста, но вы будете в дальнейшем очень часто пользоваться этой функцией, а потому неплохо бы иметь её под рукой, и проще найти её будет в начале книги.
Например, наш запрос к glBufferData() абсолютно прямой. Наша вершинная информация хранится в массиве vertices. И хотя в нашем примере мы статично распределили её, вы можете считать эти данные из файла модели, или генерировать значения с помощью алгоритмов. Поскольку наши данные имеют атрибут вершин, мы превратим это в буфер GL_ARRAY_BUFFER, определив эту информацию как первый параметр. Нам также нужно указать объем выделенной памяти в байтах, так что мы просто вычисляем sizeof (vertices), которая сама сделает всю тяжелую работу. Наконец, нам нужно указать, как OpenGL должна обрабатывать эти данные. Поскольку эти данные будут использоваться для отрисовки геометрии, и не будут изменяться вовсе, мы выбирает GL_STATIC_DRAW для параметра применения glBufferData().
Для параметра “применение” есть много опций, о которых будет рассказано в Главе 3.
Взглянув на значения в массиве vertices, вы можете заметить, что они все в диапазоне [-1, 1] по обеим осям (x, y). На деле, OpenGL знает только как нарисовать геометрические примитивы в сетке координат. Эти координаты называются normalized-device coordinates (проще, NDCs). И хотя это похоже не ограничение, это совсем не так. В Главе 5 подробно будут обсуждаться все математические вычисления для создания сложнейших фигур в трехмерном пространстве, и их маппинг в NDCs. Здесь мы использовали NDCs только для облегчения примера. Вам же почти всегда предстоит использовать более сложные сетки координат.
На данный момент, нам успешно удалось создание вершинного объекта и сохранение его в объект буфера. Далее мы зададим шейдеры, которые должна будет использовать наша программа.
