В этой лабораторной работе мы поговорим о применении графических эффектов в играх и об анимации персонажей.
Цель работы
Научиться применять графические эффекты и приемы анимации
Задачи работы
Рассмотреть основы анимации трехмерных персонажей
Рассмотреть применение графических эффектов в компьютерных играх, созданных на базе XNA
Анимация персонажей
Компьютерные игры наполнены анимированными персонажами. XNA содержит встроенные средства, поддерживающие базовые операции по анимации трехмерных персонажей. В частности, это средства для работы с так называемым скелетом персонажа. Скелет состоит из взаимосвязанных костей, управляя которыми можно анимировать модель. Создание анимированных моделей – это отдельная, достаточно трудоемкая задача, которая требует серьезных познаний в области 3D-моделирования. Для подготовки анимированных трехмерных моделей вы можете использовать практически любой 3D-редактор.
XNA содержит лишь базовые средства для управления скелетами объектов. Так, мы можем получить коллекцию костей объекта и проводить с ними какие-либо преобразования, вызывающие движение модели. Однако, такой подход ограничен – с его использованием весьма сложно создать реалистичное движение для сложных моделей. На основе базовых средств XNA разработаны библиотеки кода, которые содержат компоненты, позволяющие анимировать персонажи на более высоком уровне. В частности, одну из таких библиотек – XNAAnimation – можно найти на сайте http://www.codeplex.com/xnanimation.
Здесь мы рассмотрим базовый подход к анимации модели. Он заключается в следующем. Можно менять параметры отдельных костей модели и выводить ее на экран с учетом этих изменений. Мы использовали бесплатную модель Ballpen.fbx, взятую с сайта http://www.turbosquid.com.
Создадим новый проект P18_1. Загрузим в него модель Ballpen.fbx. На рис. 18.1. представлено окно Solution Explorer нашего нового проекта.
Рис. 18.1. Окно Project Explorer
Код проекта сосредоточен в классе Game1.cs, его код вы можете найти в листинге 18.1.
usingSystem;usingSystem.Collections.Generic;usingMicrosoft.Xna.Framework;usingMicrosoft.Xna.Framework.Audio;usingMicrosoft.Xna.Framework.Content;usingMicrosoft.Xna.Framework.Graphics;usingMicrosoft.Xna.Framework.Input;namespace P18_1{publicclass Game1 : Microsoft.Xna.Framework.Game{ GraphicsDeviceManager graphics;//Матрицы Matrix viewMatrix; Matrix projMatrix;//Модель Model pen;public Game1(){ graphics =new GraphicsDeviceManager(this); Content.RootDirectory="Content";}protectedoverridevoid Initialize(){base.Initialize();}protectedoverridevoid LoadContent(){//Загрузка модели pen = Content.Load<Model>("ballpen");//Соотношение сторонfloat aspectRatio =(float)graphics.GraphicsDevice.Viewport.Width/(float)graphics.GraphicsDevice.Viewport.Height;//Устанавливаем камеру viewMatrix = Matrix.CreateLookAt(new Vector3(0, 30, 80), new Vector3(-32, 0, 0), new Vector3(0, 1, 0));//Устанавливаем проекционную матрицу projMatrix = Matrix.CreatePerspectiveFieldOfView(MathHelper.ToRadians(45.0f), aspectRatio, 1.0f, 1000.0f);}protectedoverridevoid Update(GameTime gameTime){//Перемещение частей ручки PenMove();base.Update(gameTime);}//Процедура для перемещения частей ручкиvoid PenMove(){//Получить состояние клавиатуры KeyboardState keyboardState = Keyboard.GetState();//Поворот зажима - он представлен родительской костью сети №3//Умножим его матрицу трансформации на матрицу поворота по оси X pen.Meshes[3].ParentBone.Transform= pen.Meshes[3].ParentBone.Transform* Matrix.CreateRotationX(MathHelper.ToRadians(-1.0f));//Кнопка ручки перемещается по клавиатурным командам//При нажатии кнопки "Влево"//Она выходит из ручкиif(keyboardState.IsKeyDown(Keys.Left)){//Умножим матрицу трансформации родительской костью сети №1//На матрицу трансляции, перемещающей ее на 2 позиции влево по оси Х pen.Meshes[1].ParentBone.Transform= pen.Meshes[1].ParentBone.Transform* Matrix.CreateTranslation(new Vector3(-2, 0, 0));}//При нажатии кнопки "Вправо"//Кнопка входит в ручкуif(keyboardState.IsKeyDown(Keys.Right)){//Умножим матрицу трансформации родительской кости сети №1//На матрицу трансляции, перемещающую ее на 2 позиции по оси X вправо pen.Meshes[1].ParentBone.Transform= pen.Meshes[1].ParentBone.Transform* Matrix.CreateTranslation(new Vector3(2, 0, 0));}}protectedoverridevoid Draw(GameTime gameTime){ graphics.GraphicsDevice.Clear(Color.CornflowerBlue);//Новый массив матриц размером, соответствующим количеству//костей в скелете модели Matrix[] absoluteTransformations =new Matrix[pen.Bones.Count];//Скопировать матрицы трансформации костей в массив матриц pen.CopyAbsoluteBoneTransformsTo(absoluteTransformations);foreach(ModelMesh mesh in pen.Meshes){foreach(BasicEffect effect in mesh.Effects){ effect.LightingEnabled= true; effect.EnableDefaultLighting(); effect.Projection= projMatrix; effect.View= viewMatrix;//Установим новую мировую матрицу для родительской кости текущей сети//это приводит к перемещению кнопки и к перемещению зажима//Так же здесь мы уменьшаем модель, применяя коэффициент масштабирования 0,13 effect.World= absoluteTransformations[mesh.ParentBone.Index]* Matrix.CreateScale(0.13f);}//Выводим подготовленную сеть mesh.Draw();}base.Draw(gameTime);}}}
На рис. 18.2. вы можете видеть игровой экран проекта P18_1.
Рис. 18.2. Игровой экран проекта P18_1
Эффекты
Простые графические эффекты в XNA можно реализовать при выводе изображений стандартными средствами. Но стандартные эффекты не способны удовлетворить потребности разработчиков. В одной из предыдущих лекций мы уже упоминали о том, что XNA поддерживает использование шейдерных программ, которые позволяют управлять графическими эффектами.
Шейдерные программы или просто шейдеры представлены в виде файлов с расширением FX. Эти файлы можно создавать как с помощью специального ПО для разработки и отладки шейдеров, так и вручную, в редакторе кода Visual Studio. Для описания шейдерных программ существует специальный язык – HLSL – High Level Shader Language – Высокоуровневый язык описания шейдеров.
Шейдерные программы исполняются графическим процессором видеокарты, с их помощью можно создать множество графических эффектов, которыми наполнены современные игры.
Шейдеры принято делить на вершинные (Vertex Shader) и пиксельные (Pixel Shader).
Вершинные работают с вершинами объектов. С их помощью можно осуществить такие операции, как деформацию объектов, анимацию, перемещение и т.д. После того, как вершинный шейдер завершит работу по модификации модели, за дело принимается пиксельный шейдер.
Пиксельные шейдеры применяются к отдельным пикселям изображения. В частности, они отвечают за цвет пикселей, позволяют применять к изображению различные эффекты.
Для того, чтобы создать новый FX-файл в XNA-проект, достаточно выполнить щелчок левой кнопкой мыши по папке Content, выбрать пункт Add(New Item и в появившемся окне выбрать в качестве типа добавляемого файла Effect File. После того, как файл эффектов будет добавлен в проект, он будет содержать некоторые стандартные части, которые представляют собой объявление переменных, необходимых для работы шейдера, объявление точки входа в шейдер и, собственно, шаблоны двух шейдеров – вершинного и пиксельного.
Для того, чтобы разрабатывать шейдеры самостоятельно с помощью XNA-редактора FX-файлов, вам нужно ознакомиться с языком HLSL. Вы можете сделать это, воспользовавшись справочной службой MSDN, в частности, этим разделом: http://msdn2.microsoft.com/en-us/library/bb509561.aspx.
На практике лучше всего пользоваться специализированным ПО для разработки и отладки шейдеров.
Рассмотрим пример работы с шейдерными эффектами – он создан с использованием примера применения шейдеров, приведенного в документации к XNA.
Создадим новый проект P18_2. На рис. 18.3. вы можете видеть его окно Solution Explorer.
Рис. 18.3. Окно Solution Explorer для проекта P18_2
Файл shader.fx содержит код шейдера, файл tex.png используется в качестве текстуры для наложения на модель, созданную программными средствами. Код программы реализован в классе Game1. Рассмотрим его (листинг. 18.2.).
//Переменные, устанавливаемые при настройке шейдера//Матрица - результат перемножений мировой, видовой, проекционной матрицuniformexternfloat4x4 WorldViewProj : WORLDVIEWPROJECTION;//Текстураuniformextern texture UserTexture;//Множительuniformexternfloat Multiplier;struct VS_OUTPUT{float4 position :POSITION;float4 textureCoordinate :TEXCOORD0;};sampler textureSampler = sampler_state{ Texture =<UserTexture>; mipfilter = LINEAR;};//Вывод вершин текстуры//Вершинный шейдер, который используется//во всех вариантах эффектаVS_OUTPUT Transform(float4 Position :POSITION,float4 TextureCoordinate :TEXCOORD0){ VS_OUTPUT Out =(VS_OUTPUT)0; Out.position= mul(Position, WorldViewProj); Out.textureCoordinate= TextureCoordinate*Multiplier;return Out;}//Вывод текстуры без изменений для P0, пиксельный шейдер//Возврат того же цвета, который был передан//шейдеруfloat4 ApplyTexture(VS_OUTPUT vsout):COLOR{returntex2D(textureSampler, vsout.textureCoordinate).rgba;}//Вывод размытой текстуры для P1, пиксельный шейдер//Цвет устанавливается исходя из цвета//соседних пикселей//Полученная цветовая информация делится на 5//для сохранения исходной яркостиfloat4 ApplyTextureBlur(VS_OUTPUT vsout):COLOR{float4 Col; Col =tex2D(textureSampler, vsout.textureCoordinate); Col +=tex2D(textureSampler, vsout.textureCoordinate+(0.01)); Col +=tex2D(textureSampler, vsout.textureCoordinate+(0.02)); Col +=tex2D(textureSampler, vsout.textureCoordinate+(0.03)); Col +=tex2D(textureSampler, vsout.textureCoordinate+(0.04)); Col = Col /5;//Возвращаем найденный цвет пикселяreturn Col;}//Вывод текстуры в оттенках серого для P2 - пиксельный шейдер//Интенсивность света всех каналов изображения складывается и //делится на количество каналов//Альфа-канал устанавливается равным 1float4 ApplyTextureGray(VS_OUTPUT vsout):COLOR{float4 Col; Col =tex2D(textureSampler, vsout.textureCoordinate); Col.a=1.0f; Col.rgb=(Col.r+Col.g+Col.b)/3;return Col;}//Техника шейдераtechnique TransformAndTexture{//Проход P0, имеет индекс 0pass P0{//Вершинный шейдер для данного прохода vertexShader = compile vs_2_0 Transform();//Пиксельный шейдер для данного прохода pixelShader = compile ps_2_0 ApplyTexture();}//Проход P1, имеет индекс 1pass P1{ vertexShader = compile vs_2_0 Transform(); pixelShader = compile ps_2_0 ApplyTextureBlur();}//Проход P2, имеет индекс 2pass P2{ vertexShader = compile vs_2_0 Transform(); pixelShader = compile ps_2_0 ApplyTextureGray();}}
На рис. 18.4., 18.5., 18.6. вы можете видеть игровое окно проекта в различных режимах применения шейдера.
Рис. 18.4. Наложение текстуры без изменения цвета, с множителем 2
Рис. 18.5. Наложение текстуры с размытием, множитель 1
Рис. 18.6. Текстура в оттенках серого, множитель 1
Вопросы
1) Что такое скелетная анимация?
a. Анимация трехмерных персонажей с помощью модификации их видовой матрицы
b. Анимация трехмерных персонажей с помощью модификации отдельных частей их скелета с последующим отражением изменений в мировой матрице
c. Анимация трехмерных персонажей с помощью модификации параметров проекционной матрицы
2) Какой объект может описывать следующий код: pen.Meshes[3].ParentBone.Transform?
a. Кость №3 модели pen
b. Сеть №3 модели Pen
c. Матрицу трансформации родительской кости для сети №3 модели pen
d. Модель pen
3) Метод CopyAbsoluteBoneTransformsTo объекта типа Model предназначен для
a. Копирования объекта
b. Копирования отдельных сетей объекта
c. Копирования матриц трансформаций костей объекта в специально созданную матрицу
d. Копирования скелета объекта
4) Что такое HLSL?
a. Высокоуровневый язык описания шейдеров
b. Библиотека XNA для работы с шейдерами
c. Формат моделей, которые поддерживают шейдеры
d. Программа для разработки шейдеров
5) Какое расширение имеют файлы эффектов в XNA?
a. FBX
b. TXT
c. X
d. FX
6) Какова основная функция вершинных шейдеров?
a. Они позволяют копировать информацию о вершинах объекта в файлы
b. Они предназначены для модификации трехмерных объектов путем изменения параметров их вершин
c. Они позволяют обрабатывать отдельные пиксели объектов, применяя к ним различные графические эффекты
d. Они позволяют загружать в игру трехмерные модели
7) Какова основная функция пиксельных шейдеров?
a. Они позволяют копировать информацию о вершинах объекта в файлы
b. Они предназначены для модификации трехмерных объектов путем изменения параметров их вершин
c. Они позволяют обрабатывать отдельные пиксели объектов, применяя к ним различные графические эффекты
d. Они позволяют загружать в игру трехмерные модели
8) Можно ли редактировать файлы шейдеров, написанных на HLSL-, используя встроенные средства XNA?
a. Да
b. Нет
9) Объект какого типа используется для загрузки шейдеров в игру?