G-буферы

G-буферы

В рамках следующих нескольких статей я хотел бы рассмотреть понятие G-буферов.

G-буфер (geometry buffer) – это некоторый буфер, в котором хранится нужная нам информация о геометрии или других характеристиках сцены. Это очень непонятное определение, но пока лучше я не придумал.

Например, видеокарты с давних времен поддерживают на аппаратном уровне z-buffer – буфер глубины. В нем содержится информация о глубине каждой точки видимой сцены, то есть удаленности от наблюдателя. На основе данных из z-buffer выполняется огромное количество разных алгоритмов. Наример, удаление невидимых граней. Так можно не тратить время на рисование объектов, которые все равно заслонены другими.

На аппаратном уровне поддерживается не только z-buffer, но и другие, но все равно нам может понадобиться еще какой-нибудь свой буфер для создания сложных визульных эффектов. К счастью, при переходе к использованию программируемого конвейера – это не проблема.

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

Начнем с рисования о освещения примитивов:

Нам понадобится обычный эффект освещения по Фонгу, BasicEffect я использовать не хочу

float4x4 World;

float4x4 View;

float4x4 Projection;

float4 AmbientColor = float4(0.1, 0.1, 0.1, 1);

float ka = 0.4f;

float4 DiffuseColor = float4(0.4, 0, 0, 1);

float kd = 1;

float4 SpecularColor = float4(1, 1, 1, 1);

float ks = 1;

float SpecularPower = 8;

float3 LightPosition;

float3 Eye;

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

float3 WorldPosition : TEXCOORD0;

float3 Normal : TEXCOORD1;

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

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

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

float4 viewPosition = mul(worldPosition, View);

output.Position = mul(viewPosition, Projection);

output.WorldPosition = worldPosition;

output.Normal = worldNormal;

// TODO: add your vertex shader code here.

return output;

}

float4 PixelShaderFunction(VertexShaderOutput input) : COLOR0

{

// TODO: add your pixel shader code here.

float3 worldPosition = input.WorldPosition;

float3 worldNormal = normalize(input.Normal);

float4 Ambient = ka * AmbientColor;

float3 lightDirection = normalize(LightPosition — worldPosition);

float4 Diffuse = kd * max(0, dot(worldNormal, lightDirection)) * DiffuseColor;

float3 eyeDirection = normalize(Eye — worldPosition);

float3 reflectedLight = normalize(reflect(-lightDirection, worldNormal));

float4 Specular = ks * pow(max(0, dot(eyeDirection, reflectedLight)), SpecularPower) * SpecularColor;

return Ambient + Diffuse + Specular;

}

technique Light

{

pass Pass1

{

// TODO: set renderstates here.

VertexShader = compile vs_1_1 VertexShaderFunction();

PixelShader = compile ps_2_0 PixelShaderFunction();

}

}

В шейдере я использую «собственный» цвет объекта, так что в дальнейшем я буду назначать цвет объектам через DiffuseColor.

Теперь нарисуем примитивы:


CubePrimitive cube;


TeapotPrimitive teapot;


CylinderPrimitive cylinder;


Effect effectLight;


protected
override
void Initialize()

{


// TODO: Add your initialization logic here

cube = new
CubePrimitive(GraphicsDevice);

teapot = new
TeapotPrimitive(GraphicsDevice);

cylinder = new
CylinderPrimitive(GraphicsDevice);


base.Initialize();

}


protected
override
void LoadContent()

{


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

spriteBatch = new
SpriteBatch(GraphicsDevice);

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

}


protected
override
void Draw(GameTime gameTime)

{


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


base.Draw(gameTime);

}


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

{

GraphicsDevice.RenderState.AlphaBlendEnable = false;

GraphicsDevice.RenderState.DepthBufferEnable = true;

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

}

Получим что-то похожее на это:

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

Для того, чтобы не устанавливать постоянно альфа-компоненту цветов пикселей, я отлючил альфа смешивание

GraphicsDevice.RenderState.AlphaBlendEnable = false;

Также я включил буфер глубины на всякий случай.

GraphicsDevice.RenderState.DepthBufferEnable = true;

Исходный код:

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;


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


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

}


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

{


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


base.Draw(gameTime);

}


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

{

GraphicsDevice.RenderState.AlphaBlendEnable = false;

GraphicsDevice.RenderState.DepthBufferEnable = true;

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