Спонсор: Microsoft
Опубликован: 10.04.2009 | Уровень: специалист | Доступ: свободно
Самостоятельная работа 14:

Вывод трехмерных объектов на экран

< Лекция 5 || Самостоятельная работа 14: 12 || Самостоятельная работа 15 >
Аннотация: В этой лабораторной работе мы рассмотрим вывод трехмерных объектов. В частности – формирование объектов средствами XNA Framework и работу с загружаемыми трехмерными моделями. Так же здесь мы поговорим о текстурировании объектов.

Примеры к лабораторной работе

Задачи работы

  • Научиться рисовать трехмерные объекты средствами 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.

Окно проекта P14_1

увеличить изображение
Рис. 19.1. Окно проекта P14_1
< Лекция 5 || Самостоятельная работа 14: 12 || Самостоятельная работа 15 >
Alina Lasskaja
Alina Lasskaja

Быть может кто-то из Вас знает игру Sims, к какому жанру она относиться? Жизненная симуляция, ролевая игра, там можно и дома строить.....