Быть может кто-то из Вас знает игру 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.