Невозможно пройти тесты, в окне с вопросами пусто |
Основы трехмерной графики в XNA
Цель работы: Научиться выводить трёхмерные объекты на игровой экран
23.1. Рисование трехмерных объектов средствами XNA
Рассмотрим особенности рисования трехмерных объектов средствами XNA на примере проекта P16_1. Здесь мы рассматриваем использование вершинного буфера для вывода куба. Пример подготовлен на основе кода, доступного в рамках Microsoft Permissive License (Ms-PL).
В листинге 23.1 вы можете найти код класса Game1 проекта P14_1. Код подробно прокомментирован.
Отметим, что для вывода изображения нам необходимо выполнить следующие шаги:
- Установить мировую, проекционную и видовую матрицы.
- Создать и настроить объект типа BasicEffect для вывода изображений.
- Создать наборы вершин (в нашем случае они представлены данными о координате, нормали и текстуре), которые мы будем использовать при выводе.
- Создать и заполнить вершинный буфер, установить его в объект GraphicDevice для вывода изображения.
- Вывести изображение из буфера, пользуясь инструментами объекта типа 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 вы можете видеть игровое окно