Delphi Графика. Экспорт анимации из 3DS MAX Часть 1:
Создание анимированного персонажа и вывод на экран :
Специально для тех, кто не владеет навыками работы с 3D Studio Max и Character Studio, я создал модель бегающего человечка. Она находится в папке MAX, и файл называется BodyRun.max. Если у Вас вообще нет пакета 3D Studio Max, то файл GMS с сетками этого человечка находится в папке GMS и называется ManRun.gms.
Итак, запустите среду 3D Studio Max и создайте анимированного персонажа или загрузите его из файла BodyRun.max. Запустите утилиту MEGA, как это делалось в разделе Знакомство с утилитой MEGA V1.0. Установите значение поля From =0, значение поля To установите в кадр, на котором заканчивается анимация, в случае с файлом BodyRun.max это значение нужно установить в 11. Значение поля Step установите в еденицу. Выделите сетку персонажа.
Внимание: убедитесь, что Вы выделили именно сетку персонажа и только ее. Пометьте флажок Selected Only. Для анимации сетки используется скелет. Он создается и подгоняется под размеры и форму тела, затем вершины сетки связываются с костями скелета. При анимации изменяются параметры положения частей скелета, а сетка лишь следует за ними. Поэтому, всегда, когда используется этот подход, в сцене помимо сетки присутствует скелет. Вот почему необходимо выделить только сетку и пометить флажок Selected Only.
После того, как Вы выполнили все операции укзанные выше, экспортируйте объект в файл GMS. В процессе экспорта Вы должны увидеть, как последовательно перемещается ползунок расположенный внизу экрана, отсчитывая кадры анимации, и как меняются кадры в проекционных окнах 3D Studio Max. Процесс экспорта завершится, когда ползунок достигнет конечного значения.
Готовый проект лежит в папке Ch02. Откомпилируйте его и запустите на выполнение. На экране вы должны увидеть примерно то, что изображено на рисунке. Нажатием кнопки "Анимировать" можно запускать или останавливать анимацию. Если Ваш компьютер оснащен 3D ускорителем, то лучше развернуть окно на весь экран - так медленнее. Теперь разберем исходный код программы. Он дополнился новым объектом TGLMultyMesh, который создан для загрузки и последовательной отрисовки нескольких сетчатых объектов.
Список Meshes хранит все сетки загруженные из файла. Переменная Action указывает выполняется анимация или нет, а CurrentFrame содержит номер текущего кадра анимации.
procedure TGLMultyMesh.LoadFromFile;
var
f : TextFile;
S : String;
//Встроенная процедура
procedure ReadNextMesh;
var
i : Integer;
Vertex : TGLVertex;
Face : TGLFace;
MaxVertex : GLFloat;
NextMesh : TGLMesh;
begin
NextMesh := TGLMesh.Create;
repeat
ReadLn(f, S);
until (S = 'numverts numfaces') or eof(f);
// Читаем количество вершин и граней
Readln(f,NextMesh.VertexCount,NextMesh.FacesCount);
// Выделяем память для хранения сетки
GetMem(NextMesh.Vertices,NextMesh.VertexCount*SizeOf(TGLVertex));
GetMem(NextMesh.Faces,NextMesh.FacesCount*SizeOf(TGLFace));
GetMem(NextMesh.FasetNormals,NextMesh.FacesCount*SizeOf(TGLVector));
ReadLn(f,S); // Пропускаем строку Mesh vertices:
// Считываем вершины
for i := 0 to NextMesh.VertexCount - 1 do
begin
Readln(f,Vertex.x,Vertex.y,Vertex.z);
NextMesh.Vertices[i] := Vertex;
end;
ReadLn(f,S); // Пропускаем строку end vertices
ReadLn(f,S); // Пропускаем строку Mesh faces:
// Считываем грани
for i := 0 to NextMesh.FacesCount - 1 do
begin
Readln(f,Face[0],Face[1],Face[2]);
Face[0] := Face[0] - 1;
Face[1] := Face[1] - 1;
Face[2] := Face[2] - 1;
NextMesh.Faces[i] := Face;
end;
// Рассчитываем масштаб
MaxVertex := 0;
for i := 0 to NextMesh.VertexCount - 1 do
begin
MaxVertex := Max(MaxVertex,NextMesh.Vertices[i].x);
MaxVertex := Max(MaxVertex,NextMesh.Vertices[i].y);
MaxVertex := Max(MaxVertex,NextMesh.Vertices[i].z);
end;
NextMesh.fExtent := 1/MaxVertex;
NextMesh.CalcNormals;
Meshes.Add(NextMesh);
end;
//Код основной процедуры
begin
Meshes := TList.Create;
AssignFile(f,FileName);
Reset(f);
While not Eof(f) do
begin
Readln(f,S);
if S = 'New object' then ReadNextMesh;
end;
CloseFile(f);
end;
Код загрузки объекта TGLMultyMesh практически идентичен коду загрузки объекта TGLMesh. Небольшое отличие состоит в том, что объект TGLMultyMesh предполагает, что файл содержит несколько сеток. Поэтому при загрузке проиходит поиск строки "New Object", создается объект TGLMesh, который помещается в список Meshes и в него считывается информация из файла. Затем весь цикл повторяется до тех пор, пока не кончится файл. Процедуры создания, уничтожения и отрисовки объекта тоже почти не изменились:
procedure TGLMultyMesh.Draw;
begin
if Extent then
begin
fExtent := TGLMesh(Meshes.Items[CurrentFrame]).fExtent;
glScalef(fExtent,fExtent,fExtent);
end;
TGLMesh(Meshes.Items[CurrentFrame]).Draw; // Рисование текущего кадра
if Action then
begin // Если включена анимация увеличить
//значение текущего кадра
inc(CurrentFrame);
if CurrentFrame > (Meshes.Count - 1) then
CurrentFrame := 0;
end;
end;
constructor TGLMultyMesh.Create;
begin
Action := False;
CurrentFrame := 0;
end;
destructor TGLMultyMesh.Destroy;
Var
i : Integer;
begin
for i := 0 to Meshes.Count - 1 do
begin
TGLMesh(Meshes.Items[i]).Destroy;
end;
Meshes.Free;
end;
Немного изменился и вызов функции загрузки в модуле frmMain.pas.
procedure TfrmGL.N1Click(Sender: TObject);
begin
if OpenDialog.Execute then
begin
MyMesh.Destroy;
Mymesh := TGLMultyMesh.Create;
MyMesh.LoadFromFile( OpenDialog.FileName );
MyMesh.Extent := true;
// Проверяем сколько сеток загружено и возможна ли анимация
if MyMesh.Meshes.Count <= 1 then N2.Enabled := False
else N2.Enabled := True;
end;
end;
procedure TfrmGL.N2Click(Sender: TObject); // Включение анимации
begin
MyMesh.Action := not MyMesh.Action;
N2.Checked := not N2.Checked;
end;
Здесь все должно быть предельно ясно, не будем акцентировать на этом внимание, и так статья длиннее получается, чем я расчитывал.
Да, конечно, человечек убогий. Мало того, что он кривой, так еще и прихрамывает. Что делать, чтобы создавать красивых человечков с минимальным количеством граней нужно быть профессионалом 3D моделирования. Все же, мы еще попытаемся его улучшить.
Вероятно, Вы заметили, огрехи воспроизведения объектов на экране, выражающиеся в каких - то непонятных черных треугольниках в тех местах, где их не должно быть. Сам я понятия не имею, откуда они взялись. Если Вас не удовлетворяет такой вид объектов, значит, настала пора поговорить о нормалях.
Что такое нормали?
Нормалью называется перпендикуляр к чему-либо. В нашем случае это перпендикуляр к грани. Хотелось бы, но, к сожалению, без нормалей никак не обойтись. Дело в том, что по нормалям расчитывается освещение объекта. Так, например, если нормаль грани направлена на источник света, то грань будет освещена максимально. Чем больше нормаль отвернется от источника света, тем менее грань будет освещена. В случае с OpenGL, если нормаль отвернется от экрана более чем на 90 градусов, мы вообще не увидим грань, она не будет отрисовываться. Если бы мы не использовали нормали, то наш объект был бы закрашен одним цветом, то есть мы бы увидели только силует объекта. Трехмерный эффект достигается окрашиванием граней объекта в разные по яркости цвета, или наложением теней, кому как больше нравится это называть. Кроме того, степень освещенности зависит также от длины вектора нормали, но, как правило, длина вектора нормали должна находится в пределах (0; 1).
Теперь я думаю, стало ясно, что такое нормали и зачем они нужны.
Загрузка фасетных нормалей из файла GMS :
Что такое фасетная нормаль? Фасетная нормаль, это самая обычная нормаль к грани, а называется она так по производимому воздействию на изображаемый объект. После применения фасетных нормалей грани объекты хоть и освещены по-разному, но каждая грань освещена равномерно и соответственно закрашена одним цветом, что приводит к тому, что объект выглядит граненым. Отсюда и название. По-нашему "фасетная нормаль" это "граненая нормaль". В предыдущих примерах фасетные нормали рассчитывались по математическому алгоритму (процедура CalcNormals), но по всей видимости он иногда дает сбои. Не все то хорошо для программиста, что хорошо для математика. В результате и появляются черные треугольники там где их не должно быть.
К счастью, внутренний язык 3D Studio Max позволил мне найти фасетные нормали, которые он использовал для отображения объекта, а отображались объекты в 3D Studio Max правильно. Приложение, использующее нормали, взятые из 3D Studio Max, находится в папке Ch03. А какая при этом получается разница, Вы можете увидеть на картинках ниже:
Теперь наша баранка выглядит правильно. В процедуре загрузки сетки добавился блок считывания фасетных нормалей из файла GMS. Процедуру CalcNormals я оставил в исходном тексте, но закоментировал.
ReadLn(f, S); //Пропускаем строку "end faces"
ReadLn(f, S); // Пропускаем строку "Faset normals"
// фасетные нормали
for i := 0 to FacesCount - 1 do
begin
Readln(f,Normal.x,Normal.y,Normal.z);
FasetNormals[i] := Normal;
end;
Естественно, что количество фасетных нормалей равняется количеству граней.
Загрузка сглаживающих нормалей из файла GMS :
Все-таки, несмотря на то, что объект теперь отображается правильно, хочется чего-то еще. Ну кому понравится граненая баранка? Или футбольный мяч такой, будто его вытесали из гранита? И, несмотря на то, что уровень детализации в данном примере не высок, можно еще улучшить внешний вид объекта. На помощь приходят сглаживающие нормали. Об этом стоит рассказать подробнее.
Когда я понял, что, используя команду glShadeModel, мне не удастся сгладить мой объект (и у Вас не получится тоже), я затосковал. Нужно было что-то делать, и я решил заняться этим вопросом вплотную. Вот что мне удалось выяснить. Оказывается к одной грани можно построить не одну нормаль, а столько, сколько душа пожелает. Но это еще ничего не дает. А вот если мы нормаль отклоним в сторону, так что она станет, не перпендикулярна грани, то грань окрасится неравномерно. Конечно, слова о том, что "нормаль не перпендикулярна", могут показаться немного странными для математика, но программиста это смущать не должно :). Я попробую пояснить подробнее, что же получается в этом случае, на рисунках.
Взгляните на них. Как видно из рисунков, мы имеем четырехугольную грань, в каждом углу которой построена нормаль. На первом рисунке все нормали перпендикулярны грани, и грань выглядит плоской. На втором рисунке нормали разведены в стороны от центра грани и грань освещена неравномерно, так будто она выпукла, хотя на самом деле она плоская. Если же свести нормали к центру грани, то грань станет вогнутой.
Это можно применять следующим образом. Чтобы добиться эффекта сглаживания, строить нормали нужно к вершинам грани, на каждую вершину по одной нормали. Для построения нормали, необходимо узнать к каким граням принадлежит вершина (теоретически вершина может принадлежать бесконечному числу граней - на практике не больше 12), взять фасетные нормали от этих граней, расчитать от них среднюю нормаль и построить ее к вершине. Как это сделать? Какими формулами это считается? Честно говоря, я понятия не имею. Есть такой сайт: http://www.pobox.com/~nate Ната Робинсона, там лежит пример на сглаживание и не только. Правда, написан он на Сях. Мне бы не составило труда переписать его на Дельфи, но... Зачем утруждать себя, если есть Баунти? Снова берем 3D Studio Max, лезем внутрь, хватаем сглаживающие нормали и... Вуаля!
Проект находится в папке Ch04. Скомпилируйте его и запустите на выполнение. Теперь Вы можете наслаждаться внешним видом сглаженного бублика нажав на кнопку Фасеты/Сгладить. Выглядит это примерно так:
Код программы, как всегда существенно не изменился. В процедуру загрузки добавился блок загрузки сглаживающих нормалей:
ReadLn(f,S); // Пропускаем строку end faset normals
ReadLn(f,S); // Пропускаем строку SmoothNormals:
// Считываем сглаживающие нормали
for i := 0 to NextMesh.VertexCount - 1 do
begin
Readln(f,Normal.x,Normal.y,Normal.z);
NextMesh.SmoothNormals[i] := Normal;
end;
procedure TGLMesh.Draw(Smooth: Boolean);
var
i : Integer;
Face : TGLFace;
begin
for i := 0 to FacesCount - 1 do
begin
glBegin(GL_TRIANGLES);
Face := Faces[i];
if Smooth then
begin
// Если сглаживать тогда перед каждой
// вершиной рисуем сглаживающую нормаль
glNormal3fv(@SmoothNormals[Face[0]]);
glVertex3fv(@Vertices[Face[0]]);
glNormal3fv(@SmoothNormals[Face[1]]);
glVertex3fv(@Vertices[Face[1]]);
glNormal3fv(@SmoothNormals[Face[2]]);
glVertex3fv(@Vertices[Face[2]]);
end
else
// Если не сглаживать один раз рисуем фасетную нормаль
begin
glNormal3fv(@FasetNormals[i]);
glVertex3fv(@Vertices[Face[0]]);
glVertex3fv(@Vertices[Face[1]]);
glVertex3fv(@Vertices[Face[2]]);
end;
glEnd;
end;
end;
procedure TGLMultyMesh.Draw;
begin
if Extent then
begin
fExtent := TGLMesh(Meshes.Items[CurrentFrame]).fExtent;
glScalef(fExtent,fExtent,fExtent);
end;
TGLMesh(Meshes.Items[CurrentFrame]).Draw(fSmooth);
if Action then
begin
inc(CurrentFrame);
if CurrentFrame > (Meshes.Count - 1) then
CurrentFrame := 0;
end;
end;
Сам объект TGLMesh дополнился массивом для сглаживающих нормалей, а TGLMultyMesh - флагом указывающим следует ли сглаживать или нет. Этот флаг передается в процедуру отрисовки объекта TGLMesh. Деструктор пополнился строкой уничтожающей массив сглаживающих нормалей. В модуле frmMain появился обработчик нажатия пункта меню Фасеты/Сгладить.
Вот, пожалуй, и все. Могу только добавить, что не всегда удобно пользоваться сглаживающими нормалями из файла GMS, хотя в большинстве случаев они подходят. Загрузите, к примеру, объект Zban.gms и установите сглаживающий режим. Видите, все сглажено, а в 3D Studio Max он выглядел по-другому. Сверху и снизу у него были полукруглые крышки, но посередине был четкий цилиндр, с резкой границей в местах состыковки с полукруглыми крышками. Это побочный эффект сглаживания. Если Вы хотите добится исчезновения этого эффекта, Вам придется написать приложение для ручной корректировки нормалей, или программно отслеживать ситуацию, когда излом достиг критического угла и следует воспользоваться фасетной нормалью. Теперь, пожалуй, действительно все.
Автор дает полное право всем желающим на копирование, распространение и модификацию файлов примеров программ. Авторские права на данную статью принадлежат Ивану Дышленко. Право на копирование и изменение любой части текста данной статьи принадлежит только автору, данную статью разрешается копировать и распространять только полностью, с файлами и примерами программ. Не разрешается модифицировать и распространять модифицированные варианты утилиты MEGA.ms, поскольку утилита будет наращиваться и автору хотелось бы избавить пользователей от проблем с вопросами совместимости.
Иван Дышленко ,
12 февраля 2001 г
Скачать :
Полный архив :Export3D.zip ( 561 K) файлы проектов + GMS файлы + утилита MEGA.ms