5. Разработка модели освещения 3D сцены
Рассмотрим управление освещением и отображением теней в XNA. Существуют различные техники работы с тенями, мы рассмотрим отрисовку теней с использованием так называемого буфера трафаретов, или, по-английски - Stencil Buffer.
Stencil Buffer является стандартным устройством, входящим в состав современных видеокарт. Однако, различные видеокарты могут иметь различный размер этого буфера, поэтому, перед его использованием, необходимо определить, какой именно буфер доступен на видеокарте, используемой в данный момент.
Для создания тени мы воспользуемся методом CreateShadow объекта Matrix. Он позволяет создавать тень от объекта на основе информации об источнике освещения и плоскости, на которую должна проецироваться тень.
После того, как сознана матрица, представляющая собой тень, мы используем эту матрицу для вывода тени. Причем, техника работы такова: сначала вывести сцену, освещенную так, как нужно, после этого соответствующим образом настроить буфер трафаретов и вывести ту же сцену без освещения, модифицировав мировую матрицу с помощью полученной матрицы тени.
В этой программе так же разработаем перемещением источника света. В частности, мы применим для рисования объектов один направленный источник света, направление которого можно менять с помощью клавиш клавиатуры - координата Z изменяется с помощью клавиш-Z и X, координата X - С и V, координата Y - B и N. Изменение направления освещения влияет не только на освещение объектов, но и на тень.
6. Программная реализация системы моделирования движения 3D объекта modCls, который будет отвечать за хранение параметров, соответствующих этим объектам и за их визуализацию
public class modCls : Microsoft.Xna.Framework.DrawableGameComponent
{
//Модель
public Model myModel;
//Мировая матрица, матрицы вида и проекции
publicMatrix WorldMatrix;
publicMatrix ViewMatrix;
publicMatrix ProjectMatrix;
//Направление света
public Vector3 LightDirection;
//Матрица для отображения тени
Matrix shadow;
//Плоскость, на которой отображается тень
Plane sPlane;
//Соотношение сторон экрана
publicfloat aspectRatio;
//Для управления графическим устройством
GraphicsDeviceManager graphics;
//Конструктор получает на вход
//игровой класс, модель, объект для управления графическим устройством
public modCls(Game game, Model mod, GraphicsDeviceManager grf, Plane pl)
base(game)
{
myModel = mod;
graphics = grf;
sPlane = pl;
aspectRatio = (float)graphics.GraphicsDevice.Viewport.Width /
(float)graphics.GraphicsDevice.Viewport.Height;
LightDirection = new Vector3();
}
После того как мы получили базу которая хранит данные об объектам. Настраиваем вывод на экран этих моделей используя метод Draw()
public override void Draw(GameTime gameTime)
{
//Выводим объект
foreach (ModelMesh mesh in myModel.Meshes)
{
//Для каждого эффекта в сети
foreach (BasicEffecteffect in mesh.Effects)
{
//Включить источник направленного света №0
effect.DirectionalLight0.Enabled = true;
//Настроить параметры
effect.DirectionalLight0.DiffuseColor = Vector3.One;
effect.DirectionalLight0.SpecularColor = Vector3.One;
//Направление света - в класса Game1 мы меняем направление
//по клавиатурным командам
effect.DirectionalLight0.Direction = Vector3.Normalize(LightDirection);
effect.LightingEnabled = true;
//установить матрицы
effect.World = WorldMatrix;
effect.View = ViewMatrix;
effect.Projection = ProjectMatrix;
}
mesh.Draw();
}
//Создать матрицу тени
shadow = Matrix.CreateShadow(-LightDirection, sPlane);
graphics.GraphicsDevice.DepthStencilState = DepthStencilState.Default;
graphics.GraphicsDevice.BlendState = BlendState.Opaque;
//Выводим тень
foreach (ModelMesh mesh in myModel.Meshes)
{
foreach (BasicEffecteffect in mesh.Effects)
{
effect.AmbientLightColor = Vector3.Zero;
effect.Alpha = 0.5f;
effect.DirectionalLight0.Enabled = false;
effect.DirectionalLight1.Enabled = false;
effect.DirectionalLight2.Enabled = false;
effect.View = ViewMatrix;
effect.Projection = ProjectMatrix;
//При выводе тени умножаем мировую матрицу
//на матрицу вывода тени
effect.World = WorldMatrix*shadow;
}
mesh.Draw();
}
base.Draw(gameTime);
}
После объявляем список переменных которые будет использоваться в проекте.
GraphicsDeviceManager graphics;
//Матрицы
Matrix viewMatrix;
Matrix projMatrix;
const int shadowMapWidthHeight = 2048;
//Модели
Model ball, cube, ball2;
// Позиция объекта, поворот
Vector3avatarPosition = new Vector3(0, -1f, -50);
float avatarlRotation;
//Массив моделей сцены
modCls[] cls;
//Игровой объект
modCls ballObj;
//Плоскость
modCls plane;
//Направление света
Vector3 LightDirection;
BoundingFrustum cameraFrustum = new BoundingFrustum(Matrix.Identity);
// Положение камеры
Vector3 cameraReference = new Vector3(0, 0, 10);
Vector3 thirdPersonReference = new Vector3(0, 200, -200);
// Скорости поворота и движения
float rotationSpeed = 1f / 60f;
float forwardSpeed = 50f / 60f;
//Поле зрения камеры
float viewAngle = MathHelper.ToRadians(45.0f);
//Расстояние от камеры до переднего и заднего плана
float nearClip = 1.0f;
float farClip = 2000.0f;
// Установка позиции камеры 2 - вид от третьего лица
//1 - от первого лица
int cameraState = 2;
//Соотношение сторон экрана
float aspectRatio;
Загружаем модели которые мы будем использовать в проекте. Создаем базу для хранения этих моделей.
protected override void LoadContent()
{
//Загрузка моделей
ball = Content.Load<Model>("ball");
cube = Content.Load<Model>("cube");
ball2 = Content.Load<Model>("ball2");
aspectRatio = (float)graphics.GraphicsDevice.Viewport.Width /
(float)graphics.GraphicsDevice.Viewport.Height;
cls = new modCls[75];
LightDirection = new Vector3(80, -136, 80);
}
Метод отвечающий за изменения положения источника света. За изменения по оси Z отвечают клавиши Z и X(первая уменьшает позицию, вторая увеличивает), ось X- C и V, ось Y- B и N.
//Изменение позиции источника света
void LightSourceControl()
{
//Получим состояние клавиатуры
KeyboardState key = Keyboard.GetState();
//Клавиша z - уменьшим позицию по Z
if (key.IsKeyDown(Keys.Z))
{
LightDirection.Z -= 0.5f;
}
//Клавиша x - увеличим Z
if (key.IsKeyDown(Keys.X))
{
LightDirection.Z += 0.5f;
}
//c - уменьшим X
if (key.IsKeyDown(Keys.C))
{
LightDirection.X -= 0.5f;
}
//"v" - увеличим X
if (key.IsKeyDown(Keys.V))
{
LightDirection.X += 0.5f;
}
//"b" - увеличим Y
if (key.IsKeyDown(Keys.B))
{
LightDirection.Y += 0.5f;
}
//"n" - уменьшим Y
if (key.IsKeyDown(Keys.N))
{
LightDirection.Y -= 0.5f;
}
//Выведем в заголовок окна информацию о направлении
this.Window.Title = "Light source: " + LightDirection.ToString();
}
За движения объекта отвечает класс UpdateAvatarPosition(). Управление производится стрелками клавиатуры. При нажатие на клавишу вперед или назад производится соответствующие движения. А при нажатие клавиш влево или вправо происходит соответствующий поворот относительно оси Y.
//Обновляем состояние объекта
void UpdateAvatarPosition()
{
KeyboardState keyboardState = Keyboard.GetState();
//Поворот влево
if (keyboardState.IsKeyDown(Keys.Left))
{
avatarlRotation += rotationSpeed;
}
//Поворот вправо
if (keyboardState.IsKeyDown(Keys.Right))
{
avatarlRotation -= rotationSpeed;
}
//Движение вперед
if (keyboardState.IsKeyDown(Keys.Up))
{
Matrix forwardMovement = Matrix.CreateRotationY(avatarlRotation);
Vector3 v = new Vector3(0, 0, forwardSpeed);
v = Vector3.Transform(v, forwardMovement);
avatarPosition.Z += v.Z;
avatarPosition.X += v.X;
while (IsCollide())
{
avatarPosition.Z -= v.Z;
avatarPosition.X -= v.X;
}
}
//Движение назад
if (keyboardState.IsKeyDown(Keys.Down))
{
Matrix forwardMovement = Matrix.CreateRotationY(avatarlRotation);
Vector3 v = new Vector3(0, 0, -forwardSpeed);
v = Vector3.Transform(v, forwardMovement);
avatarPosition.Z += v.Z;
avatarPosition.X += v.X;
while (IsCollide())
{
avatarPosition.Z -= v.Z;
avatarPosition.X -= v.X;
}
}
//Уменьшение угла обзора камеры
if (keyboardState.IsKeyDown(Keys.R))
{
viewAngle -= MathHelper.ToRadians(1.0f);
}
//Увеличение угла обзора камеры
if (keyboardState.IsKeyDown(Keys.F))
{
viewAngle += MathHelper.ToRadians(1.0f);
}
//Если новый угол обзора вышел за дозволенные пределы
//изменяем его
if (viewAngle > MathHelper.ToRadians(180.0f)) viewAngle = MathHelper.ToRadians(179.9f);
if (viewAngle < MathHelper.ToRadians(0.9f)) viewAngle = MathHelper.ToRadians(1f);
// Выход из программы при нажатие Esc.
if (keyboardState.IsKeyDown(Keys.Escape))
{
Exit();
}
}
Создаем булевую функцию отвечающие за проверку столкновений объектов.
bool IsCollide()
{
//Для объекта BoundingSphere, соответствующего
//текущему объекту сцены
BoundingSphere b1;
//Получить BoundingSphere для игрового объекта
BoundingSphere b = ball.Meshes[0].BoundingSphere;
//Установить центр сферы в соответствии с положением
//игрового объекта
b.Center =avatarPosition;
//Переменная для хранения вектора размера модели
Vector3 scale;
//Переменная для хранения информации о повороте модели
Quaternion rotation;
//Переменая для хранения информации о позиции модели
Vector3 translation;
//Цикл обхода объектов сцены
for (int i = 0; i < 75; i++)
{
//Получить BoundingBox для текущего объекта
b1 =cls[i].myModel.Meshes[0].BoundingSphere;
//Получить параметры - размер, поворот, позицию для объекта
cls[i].WorldMatrix.Decompose(out scale, out rotation, out translation);
//Установить соответствии с позицией объекта
b1.Center = translation;
//Если сферы игрового объекта и текущего объекта
if (b1.Intersects(b))
{
//Возвратим True
return true;
}
} //Если выполняется этот код -
//столкновения не было
//и вернем false
return false;
}
Последний метод отвечающий за построение самой сцены и самого объекта
//Вывод объектов сцены
void DrawScene()
{
Components.Clear();
int i = 0;
//Она немного выше плоскости, которую мы выводим на экран
//для того, чтобы тень была видна
Plane pl1 = new Plane(new Vector3(0, -2f, 0), new Vector3(2, -2f, 1), new Vector3(-1, -2f, -2));
//Вывести кубы, расположенные в пять рядов
//по пять штук в трех уровнях
for (int x = 0; x < 5; x++)
{
for (int y = 0; y < 3; y++)
{
for (int z = 0; z < 5; z++)
{
//Добавляем в массив новый объект
cls[i] = new modCls(this, ball2, graphics, pl1);
//Устанавливаем его свойства
cls[i].WorldMatrix = Matrix.CreateTranslation(x * 40, y*3, z * 40);
cls[i].ViewMatrix = viewMatrix;
cls[i].ProjectMatrix = projMatrix;
cls[i].LightDirection = LightDirection;
//Добавляем в коллекцию компонентов
Components .Add (cls[i]);
i++;
}
}
}
ballObj = new modCls(this, ball, graphics, pl1);
ballObj.WorldMatrix = Matrix.CreateRotationY(avatarlRotation) * Matrix.CreateTranslation(avatarPosition);
ballObj.ViewMatrix = viewMatrix;
ballObj.ProjectMatrix = projMatrix;
ballObj.LightDirection = LightDirection;
Components.Add(ballObj);
plane = new modCls(this, Content.Load<Model>("plane"), graphics, new Plane());
//Настраиваем параметры плоскости
plane.WorldMatrix = Matrix.CreateScale(100) * Matrix.CreateRotationY(MathHelper.ToRadians(90)) *
Matrix.CreateRotationZ(MathHelper.ToRadians(90)) *
Matrix.CreateTranslation(80, -2.5f, 80);
plane.ViewMatrix = viewMatrix;
plane.ProjectMatrix = projMatrix;
plane.LightDirection = LightDirection;
Components.Add(plane);
}
}
}
- 3D Studio max 6- discreet: Новая версия 3d Studio max предлагает еще больше усовершенствованных возможностей.
- Задания по программе 3d Studio Max
- Состав комплекса 3d-моделей:
- 15 Советов по работе в 3d Studio Max
- Часть III - Текстуры 3d max.
- Глава I 3d max и его возможности
- 19. Программа 3d Max
- Пакет Autodesk 3ds Max (3d Studio max). Скриптовое программирование