Глава 50. Практика. Создание мира 


Мы поможем в написании ваших работ!



ЗНАЕТЕ ЛИ ВЫ?

Глава 50. Практика. Создание мира



 

Здесь описывается создание земли, по земле ходит актёр с камерой от первого и третьего лица, вокруг растут деревья, а под деревьями грибы. Ну и ещё сделаем воду.

Начали. Поместим на форму GLScene1, GLSceneViewer1, GLCadencer1, GLNavigator1, GLUserInterface1, GLMaterialLibrary1, GLBitmapHDS1, Timer1. Пользуясь рисунком, создадим необходимые компоненты.

   
В таблице приведены имена компонентов и свойства, которые нужно у них изменить.

Имя Свойство Значение
GLSceneViewer1 Camera GLCamera2
  Align alClient
GLNavigator1 MovingObject DummyCube2
  UseVirtualUp True
  VirtualUp X=0, Y=0, Z=1
GLUserInterface1 GLNavigator GLNavigator1
  MouseSpeed  
GLCadencer1 Scene GLScene1
GLWaterPlane1 Position X=-25, Y=-100, Z=-40
  Scale X=2000, Y=2000, Z=2000
  Elastic  
  RainForce  
  RainTimeInterval  
  Up X=0, Y=0, Z=1
GLLightSource1 Position X=50, Y=50, Z=50
FreeForm1 Position X=10, Y=10, Z=5
  Scale X=5, Y=5, Z=5
  Up X=0, Y=1, Z=0
DummyCube2 Up X=0, Y=0, Z=1
GLCamera1 DepthOfView  
  Up X=0, Y=1, Z=0

 


  Position X=0, Y=3, Z=0
GLCamera2 DepthOfView  
  Position X=0, Y=2, Z=-5
  Up X=0, Y=0, Z=1
  TargetObject DummyCube2
Actor1 Up X=1, Y=0, Z=0
  Material Texture-убрать галочку с Disable
Actor2 Material Texture-убрать галочку с Disable
GLTerrainRenderer1 HeightDataSource GLBitmapHDS1
  Material Texture-убрать галочку с Disable
  Scale X=10, Y=10, Z=0,5
  Up X=0, Y=1, Z=0
FreeForm2 Scale X= 0,01, Y= 0,01, Z= 0,01
  Up X=0, Y=1, Z=0

 

Кое-что поясню по поводу создания воды. Рекомендую поэкспериментировать с цветом у GLWaterPlane1. Зайдите в свойство Material у GLWaterPlane1 поставьте BlendingMode в bmTransparency чтобы изменения цвета отображались на воде. Для придания реалистичности сделайте воду прозрачной, для этого щёлкните на Front, и на вкладке Diffuse изменяйте свойство Alpha. С помощью свойства Elastic меняется эластичность воды, чем больше, тем менее эластична. Свойство RainTimeInterval регулирует, с какой частотой появляются капли на воде. Свойство RainForce регулирует силу волны. Замечание: при изменении какого-либо параметра изменения отображаются на воде только после второй компиляции (по крайней мере, у меня так).

Ну что ж начнём кодить. Сначала подключим необходимые модули.

uses

Jpeg, Tga, GLkeyboard, VectorGeometry, GLFile3DS, GLFileMD2;

Далее объявим переменные:

public

GLTree: TGLTree; // наше дерево

proxy, proxy2: TGLProxyObject; // прокси-объекты дерева и гриба

end;

Следом загружаем всё необходимое. В OnCreate для формы пишем

procedure TForm1.FormCreate(Sender: TObject);

begin

// загружаем модель гриба

FreeForm2.LoadFromFile(‘media\mushroom.3ds’);

// загружаем карту высот для нашей земли

GLBitmapHDS1.Picture.LoadFromFile(‘media\terrain.bmp’);

// загружаем текстуру на землю

GLTerrainRenderer1.Material.Texture.Image.

LoadFromFile(‘media\clover.jpg’);

// модель актёра

Actor1.LoadFromFile(‘media\waste.md2’);

// его текстура

Actor1.Material.Texture.Image.LoadFromFile(‘media\waste.jpg’);

// его анимация

Actor1.Animations.LoadFromFile(‘media\Quake2Animations.aaf’);

// делаем его поменьше

Actor1.Scale.SetVector(0.04, 0.04, 0.04, 0);

// грузим нашему актёру пушку

Actor2.LoadFromFile(‘media\WeaponWaste.md2’);

// и текстуру к ней

Actor2.Material.Texture.Image.LoadFromFile(‘media\WeaponWaste.jpg’);

// анимация проигрывается циклически

Actor1.AnimationMode:=aamLoop;

// проигрывать анимацию стоять на месте

Actor1.SwitchToAnimation(‘stand’);

// активируем управление мышкой

GLUserInterface1.MouseLookActivate;

// далее добавляем текстуру для нашего дерева

with GLMaterialLibrary1.AddTextureMaterial(

‘LeafFront’,’media\maple_multi.tga’) do

begin

Material.BlendingMode:=bmAlphaTest50;

Material.Texture.TextureMode:=tmModulate;

Material.Texture.TextureFormat:=tfRGBA;

end;

with GLMaterialLibrary1.AddTextureMaterial(

‘LeafBack’,’media\maple_multi.tga’) do

begin

Material.BlendingMode:=bmAlphaTest50;

Material.Texture.TextureMode:=tmModulate;

Material.Texture.TextureFormat:=tfRGBA;

end;

GLMaterialLibrary1.AddTextureMaterial(‘Branch’,

’media\zbark_016.jpg’).Material.Texture.TextureMode:=tmModulate;

// добавляем прокси-деревья

AddTree;

// добавляем дерево

NewTree;

// добавляем грибы

AddMushroms;

end;

Пора добавить дерево на сцену, для этого создадим процедуру NewTree:

procedure TForm1.NewTree;

begin

// добавляем во FreeForm1 дерево

GLTree:=TGLTree(FreeForm1.AddNewChild(TGLTree));

with GLTree do

begin

// присваиваем библиотеку материалов и берём от туда текстуры

MaterialLibrary:=GLMaterialLibrary1;

LeafMaterialName:=‘LeafFront’;

LeafBackMaterialName:=‘LeafBack’;

BranchMaterialName:=‘Branch’;

// размеры

Depth:=8;

// регулирует, насколько дерево заросло листьями

LeafSize:=0.2;

// толщина ствола

BranchRadius:=0.08; BranchNoise:=0.5; Randomize;

Seed:=Round((2*Random-1)*(MaxInt-1));

end;

end;

Но что нам одно дерево, наделаем ему двойников, и опять нужно писать новую процедуру AddTree:

procedure TForm1.AddTree;

var

i: Integer;

s: TVector;

f: Single;

begin

// создаём 50 клонов одного дерева,

// которое содержится у нас во FreeForm1

for i:=0 to 50 do

begin

// создаем очередной прокси-объект

proxy:=TGLProxyObject(DummyCube1.AddNewChild(TGLProxyObject));

with proxy do

begin

// наследуем только структуру объекта

ProxyOptions:=[pooObjects];

//В свойство MasterObject записываем наше дерево-образец

MasterObject:=FreeForm1;

// ориентация в пространстве такая же, как и у FreeForm1

Direction.SetVector(FreeForm1.Direction.AsVector);

Up.SetVector(FreeForm1.Up.AsVector);

// делаем их разной величины

s:=FreeForm1.Scale.AsVector;

f:=(1*Random+1); ScaleVector(s, f); Scale.AsVector:=s;

// позиция по X и Y выбирается случайно для

// каждого созданного прокси-объекта

Position.SetPoint(Random(990), Random(990), 0);

// и ставим ровно на поверхность земли

with Position do

Z:=GLTerrainRenderer1.InterpolatedHeight(AsVector);

// случайно поворачиваем на какой-то угол

RollAngle:=Random(360);

TransformationChanged;

end;

end;

end;

 

Как мы достигаем, что деревья располагаются не абы как, а точно по склонам созданной нами земли. Объясняю: создаётся очередной прокси-объект, X и Y выбираются случайно, а Z присваивается значение высоты поверхности в точке с теми самыми случайными координатами X, Y. Всё, деревья растут точно по склонам, теперь надо, чтобы и грибы росли так же. Эта процедура аналогична предыдущей, поэтому просто скопируйте предыдущую (только мастер-объектом будет FreeForm2). Еще сделайте другое распределение грибов, а именно:

Position.SetPoint(Random(1000)-(1000/5), Random(1000)-(1000/5), 0);

Такой способ расположения объектов отличается от предыдущего тем, что он раскидывает грибы по кругу, а не в одну сторону, как это сделано с деревьями, т.к. деревья не очень смотрятся в воде.

Ну вот и готово, растительность расставлена по местам. Осталось научить ходить нашего героя, для этого у GLCadencer1 в OnProgress запишем следующее:

procedure TForm1.GLCadencer1Progress(Sender: TObject;

const deltaTime, newTime: Double);

var

speed: Single;

moving: string;

begin

// проигрывается анимация стоять на месте

moving:=‘stand’;

if IsKeyDown(VK_SHIFT) then

begin

// бежим быстрей

Actor1.Interval:=100;

// и ускоряем анимацию

speed:=50*deltaTime

end

else

begin

// Бежим медленнее

Actor1.Interval:=150;

speed:=10*deltaTime;

end;

// при нажатии на Q (Й) переключаем камеру2 на камеру1

if IsKeyDown(Ord(‘Q’)) then GLSceneViewer1.Camera:=GLCamera1;

// при нажатии на E (У) наоборот

if IsKeyDown(Ord(‘E’)) then GLSceneViewer1.Camera:=GLCamera2;

// обрабатывам нажатия клавиш движения

if IsKeyDown(Ord(‘W’)) then

begin

GLNavigator1.MoveForward(speed);

// играется анимация бежать

moving:=‘run’;

end;

if IsKeyDown(Ord(‘S’)) then

begin

GLNavigator1.MoveForward(-speed);

moving:=‘run’;

end;

if IsKeyDown(Ord(‘A’)) then

begin

GLNavigator1.StrafeHorizontal(-speed);

moving:=‘run’;

end;

if IsKeyDown(Ord(‘D’)) then

begin

GLNavigator1.StrafeHorizontal(speed);

moving:=‘run’;

end;

// при нажатии на эскейп завершаем программу

if IsKeyDown(VK_ESCAPE) then

Close;

// удерживаем игрока на поверхности земли

with DummyCube2.Position do

Z:=GLTerrainRenderer1.InterpolatedHeight(AsVector)+1;

 

// играть анимацию которая находиться в moving

if Actor1.CurrentAnimation<>moving then

Actor1.SwitchToAnimation(moving);

// синхронизируемся с первым актёром

Actor2.Synchronize(Actor1);

 

// обрабатываем движение мыши

GLUserInterface1.MouseUpdate;

GLUserInterface1.MouseLook;

end;

 

Добавим ещё кое что, а именно регулирование расстояния до актёра в виде от третьего лица колесиком мыши. Пишем в FormMouseWheel у Form1:

GLCamera2.AdjustDistanceToTarget(Power(1.1, WheelDelta/120));

 

Ну и последний штрих в виде подсчёта фпс. В OnTimer у Timer1 пишем:

Form1.Caption:=Format(‘%.2f FPS’, [GLSceneViewer1.FramesPerSecond]);

GLSceneViewer1.ResetPerformanceMonitor;

Теперь у нас в заголовке формы будет показываться сколько сейчас фпс.

Похожая демка лежит здесь: Demos\meshes\actortwocam


Приложение

Структура рендера в GLScene

Первым делом у вьювера вызывается пользовательское событие BeforeRender, сразу после это вызывается метод TGLSceneBuffer.Render который вызывает TGLSceneBuffer.DoBaseRender из которого уже вызывается TGLSceneBuffer.RenderScene, в каждой из процедур постепенно инициализируются/сбрасываются основные состояния ОГЛ, настраивается вьювер и камера, после чего идет вызов TGLBaseSceneObject.Render, и в конце рендеринга применяются все пост эффекты. Перед этим все объекты сортируются.

Сама процедура TGLBaseSceneObject.Render так же простая. Первым делом идет отсечение невидимых объектов через фрастум куллинг. В сцене есть два способа это сделать: для каждого объекта и по иерархии (если родителя не видно - не рисуем чаилдов). Далее грузится модельная матрица объекта и вызывается виртуальный метод DoRender, который перекрывается потомками от TGLBaseSceneObject.

Сам метод TGLBaseSceneObject.DoRender состоит из двух частей - первым идет вызов BuildList, в котором осуществляется создание геометрии с компиляцией ее в дисплейный список, и если список создан - вызывается внутренний рендер объекта. Вторая часть - рендеринг чаилдов, для чего вызывается метод RenderChildren.

Работа с векторами

 

VectorMake/AffineVectorMake — возращают 4/3-вектор из набора координат или 3/4-вектора

 

VectorAdd — складывает два вектора.

AddVector — складывает вектора v1, v2 и помещает результат в v1. Есть перегруженная функция, которая к каждой координате вектора v прибавляет число f.

VectorSubtract — вычитает v2 из v1. Есть перегруженная функция, которая из каждой координаты вектора v1 вычитает число delta.

SubtractVector — вычитает v2 из v1 и результат заносит в v1

 

VectorScale — каждую координату вектора v умножает на factor.

VectorDivide — делит координаты v на соответствующие координаты divider.

VectorNegate — возвращает противоположный вектор.

VectorAbs — возвращает вектор из модулей координат исходного.

У этих функций есть аналоги в форме процедур (ScaleVector, DivideVector, NegateVector, AbsVector).

 

VectorEquals — сравнивает два 4-вектора и возвращает true, если они равны.

AffineVectorEquals — сравнивает два 3-вектора и возвращает true, если они равны.

VectorIsNull — возвращает true если координаты X = Y = Z = 0, (w игнорируется).

VectorLess/More(Equals)Then — сравнивает векторы различными способами.

 

VectorDotProduct — скалярное произведение векторов

VectorCrossProduct — векторное произведение векторов

VectorAngleCosine — возвращает косинус угла между векторами.

PointProject — возвращает длину проекции вектора от origin до p на вектор из origin в направлении direction. Аналогичен VectorDotProduct(VectorSubtract(p, origin), direction).

 

VectorLength — вычисляет длину вектора: Result:= Sqrt (x*x + y*y + z*z + w*w).

VectorNorm — вычисляет вторую норму: Result:= x*x+y*y+z*z+w*w (квадрат длины по сути).

VectorNormalize — возвращает нормализованный вектор.

NormalizeVector — нормализует вектор, т.е. делает длину вектора равной единице.

 

VectorDistance — вычисляет расстояние между двумя точками (длина разности).

VectorDistance2 — вычисляет квадрат расстояния между двумя точками (первая норма разности)

VectorSpacing — вычисляет первую норму разности векторов — сумму модулей разностей координат Result:= Abs(v1[x]-v2[x]) + Abs(v1[y]-v2[y]) + Abs(v1[z]-v2[z]) + Abs(v1[w]-v2[w])

 

RotateVector — поворачивает вектор относительно оси axis на угол angle.

VectorRotateAroundX/Y/Z — поворачивает вектор вокруг оси X/Y/Z на угол alpha.

VectorReflect — поворачивает V на 180 градусов относительно N (N должен быть нормализован)

 

VectorLerp — возвращает вектор, промежуточный между v1 и v2, t изменяется от 0 до 1 и задает, к какому из них результат будет ближе. Формула: Result:= v1 + t*(v2-v1).

CombineVector — в переменную vr заносит линейную комбинацию векторов vr и v: vr:= vr + v*f.

VectorCombine — линейная комбинация двух векторов по формуле Result:= V1*F1 + V2*F2.

VectorCombine3 — линейная комбинация трех векторов: Result:= V1*F1 + V2*F2 + V3*F3.

VectorTransform — преобразует вектор заданной матрицей преобразования

Работа с матрицами

 

У каждого объекта в GLScene есть свойства, хранящие матрицы. В этих матрицах записаны ориентация, положение и масштаб объекта. Эти свойства:

Matrix — локальная матрица объекта, содержащая его трансформации в системе координат родителя;

AbsoluteMatrix — абсолютные трансормации объекта (в глобальной системе координат)

InvAbsoluteMatrix — обращенная абсолютная матрица объекта.

Также есть свойства, хранящие указатели на эти матрицы (удобны в некоторых случаях).

Формат этих матриц (кроме, понятно, обращенной):

Sx Dx Dy Dz U — компоненты Up

Ux Sy Uy Uz D — компоненты Direction

Lx Ly Sz Lz L — компоненты Left

Px Py Pz 1 P — компоненты Position

S — компоненты Scale

 

MatrixMultiply — перемножение двух матриц.

ScaleMatrix — умножает все элементы матрицы на factor.

MatrixInvert — вычисляет обратную матрицу.

MatrixDeterminant — определитель матрицы.

MatrixEquals — сравнивает матрицы.

TransposeMatrix — транспонирование матрицы. Она может пригодиться, в частности, потому, что в OpenGL матрица записывается по столбцам, а в GLScene по строкам.

Create***Matrix — возвращает матрицы разных преобразований.

TranslateMatrix — к третьей строке (обычно позиция объекта) добавляет вектор v (W не учитывается)

У MatrixMultiply и MatrixInvert есть процедурные аналоги.

 

Это не все функции для векторов и матриц. Также есть еще множество функций для работы с плоскостями, углами, кватернионами, функций проверки пересечений, оптимизированных тригонометрических и математических функций, и многих других. В общем, посмотрите сами.

Методы объектов GLScene

 

Move, Lift, Slide — двигают объект вдоль вектора Direction/Up/Left.

Translate — перемещает объект на соответствующее расстояние.

 

MoveObjectAround — вращает объект вокруг указанного на заданные углы по горизонтали и вертикали.

Pitch, Turn Roll — поворачивают объект вокруг оси X/Y/Z.

ResetRotations — сбрасывает все повороты и восстанавливает Direction и Up по умолчанию.

ResetAndPitchTurnRoll — сбрасывает все повороты и затем вращает на заданные углы.

PointTo — устанавливает Direction объекта по направлению на указанный объект или точку, требует вектор Up (ведь после поворота Left и Up могут быть искажены).

 

DistanceTo — возвращает расстояние до указанного объекта или точки.

SqrDistanceTo — аналогично, но квадрат расстояния. Вычислительно проще, поэтому его лучше использовать для сравнения расстояний.

PointInObject — проверяет, попадает ли точка в объект. Используйте с осторожностью: зачастую он проверяет только попадание точки в ограничивающий параллелепипед объекта.

RayCastIntersect — проверяет, пересекается ли с объектом луч из rayStart в направлении rayVector.

 

MoveUp/Down/First/Last — перемещает объект в иерархии объекта-родителя.

Действие остальных методов обычно понятно из названия.



Поделиться:


Последнее изменение этой страницы: 2016-04-19; просмотров: 356; Нарушение авторского права страницы; Мы поможем в написании вашей работы!

infopedia.su Все материалы представленные на сайте исключительно с целью ознакомления читателями и не преследуют коммерческих целей или нарушение авторских прав. Обратная связь - 18.225.11.98 (0.141 с.)