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

Игровой мир, освещение, тени

Управление освещением, тени

Рассмотрим управление освещением и отображением теней в XNA. Существуют различные техники работы с тенями, мы рассмотрим отрисовку теней с использованием так называемого буфера трафаретов, или, по-английски - Stencil Buffer. В частности, мы модифицируем проект P16_2 таким образом, чтобы шарики, падающие на плоскость, отбрасывали тени.

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

Для создания тени мы воспользуемся методом CreateShadow объекта Matrix. Он позволяет создавать тень от объекта на основе информации об источнике освещения и плоскости, на которую должна проецироваться тень.

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

В этом же примере мы рассмотрим настройку источника света. В частности, мы применим для рисования объектов один направленный источник света, направление которого можно менять с помощью клавиш клавиатуры – координата Z изменяется с помощью клавиш-стрелок вверх-вниз, координата X – клавишами вправо-влево, координата Y – клавишами W и S. Изменение направления освещения влияет не только на освещение объектов, но и на тень.

Создадим новый проект – P17_2.

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

Окно Project Explorer проекта P17_2

Рис. 22.3. Окно Project Explorer проекта P17_2

Для представления моделей, выводимых на экран, мы используем класс modCls, в качестве ресурсов игры используем шар ball2 и плоскость plane.

Рассмотрим код класса modCls (листинг. 22.2.) В проекте P16_2 этот класс отвечал за вывод изображений на экран. Сейчас он выполняет ту же функцию, дополнив вывод изображений визуализацией теней.

using System;
using System.Collections.Generic;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.GamerServices;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Storage;
using Microsoft.Xna.Framework.Content;

namespace P17_2
{
    /// <summary>
    /// This is a game component that implements IUpdateable.
    /// </summary>
    public class modCls : Microsoft.Xna.Framework.DrawableGameComponent
    {
        //Модель
        public Model myModel;
        //Мировая матрица, матрицы вида и проекции
        public Matrix WorldMatrix;
        public Matrix ViewMatrix;
        public Matrix ProjectMatrix;
        //Направление света
        public Vector3 LightDirection;
        //Матрица для отображения тени
        Matrix shadow;
        //Плоскость, на которой отображается тень
        Plane sPlane;
        //Соотношение сторон экрана
        public float aspectRatio;
        //Для управления графическим устройством
        GraphicsDeviceManager graphics;
        //Конструктор получает на вход 
        //игровой класс, модель, объект для управления графическим устройством и плоскость для вывода тени
        public modCls(Game game, Model mod, GraphicsDeviceManager grf, Plane pl)
            : base(game)
        {
            myModel = mod;
            graphics = grf;
            sPlane = pl;
            aspectRatio = (float)graphics.GraphicsDevice.Viewport.Width /
(float)graphics.GraphicsDevice.Viewport.Height;
            LightDirection = new Vector3();
        }

        public override void Initialize()
        {
            // TODO: Add your initialization code here
            base.Initialize();
        }

        public override void Draw(GameTime gameTime)
        {
            //Выводим объект
            foreach (ModelMesh mesh in myModel.Meshes)
            {
                //Для каждого эффекта в сети
                foreach (BasicEffect effect in mesh.Effects)
                {
                    //Включить источник направленного света №0
                    effect.DirectionalLight0.Enabled = true;
                    //Настроить параметры
                    effect.DirectionalLight0.DiffuseColor = Vector3.One;
                    effect.DirectionalLight0.SpecularColor = Vector3.One;
                    //Направление света - в класса Game1 мы меняем направление
                    //по клавиатурным командам
                    effect.DirectionalLight0.Direction = Vector3.Normalize(LightDirection);
                    //Включить освещение
                    effect.LightingEnabled = true;
                    //Установка матриц
                    effect.World = WorldMatrix;
                    effect.View = ViewMatrix;
                    effect.Projection = ProjectMatrix;
                }
                mesh.Draw();
            }

            //Создать матрицу тени
            shadow = Matrix.CreateShadow(-LightDirection, sPlane);
            //Очистить буфер трафаретов
            graphics.GraphicsDevice.Clear(ClearOptions.Stencil, Color.Black, 0, 0);
            //Включть буфер трафаретов
            graphics.GraphicsDevice.RenderState.StencilEnable = true;
            // Вывод на экран элемента буфера в том случае, если он установлен в 0         
            graphics.GraphicsDevice.RenderState.ReferenceStencil = 0;
            graphics.GraphicsDevice.RenderState.StencilFunction = CompareFunction.Equal;
            //При выводе применяем операцию увеличения
            graphics.GraphicsDevice.RenderState.StencilPass = StencilOperation.Increment;
            //Включаем альфа-смешивание для того, чтобы сделать тень полупрозрачной
            graphics.GraphicsDevice.RenderState.AlphaBlendEnable = true;
            graphics.GraphicsDevice.RenderState.SourceBlend = Blend.SourceAlpha;
            graphics.GraphicsDevice.RenderState.DestinationBlend = Blend.InverseSourceAlpha;
            //Выводим тень
            foreach (ModelMesh mesh in myModel.Meshes)
            {
                foreach (BasicEffect effect in mesh.Effects)
                {
                    effect.AmbientLightColor = Vector3.Zero;
                    effect.Alpha = 0.5f;
                    effect.DirectionalLight0.Enabled = false;
                    effect.DirectionalLight1.Enabled = false;
                    effect.DirectionalLight2.Enabled = false;
                    effect.View = ViewMatrix;
                    effect.Projection = ProjectMatrix;
                    //При выводе тени умножаем мировую матрицу
                    //на матрицу вывода тени
                    effect.World = WorldMatrix*shadow;
                }
                mesh.Draw();
            }
            //Отключить буфер
            graphics.GraphicsDevice.RenderState.StencilEnable = false;
            //Отключить альфа-смешивание
            graphics.GraphicsDevice.RenderState.AlphaBlendEnable = false;
            base.Draw(gameTime);
        }
    }
}
Листинг 22.2. Код класса modCls
Alina Lasskaja
Alina Lasskaja

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

Дмитрий Кацман
Дмитрий Кацман
Израиль
Андрей Веденин
Андрей Веденин
Россия, Белгород