Модель освещения Ламберта

Модель освещения Ламберта

В данной статье мы рассмотрим, пожалуй, простейшую модель освещения – модель Ламберта. Стоит заметить, что данная модель является базовой для большинства остальных моделей освещения, поэтому она заслуживает отдельного внимания.


Напомню основные обозначения:

Введем некоторые основные обозначения:


  • N – нормаль к поверхности
  • L – направление к источнику света
  • R – направление отраженного луча
  • V – направление к наблюдателю

Причем каждый из векторов является единичным.

Простейшая модель освещения — чисто диффузное освещение. Считается, что свет падающий в точку, одинакового рассеивается по всем направлением полупространства. Таким образом, освещенность в точке определяется только плотностью света в точке поверхности, а она линейно зависит от косинуса угла падения.

Для отсечения случая, когда косинус угла отрицателен, в уравнение включено отсечение значений косинуса, меньших нуля.

Мы будем использовать собственный новый эффект, для этого создаем новый файл Light.fx в папке Content.

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

struct VertexShaderInput

{


float4 Position : POSITION0;


float3 Normal : NORMAL;

};

struct VertexShaderOutput

{


float4 Position : POSITION0;


float3 WorldPosition : TEXCOORD0;


float3 Normal : TEXCOORD1;

};

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

float4x4 World;

float4x4 View;

float4x4 Projection;

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

float ka = 1;

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

float kd = 1;

float3 LightPosition;

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

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;


return Ambient + Diffuse;

}

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

Теперь осталось загрузить шейдер в программе и инициализировать его параметры.


Effect effect;


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

effect = Content.Load<Effect>(«light»);

}


protected
override
void Draw(GameTime gameTime)

{

GraphicsDevice.Clear(Color.CornflowerBlue);


Matrix view = camera.View;


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


// TODO: Add your drawing code here

effect.Parameters[«World»].SetValue(Matrix.CreateRotationY((float)gameTime.TotalGameTime.TotalSeconds));

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

effect.Parameters[«Projection»].SetValue(proj);

effect.Parameters[«LightPosition»].SetValue(light.Position);

teapot.Draw(effect);

light.Draw(view, proj);


base.Draw(gameTime);

}

Результатом будет следующая картинка:


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

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 MyGame

{


///
<summary>


/// This is the main type for your game


///
</summary>


public
class
Game1 : Microsoft.Xna.Framework.Game

{


GraphicsDeviceManager graphics;


SpriteBatch spriteBatch;


Light light;


Effect effect;


TeapotPrimitive teapot;


FreeCamera camera;


public Game1()

{

graphics = new
GraphicsDeviceManager(this);

Content.RootDirectory = «Content»;

graphics.SynchronizeWithVerticalRetrace = false;

IsFixedTimeStep = false;

}


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

Components.Add(new
FPSCounter(this));

light = new
Light(this);

light.DefaultPosition = new
Vector3(1, 1, 1);

Components.Add(light);

camera = new
FreeCamera(this, new
Vector3(0, 0, 2), Vector3.Zero);

Components.Add(camera);

teapot = new
TeapotPrimitive(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

effect = 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)

{

GraphicsDevice.Clear(Color.CornflowerBlue);


Matrix view = camera.View;


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


// TODO: Add your drawing code here

effect.Parameters[«World»].SetValue(Matrix.CreateRotationY((float)gameTime.TotalGameTime.TotalSeconds));

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

effect.Parameters[«Projection»].SetValue(proj);

effect.Parameters[«LightPosition»].SetValue(light.Position);

teapot.Draw(effect);

light.Draw(view, proj);


base.Draw(gameTime);

}

}

}

Light.fx

float4x4 World;

float4x4 View;

float4x4 Projection;

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

float ka = 1;

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

float kd = 1;

float3 LightPosition;

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


return Ambient + Diffuse;

}

technique Light

{


pass Pass1

{


// TODO: set renderstates here.


VertexShader = compile
vs_1_1 VertexShaderFunction();


PixelShader = compile
ps_2_0 PixelShaderFunction();

}

}

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

6 комментариев на «Модель освещения Ламберта»

  1. Дмитрий:

    Здравствуйте, а как осветить модель с текстурой?

  2. например, в пиксельном шейдере прочитать цвет из текстуры (tex2D) и домножить его на освещенность

  3. Дмитрий:

    А если модель и текстура хранятся в одном файле .fbx, то как мне получить эту текстуру (к примеру наложена на объект в 3DMax )?

    • ну тогда можно получить текстуру из материала модели, например, при загрузке. Там она должна автоматом подгрузиться в model.modelmesh[0].effect[0].texture
      а потом ее можно уже передавать в любой шейдер.

  4. Дмитрий:

    Спасибо большое. Разобрался

  5. Александр:

    Выражаю огромную благодарность вам за ваши статьи. XNA начал изучать совсем недавно и настоящей !полезной! информации по ней нахожу мало, а ваш сайт прямо радует) Потратил неделю на поиски того, чтобы узнать как подгрузить из готовой модели текстуру и использовать в своих целях. А тут совершенно случайно наткнулся на ваш комментарий и все стало понятно. Нашел метод который вовращает из модели текстуру полигона — XNA 4.0 — model.Meshes[0].MeshParts[0].Effect.Parameters[0].GetValueTexture2D();

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

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

Логотип WordPress.com

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

Фотография Twitter

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

Фотография Facebook

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

Google+ photo

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

Connecting to %s