|
Быть может кто-то из Вас знает игру Sims, к какому жанру она относиться? Жизненная симуляция, ролевая игра, там можно и дома строить..... |
Вывод трехмерных объектов на экран
Задачи работы
- Научиться рисовать трехмерные объекты средствами XNA
- Научиться выводить трехмерные модели
- Научиться текстурировать трехмерные модели
Рисование трехмерных объектов средствами XNA
Рассмотрим особенности рисования трехмерных объектов средствами XNA на примере проекта P14_1.
Здесь мы рассматриваем два подхода к выводу трехмерных примитивов. Первый заключается в использовании вершинного буфера и вывода объектов из него, второй – с использованием матриц вершин, которые указываются в качестве одного из параметров при выводе объектов.
В этом примере мы выведем на экран следующие объекты:
- Треугольник
- Два прямоугольника
- Прямую линию
- 1000 точек со случайными координатами
- 200 треугольников со случайными координатами, одна из вершин каждого из которых расположена в одной точке.
В листинге 19.1. вы можете найти код класса Game1 проекта P14_1. Код подробно прокомментирован.
Отметим, что для вывода изображения нам необходимо выполнить следующие шаги:
Установить мировую, проекционную и видовую матрицы.
Создать и настроить объект типа BasicEffect для вывода изображений.
Создать наборы вершин, которые мы будем использовать при выводе
Создать и заполнить вершинный буфер, который нужно будет загрузить в один из элементов коллекции Vertices объекта, используемого для вывода изображений
Вывести изображение из буфера, при необходимости вывести изображения, сгенерированные на основе массивов вершин, не внесенных в вершинный буфер.
Массив вершин может быть интерпретирован по-разному. Например, при интерпретации его в качестве PointList массив выводится в виде списка точек, при интерпретации в качестве TriangleList – как набор треугольников, при интерпретации в качестве TriangleFan – как "веер" из треугольников, одна из вершин которых совпадает, при интерпретации как LineList – в качестве набора линий.
При выводе изображения в методе Draw необходимо очищать экран от предыдущего вывода – иначе он покроется копиями изображений.
Мы используем мировую матрицу для вращения выведенной сцены, модифицируя ее на 1 градус при каждом проходе цикла Update.
using System;
using System.Collections.Generic;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.GamerServices;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Net;
using Microsoft.Xna.Framework.Storage;
namespace P14_1
{
public class Game1 : Microsoft.Xna.Framework.Game
{
GraphicsDeviceManager graphics;
//Вывод изображений
BasicEffect basicEffect;
//Мировая матрица
Matrix worldMatrix;
//Матрица вида
Matrix viewMatrix;
//Проекционная матрица
Matrix projectionMatrix;
//Вершинный буфер
VertexBuffer vertBuffer;
//Массивы для хранения координат вершин
//Которые используются для вывода изображения
//без использования вершинного буфера
VertexPositionColor[] vert1;
VertexPositionColor[] vert2;
VertexPositionColor[] vert3;
VertexPositionColor[] vert4;
VertexPositionColor[] vert5;
//Переменная для хранения текущего значения
//поворота мировой матрицы
float wM = 0;
public Game1()
{
graphics = new GraphicsDeviceManager(this);
Content.RootDirectory = "Content";
}
protected override void Initialize()
{
base.Initialize();
}
protected override void LoadContent()
{
//Настройка матриц
SetMatrix();
//Настройка эффектов вывода
TuneUpEff();
//Создание объектов для вывода
CreateFigures();
}
void SetMatrix()
{
//мировая матрица, содержащая 1 по диагонали
//она не влияет на состояние объекта
worldMatrix = Matrix.Identity;
//матрица вида
//При ее создании задаем следующие параметры
//1 - положение камеры
//2 - направление камеры
//3 - что считать "верхом" для камеры
viewMatrix = Matrix.CreateLookAt(new Vector3(0.0f, 0.0f, 10.0f), Vector3.Zero, Vector3.Up);
//Находим соотношение сторон пикселей для корректного вывода
//изображений на экран
float aspectRatio = (float)graphics.GraphicsDevice.Viewport.Width / (float)graphics.GraphicsDevice.Viewport.Height;
//Матрица проекции
//При ее создании задаем следующие параметры:
//1 - угол зрения в радианах
//Соотношение сторон пикселей экрана
//Ближний план пространства
//Дальний план пространства
projectionMatrix = Matrix.CreatePerspectiveFieldOfView(MathHelper.ToRadians(45), aspectRatio, 1.0f, 19.0f);
}
void TuneUpEff()
{
//Создаем экземпляр класса BasicEffect
//Он используется для вывода изображений - это напоминает
//SpriteBatch при работе с двумерной графикой
basicEffect = new BasicEffect(graphics.GraphicsDevice, null);
//Настраиваем параметры basicEffect
//Освещение
basicEffect.LightingEnabled = true;
basicEffect.EnableDefaultLighting();
//Установка матриц
basicEffect.World = worldMatrix;
basicEffect.View = viewMatrix;
basicEffect.Projection = projectionMatrix;
}
void CreateFigures()
{
//Массив вершин для построения треугольника
VertexPositionColor [] vert = new VertexPositionColor[3];
//Массив для построения линий, расположенных в виде прямоугольника
vert1 = new VertexPositionColor[5];
//Массив для построения второго прямоугольника, расположенного выше, чем
//первый
vert2 = new VertexPositionColor[5];
//Массив для построения линии
vert3 = new VertexPositionColor[2];
//Массив для построения точек со случайными координатами
vert4 = new VertexPositionColor[1000];
//Массив для построения группы треугольников со случайными координатами
vert5 = new VertexPositionColor[200];
//Генераратор случайных чисел
Random rnd = new Random();
//Задаем параметры для построения треугольника
vert[0] = new VertexPositionColor(new Vector3(1.0f, 2.0f, -5.0f), Color.White);
vert[1] = new VertexPositionColor(new Vector3(2.0f, 0.0f, -5.0f), Color.White);
vert[2] = new VertexPositionColor(new Vector3(1.0f, -2.0f, -5.0f), Color.White);
//Задаем параметры для построения прямоугольника
//его центр расположен в начале координат
vert1[0] = new VertexPositionColor(new Vector3(-1.0f, -1.0f, 0.0f), Color.White);
vert1[1] = new VertexPositionColor(new Vector3(-1.0f, 1.0f, 0.0f), Color.White);
vert1[2] = new VertexPositionColor(new Vector3(1.0f, 1.0f, 0.0f), Color.White);
vert1[3] = new VertexPositionColor(new Vector3(1.0f, -1.0f, 0.0f), Color.White);
vert1[4] = new VertexPositionColor(new Vector3(-1.0f, -1.0f, 0.0f), Color.White);
//Задаем параметры для второго прямоугольника - он перпендикулярен первому
//и смещен вверх
vert2[0] = new VertexPositionColor(new Vector3(0.0f, 0.0f, 1.0f), Color.White);
vert2[1] = new VertexPositionColor(new Vector3(0.0f, 0.0f, -1.0f), Color.White);
vert2[2] = new VertexPositionColor(new Vector3(0.0f, 2.0f, -1.0f), Color.White);
vert2[3] = new VertexPositionColor(new Vector3(0.0f, 2.0f, 1.0f), Color.White);
vert2[4] = new VertexPositionColor(new Vector3(0.0f, 0.0f, 1.0f), Color.White);
//Параметры для построения линии
vert3[0] = new VertexPositionColor(new Vector3(2.0f, 2.0f, 2.0f), Color.White);
vert3[1] = new VertexPositionColor(new Vector3(-2.0f, -2.0f, -2.0f), Color.White);
//Генерируем случайные координаты для построения точек
for (int i = 0; i < 1000; i++)
{
//Диапазон координат от -3 до 3
float x = (float)(rnd.NextDouble() - rnd.NextDouble()) * 3;
float y = (float)(rnd.NextDouble() - rnd.NextDouble()) * 3;
float z = (float)(rnd.NextDouble() - rnd.NextDouble()) * 3;
vert4[i] = new VertexPositionColor(new Vector3(x, y, z), Color.White);
}
//Генерируем случайные координаты для построения треугольников
for (int i = 0; i < 200; i++)
{
//Координаты изменяются в пределах от -1 до 1
float x = (float)(rnd.NextDouble() - rnd.NextDouble());
float y = (float)(rnd.NextDouble() - rnd.NextDouble());
//группа треугольников сдвинута на 3 по оси Z
float z = (float)(rnd.NextDouble() - rnd.NextDouble())-3;
vert5[i] = new VertexPositionColor(new Vector3(x, y, z), Color.White);
}
//Создаем вершинный буфер
//При создании указываем графическое устройство, размер буфера и способ работы с буфером
vertBuffer = new VertexBuffer(graphics.GraphicsDevice, 3 * VertexPositionColor.SizeInBytes,BufferUsage.WriteOnly);
//Загружаем в буфер массив вершин vert
vertBuffer.SetData<VertexPositionColor>(vert);
}
protected override void UnloadContent()
{
if (vertBuffer != null)
{
vertBuffer.Dispose();
vertBuffer = null;
}
if (basicEffect != null)
{
basicEffect.Dispose();
basicEffect = null;
}
}
protected override void Update(GameTime gameTime)
{
//Уменьшаем на 1 градус значение поворота матрицы
wM = wM - 1f;
//Модифицируем мировую матрицу
worldMatrix = Matrix.CreateRotationY(MathHelper.ToRadians(wM));
//Меняем мировую матрицу
basicEffect.World = worldMatrix;
base.Update(gameTime);
}
protected override void Draw(GameTime gameTime)
{
//Очищаем окно вывода
graphics.GraphicsDevice.Clear(Color.CornflowerBlue);
//Устанавиливаем объект VertexDeclaration - он используется для вывода изображения
graphics.GraphicsDevice.VertexDeclaration = new VertexDeclaration
(graphics.GraphicsDevice, VertexPositionColor.VertexElements);
//Устанавливаем в качестве источника для вывода ранее созданный вершинный буфер
graphics.GraphicsDevice.Vertices[0].SetSource(vertBuffer, 0, VertexPositionColor.SizeInBytes);
//Начинаем вывод изображения
basicEffect.Begin();
//Для каждого прохода эффекта в коллекции примененных эффектов
//выведем изображение. В нашем случае вывод осуществляется в 1 проход
foreach (EffectPass CurrentPass in basicEffect.CurrentTechnique.Passes)
{
//Начинаем вывод для текущего прохода
CurrentPass.Begin();
//Выводим треугольник, пользуясь параметрами созданного вершинного буфера
//Тип графического примитива - список треугольников, отсчет от 0 элемента буфера
//Количество треугольников - 1. По умолчанию задняя часть объекта не видна
graphics.GraphicsDevice.DrawPrimitives(PrimitiveType.TriangleList, 0, 1);
//Выводим набор линий для рисования первого прямоугольника, используя
//массив вершин vert1
graphics.GraphicsDevice.DrawUserPrimitives<VertexPositionColor>(PrimitiveType.LineStrip, vert1, 0, 4);
//Выводим второй прямоугольник
graphics.GraphicsDevice.DrawUserPrimitives<VertexPositionColor>(PrimitiveType.LineStrip, vert2, 0, 4);
//Выводим линию - она проходит через начало координат
graphics.GraphicsDevice.DrawUserPrimitives<VertexPositionColor>(PrimitiveType.LineStrip, vert3, 0, 1);
//Выводим группу точек
graphics.GraphicsDevice.DrawUserPrimitives<VertexPositionColor>(PrimitiveType.PointList, vert4, 0, 1000);
//Выводим набор треугольников, одна из вершин которых совпадает с начальной точкой
//Количество треугольников на 2 меньше, чем количество вершин, заданных в массиве Vert5
graphics.GraphicsDevice.DrawUserPrimitives<VertexPositionColor>(PrimitiveType.TriangleFan, vert5, 0, 198);
//Окончание текущего прохода
CurrentPass.End();
}
//Окончание вывода изображений
basicEffect.End();
base.Draw(gameTime);
}
}
}
Листинг
19.1.
Код класса Game1
На рис. 19.1. вы можете видеть окно проекта P14_1.
