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

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

В этот раз мы посмотрим, как можно использовать информацию из буфера идентификаторов для рисования силуэта объектов.

Шейдер использует простой алгоритм выделения границ:

sampler Sampler : register(s0);

float4 PixelShaderFunction(float2 pos : TEXCOORD) : COLOR0

{

float2 deltaX = float2( 1.0 / 800, 0);

float2 deltaY = float2( 0, 1.0 / 600);


float4 color1 = tex2D(Sampler, pos-deltaX-deltaY);

float4 color2 = tex2D(Sampler, pos-deltaX+deltaY);

float4 color3 = tex2D(Sampler, pos+deltaX-deltaY);

float4 color4 = tex2D(Sampler, pos+deltaX+deltaY);

float4 delta = abs(color1 — color4) + abs(color2 — color3);


float idEdges = dot(delta.xyz, 1);

float4 drawColor;

drawColor.rgb = idEdges;

drawColor.a = 1;


return drawColor;

}

technique EdgeDetection

{


pass Pass1

{


// TODO: set renderstates here.


PixelShader = compile
ps_2_0 PixelShaderFunction();

}

}

Шейдер является постэффектом, так что для его использования будем работать со SpriteBatch. Метод DrawEdges может вычислить границы по любому из буферов, если, конечно, формат данных в нем совпадает с нашим.


private
void DrawEdges(RenderTarget2D target)

{

GraphicsDevice.SetRenderTarget(0, targetEdges);

GraphicsDevice.Clear(Color.Black);

effectEdge.Begin();

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

effectEdge.CurrentTechnique.Passes[0].Begin();

spriteBatch.Draw(target.GetTexture(), Vector2.Zero, Color.White);

effectEdge.CurrentTechnique.Passes[0].End();

effectEdge.End();

spriteBatch.End();

}

Изображение получается следующее:

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

Исходный код 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;


Effect effectDiffuse;


Effect effectDepth;


Effect effectID;


Effect effectNormals;


Effect effectEdge;


RenderTarget2D targetDiffuse;


RenderTarget2D targetDepth;


RenderTarget2D targetID;


RenderTarget2D targetNormals;


RenderTarget2D targetEdges;


SpriteComponent spriteComp;


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

effectDepth = Content.Load<Effect>(«drawDepth»);

effectDiffuse = Content.Load<Effect>(«drawDiffuse»);

effectID = Content.Load<Effect>(«drawID»);

effectNormals = Content.Load<Effect>(«drawNormals»);

effectEdge = Content.Load<Effect>(«postEdge»);

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

targetDepth = new
RenderTarget2D(GraphicsDevice, graphics.PreferredBackBufferWidth, graphics.PreferredBackBufferHeight, 1, SurfaceFormat.Color);

targetDiffuse = new
RenderTarget2D(GraphicsDevice, graphics.PreferredBackBufferWidth, graphics.PreferredBackBufferHeight, 1, SurfaceFormat.Color);

targetID = new
RenderTarget2D(GraphicsDevice, graphics.PreferredBackBufferWidth, graphics.PreferredBackBufferHeight, 1, SurfaceFormat.Color);

targetNormals = new
RenderTarget2D(GraphicsDevice, graphics.PreferredBackBufferWidth, graphics.PreferredBackBufferHeight, 1, SurfaceFormat.Color);

targetEdges = new
RenderTarget2D(GraphicsDevice, graphics.PreferredBackBufferWidth, graphics.PreferredBackBufferHeight, 1, SurfaceFormat.Color);

}


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

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

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

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

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

DrawEdges(targetID);

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

spriteComp.Clear();

spriteComp.Add(targetDepth.GetTexture());

spriteComp.Add(targetNormals.GetTexture());

spriteComp.Add(targetID.GetTexture());

spriteComp.Add(targetDiffuse.GetTexture());

spriteComp.Add(targetEdges.GetTexture());


base.Draw(gameTime);

}


private
void DrawEdges(RenderTarget2D target)

{

GraphicsDevice.SetRenderTarget(0, targetEdges);

GraphicsDevice.Clear(Color.Black);

effectEdge.Begin();

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

effectEdge.CurrentTechnique.Passes[0].Begin();

spriteBatch.Draw(target.GetTexture(), Vector2.Zero, Color.White);

effectEdge.CurrentTechnique.Passes[0].End();

effectEdge.End();

spriteBatch.End();

}


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

{

GraphicsDevice.SetRenderTarget(0, targetDiffuse);

GraphicsDevice.Clear(Color.Black);

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

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

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

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

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

teapot.Draw(effectDiffuse);

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

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

cube.Draw(effectDiffuse);

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

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

cylinder.Draw(effectDiffuse);

}


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

{

GraphicsDevice.SetRenderTarget(0, targetDepth);

GraphicsDevice.Clear(Color.Black);

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

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

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

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

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

teapot.Draw(effectDepth);

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

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

cube.Draw(effectDepth);

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

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

cylinder.Draw(effectDepth);

}


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

{

GraphicsDevice.SetRenderTarget(0, targetNormals);

GraphicsDevice.Clear(Color.Black);

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

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

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

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

teapot.Draw(effectNormals);

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

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

cube.Draw(effectNormals);

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

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

cylinder.Draw(effectNormals);

}


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

{

GraphicsDevice.SetRenderTarget(0, targetID);

GraphicsDevice.Clear(Color.Black);

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

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

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

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

effectID.Parameters[«ID»].SetValue(new
Vector3(0,1,0));

teapot.Draw(effectID);

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

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

effectID.Parameters[«ID»].SetValue(new
Vector3(0, 0, 1));

cube.Draw(effectID);

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

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

effectID.Parameters[«ID»].SetValue(new
Vector3(1, 0, 0));

cylinder.Draw(effectID);

}


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