Заглавная страница Избранные статьи Случайная статья Познавательные статьи Новые добавления Обратная связь FAQ Написать работу КАТЕГОРИИ: АрхеологияБиология Генетика География Информатика История Логика Маркетинг Математика Менеджмент Механика Педагогика Религия Социология Технологии Физика Философия Финансы Химия Экология ТОП 10 на сайте Приготовление дезинфицирующих растворов различной концентрацииТехника нижней прямой подачи мяча. Франко-прусская война (причины и последствия) Организация работы процедурного кабинета Смысловое и механическое запоминание, их место и роль в усвоении знаний Коммуникативные барьеры и пути их преодоления Обработка изделий медицинского назначения многократного применения Образцы текста публицистического стиля Четыре типа изменения баланса Задачи с ответами для Всероссийской олимпиады по праву Мы поможем в написании ваших работ! ЗНАЕТЕ ЛИ ВЫ?
Влияние общества на человека
Приготовление дезинфицирующих растворов различной концентрации Практические работы по географии для 6 класса Организация работы процедурного кабинета Изменения в неживой природе осенью Уборка процедурного кабинета Сольфеджио. Все правила по сольфеджио Балочные системы. Определение реакций опор и моментов защемления |
Глава 23. Использование чистого OpenGLСодержание книги
Поиск на нашем сайте
GLScene является надстройкой над интерфейсом OpenGL, но это не означает, что он может его (интерфейс) полностью заменить. Поэтому существует возможность вставлять фрагменты на чистом OpenGL. Для этой цели служит GLDirectOpenGL . Сделаем небольшое приложение с использованием чистого OpenGL и GLScene. Создадим стандартный проект (у GLCamera1 Position.Z = 4), а затем создадим в GLScene1 объект GLDirectOpenGL. Строки кода OpenGL нужно вставлять в событие OnRender. Попробуем вывести на экран плоскость. Чтобы компилятор понимал команды OpenGL, подключите модуль OpenGL1x. Здесь же стоит отметить, что, несмотря на название, в этом модуле имеются описания функций, присущих более поздним версиям OpenGL. procedure TForm1.GLDirectOpenGL1Render(Sender: TObject; var rci: TRenderContextInfo); begin gl.LoadIdentity; // заменяем текущую матрицу на единичную. gl.Translatef(0.0, 0.0, -8.0); gl.Begin(GL_POLYGON); // Начало операторный скобки. Она определяет, что вершины будут объединены gl.Vertex3f(1.0, 1.0, 1.0); //Рисуем вершину gl.Vertex3f(-1.0, 1.0, 1.0); gl.Vertex3f(-1.0, -1.0, 1.0); gl.Vertex3f(1.0, -1.0, 1.0); gl.End; // Конец операторной скобки end;
Всё! Запускаем и смотрим на эту плоскость. Когда вы создадите событие OnRender и попробуете туда что-либо написать, компилятор возможно выдаст вам ошибку. Это несложно исправить: подключите к вашему проекту модуль GLRenderContextInfo и всё будет работать.
В последних версиях сцены (от мая 2010) появилась полезная особенность. Для предотвращения двойного переключения состояний (state), все состояния OpenGL теперь должны переключаться через rci.GLStates.SetXXX где XXX — название вызываемой функции OpenGL без букв «gl». Приведу пример: чтобы вызвать glBlendFunc (GL_ONE, GL_ONE), вы должны написать в OnRender: rci.GLState.SetBlendFunc(bfOne, bfOne) Иногда на GLScene переносят сложные приложения, работающие с текстурами. В таких случаях удобно использовать класс TGLTextureHandle, который предоставляет очень похожий на чистый OpenGL доступ к текстуре. Ниже приведён пример создания и настройки текстуры подобным образом: var TH: TGLTextureHandle; … TH:=TGLTextureHandle.Create; TH.AllocateHandle; glEnable(GL_TEXTURE_RECTANGLE_ARB); glBindTexture(GL_TEXTURE_RECTANGLE_ARB, dt.Handle); glTexParameteri(GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); glTexParameteri(GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
В новейших версиях GLScene в командах OpenGL после gl ставится точка, после чего пишется остальная часть имени. Такая система позволяет работать с несколькими контекстами. Например, вместо glvertex3f пишется GL.vertex3f, а вместо GLBegin/GLEnd пишеся GL.Begin_/GL.End_. Такая система вызывает необходимость подключать новые, ранее не используемые для вызова OpenGL-команд, модули: вместо OpenGL1x подключается GLContext и либо OpenGLTokens, либо OpenGLAdapter. OpenGLTokens содержит команды OpenGL, они позволяют компилятору понимать текст, идущий за GL.. OpenGLAdapter даёт возможность иметь список расширений и точек входа функций OGL на каждый контекст. Такая система позволяет делать приложение с несколькими окнами, в которых может быть разный формат пикселя.
Зачастую необходимо получить объект, который в данный момент времени отрисовывается. Для этого ввели vCurrentRenderingObject: TGLBaseSceneObject; нужно смотреть в цикле, является ли интересующий вас объект vCurrentRenderingObject (IF Obj= vCurrentRenderingObject then …). Глава 24. VBO Начинаем осваивать… Расширение VBO является лучшим вариантом вывода всей сцены. Однако один только класс MeshObjects из всех классов GLScene использует эту технологию. Большинство компонентов задействуют вершинные массивы.
Одним из самых «узких» мест в работе с современным графическим ускорителем является передача ему данных — текстур, вершин, нормалей и т.п. Для повышения быстродействия следует уменьшить количество запросов на передачу данных и передавать их как можно большими частями. Если с текстурами все достаточно просто — они один раз загружаются в память графического ускорителя и больше не изменяются, то с геометрическими данными (координатами вершин, текстурными координатами, нормалями и т.д.) дело обстоит значительно хуже. Самый старый способ передачи данных в операторных скобках glBegin()/glEnd является крайне неэффективным, поскольку требует одного вызова функции для каждого атрибута каждой вершины. Ещё одной проблемой glBegin()/glEnd является избыточная обработка вершин, являющихся общими для нескольких смежных полигонов. Поэтому разработчики пытались улучшить ситуацию. В версии 1.1 были ведены так называемые вершинные массивы (стоит отметить, что некоторые разработчики реализовали вершинные массивы в виде расширений ещё на момент выхода OpenGL 1.0). В них данные хранятся на стороне CPU, а ускорителю передается только указатель на них и их размер. При использовании этого способа передача осуществляется сразу большими блоками, количество обращений к графическому ускорителю (GPU) заметно сокращается. Кроме того, использование вершинных массивов может устранить избыточность при обработке общих вершин. Всё это приводит к гораздо более эффективному использованию GPU и общему повышению быстродействия программы. Однако этот способ требует постоянной передачи массивов данных от CPU к GPU — вызов любого оператора OpenGL с указателем на массив приводит к его передаче графическому ускорителю, даже если передаваемые данные не устарели (т.е. те данные, что содержит GPU, и те, что будут ему присланы, полностью идентичны). Поэтому было бы гораздо эффективнее сразу загрузить такие данные в память GPU, после чего использование этих данных графическим ускорителем не будет требовать их передачи от CPU. Именно подобную возможность и предоставляет пользователю расширение ARB_vertex_buffer_object, введенное в ядро начиная с версии 1.5. Использование данного расширения позволяет кэшировать (хранить) различные типы данных в быстрой памяти графического ускорителя. Для такого хранения блоки данных помещаются в так называемые вершинные буферы (vertex buffer objects, VBO), при этом каждый такой буфер представляет собой просто массив байт. Давайте сделаем приложение, которое будет использовать вершинные буферы. Я надеюсь, многие аспекты реализации после этого отпадут. Мы будем выводить затекстурированную плоскость средствами GLScene и OpenGL. Поместите на форму компоненты GLScene, GLSceneViewer, GLMaterialLibrary. В инспекторе объектов сцены создайте камеру под именем GLCamera и установите ее вьюверу, свойству Position.Z присвойте значение 2. В GLMaterialLibrary создайте материал и свойству Disabled материала присвойте False. Загрузите в материал любую 2D текстуру. Подключите столь необходимые для работы модули OpenGLTokens и GLContext. Переходим к коду. Я постараюсь максимально комментировать всё, что мы будем делать. Создайте массив: var vertexBuffers: array[0..1] of GLInt; Это статический массив — два объекта вершинного буфера. Один для хранения позиций вершин (vertices), другой для текстурных координат (texcoords). Теперь создайте событие FormCreate. В нем будут проходить все действия по подготовке к рисованию. Первое что мы сделаем — объявим необходимые для работы переменные, отвечающие за положение каждой из 4-х вершин и 4-х текстурных координат. var Vertices: array of array[0..1] of GLFloat; TexCoords: array of array[0..1] of GLFloat; Код здесь хитрый и требует пояснения. Vertices по факту — конечная точка, из которых будет состоять наша плоскость. Всего будет 4 точки. Они соединёны треугольниками по принципу «две предыдущих точки вместе с третьей образуют новый треугольник». Красным обозначены точки, на рисунке проставлен порядок их рендеринга — сначала рисуется точка №1, затем №2, №3 и, наконец, №4. Соответственно, сначала рисуется синий прямоугольник, а затем второй — чёрный. Точка у нас в проекте имеет координаты X и Y, но обращаться мы к ним будем через Vertices[0] и Vertices[1] (как у векторов). Итак… Возвращаемся к редактору кода и пишем дальше в событие OnCreate формы. SetLength(Vertices,4*2); Vertices[0][0]:=1; Vertices[0][1]:=-1; Vertices[1][0]:=-1; Vertices[1][1]:=-1; Vertices[2][0]:=1; Vertices[2][1]:= 1; Vertices[3][0]:=-1; Vertices[3][1]:= 1; SetLength(TexCoords,4*2); TexCoords[0][0]:=0; TexCoords[0][1]:=-1; TexCoords[1][0]:=-1; TexCoords[1][1]:=-1; TexCoords[2][0]:=0; TexCoords[2][1]:=0; TexCoords[3][0]:=-1; TexCoords[3][1]:=0; Напомню, SetLength изменяет размер массива. Размер массива считается как произведение количества координат, передаваемых для одной вершины на количество вершин нашего объекта. Кроме этого, мы задаём позиции каждой вершины и текстурной координаты. В последних снимках GLScene при работе с OpenGL требуется обязательно активировать контекст. Делается это так: GLSceneViewer1.Buffer.RenderingContext.Activate; Если это не прописано, то при запуске вылетает Access violation. Далее запишите: gl.GenBuffers(2, @vertexBuffers); Этим оператором мы сгенерировали два буферных объекта. Далее один мы будем использовать для хранения позиций вершинных координат, второй для текстурных координат. Возможно, в будущее вам понадобятся ещё 4 буфера, но о них позже. Теперь нужно инициализировать и заполнить буферы данными.: gl.BufferData(GL_ARRAY_BUFFER, SizeOf(GLFloat)*Length(Vertices), @Vertices[0], GL_STATIC_DRAW); Первый параметр — данные, которые будет содержать буфер. Это нужно либо для информации о вершинах (позиция, нормаль и т.д.), либо для индексов в другие массивы (об этом позже). Второй параметр задаёт размер буфера. Третий параметр — ссылка на область памяти, где хранится вершинный массив. Четвёртый параметр несёт в себе информацию о предполагаемом назначении буфера. Эта информация является лишь намеком и служит для того, чтобы система могла более эффективно управлять выделением памяти для вершинного буфера. Параметр имеет вид GL_XXX_VVV_ARB.
Поскольку мы собираемся вывести плоскость с текстурой и больше ничего не изменять, мы выберем GL_STATIC_DRAW. Далее мы должны освободить активный вершинный буфер: gl.BindBuffer(GL_ARRAY_BUFFER, 0); Первый параметр — данные, которые будет содержать буфер — может иметь всего два очень важных зарезервированных значения, определяющих тип данных: GL_ARRAY_BUFFER и GL_ELEMENTS_ARRAY_BUFFER. Первое задаёт информацию о вершинах, другими словами вершинный атрибут (позиция, нормаль, текстурные координаты). Второе значение сообщает о том, что данные из вершинного буфера будет использоваться как индексы массива с информаций о вершинах, то есть второе значение связано с первым. Второй параметр должен привязывать первый параметр к буферному объекту. Но сами объекты считаются с 1, поэтому если написать 0 во втором параметре и GL_ARRAY_BUFFER в первом, то активный вершинный буфер освободится. Теперь запишите кодовый блок для использования вершинного буфера для текстуры (аналогичен предыдущему): gl.BindBuffer(GL_ARRAY_BUFFER, vertexBuffers[1]); gl.BufferData(GL_ARRAY_BUFFER, SizeOf(GLFloat)*Length(TexCoords), @TexCoords[0], GL_STATIC_DRAW); gl.BindBuffer(GL_ARRAY_BUFFER, 0); Сейчас уже будет попроще… В инспекторе объектов создайте GLDirectOpenGL и в его событии OnRender запишите GLMaterialLibrary.Materials[0].Apply(rci); Это делает активной указанную текстуру. В данном случае библиотека материалов это просто список указателей на текстурные дескрипторы, а чтобы текстура применилась к отрисовываемому объекту, её нужно активировать. При отображении стандартных объектов это делается GLScene автоматически, но когда мы используем DirectOpenGL, это нужно делать кодом. А теперь включите вершинный массив координат вершин и массив координат текстуры gl.EnableClientState(GL_VERTEX_ARRAY); gl.EnableClientState(GL_TEXTURE_COORD_ARRAY); Всего существует 6 таких массивов. Для использования расширения VBO понадобятся только 4:
Далее необходимо сообщить OpenGL, что мы собираемся использовать vertexbuffers[0] и vertexbuffers[1] как вершинные массивы при рисовании. Сделаем мы это через команду glBindBuffer: gl.BindBuffer(GL_ARRAY_BUFFER, vertexBuffers[0]); Следующим оператором мы укажем, какое количество координат будем задавать каждой вершине (две, три или четыре); тип данных для каждой координаты в массиве (GL_SHORT, GL_INT, GL_FLOAT или GL_DOUBLE); нулевой промежуток памяти в байтах между координатами соседних последовательных вершин и отсутствие указателя на область, где содержатся координаты первой вершины (обычно для VBO задаётся именно отсутствие указателя). glVertexPointer(2, GL_FLOAT, 0, nil); Всего существует 4 оператора GL…Pointer, используемых с буферами вершинных массивов. Это:
Пришло время сделать то же для текстурных координат. gl.BindBuffer(GL_ARRAY_BUFFER, vertexBuffers[1]); gl.TexCoordPointer(2, GL_FLOAT, 0, nil); Ну а теперь рисуем два треугольника gl.DrawArrays(GL_TRIANGLE_STRIP, 0, 4); Эта команда конструирует последовательность геометрических примитивов. Параметр mode указывает, какие примитивы следует построить, и принимает те же значения, что и единственный параметр glBegin(). Ниже приведена полная таблица значений. Второй и третий параметры указывают, с какой координаты начинаются координаты, которые читаются из массива и количество точек.
В старых книгах вы можете встретить так же режимы GL_QUADS, GL_QUAD_STRIP и GL_POLYGON; используйте их только для OpenGL 1.5-2.1.
Наконец, выключаем применения массива позиций вершин и массива текстурных координат. gl.DisableClientState(GL_VERTEX_ARRAY); gl.DisableClientState(GL_TEXTURE_COORD_ARRAY);
И делаем неактивной указанную текстуру: GLMaterialLibrary.Materials[0].UnApply(rci); В конце необходимо освободить видеопамять т.к. Delphi за нас это сделать не может. Создайте событие OnDestroy у главной формы приложения и запишите в нем: gl.DeleteBuffers(1,@vertexBuffers[0]); gl.DeleteBuffers(1,@vertexBuffers[1]); Всё. Готовый пример можно посмотреть здесь: http://www.glscene.ru/download.php?view.443. Хорошая статья о VBO от Алексея Капернюка (aka Fantom): http://www.glscene.ru/forum_viewtopic.php?6.36114.230 Частично материал позаимствован с сайта А. Берескова, отсюда: http://steps3d.narod.ru/tutorials/tutorial-VBO.html. Но я не рекомендую эту статью к прочтению. Множество неточностей делает её трудной для понимания и почти непригодной для использования на практике. Итак, мы сделали демо проект, который показывает возможности VBO. Мы использовали чистый OpenGL код или, как ещё говорят, OpenGL API. Тоже самое можно было сделать и используя классы GLScene. Я приведу несколько сокращённый код такой программы: uses VectorLists; var VertexBuffer,TexCoordBuffer: TGLVBOArrayBufferHandle;
procedure TForm1.FormCreate(Sender: TObject); var Vertices: TTexPointList; TexCoords: TTexPointList; begin Vertices:=TTexPointList.Create; TexCoords:=TTexPointList.Create; Vertices.Add(1,-1); Vertices.Add(-1,-1); Vertices.Add(1,1); Vertices.Add(-1,1);
TexCoords.Add(0,-1); TexCoords.Add(-1,-1); TexCoords.Add(0,0); TexCoords.Add(-1,0);
Form1.GLSceneViewer.Buffer.RenderingContext.Activate; VertexBuffer:=TGLVBOArrayBufferHandle.CreateFromData( Vertices.List,SizeOf(GLFLoat)*2*Vertices.Count,GL_STATIC_DRAW); TexCoordBuffer:=TGLVBOArrayBufferHandle.CreateFromData(TexCoords.List, SizeOf(GLFLoat)*3*TexCoords.Count, GL_STATIC_DRAW); gl.EnableClientState(GL_VERTEX_ARRAY); gl.EnableClientState(GL_TEXTURE_COORD_ARRAY); end;
procedure TForm1.GLDirectOpenGLRender(Sender: TObject; var rci: TRenderContextInfo); begin GLMaterialLibrary.Materials[0].Apply(rci); gl.BindBuffer(GL_ARRAY_BUFFER, VertexBuffer.Handle); gl.VertexPointer(2, GL_FLOAT, 0, nil); gl.BindBuffer(GL_ARRAY_BUFFER, TexCoordBuffer.Handle); gl.TexCoordPointer(2, GL_FLOAT, 0, nil); gl.DrawArrays(GL_TRIANGLE_STRIP, 0, 4); GLMaterialLibrary.Materials[0].UnApply(rci); end;
procedure TForm1.FormDestroy(Sender: TObject); begin GLSceneViewer.Buffer.RenderingContext.Activate; gl.DisableClientState(GL_VERTEX_ARRAY); gl.DisableClientState(GL_TEXTURE_COORD_ARRAY); gl.DeleteBuffers(2,@vertexBuffer); end; Полный исходник использования VBO через классы GLScene можно скачать здесь: http://narod.ru/... Не путайте вершинные массивы и буферы вершинных массивов (VBO).
Продолжаем OGL изыскания
Выше мы изучили использование VBO для видеокарт с OpenGL 1.5 — 2.1. Это не самые последние версии графической библиотеки. Для OpenGL 3 — 4 все трансформации нужно переносить в вершинный шейдер. Есть такое понятие — вершинный атрибут; к нему относятся цвет вершины, текстура, нормаль и другие. В OpenGL 3-4 убрали так называемые «встроенные» вершинные атрибуты, то есть этот самый цвет вершин, эту самую текстуру, нормаль. Поэтому исчезли и все функции glXXXPointer, которые мы использовали выше. Вместо этого нам теперь дают вjзможность самим создавать вершинные атрибуты. Делается это вот так. Этот код нужно положить в FormCreate или в другую процедуру, вызываемую один раз: var AttribPosition: GLInt; AttribPosition:=glGetAttribLocation(ProgramName, 'Position'); Здесь мы получаем адрес новоиспечённого атрибута. Обратите внимание, что для этого необходимо иметь шейдерную программу, её handle передаётся первым параметром, а вторым передаётся произвольное имя атрибута. В рисующую процедуру нужно добавить такой код: glBindBuffer(GL_ARRAY_BUFFER, IndicesBuffer); glVertexAttribPointer(AttribPosition, 2, GL_UNSIGNED_BYTE, False, 0, 0); glBindBuffer(GL_ARRAY_BUFFER, 0); glEnableVertexAttribArray(AttribPosition); glDrawArrays(GL_TRIANGLES, 0, VertexCount); glDisableVertexAttribArray(AttribPosition); Глава 25. FBO Или расширение EXT_framebuffer_object в OpenGL
EXT_framebuffer_object предоставляет очень простую и платформенно-независимую альтернативу использованию р-буферов, которые были непроизводительны и слишком запутаны. Данное расширение вводит новый тип объекта в OpenGL — фреймбуфер (framebuffer object, FBO). Фреймбуфер состоит из набора отдельных логических буферов (цвета, глубины, трафарета) и параметров состояния. В качестве логических буферов могут выступать как текстуры подходящих форматов, так и специальные объекты еще одного вводимого типа — рендербуферы (renderbuffer object, RBO). Рендербуфер содержит внутри себя простое двухмерное изображение (без использования пирамидального фильтрования) и может использоваться для хранения результатов рендеринга в качестве одного из логических буферов. Приложение может использовать много фреймбуферов, но все они разделяются на начальный фреймбуфер, созданный оконной системой для приложения (он имеет идентификатор 0), и созданные самим приложением фреймбуферы (они идентифицируются натуральными числами). Приложение может выбрать (bind) один из имеющихся фреймбуферов как текущий. Тогда логические буферы данного фреймбуфера используются (как для чтения, так и для записи) при всех фрагментных операциях. Я приведу две ссылки, где описывается процесс создания и использования FBO: http://steps3d.narod.ru/tutorials/framebuffer-object-tutorial.html http://www.gamedev.ru/community/opengl/articles/framebuffer_object GLFBORenderer
В GLScene уже есть компонент для FBO — это GLFBORenderer .
Здесь лежит простая демка: http://glscene.ru/e107_files/public/3800_fboposteffect.rar Здесь можно скачать демку с использованием технологии MRT: http://glscene.ru/download.php?view.501
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Последнее изменение этой страницы: 2016-04-19; просмотров: 541; Нарушение авторского права страницы; Мы поможем в написании вашей работы! infopedia.su Все материалы представленные на сайте исключительно с целью ознакомления читателями и не преследуют коммерческих целей или нарушение авторских прав. Обратная связь - 18.220.7.116 (0.01 с.) |