- •Учебник
- •Оглавление
- •Введение
- •Глава 1
- •I. Установка
- •II. Удаление
- •Глава 2. Первый проект
- •Глава 3. Создание сцены в режиме design time
- •Создание объектов:
- •Глава 4. Примитивные объекты. Вкладка Basic geometry.
- •Глава 5. Ориентация, координаты.
- •Глава 6. Движение.
- •Глава 7. Как узнать, сколько сейчас fps?
- •Глава 8. Полноэкранный режим.
- •Глава 9. Работа с материалами.
- •I. Прямое обращение к .Material
- •Глава 10. Использование 3d моделей.
- •I. Форматы моделей
- •Глава 11. Прокси объекты.
- •Глава 12. Создание земной поверхности с glTerrainRenderer.
- •Глава 13. Создание земной поверхности с glFreeForm.
- •Глава 14. Итоги работы с glTerainrender и glFreeForm.
- •Глава 15. Создание земной поверхности с glHeightField.
- •Глава 15. Создание неба.
- •Глава 16. GlAtmosphere.
- •Глава 17. Спецэффекты.
- •I. Огонь
- •II. Молния
- •III. Дождь
- •IV. Снег
- •VI. Блики от источников света
- •Глава 18. Растительность.
- •I. Деревья и кустарники
- •II. Трава
- •Глава 19. Работа со шрифтами и вывод надписей.
- •I. Вывод текста через компонент glWindowsBitmapFont
- •II. Проблема с glWindowsBitmapFont в Windows Vista
- •Глава 20. Выделение объекта мышкой.
- •Глава 21. Создание сцены в режиме runtime.
- •Глава 22. Тени.
- •Глава 23. Туман.
- •Глава 24. Vbo или расширение arb_vertex_buffer_object в OpenGl.
- •Глава 25. Fbo или расширение ext_framebuffer_object в OpenGl.
- •Глава 26. Рисование на канве.
- •Глава 27. Использование чистого OpenGl.
- •Глава 28. Звуки.
- •I. Проигрывание звуков с помощью bass
- •II. Проигрывание звуков с помощью fmod
- •III. Проигрывание звуков с помощью OpenAl
- •Глава 29. Игровое меню.
- •Глава 30. Примитивная физика dce.
- •Глава 31. Физика ode.
- •II. Основы библиотеки ode(: tglodeManager и tglodeJointList) на примере создания подобия боулинга.
- •Глава 32. Физика Newton.
- •Глава 33. Ручная проверка коллизий.
- •V. Ручная проверка коллизии
- •Глава 34. Шейдеры. Терменология.
- •Глава 35. Шейдеры. История и компоненты.
- •Глава 36. Шейдеры glsl. Введение.
- •Глава 37. Шейдеры glsl. Использование без компонентов. Самый примитивный шейдер.
- •Глава 38. Шейдеры glsl. Немного о спецификации. Типы данных и переменные
- •Стандартные функции
- •Поддерживаемые компоненты векторов
- •Глава 40. Шейдеры glsl. Текстурирование.
- •Глава 41. Шейдеры glsl. Использование без компонентов. Мультитекстурирование. Смешение трёх цветов по базовой карте.
- •Глава 42. Шейдеры glsl. Использование через компонент glslShader. Часть первая – Первые шаги.
- •Глава 43. Шейдеры glsl. Использование через компонент glslShader. Часть вторая – знакомимся со светом. Передача параметров в шейдер.
- •Глава 44. Шейдеры glsl. Использование через компонент glslShader. Часть третья – текстуры.
- •Глава 45. RenderMonkey.
- •Глава 46. Оптимизация программы.
- •Глава 47. Практика. Создание мира
- •Приложение
- •I. Типы векторов и матриц
- •II. Работа с векторами
- •III. Работа с матрицами
- •IV. Методы объектов glScene
- •Предметный указатель по компонентам glScene
Глава 23. Туман.
Регулировать настройки тумана можно через FogEnvironment, который находится в Buffer’е. Сначало туман нужно включить:
GLSceneViewer1.Buffer.FogEnable:=true;
У тумана есть следующие свойства:
Свойство |
Описание |
FogColor |
Цвет тумана. Можно задать так: GLSceneViewer1.Buffer.FogEnvironment.FogColor.SetColor(r,g,b,a); где r , g , b – красная, зеленая и синяя составляющие соответственно; a – прозрачность; |
FogStart |
|
FogEnd |
|
FogMode |
может принимать значения fmLinear, fmExp, fmExp2 |
FogDistance |
может принимать значения fdDefault, fdEyeRadial, fdEyePlane |
Хороший пример можно посмотреть в стандартной папке demos по адресу …demos\Demos\rendering\fog
Глава 24. Vbo или расширение arb_vertex_buffer_object в OpenGl.
Расширение VBO является лучшим вариантом вывода всей сцены. Однако один только класс MeshObjects из всех классов GLScene использует эту технологию. Большинство компонентов задействовывают вершинные массивы.
Одним из самых "узких" мест в работе с современным графическим ускорителем является передача ему данных - текстур, вершин, нормалей и т.п. Для повышения быстродействия следует уменьшить количество запросов на передачу данных и передавать их как можно большими частями.
Если с текстурами все достаточно просто - они один раз загружаются в память графического ускорителя и больше не изменяются, то с геометрическими данными (координаты вершин, нормали, текстурные координаты) дело обстоит значительно хуже.
Самый старый способ передачи данных в операторных скобках GLBegin()/GLEnd является крайне неэффективным, поскольку требует одного вызова функции для каждого атрибута каждой вершины. Ещё одной проблемой GLBegin()/GLEnd является избыточная обработка вершин, являющихся общими для нескольких смежных полигонов.
Поэтому разработчики пытались улучшить ситуацию. В версии 1.1 были ведены так называемые вершинные массивы (стоит отметить, что некоторые разработчики реализовали вершинные массивы в виде расширений ещё на момент выхода OpenGL 1.0). В них данные хранятся на стороне CPU и ускорителю передается только указатель на них, а так же размер. При использовании этого способа передача осуществляется сразу большими блоками, количество обращений к графическому ускорителю (GPU) заметно сокращается. Кроме того, использование вершинных массивов может устранить избыточность при обработке общих вершин. Всё это приводит к гораздо более эффективному использованию GPU и общему повышению быстродействия программы. Однако этот способ требует постоянной передачи массивов данных от CPU к GPU – вызов любого оператора OpenGL с указателем на массив приводит к его передаче графическому ускорителю, даже если передаваемые данные не устарели (т.е. те данные, что содержит GPU и те, что будут ему присланы полностью индеинтичны).
Версии 1.1 было введено понятии Interleaved array. К этому понятию относится команда glInterleavedArrays, позволяющая задавать сразу несколько массивов за один вызов. Но решение было отброшено так как всё равно требовало постоянной передачи данных. Подробнее о Interleaved array можите прочитать переведённую спецификацию OpenGL здесь: http://opengl.gamedev.ru/doc/?func=glInterleavedArrays.
http://steps3d.narod.ru/tutorials/r2vb-tutorial.html
Поэтому было бы гораздо эффективнее сразу загрузить такие данные в память GPU, после чего использование этих данных графическим ускорителем не будет требовать их передачи от CPU.
Именно подобную возможность и предоставляет пользователю расширение ARB_vertex_buffer_object, введенное в ядро начиная с версии 1.5. Использование данного расширения позволяет кэшировать (хранить) различные типы данных в быстрой памяти графического ускорителя. Для такого хранения блоки данных помещаются в так называемые вершинные буфера (vertex buffer objects, VBO), при этом каждый такой буфер представляет из себя просто массив байт.
Давайте сделаем приложение, которое будет использовать вершинные буфера. Я надеюсь, многие аспекты реализации после этого отпадут. Мы будем выводить затекстурированную плоскость средствами GLScene и OpenGL.
Поместите на форму компоненты GLScene, GLSceneViewer, GLMaterialLibrary. В инспекторе объектов сцены (в GLScene) создайте камеру под именем GLCamera. Свойству GLSceneViewer.Camera присвойте объект GLCamera. Свойству GLCamera.Position.Z присвойте значение 2. В GLMaterialLibrary создайте материал. Свойству Disabled материала присвойте значение False. Загрузите в материал любую 2D текстуру.
Переходим к коду. Я постараюсь максимально комментировать всё, что мы сейчас будем делать.
Создайте массив:
var vertexBuffer:array[0..1] of GLInt;
Это массив - два объекта вершинного буфера. Один для хранения позиций вершин (vertexes), другой для текстурных координат (texcoords).
Теперь запишите вот этот код:
type
Vertex=array[0..1] of GLFloat;
TexCoord=array[0..1] of GLFloat;
Что мы здесь сделали? Мы объявили, что тип Vertex эквиволентен массиву чисел GLFloat с двумя индексами.
Теперь запишите это:
var
Vertexes:array of Vertex;
TexCoords:array of TexCoord;
Массив Vertexes служит для хранения вершин; массив TexCoords нужен для хранения текстурных координат. Обратите внимание, здесь мы осуществляем вывод в двухмерном пространстве!
Теперь создайте процедуру VBOInit. В ней мы будем прописывать все необходимые операторы для создания и использования вершинных буферов.
Procedure VBOInit;
begin
end;
Теперь мы будем записывать операторы в эту процедуру, пока я не скажу делать это в другую.
Запишите:
glGenBuffers( 2, @vertexBuffer);
Этим оператором мы сгенерировали два буферных объекта. Далее один мы будем использовать для хранения позиций вершинных координат, второй для текстурных координат. Возможно, в будущее вам понадобятся ещё 4 буфера. Но о них позже.
Теперь нужно прописать:
glBufferData( GL_ARRAY_BUFFER, sizeof(GLFloat)*(high(Vertexes)+1), @Vertexes[0], GL_STATIC_DRAW );
Командой задаётся инициализация и заполнение буфера данными.
Первый параметр – данные, которые будет содержать буфер. В случае с VBO это всегда информация о вершинах (позиция, нормаль и т.д.).
Второй параметр задаёт размер буфера. Хитрым кодом “sizeof(GLFloat)*(high(Vertexes)+1)” мы в точности подобрали нужный. Команда SizeOF (относящаяся к модулю System (Borland Software Corporation)) с параметром GLFloat вычисляет занимаемый объём памяти одним значением типа GLFloat. Далее его мы умножаем на количество индексов массива Vertexes (оператор high(Vertexes)), прибавив к получившемуся значению 1.
Третий параметр – ссылка на область памяти, где хранится вершинный массив. Тут всё понятно.
Четвёртый параметр несёт в себе информацию о предполагаемом назначении буфера. Эта информация является лишь намеком и служит для того, чтобы система могла более эффективно управлять выделением памяти для вершинного буфера. Параметр имеет вид GL_XXX_VVV_ARB.
Значение XXX |
Описание |
STREAM |
Предполагается, что после каждого вывода данных они будут изменятся. |
DYNAMIC |
Предполагается, что будет частое использование и изменение содержимого буфера. |
STATIC |
Предполагается, что данные будут заданы один раз и потом много раз использованы или вообще не будут изменяться. |
Значение VVV |
Описание |
DRAW |
Буфер будет использоваться для передачи данных GPU, например, для вывода объектов. |
READ |
Буфер будет использоваться пользователем для чтения из него. |
COPY |
Буфер будет использоваться как для чтения данных из GPU, так и для вывода объектов. |
Поскольку мы собираемся вывести плоскость с текстурой и больше ничего не изменять, мы выберем GL_STATIC_DRAW.
Далее всё в той же процедуре VBOInit пропишите
glVertexPointer(2, GL_FLOAT, 0, nil);
Здесь мы указываем, какое количество координат будем задавать каждой вершине (две, три или четыре); тип данных для каждой координаты в массиве (GL_SHORT, GL_INT, GL_FLOAT или GL_DOUBLE); промежуток памяти в байтах между координатами соседних последовательных вершин (всегда ставьте в 0) и указали на область, где содержатся координаты первой вершины (когда используете VBO, ставьте этот параметр в nil).
Всего существует 4 оператора GL…Pointer, используемых с буферами вершинных масивов. Это:
GIVertexPointer — задает адрес массива координат вершин;
GINormalPointer — задает адрес массива нормалей в вершинах;
GlColorPointer — задает адрес массива цветов, связанных с вершинами;
GlTexCoordPointer — задает адрес массива координат текстуры материала, задаваемой в вершинах.
Далее мы должны освободить активный вершинный буфер:
glBindBuffer( GL_ARRAY_BUFFER, 0 );
Первый параметр – данные, которые будет содержать буфер. Это всегда информация о вершинах (позиция, нормаль и т.д.) - GL_ARRAY_BUFFER_ARB.
Второй параметр должен привязывать первый параметр к буферному объекту, но сами объекты считаются с 1; поэтому если написать 0 во втором параметре и GL_ARRAY_BUFFER в первом, то активный вершинный буфер освободится.
Теперь запишите сразу весь кодовый блок для использования вершинного буфера для текстуры:
glBindBuffer( GL_ARRAY_BUFFER, vertexBuffer[1]);
glBufferData( GL_ARRAY_BUFFER, sizeof(GLFloat)*(high(TexCoords)+1), @TexCoords[0], GL_STATIC_DRAW );
glTexCoordPointer(2, GL_FLOAT, 0, nil);
glBindBuffer( GL_ARRAY_BUFFER, 0);
Объяснять заново команды заново нет никакого смысла. Тем более что язык уже отсох.
Сейчас уже будет попроще… Создайте событие FormCreate и теперь прописывайте в нём
setlength(Vertexes,2*6);
Vertexes[0][0]:=-1; Vertexes[0][1]:=-1;
Vertexes[1][0]:=1; Vertexes[1][1]:=-1;
Vertexes[2][0]:=1; Vertexes[2][1]:= 1;
Vertexes[3][0]:=1; Vertexes[3][1]:= 1;
Vertexes[4][0]:=-1; Vertexes[4][1]:= 1;
Vertexes[5][0]:=-1; Vertexes[5][1]:=-1;
setlength(TexCoords,2*6);
TexCoords[0][0]:=-1; TexCoords[0][1]:=-1;
TexCoords[1][0]:=1; TexCoords[1][1]:=-1;
TexCoords[2][0]:=1; TexCoords[2][1]:=0;
TexCoords[3][0]:=1; TexCoords[3][1]:=1;
TexCoords[4][0]:=-1; TexCoords[4][1]:=1;
TexCoords[5][0]:=-1; TexCoords[5][1]:=0;
VBOInit;
Напомню, Setlength изменяет размер массива. Размер массива считается произведение количества координат, передаваемых для одной вершины, на количество вершин нашего объекта. Помимо выделения памяти, как вы наверно уже догадались, мы задаём позиции каждой вершины и текстурной координаты.
В конце мы вызываем процедуру VBOInit (надеюсь, вы не забыли её создать).
Теперь в инспекторе объектов создайте GLDirectOpenGL. В его событии OnRender пишите
GLMaterialLibrary.Materials[0].Apply(rci);
Это делает активной указанную текстуру. В данном случае библиотека материалов это просто список указателей на текстурные дискрипторы, а чтобы текстура применилась к отрисовываемыму объекту, её нужно активировать. При отображении стандартных объектов это делается GLScene автоматически, но когда мы используем DirectOpenGL, нужно делать это кодом.
Обратите внимание, что сейчас используется материал с индексом 0. И поэтому если у вас много материалов следите, какой вы используете.
А теперь включите массив координат вершин и массив координат текстуры
glEnableClientState( GL_VERTEX_ARRAY);
glEnableClientState(GL_TEXTURE_COORD_ARRAY);
Всего существует 6 таких массивов. Для использования расширения VBO понадобятся только 4. Их я и приведу:
GL_VERTEX_ARRAY |
массив координат вершин |
GL_COLOR_ARRAY |
массив цветов в режиме RGBA |
GL_NORMAL_ARRAY |
массив координат векторов нормалей |
GL_TEXTURE_COORD_ARRAY |
массив координат текстуры |
Далее необходимо привязывать вершинный буфер к массиву. Следующим оператором мы укажем количество координат на каждую вершину, тип данных для каждой координаты, нулевой промежуток памяти в байтах и что координата первой вершины содержится в самом начале массива. Мы сделаем всё это дважды: для координат вершин и текстурных координат.
glBindBuffer( GL_ARRAY_BUFFER, vertexBuffer[1]);
glTexCoordPointer(2, GL_FLOAT, 0, nil);
glBindBuffer(GL_ARRAY_BUFFER, vertexBuffer[0]);
glVertexPointer( 2, GL_FLOAT, 0, nil);
Ну а теперь рисуем два треугольника
glDrawArrays(GL_TRIANGLES, 0, 6);
Эта команда конструирует последовательность геометрических примитивов. Параметр mode указывает, какие примитивы следует построить, и принимает те же значения, что и единственный параметр glBegin(), например GL_POLYGON, GL_LINE_LOOP. Ниже приведена полная таблица значений. Второй и третий параметры указывают с какой координаты начинаются (всегда 0) и где закончиваются координаты, которые читаются из массива (например, если бы мы указали первый параметр glDrawArrays как GL_QUADS и рисовали бы один квадрат, то в третьем параметре указали бы 4).
Значение |
Соответствующие примитивы |
GL_POINTS |
Индивидуальные точки. |
GL_LINES |
Вершины попарно интерпретируются как самостоятельные отрезки. |
GL_LINE_STRIP |
Серия соединенных отрезков (ломаная). |
GL_LINE_LOOP |
Аналогично предыдущему, но, кроме того, автоматически добавляется отрезок, соединяющий первую и последнюю вершины (замкнутая ломаная). |
GL_TRIANGLES |
Каждая тройка вершин интерпретируется как треугольник. |
GL_TRIANGLE_STRIP |
Цепочка соединенных треугольников. |
GL_TRIANGLE_FAN |
Веер из соединенных треугольников. |
GL_QUADS |
Каждая четверка вершин интерпретируется как четырехугольный полигон. |
GL_QUAD_STRIP |
Цепочка соединенных четырехугольников. |
GL_POLYGON |
Граница простого выпуклого полигона. |
glDisableClientState(GL_VERTEX_ARRAY);
glDisableClientState(GL_TEXTURE_COORD_ARRAY);
Выключаем применения массива позиций вершин и массива текстурных координат.
GLMaterialLibrary.Materials[0].UnApply(rci);
Вот это делает неактивной указанную текстуру. Обратите внимание, что сейчас используется материал с индексом 0. И поэтому следите, какой материал вы используете.
Всё. Запускайте и смотрите. А внизу расположена картинка “как это получилась у меня”.