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

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

Следующий буфер, который мы рассмотрим – это буфер нормалей.

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

Особенных отличный от буфера глубины, с точки зрения логики работы, тут не будет, так что сразу создадим новый шейдер:

float4x4 World;

float4x4 View;

float4x4 Projection;

// TODO: add effect parameters here.

struct VertexShaderInput

{


float4 Position : POSITION0;


float3 Normal : NORMAL;


// TODO: add input channels such as texture


// coordinates and vertex colors here.

};

struct VertexShaderOutput

{


float4 Position : POSITION0;


float4 Color : COLOR0;


// TODO: add vertex shader outputs such as colors and texture


// coordinates here. These values will automatically be interpolated


// over the triangle, and provided as input to your pixel shader.

};

VertexShaderOutput VertexShaderFunction(VertexShaderInput input)

{

VertexShaderOutput output;


float4 worldPosition = mul(input.Position, World);


float4 viewPosition = mul(worldPosition, View);

output.Position = mul(viewPosition, Projection);


float3 worldNormal = normalize(mul(input.Normal, World));

output.Color.rgb = (worldNormal + 1) / 2.0;

output.Color.a = 1;


return output;

}

float4 PixelShaderFunction(VertexShaderOutput input) : COLOR0

{


return input.Color;

}

technique Technique1

{


pass Pass1

{


// TODO: set renderstates here.


VertexShader = compile
vs_1_1 VertexShaderFunction();


PixelShader = compile
ps_1_1 PixelShaderFunction();

}

}

Что тут стоит отметить, так это преобразование нормалей. Нормали в мировых координатах могут принимать значения в интервале от -1 до 1, нам же нужно получить значение от 0 до 1. Следующая строка выполняет описанное проебразование:

output.Color.rgb = (worldNormal + 1) / 2.0;

Кстати, в примерах из этой серии мы вычисляем значения параметров (нормалей, глубун) в вершинах, а не в пикселях. Я решил, что для таких примеров не имеет смысла делать более точные вычисления, в реальных приложениях может понадобиться вычисление параметров пикселях.

По аналогии с буфером глубуны пишем код для создания буфера нормалей.

Effect effectNormals;

RenderTarget2D targetNormals;


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

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

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

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

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

}


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

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

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

spriteComp.Clear();

spriteComp.Add(targetDepth.GetTexture());

spriteComp.Add(targetNormals.GetTexture());


base.Draw(gameTime);

}


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

}

Получается такая картинка, цвета обозначают направление векторов нормалей.

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


Effect effectNormals;


RenderTarget2D targetDepth;


RenderTarget2D targetNormals;


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

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

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

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

targetNormals = 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);

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

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

spriteComp.Clear();

spriteComp.Add(targetDepth.GetTexture());

spriteComp.Add(targetNormals.GetTexture());


base.Draw(gameTime);

}


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