Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Учебник по GLScene.doc
Скачиваний:
255
Добавлен:
16.12.2018
Размер:
7.18 Mб
Скачать

Глава 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. И поэтому следите, какой материал вы используете.

Всё. Запускайте и смотрите. А внизу расположена картинка “как это получилась у меня”.