3D Графика для Windows Phone 7 с использованием XNA Framework. Часть 3

3D эффекты для Windows Phone 7 в XNA Framework

XNA Framework для Windows и Xbox 360 полностью поддерживает шейдерные эффекты, однако для Windows Phone 7 сейчас поддерживаются только пять встроенных эффектов. В этой части мы пройдемся по каждому этих эффектов.

BasicEffect

Мы уже видели работу BasicEffect в прошлом примере, а сейчас мы более подробно рассмотрим детали, так как этот эффект, пожалуй, используется чаще всего. Также стоит отметить, что этот эффект является эффектом по умолчанию в XNA Game Studio.

BasicEffect реализует модель освещения Блинна-Фонга с поддержкой до трех направленных источников света, может работать в попиксельном или повершинном режиме. Поддерживаются модели с текстурами или без них.

Начнем с размещения источников света.

У нас будет красный источник света слева, синий справа и зеленый прямо перед нашей моделью.

Замечание:

На самом деле нам нужно указывать направление, а не позицию, источников света. Я использую позицию просто, чтобы немного облегчить понимание.

Вот как мы сделаем это в коде, просто нужно установить параметры DirectionalLight0, DirectionalLight1 и DirectionalLight2:

 

protected override void Draw(GameTime gameTime)
{
    GraphicsDevice.Clear(Color.Black);
 
    // TODO: Add your drawing code here
 
    Matrix world = Matrix.CreateRotationY((float)gameTime.TotalGameTime.TotalSeconds)
        * Matrix.CreateTranslation(0, -0.4f, 0);
    Matrix view = Matrix.CreateLookAt(new Vector3(0, 0, 1.2f), Vector3.Zero, Vector3.Up);
    Matrix projection = Matrix.CreatePerspectiveFieldOfView(MathHelper.ToRadians(45), GraphicsDevice.Viewport.AspectRatio, 0.1f, 10f);
 
    Matrix[] transforms = new Matrix[model.Bones.Count];
    model.CopyAbsoluteBoneTransformsTo(transforms);
 
    foreach (ModelMesh mesh in model.Meshes)
    {
        foreach (BasicEffect effect in mesh.Effects)
        {
            effect.PreferPerPixelLighting = true;
 
            effect.LightingEnabled = true;
            effect.AmbientLightColor = new Vector3(0.1f, 0.1f, 0.1f);
 
            effect.SpecularColor = new Vector3(1, 1, 1);
            effect.SpecularPower = 24;
 
            // Set direction of light here, not position!
            effect.DirectionalLight0.Direction = new Vector3(1, -1, 0);
            effect.DirectionalLight0.DiffuseColor = new Vector3(1, 0, 0);
            effect.DirectionalLight0.SpecularColor = new Vector3(1, 0, 0);
            effect.DirectionalLight0.Enabled = true;
 
            // Set direction of light here, not position!
            effect.DirectionalLight1.Direction = new Vector3(0, -1, -1);
            effect.DirectionalLight1.DiffuseColor = new Vector3(0, 1, 0);
            effect.DirectionalLight1.SpecularColor = new Vector3(0, 1, 0);
            effect.DirectionalLight1.Enabled = true;
 
            // Set direction of light here, not position!
            effect.DirectionalLight2.Direction = new Vector3(-1, -1, 0);
            effect.DirectionalLight2.DiffuseColor = new Vector3(0, 0, 1);
            effect.DirectionalLight2.SpecularColor = new Vector3(0, 0, 1);
            effect.DirectionalLight2.Enabled = true;
 
            effect.View = view;
            effect.Projection = projection;
            effect.World = transforms[mesh.ParentBone.Index] * world;
        }
        mesh.Draw();
    }
 
    base.Draw(gameTime);
}

Следующая картинка показывает результат:

Еще одной интересной деталью BasicEffect является поддержка эффекта тумана. Это может быть использовано, например, в сценах с большим открытым пространством, где объекты, сильно удаленные от камеры, должны быть немного «затуманены».

Тумал в BasicEffect контролируется следущими четырьмя параметрами:FogColor, FogEnabled, FogStart, FogEnd.

FogEnabled включает или отключает тунам, FogColor – это просто цвет тумана. Значение оставшихся двух параметров (FogStart и FogEnd) легче понять по следующей картинке.

Для того, чтобы увидеть эффект тумана в сцене с несколькими моделями мы сначала создадим метод, который будет рисовать одну модель. Также будем задавать параметры тумана в этом методе.

private void DrawTeapot(Matrix world, Matrix view, Matrix projection, Matrix[] transforms)
{
 
    foreach (ModelMesh mesh in model.Meshes)
    {
        foreach (BasicEffect effect in mesh.Effects)
        {
            effect.PreferPerPixelLighting = true;
 
            effect.LightingEnabled = true;
            effect.AmbientLightColor = new Vector3(0.1f, 0.1f, 0.1f);
 
            effect.SpecularColor = new Vector3(1, 1, 1);
            effect.SpecularPower = 24;
 
            effect.DirectionalLight0.Direction = new Vector3(1, -1, 0);
            effect.DirectionalLight0.DiffuseColor = new Vector3(1, 0, 0);
            effect.DirectionalLight0.SpecularColor = new Vector3(1, 0, 0);
            effect.DirectionalLight0.Enabled = true;
 
            effect.DirectionalLight1.Direction = new Vector3(0, -1, -1);
            effect.DirectionalLight1.DiffuseColor = new Vector3(0, 1, 0);
            effect.DirectionalLight1.SpecularColor = new Vector3(0, 1, 0);
            effect.DirectionalLight1.Enabled = true;
 
            effect.DirectionalLight2.Direction = new Vector3(-1, -1, 0);
            effect.DirectionalLight2.DiffuseColor = new Vector3(0, 0, 1);
            effect.DirectionalLight2.SpecularColor = new Vector3(0, 0, 1);
            effect.DirectionalLight2.Enabled = true;
 
            effect.FogEnabled = true;
            effect.FogColor = new Vector3(0.1f, 0.1f, 0.1f); // Dark grey
            effect.FogStart = 2;
            effect.FogEnd = 5;
 
            effect.View = view;
            effect.Projection = projection;
            effect.World = transforms[mesh.ParentBone.Index] * world;
        }
        mesh.Draw();
    }
}

 

Теперь нарисуем несколько чайников на различной дистанции от камеры.

protected override void Draw(GameTime gameTime)
{
    GraphicsDevice.Clear(Color.Black);
 
    // TODO: Add your drawing code here
 
    Matrix world = Matrix.CreateRotationY((float)gameTime.TotalGameTime.TotalSeconds)
        * Matrix.CreateTranslation(0, -0.4f, 0);
    Matrix view = Matrix.CreateLookAt(new Vector3(0, 0, 1.2f), Vector3.Zero, Vector3.Up);
    Matrix projection = Matrix.CreatePerspectiveFieldOfView(MathHelper.ToRadians(45), GraphicsDevice.Viewport.AspectRatio, 0.1f, 10f);
 
    Matrix[] transforms = new Matrix[model.Bones.Count];
    model.CopyAbsoluteBoneTransformsTo(transforms);
 
    DrawTeapot(world, view, projection, transforms);
    DrawTeapot(world * Matrix.CreateTranslation(0.5f, 0, -1), view, projection, transforms);
    DrawTeapot(world * Matrix.CreateTranslation(-0.5f, 0, -2), view, projection, transforms);
    DrawTeapot(world * Matrix.CreateTranslation(0.5f, 0, -3), view, projection, transforms);
    DrawTeapot(world * Matrix.CreateTranslation(-0.5f, 0, -4), view, projection, transforms);
    DrawTeapot(world * Matrix.CreateTranslation(0.5f, 0, -5), view, projection, transforms);
    DrawTeapot(world * Matrix.CreateTranslation(-0.5f, 0, -6), view, projection, transforms);
 
    base.Draw(gameTime);
}

 

На следующей картинке видно как интенсивность тумана растет с ростом дистанции до камеры.

DualTextureEffect

DualTextureEffect – это простой эффект мутитекстурирования. Он позволяет наложить сразу две текстуры на модель. Эффект также поддерживает туман.

DualTextureEffect очень хорош из-за своей «дешевизны» для железа (по сравнению с другими эффектами) и он может использоваться для наложения карт освещения, масок, декалей и т.д.

DualTextureEffect используем 2Х модуляцию, при которой цвета пикселей вычисляются по следующей формуле:

Color = Texture1.rgb * Texture2.rgb * 2

Это означает, что серый цвет во второй текстуре (Texture2) не изменит базовый цвет, любой более темный цвет сделает пиксель более темным и т.д.

Давайте рассмотрим простой пример использования DualTextureEffect. Сейчас мы сделаем эффект глобального освещения.

Создание теней в реальном времени – это всегда очень сложная задача для аппаратного обеспечения, также тени, построенные в реальном времени, обычно содержат различные визуальные артефакты, что тоже плохо.

Вот что мы можем сделать: построить красивые и точные карты освещения во время моделирования и использовать их в своей игре.

Вот изображение освещения сцены, которое я получил в своей программе для трехмерного моделирования:

Тут есть один точечный источник света перед смежными ящиками и один направленный источник света слева.

Замечание:

Для DualTextureEffect требуется наличие двух каналов текстурных координат у модели.

Я экспортировал карту освещения и создал диффузную карту (по сути, просто текстуру для модели):

Для начала мы просто отрендерим нашу модель при помощи BasicEffect, чтобы убедиться в том, что все хорошо.

 

Model model;
Texture2D lightMap;
Texture2D diffuseMap;
 
protected override void LoadContent()
{
    // Create a new SpriteBatch, which can be used to draw textures.
    spriteBatch = new SpriteBatch(GraphicsDevice);
 
    model = Content.Load<Model>("boxes");
    lightMap = Content.Load<Texture2D>("light");
    diffuseMap = Content.Load<Texture2D>("diffuse");
 
}
protected override void Draw(GameTime gameTime)
{
    GraphicsDevice.Clear(Color.Black);
 
    // TODO: Add your drawing code here
 
    Matrix world = Matrix.CreateRotationY(MathHelper.ToRadians(30)) * Matrix.CreateScale(0.003f)
        * Matrix.CreateTranslation(0, -0.4f, 0);
    Matrix view = Matrix.CreateLookAt(new Vector3(0, 0, 1.2f), Vector3.Zero, Vector3.Up);
    Matrix projection = Matrix.CreatePerspectiveFieldOfView(MathHelper.ToRadians(45), GraphicsDevice.Viewport.AspectRatio, 0.1f, 10f);
 
    Matrix[] transforms = new Matrix[model.Bones.Count];
    model.CopyAbsoluteBoneTransformsTo(transforms);
 
 
    foreach (ModelMesh mesh in model.Meshes)
    {
        foreach (BasicEffect effect in mesh.Effects)
        {
            effect.TextureEnabled = true;
            effect.Texture = diffuseMap;
 
            // uncomment this to render with lightmap
            //effect.Texture = lightMap;
 
            effect.View = view;
            effect.Projection = projection;
            effect.World = transforms[mesh.ParentBone.Index] * world;
        }
            mesh.Draw();
    }
 
 
    base.Draw(gameTime);
}

Рендеринг модели с двумя нашими текстурами (по очереди) даст следующий результат:


Теперь начнем использовать DualTextureEffect. Сначала установим правильные параметры Content Processor. Переходим к параметрам файла с моделью в Content проекте и установим Content Processor -> Default Effect равным DualTextureEffect.

Еще одна вещь, которую мы сделаем перед тем как переходить к рендерингу, – это создание специальной текстуры размером 1х1, содержащей только один пиксель серого цвета. Мы будем использовать ее для того, чтобы временно отключать одну из текстур.

Texture2D grey;
protected override void LoadContent()
{
    // Create a new SpriteBatch, which can be used to draw textures.
    spriteBatch = new SpriteBatch(GraphicsDevice);
 
    model = Content.Load<Model>("boxes");
    lightMap = Content.Load<Texture2D>("light");
    diffuseMap = Content.Load<Texture2D>("diffuse");
 
    grey = new Texture2D(GraphicsDevice, 1, 1);
    grey.SetData(new Color[] { new Color(128, 128, 128, 255) });
}
В методе Draw мы будем использовать DualTextureEffect вместо BasicEffect.
protected override void Draw(GameTime gameTime)
{
    GraphicsDevice.Clear(Color.Black);
 
    // TODO: Add your drawing code here
 
    Matrix world = Matrix.CreateRotationY(MathHelper.ToRadians(30)) * Matrix.CreateScale(0.003f)
        * Matrix.CreateTranslation(0, -0.4f, 0);
    Matrix view = Matrix.CreateLookAt(new Vector3(0, 0, 1.2f), Vector3.Zero, Vector3.Up);
    Matrix projection = Matrix.CreatePerspectiveFieldOfView(MathHelper.ToRadians(45), GraphicsDevice.Viewport.AspectRatio, 0.1f, 10f);
 
    Matrix[] transforms = new Matrix[model.Bones.Count];
    model.CopyAbsoluteBoneTransformsTo(transforms);
 
 
    foreach (ModelMesh mesh in model.Meshes)
    {
        foreach (DualTextureEffect effect in mesh.Effects)
        {
            effect.Texture = diffuseMap;
            effect.Texture2 = lightMap;
 
            // uncomment this to hide diffuse map
            //effect.Texture = grey;
            // uncomment this to hide light map
            //effect.Texture2 = grey;
 
            effect.View = view;
            effect.Projection = projection;
            effect.World = transforms[mesh.ParentBone.Index] * world;
 
        }
        mesh.Draw();
    }
 
 
    base.Draw(gameTime);
}

 

Вот и результат:

Реклама
Запись опубликована в рубрике Компьютерная графика с метками , , , , , , . Добавьте в закладки постоянную ссылку.

Один комментарий на «3D Графика для Windows Phone 7 с использованием XNA Framework. Часть 3»

  1. Уведомление:   Windows Phone / Возможности 3D графики Windows Phone by SOS Admin!

Добавить комментарий

Заполните поля или щелкните по значку, чтобы оставить свой комментарий:

Логотип WordPress.com

Для комментария используется ваша учётная запись WordPress.com. Выход / Изменить )

Фотография Twitter

Для комментария используется ваша учётная запись Twitter. Выход / Изменить )

Фотография Facebook

Для комментария используется ваша учётная запись Facebook. Выход / Изменить )

Google+ photo

Для комментария используется ваша учётная запись Google+. Выход / Изменить )

Connecting to %s