G-буферы. Часть 2

G-буферы. Часть 2

В ходе работы мы будем получать большое количество различных изображений с G-буферов. Для того, чтобы не терять потом время на то, чтобы размещать все эти изображения на экране, сейчас я приведу пример простого игрового компонента, который будет отвечать за автоматическое рисование нужных нам изображений.

Что-то вроде технического задания:

У нас будет некоторое количество изображений, скажем, от 3 до 10. Нужно уметь автоматически размещать их на экране в нескольких режимах.

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

Безусловно, пользователь должен иметь возможность управлять данным компонентом.

Код может выглядеть следующим образом:


class
SpriteComponent : DrawableGameComponent

{


KeyboardState oldState;


SpriteBatch spriteBatch;


List<Texture2D> sprites = new
List<Texture2D>();


///
<summary>


/// 0 — show all


/// 1 — show one


/// 2 — show full screen


///
</summary>


int visualState = 0;


///
<summary>


/// Если такого элемента не существует, то не показываем ничего


///
</summary>


int currentSprite = 0;


public SpriteComponent(Game game) : base(game)

{

spriteBatch = new
SpriteBatch(game.GraphicsDevice);

}


public
void Clear()

{

sprites.Clear();

}


public
void Add(Texture2D tex)

{

sprites.Add(tex);

}


public
override
void Update(GameTime gameTime)

{


KeyboardState state = Keyboard.GetState();


if (state.IsKeyDown(Keys.Space) && oldState.IsKeyUp(Keys.Space))

{

visualState++;


if (visualState > 2) visualState = 0;

}


if (state.IsKeyDown(Keys.Back) && oldState.IsKeyUp(Keys.Back))

{

visualState—;


if (visualState < 0) visualState = 2;

}


if (state.IsKeyDown(Keys.Q) && oldState.IsKeyUp(Keys.Q))

{

currentSprite—;


if (currentSprite < 0) currentSprite = sprites.Count;

}


if (state.IsKeyDown(Keys.W) && oldState.IsKeyUp(Keys.W))

{

currentSprite++;


if (currentSprite > sprites.Count) currentSprite = 0;

}

oldState = state;


base.Update(gameTime);

}


public
override
void Draw(GameTime gameTime)

{

spriteBatch.Begin(SpriteBlendMode.None, SpriteSortMode.Immediate, SaveStateMode.SaveState);


switch (visualState)

{


case 0:


int x = 0;


int y = 0;


foreach (var sprite in sprites)

{

spriteBatch.Draw(sprite, new
Rectangle(x, y, 100, 100), Color.White);


if (x + 100 > this.Game.GraphicsDevice.Viewport.Width)

{

x = 0;

y += 100;

}


else

{

x += 100;

}

}


break;


case 1:


if (currentSprite < sprites.Count)

spriteBatch.Draw(sprites[currentSprite], new
Rectangle(0,0,200, 200), Color.White);


break;


case 2:


if (currentSprite < sprites.Count)

spriteBatch.Draw(sprites[currentSprite], Vector2.Zero, Color.White);


break;

}

spriteBatch.End();


base.Draw(gameTime);

}

}

VisualState отвечает за состояние компонента и имеет значения:

0 – рисовать все изображения.

1 – рисовать сцену и одно из изображений

2 – рисовать одно изображение на весь экран

CurrentSprite указывает текущее изображение. Если элемент с индексом CurrentSprite не содержится в списке изображений, то ничего не рисуется (то есть видна текущая сцена). Это сделано несколько хитро, имеет смысл рассмотреть немного подробнее.


if (state.IsKeyDown(Keys.Space) && oldState.IsKeyUp(Keys.Space))

{

visualState++;


if (visualState > 2) visualState = 0;

}


if (state.IsKeyDown(Keys.Back) && oldState.IsKeyUp(Keys.Back))

{

visualState—;


if (visualState < 0) visualState = 2;

}


if (state.IsKeyDown(Keys.Q) && oldState.IsKeyUp(Keys.Q))

{

currentSprite—;


if (currentSprite < 0) currentSprite = sprites.Count;

}


if (state.IsKeyDown(Keys.W) && oldState.IsKeyUp(Keys.W))

{

currentSprite++;


if (currentSprite > sprites.Count) currentSprite = 0;

}

Судя по этому коду (последний if) получается, что currentSprite может совпадать с количеством элементов в списке, то есть currentSprite содержит индекс, на единицу больший, чем имеющийся в списке.

В Draw картинки рисуется слева направо, каждая имеет размер 100х100, причем картинки не должны вылезать за рамки экрана (хотя я не проверял).

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


SpriteComponent spriteComp;


Texture2D[] texArray;


protected
override
void Initialize()

{


// TODO: Add your initialization logic here

cube = new
CubePrimitive(GraphicsDevice);

teapot = new
TeapotPrimitive(GraphicsDevice);

cylinder = new
CylinderPrimitive(GraphicsDevice);

spriteComp = new
SpriteComponent(this);


this.Components.Add(spriteComp);


base.Initialize();

}


protected
override
void LoadContent()

{


// Create a new SpriteBatch, which can be used to draw textures.

spriteBatch = new
SpriteBatch(GraphicsDevice);


// TODO: use this.Content to load your game content here

effectLight = Content.Load<Effect>(«Light»);

texArray = new
Texture2D[4];

texArray[0] = Content.Load<Texture2D>(«017»);

texArray[1] = Content.Load<Texture2D>(«035»);

texArray[2] = Content.Load<Texture2D>(«041»);

texArray[3] = Content.Load<Texture2D>(«046»);

}


protected
override
void Draw(GameTime gameTime)

{

GraphicsDevice.RenderState.AlphaBlendEnable = false;

GraphicsDevice.RenderState.DepthBufferEnable = true;


Matrix world = Matrix.Identity;


Matrix view = Matrix.CreateLookAt(new
Vector3(-3, 0, 2), Vector3.Zero, Vector3.Up);


Matrix projection = Matrix.CreatePerspectiveFieldOfView(MathHelper.ToRadians(45), GraphicsDevice.Viewport.AspectRatio, 0.1f, 10f);

DrawScene(ref world, ref view, ref projection);

spriteComp.Clear();

spriteComp.Add(texArray[0]);

spriteComp.Add(texArray[1]);

spriteComp.Add(texArray[2]);

spriteComp.Add(texArray[3]);


base.Draw(gameTime);

}

Вот примерно такая картинка получается, стоит попробовать поменять режимы (пробел\backspace), картинки меняются Q\W (в последних двух режимах)

Исходный код Game1.cs

using System;

using System.Collections.Generic;

using System.Linq;

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.Media;

using Microsoft.Xna.Framework.Net;

using Microsoft.Xna.Framework.Storage;

using Primitives3D;

namespace XNA_MRT

{


///
<summary>


/// This is the main type for your game


///
</summary>


public
class
Game1 : Microsoft.Xna.Framework.Game

{


GraphicsDeviceManager graphics;


SpriteBatch spriteBatch;


CubePrimitive cube;


TeapotPrimitive teapot;


CylinderPrimitive cylinder;


Effect effectLight;


SpriteComponent spriteComp;


Texture2D[] texArray;


public Game1()

{

graphics = new
GraphicsDeviceManager(this);

Content.RootDirectory = «Content»;

}


///
<summary>


/// Allows the game to perform any initialization it needs to before starting to run.


/// This is where it can query for any required services and load any non-graphic


/// related content. Calling base.Initialize will enumerate through any components


/// and initialize them as well.


///
</summary>


protected
override
void Initialize()

{


// TODO: Add your initialization logic here

cube = new
CubePrimitive(GraphicsDevice);

teapot = new
TeapotPrimitive(GraphicsDevice);

cylinder = new
CylinderPrimitive(GraphicsDevice);

spriteComp = new
SpriteComponent(this);


this.Components.Add(spriteComp);


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()

{


// Create a new SpriteBatch, which can be used to draw textures.

spriteBatch = new
SpriteBatch(GraphicsDevice);


// TODO: use this.Content to load your game content here

effectLight = Content.Load<Effect>(«Light»);

texArray = new
Texture2D[4];

texArray[0] = Content.Load<Texture2D>(«017»);

texArray[1] = Content.Load<Texture2D>(«035»);

texArray[2] = Content.Load<Texture2D>(«041»);

texArray[3] = Content.Load<Texture2D>(«046»);

}


///
<summary>


/// UnloadContent will be called once per game and is the place to unload


/// all content.


///
</summary>


protected
override
void UnloadContent()

{


// TODO: Unload any non ContentManager content here

}


///
<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();


// TODO: Add your update logic here


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)

{

GraphicsDevice.RenderState.AlphaBlendEnable = false;

GraphicsDevice.RenderState.DepthBufferEnable = true;


Matrix world = Matrix.Identity;


Matrix view = Matrix.CreateLookAt(new
Vector3(-3, 0, 2), Vector3.Zero, Vector3.Up);


Matrix projection = Matrix.CreatePerspectiveFieldOfView(MathHelper.ToRadians(45), GraphicsDevice.Viewport.AspectRatio, 0.1f, 10f);

DrawScene(ref world, ref view, ref projection);

spriteComp.Clear();

spriteComp.Add(texArray[0]);

spriteComp.Add(texArray[1]);

spriteComp.Add(texArray[2]);

spriteComp.Add(texArray[3]);


base.Draw(gameTime);

}


private
void DrawScene(ref
Matrix world, ref
Matrix view, ref
Matrix projection)

{

GraphicsDevice.SetRenderTarget(0, null);

GraphicsDevice.Clear(Color.Black);

effectLight.Parameters[«View»].SetValue(view);

effectLight.Parameters[«Projection»].SetValue(projection);

effectLight.Parameters[«Eye»].SetValue(new
Vector3(-3, 0, 2));

effectLight.Parameters[«LightPosition»].SetValue(new
Vector3(0, 0.5f, 1));

world = Matrix.CreateRotationY(MathHelper.ToRadians(60)) * Matrix.CreateTranslation(1.5f, 0.8f, 0);

effectLight.Parameters[«World»].SetValue(world);

effectLight.Parameters[«DiffuseColor»].SetValue(new
Vector4(1, 0, 0, 1));

teapot.Draw(effectLight);

world = Matrix.CreateTranslation(1.5f, 0, 0);

effectLight.Parameters[«World»].SetValue(world);

effectLight.Parameters[«DiffuseColor»].SetValue(new
Vector4(0, 1, 0, 1));

cube.Draw(effectLight);

world = Matrix.CreateTranslation(0, 0, 0);

effectLight.Parameters[«World»].SetValue(world);

effectLight.Parameters[«DiffuseColor»].SetValue(new
Vector4(0, 0, 1, 1));

cylinder.Draw(effectLight);

}

}

}

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

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

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

Логотип WordPress.com

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

Фотография Twitter

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

Фотография Facebook

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

Google+ photo

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

Connecting to %s