Опубликован: 14.08.2012 | Уровень: специалист | Доступ: платный
Самостоятельная работа 16:

Основы трехмерной графики в XNA

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

Цель работы: Научиться выводить трёхмерные объекты на игровой экран

23.1. Рисование трехмерных объектов средствами XNA

Рассмотрим особенности рисования трехмерных объектов средствами XNA на примере проекта P16_1. Здесь мы рассматриваем использование вершинного буфера для вывода куба. Пример подготовлен на основе кода, доступного в рамках Microsoft Permissive License (Ms-PL).

В листинге 23.1 вы можете найти код класса Game1 проекта P14_1. Код подробно прокомментирован.

Отметим, что для вывода изображения нам необходимо выполнить следующие шаги:

  1. Установить мировую, проекционную и видовую матрицы.
  2. Создать и настроить объект типа BasicEffect для вывода изображений.
  3. Создать наборы вершин (в нашем случае они представлены данными о координате, нормали и текстуре), которые мы будем использовать при выводе.
  4. Создать и заполнить вершинный буфер, установить его в объект GraphicDevice для вывода изображения.
  5. Вывести изображение из буфера, пользуясь инструментами объекта типа BasicEffect, который, фактически, является одной из доступных в XNA шейдерных программ.

При выводе изображения в методе Draw необходимо очищать экран от предыдущего вывода – иначе он покроется копиями изображений.

Мы используем мировую матрицу для вращения выведенной сцены при каждом проходе цикла Update.

using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;

namespace P16_1
{
    /// <summary>
    /// This is the main type for your game
    /// </summary>
    public class Game1 : Microsoft.Xna.Framework.Game
    {
        //Мировая матрица
        Matrix worldMatrix;
        //Матрица вида
        Matrix viewMatrix;
        //Проекционная матрица
        Matrix projectionMatrix;
        //Информация по вершинам объекта
        VertexPositionNormalTexture[] cubeVertices;
        //Объявление вершин, используется для вывода изображения
        VertexDeclaration vertexDeclaration;
        //Вершинный буфер
        VertexBuffer vertexBuffer;
        //Объект для вывода изображений
        BasicEffect basicEffect;
        //Количество вершин
        const int number_of_vertices = 36;

        GraphicsDeviceManager graphics;
        //Переменные для хранения текущего значения
        //поворота мировой матрицы
        float wMX = 0;
        float wMY = 0;
        float wMZ = 0;
        //Управление  матрицей вида
        float distanceView = 5;
        public Game1()
        {
            graphics = new GraphicsDeviceManager(this);
            Content.RootDirectory = "Content";
            //Вывод на полный экран
            graphics.IsFullScreen = true;
        }
        //Создание вертексного (вершинного) буфера
        private void CreateVertexBuffer()
        {
            //Задаёт объявление вершин
            //Оно содержит новый массив типа VertexElement, содержащий сведения
            vertexDeclaration = new VertexDeclaration(new VertexElement[]
                {
                    
                    //О координате
                    new VertexElement(0, VertexElementFormat.Vector3, VertexElementUsage.Position, 0),
                    //О нормали
                    new VertexElement(12, VertexElementFormat.Vector3, VertexElementUsage.Normal, 0),
                    //О Координате текстуры
                    new VertexElement(24, VertexElementFormat.Vector2, VertexElementUsage.TextureCoordinate, 0)
                }
            );
            //Создаём вертексный буфер, используя информацию о 
            //графическом устройстве
            //Структуре описания вершин
            //Количестве вершин
            vertexBuffer = new VertexBuffer(
                graphics.GraphicsDevice,
                vertexDeclaration,
                number_of_vertices,
                BufferUsage.None
                );

            //Новый массив вершин куба
            cubeVertices = new VertexPositionNormalTexture[number_of_vertices];
            //Создаём куб
            InitializeCube();

            //Устанавливаем данные для вертексного буфера
            vertexBuffer.SetData<VertexPositionNormalTexture>(cubeVertices);
            //Подключаем вертексный буфер к устройству
            graphics.GraphicsDevice.SetVertexBuffer(vertexBuffer);
        }


        protected override void Initialize()
        {
            
            CreateVertexBuffer();

            base.Initialize();
        }

        /// <summary>
        /// LoadContent will be called once per game and is the place to load
        /// all of your content.
        /// </summary>
        protected override void LoadContent()
        {
            InitializeTransform();
            InitializeEffect();
        }

        /// <summary>
        /// Инициализирует матрицы, которые можно использовать при 
        /// </summary>
        private void InitializeTransform()
        {
            //мировая матрица, содержащая 1 по диагонали
            //она не влияет на состояние объекта
            worldMatrix = Matrix.Identity;

            //матрица вида
            //При ее создании задаем следующие параметры
            //1 - положение камеры
            //2 - направление камеры
            //3 - что считать "верхом" для камеры
            viewMatrix = Matrix.CreateLookAt(new Vector3(0.0f, 0.0f, distanceView), Vector3.Zero, Vector3.Up);


            //Матрица проекции
            //При ее создании задаем следующие параметры:
            //1 - угол зрения в радианах
            //Соотношение сторон пикселей экрана
            //Ближний план пространства
            //Дальний план пространства

            projectionMatrix = Matrix.CreatePerspectiveFieldOfView(
               MathHelper.ToRadians(45),
               (float)GraphicsDevice.Viewport.Width /
               (float)GraphicsDevice.Viewport.Height,
               1.0f, 30.0f);
        }

        /// <summary>
        /// Инициализация базовых эффектов (настроек параметров и способов обработки)
        /// используемых для работы с трехмерной моделью
        /// </summary>
        private void InitializeEffect()
        {
            //Создание объекта для вывода изображений
            basicEffect = new BasicEffect(graphics.GraphicsDevice);
            //Установка матриц
            basicEffect.World = worldMatrix;
            basicEffect.View = viewMatrix;
            basicEffect.Projection = projectionMatrix;

            // Цвета различных видов освещения
            basicEffect.AmbientLightColor = new Vector3(0.1f, 0.1f, 0.1f);
            basicEffect.DiffuseColor = new Vector3(1.0f, 1.0f, 1.0f);
            basicEffect.SpecularColor = new Vector3(0.25f, 0.25f, 0.25f);
            basicEffect.SpecularPower = 5.0f;
            basicEffect.Alpha = 1.0f;
            //Включим освещение
            basicEffect.LightingEnabled = true;
            if (basicEffect.LightingEnabled)
            {
                basicEffect.DirectionalLight0.Enabled = true; // активируем каждый источник света отдельно
                if (basicEffect.DirectionalLight0.Enabled)
                {
                    // Направление по Х
                    basicEffect.DirectionalLight0.DiffuseColor = new Vector3(1, 0, 0); // диапазон от 0 до 1
                    basicEffect.DirectionalLight0.Direction = Vector3.Normalize(new Vector3(-1, 0, 0));
                    // Направление от источника света к началу координат сцены
                    basicEffect.DirectionalLight0.SpecularColor = Vector3.One;
                }

                basicEffect.DirectionalLight1.Enabled = true;
                if (basicEffect.DirectionalLight1.Enabled)
                {
                    // Направление по У
                    basicEffect.DirectionalLight1.DiffuseColor = new Vector3(0, 0.75f, 0);
                    basicEffect.DirectionalLight1.Direction = Vector3.Normalize(new Vector3(0, -1, 0));
                    basicEffect.DirectionalLight1.SpecularColor = Vector3.One;
                }

                basicEffect.DirectionalLight2.Enabled = true;
                if (basicEffect.DirectionalLight2.Enabled)
                {
                    // Направление по Z
                    basicEffect.DirectionalLight2.DiffuseColor = new Vector3(0, 0, 0.5f);
                    basicEffect.DirectionalLight2.Direction = Vector3.Normalize(new Vector3(0, 0, -1));
                    basicEffect.DirectionalLight2.SpecularColor = Vector3.One;
                }
            }

        }

        /// <summary>
        /// Инициализирует вершины и индексы 3D-модели
        /// </summary>
        private void InitializeCube()
        {
            //Вершины
            Vector3 topLeftFront = new Vector3(-1.0f, 1.0f, 1.0f);
            Vector3 bottomLeftFront = new Vector3(-1.0f, -1.0f, 1.0f);
            Vector3 topRightFront = new Vector3(1.0f, 1.0f, 1.0f);
            Vector3 bottomRightFront = new Vector3(1.0f, -1.0f, 1.0f);
            Vector3 topLeftBack = new Vector3(-1.0f, 1.0f, -1.0f);
            Vector3 topRightBack = new Vector3(1.0f, 1.0f, -1.0f);
            Vector3 bottomLeftBack = new Vector3(-1.0f, -1.0f, -1.0f);
            Vector3 bottomRightBack = new Vector3(1.0f, -1.0f, -1.0f);
            //Текстуры
            Vector2 textureTopLeft = new Vector2(0.0f, 0.0f);
            Vector2 textureTopRight = new Vector2(1.0f, 0.0f);
            Vector2 textureBottomLeft = new Vector2(0.0f, 1.0f);
            Vector2 textureBottomRight = new Vector2(1.0f, 1.0f);
            //Нормали
            Vector3 frontNormal = new Vector3(0.0f, 0.0f, 1.0f);
            Vector3 backNormal = new Vector3(0.0f, 0.0f, -1.0f);
            Vector3 topNormal = new Vector3(0.0f, 1.0f, 0.0f);
            Vector3 bottomNormal = new Vector3(0.0f, -1.0f, 0.0f);
            Vector3 leftNormal = new Vector3(-1.0f, 0.0f, 0.0f);
            Vector3 rightNormal = new Vector3(1.0f, 0.0f, 0.0f);

            // Передняя стенка, 6 вершин для 2-х треугольников
            //из которых она состоит
            cubeVertices[0] =
                new VertexPositionNormalTexture(
                topLeftFront, frontNormal, textureTopLeft);
            cubeVertices[1] =
                new VertexPositionNormalTexture(
                bottomLeftFront, frontNormal, textureBottomLeft);
            cubeVertices[2] =
                new VertexPositionNormalTexture(
                topRightFront, frontNormal, textureTopRight);
            cubeVertices[3] =
                new VertexPositionNormalTexture(
                bottomLeftFront, frontNormal, textureBottomLeft);
            cubeVertices[4] =
                new VertexPositionNormalTexture(
                bottomRightFront, frontNormal, textureBottomRight);
            cubeVertices[5] =
                new VertexPositionNormalTexture(
                topRightFront, frontNormal, textureTopRight);

            // Задняя стенка
            cubeVertices[6] =
                new VertexPositionNormalTexture(
                topLeftBack, backNormal, textureTopRight);
            cubeVertices[7] =
                new VertexPositionNormalTexture(
                topRightBack, backNormal, textureTopLeft);
            cubeVertices[8] =
                new VertexPositionNormalTexture(
                bottomLeftBack, backNormal, textureBottomRight);
            cubeVertices[9] =
                new VertexPositionNormalTexture(
                bottomLeftBack, backNormal, textureBottomRight);
            cubeVertices[10] =
                new VertexPositionNormalTexture(
                topRightBack, backNormal, textureTopLeft);
            cubeVertices[11] =
                new VertexPositionNormalTexture(
                bottomRightBack, backNormal, textureBottomLeft);

            // Верхняя стенка
            cubeVertices[12] =
                new VertexPositionNormalTexture(
                topLeftFront, topNormal, textureBottomLeft);
            cubeVertices[13] =
                new VertexPositionNormalTexture(
                topRightBack, topNormal, textureTopRight);
            cubeVertices[14] =
                new VertexPositionNormalTexture(
                topLeftBack, topNormal, textureTopLeft);
            cubeVertices[15] =
                new VertexPositionNormalTexture(
                topLeftFront, topNormal, textureBottomLeft);
            cubeVertices[16] =
                new VertexPositionNormalTexture(
                topRightFront, topNormal, textureBottomRight);
            cubeVertices[17] =
                new VertexPositionNormalTexture(
                topRightBack, topNormal, textureTopRight);

            // Нижняя стенка
            cubeVertices[18] =
                new VertexPositionNormalTexture(
                bottomLeftFront, bottomNormal, textureTopLeft);
            cubeVertices[19] =
                new VertexPositionNormalTexture(
                bottomLeftBack, bottomNormal, textureBottomLeft);
            cubeVertices[20] =
                new VertexPositionNormalTexture(
                bottomRightBack, bottomNormal, textureBottomRight);
            cubeVertices[21] =
                new VertexPositionNormalTexture(
                bottomLeftFront, bottomNormal, textureTopLeft);
            cubeVertices[22] =
                new VertexPositionNormalTexture(
                bottomRightBack, bottomNormal, textureBottomRight);
            cubeVertices[23] =
                new VertexPositionNormalTexture(
                bottomRightFront, bottomNormal, textureTopRight);

            // Левая стенка
            cubeVertices[24] =
                new VertexPositionNormalTexture(
                topLeftFront, leftNormal, textureTopRight);
            cubeVertices[25] =
                new VertexPositionNormalTexture(
                bottomLeftBack, leftNormal, textureBottomLeft);
            cubeVertices[26] =
                new VertexPositionNormalTexture(
                bottomLeftFront, leftNormal, textureBottomRight);
            cubeVertices[27] =
                new VertexPositionNormalTexture(
                topLeftBack, leftNormal, textureTopLeft);
            cubeVertices[28] =
                new VertexPositionNormalTexture(
                bottomLeftBack, leftNormal, textureBottomLeft);
            cubeVertices[29] =
                new VertexPositionNormalTexture(
                topLeftFront, leftNormal, textureTopRight);

            // Правая стенка 
            cubeVertices[30] =
                new VertexPositionNormalTexture(
                topRightFront, rightNormal, textureTopLeft);
            cubeVertices[31] =
                new VertexPositionNormalTexture(
                bottomRightFront, rightNormal, textureBottomLeft);
            cubeVertices[32] =
                new VertexPositionNormalTexture(
                bottomRightBack, rightNormal, textureBottomRight);
            cubeVertices[33] =
                new VertexPositionNormalTexture(
                topRightBack, rightNormal, textureTopRight);
            cubeVertices[34] =
                new VertexPositionNormalTexture(
                topRightFront, rightNormal, textureTopLeft);
            cubeVertices[35] =
                new VertexPositionNormalTexture(
                bottomRightBack, rightNormal, textureBottomRight);
        }

        /// <summary>
        /// Allows the game to run logic such as updating the world,
        /// checking for collisions, gathering input, and playing audio.
        /// </summary>
        /// <param name="gameTime">Provides a snapshot of timing values.</param>
        protected override void Update(GameTime gameTime)
        {
            // Allows the game to exit
            if (GamePad.GetState(PlayerIndex.One).Buttons.Back == ButtonState.Pressed)
                this.Exit();


            //Уменьшаем градус значение поворота матрицы
            wMX = wMX - 1f;
            wMY = wMY - 2f;
            wMZ = wMZ - 3f;
            //Модифицируем мировую матрицу
            worldMatrix = Matrix.CreateRotationX(MathHelper.ToRadians(wMX)) * Matrix.CreateRotationY(MathHelper.ToRadians(wMY)) *
             Matrix.CreateRotationZ(MathHelper.ToRadians(wMZ));

            //Меняем мировую матрицу
            basicEffect.World = worldMatrix;
            
            //Если расстояние меньше 15
            if (distanceView <= 15.0f)
            {
                //Увеличим
                distanceView = distanceView + 0.01f;
            }
            //Модифицируем матрицу вида
            viewMatrix = Matrix.CreateLookAt(new Vector3(0.0f, 0.0f, distanceView), Vector3.Zero, Vector3.Up);
            //Заменим матрицу вида
            basicEffect.View = viewMatrix;

            base.Update(gameTime);
        }

        /// <summary>
        /// This is called when the game should draw itself.
        /// </summary>
        /// <param name="gameTime">Provides a snapshot of timing values.</param>
        protected override void Draw(GameTime gameTime)
        {
            //Очистим экран
            graphics.GraphicsDevice.Clear(Color.SteelBlue);
            //Используется для определения того, как преобразовывать векторные данные в пиксельные
            RasterizerState rasterizerState1 = new RasterizerState();
            //Отключаем отсечени
            rasterizerState1.CullMode = CullMode.None;
            //Установим настроенное состояние прорисовки
            graphics.GraphicsDevice.RasterizerState = rasterizerState1;
            //Для каждого прохода в эффекте
            foreach (EffectPass pass in basicEffect.CurrentTechnique.Passes)
            {
                //Применить проход
                pass.Apply();
                //Вывести
                graphics.GraphicsDevice.DrawPrimitives(
                    PrimitiveType.TriangleList,
                    0,
                    12
                );
            }


            base.Draw(gameTime);
        }
    }
}
Листинг 23.1. Код класса Game1

На рис. 23.1 вы можете видеть игровое окно

Результаты работы игрового проекта P16_1

Рис. 23.1. Результаты работы игрового проекта P16_1
< Лекция 6 || Самостоятельная работа 16: 123 || Самостоятельная работа 17 >
Гулич Анна
Гулич Анна
Невозможно пройти тесты, в окне с вопросами пусто
Сашечка Огнев
Сашечка Огнев
Россия, Красноярский край
Андрей Корягин
Андрей Корягин
Россия, Пенза, Вазерская средняя школа, 2001