Flatik.ru

Перейти на главную страницу

Поиск по ключевым словам:

страница 1
Иван Андреев

[email protected]

О реализации некоторых нефотореалистичных эффектов:

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

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

(Синяя рамка не входит в маску, а отображается для наглядности)

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

Освещенность в каждой точке объекта измеряется от 0 до 1. Аналогично, координаты точки в маске принимают значения от 0 до 1 (в шейдере).

Таким образом, можно сопоставить яркости осщения некоторый цвет в текстуре с маской. Так, тусклое освещение соответствует самому черному (слева в маске) цвету, а наиболее яркое – белому (справа в маске).

Сначала разработаем простое приложение, которое будет рисовать чайник, освещенный по модели Фонга, чтобы потом можно было легко сравнить результат нефотореалистичного рендеринга с фотореалистичным.

Исходный код может выглядеть примерно так:

(Только метод Draw, остальной код в конце статьи)

protected override void Draw(GameTime gameTime)

{

GraphicsDevice.Clear(Color.Black);


// TODO: Add your drawing code here

Matrix world = Matrix.CreateRotationY((float)gameTime.TotalGameTime.TotalSeconds) * Matrix.CreateTranslation(-1.0f, 0, 0);

Matrix view = Matrix.CreateLookAt(new Vector3(0, 1, 4), Vector3.Zero, Vector3.Up);

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


effect.CurrentTechnique = effect.Techniques["Phong"];

effect.Parameters["World"].SetValue(world);

effect.Parameters["View"].SetValue(view);

effect.Parameters["Projection"].SetValue(proj);

effect.Parameters["Eye"].SetValue(new Vector3(0, 1, 4));
teapot.Draw(effect);
world = Matrix.CreateRotationY(-(float)gameTime.TotalGameTime.TotalSeconds) * Matrix.CreateTranslation(1.0f, 0, 0);

effect.CurrentTechnique = effect.Techniques["Phong"];

effect.Parameters["World"].SetValue(world);

effect.Parameters["View"].SetValue(view);

effect.Parameters["Projection"].SetValue(proj);

effect.Parameters["Eye"].SetValue(new Vector3(0, 1, 4));


teapot.Draw(effect);

base.Draw(gameTime);

}

Шейдер:


float4x4 World;

float4x4 View;

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

float ka = 0;


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

float kd = 0.7;


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

float ks = 1;

float SpecularPower = 8;
float3 LightPosition = float3(0,0.5,1);

float3 Eye;


float4 Color = float4(0,0,0.3,1);
// 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 VertexShaderFunctionPhong(VertexShaderInput input)

{

VertexShaderOutput output;


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

float4 viewPosition = mul(worldPosition, View);

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

output.Position = mul(viewPosition, Projection);

output.WorldPosition = worldPosition;

output.Normal = worldNormal;

// TODO: add your vertex shader code here.
return output;

}
float4 PixelShaderFunctionPhong(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 Color + Ambient + Diffuse + Specular;

}
technique Phong

{

pass Pass1



{

// TODO: set renderstates here.


VertexShader = compile vs_1_1 VertexShaderFunctionPhong();

PixelShader = compile ps_2_0 PixelShaderFunctionPhong();

}

}

Чайники имеют собственный цвет (темно синий). Для лучшего визуального эффекта коэффициент рассеянного света (ka) установлен равным 0, то есть рассеяный свет не используется.



Диффузный свет имеет синий цвет, а заркальный (Specular) – красный. Таким образом, блики имеют фиолетовый цвет (при смешивании синего и красного).

Источник света помещен между чайниками, слегка приподнят и придвинут к наблюдателю.



Теперь перейдем к созданию нефотореалистичного эффекта. Для начала я буду отображать только диффузную составляющую.

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

float2 pos= float2(0,0);

pos.x = Diffuse;

float4 dColor = tex2D(LigthMaskSampler, pos) * DiffuseColor;

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

protected override void Draw(GameTime gameTime)

{

GraphicsDevice.Clear(Color.Black);


// TODO: Add your drawing code here

Matrix world = Matrix.CreateRotationY((float)gameTime.TotalGameTime.TotalSeconds) * Matrix.CreateTranslation(-1.0f, 0, 0);

Matrix view = Matrix.CreateLookAt(new Vector3(0, 1, 4), Vector3.Zero, Vector3.Up);

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


effect.CurrentTechnique = effect.Techniques["Phong"];

effect.Parameters["World"].SetValue(world);

effect.Parameters["View"].SetValue(view);

effect.Parameters["Projection"].SetValue(proj);

effect.Parameters["Eye"].SetValue(new Vector3(0, 1, 4));

effect.Parameters["LightMask"].SetValue(lightMask);


teapot.Draw(effect);
world = Matrix.CreateRotationY(-(float)gameTime.TotalGameTime.TotalSeconds) * Matrix.CreateTranslation(1.0f, 0, 0);

effect.CurrentTechnique = effect.Techniques["NPR"];

effect.Parameters["World"].SetValue(world);

effect.Parameters["View"].SetValue(view);

effect.Parameters["Projection"].SetValue(proj);

effect.Parameters["Eye"].SetValue(new Vector3(0, 1, 4));

effect.Parameters["LightMask"].SetValue(lightMask);
teapot.Draw(effect);

base.Draw(gameTime);

}

Шейдер (только новая техника):



VertexShaderOutput VertexShaderFunction(VertexShaderInput input)

{

VertexShaderOutput output;


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

float4 viewPosition = mul(worldPosition, View);

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

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

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

float3 eyeDirection = normalize(Eye - worldPosition);

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

pos.x = Diffuse;

float4 dColor = tex2D(LigthMaskSampler, pos) * DiffuseColor;

return Color + Ambient + dColor + sColor ;

}
technique NPR

{

pass Pass1



{

// TODO: set renderstates here.


VertexShader = compile vs_1_1 VertexShaderFunction();

PixelShader = compile ps_2_0 PixelShaderFunction();

}

}



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

Для эксперимента можно изменить параметры шейдера, например, таким образом.

float kd = 0.9;

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

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

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

float3 eyeDirection = normalize(Eye - worldPosition);

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

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


float2 pos= float2(0,0);

pos.x = Diffuse;

float4 dColor = tex2D(LigthMaskSampler, pos) * DiffuseColor;

pos.x = Specular;

float4 sColor = tex2D(LigthMaskSampler, pos) * SpecularColor;
return Color + Ambient + dColor + sColor ;

}

Полный исходный код:

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 NPR_1

{

///



/// This is the main type for your game

///

public class Game1 : Microsoft.Xna.Framework.Game

{

GraphicsDeviceManager graphics;



SpriteBatch spriteBatch;

TeapotPrimitive teapot;

Effect effect;

Texture2D lightMask;

public Game1()

{

graphics = new GraphicsDeviceManager(this);



Content.RootDirectory = "Content";

}
///

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

///

protected override void Initialize()

{

// TODO: Add your initialization logic here



teapot = new TeapotPrimitive(GraphicsDevice);

base.Initialize();

}
///

/// LoadContent will be called once per game and is the place to load

/// all of your content.

///

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("light");

lightMask = Content.Load("lightMask");

}
///

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

/// all content.

///

protected override void UnloadContent()

{

// TODO: Unload any non ContentManager content here



}
///

/// Allows the game to run logic such as updating the world,

/// checking for collisions, gathering input, and playing audio.

///

///
Provides a snapshot of timing values.

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

}
///

/// This is called when the game should draw itself.

///

///
Provides a snapshot of timing values.

protected override void Draw(GameTime gameTime)

{

GraphicsDevice.Clear(Color.Black);


// TODO: Add your drawing code here

Matrix world = Matrix.CreateRotationY((float)gameTime.TotalGameTime.TotalSeconds) * Matrix.CreateTranslation(-1.0f, 0, 0);

Matrix view = Matrix.CreateLookAt(new Vector3(0, 1, 4), Vector3.Zero, Vector3.Up);

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


effect.CurrentTechnique = effect.Techniques["Phong"];

effect.Parameters["World"].SetValue(world);

effect.Parameters["View"].SetValue(view);

effect.Parameters["Projection"].SetValue(proj);

effect.Parameters["Eye"].SetValue(new Vector3(0, 1, 4));

effect.Parameters["LightMask"].SetValue(lightMask);


teapot.Draw(effect);
world = Matrix.CreateRotationY(-(float)gameTime.TotalGameTime.TotalSeconds) * Matrix.CreateTranslation(1.0f, 0, 0);

effect.CurrentTechnique = effect.Techniques["NPR"];

effect.Parameters["World"].SetValue(world);

effect.Parameters["View"].SetValue(view);

effect.Parameters["Projection"].SetValue(proj);

effect.Parameters["Eye"].SetValue(new Vector3(0, 1, 4));

effect.Parameters["LightMask"].SetValue(lightMask);
teapot.Draw(effect);

base.Draw(gameTime);

}

}

}



Шейдер:

float4x4 World;

float4x4 View;

float4x4 Projection;


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

float ka = 0;


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

float kd = 0.7;


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

float ks = 1;

float SpecularPower = 8;
float3 LightPosition = float3(0,0.5,1);

float3 Eye;


texture LightMask;

sampler LigthMaskSampler=sampler_state

{

texture = ;



};
float4 Color = float4(0,0,0.3,1);
// 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 VertexShaderFunctionPhong(VertexShaderInput input)

{

VertexShaderOutput output;


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

float4 viewPosition = mul(worldPosition, View);

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

output.Position = mul(viewPosition, Projection);

output.WorldPosition = worldPosition;

output.Normal = worldNormal;

// TODO: add your vertex shader code here.
return output;

}
float4 PixelShaderFunctionPhong(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 Color + Ambient + Diffuse + Specular;

}

VertexShaderOutput VertexShaderFunction(VertexShaderInput input)



{

VertexShaderOutput output;


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

float4 viewPosition = mul(worldPosition, View);

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

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

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

float3 eyeDirection = normalize(Eye - worldPosition);

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

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


float2 pos= float2(0,0);

pos.x = Diffuse;

float4 dColor = tex2D(LigthMaskSampler, pos) * DiffuseColor;

pos.x = Specular;

float4 sColor = tex2D(LigthMaskSampler, pos) * SpecularColor;
// для экспериметов можно что-нибудь раскомментировать

//dColor = 0;

//Ambient = 0;

//sColor = 0;

return Color + Ambient + dColor + sColor ;

}
technique Phong

{

pass Pass1



{

// TODO: set renderstates here.


VertexShader = compile vs_1_1 VertexShaderFunctionPhong();

PixelShader = compile ps_2_0 PixelShaderFunctionPhong();

}

}

technique NPR



{

pass Pass1

{

// TODO: set renderstates here.


VertexShader = compile vs_1_1 VertexShaderFunction();

PixelShader = compile ps_2_0 PixelShaderFunction();



}

}


Иван Андреев

В прошлый я собирался каким-то образом использовать альфа компоненту цвета, возвращаемого вершинным шейдером

115.66kb.

17 12 2014
1 стр.


Иван Андреев

Нам осталось только каким-то образом объединить эти два изображения. Напишем для этого еще один шейдер постобработки

127.28kb.

17 12 2014
1 стр.


Иван Андреев

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

150.6kb.

17 12 2014
1 стр.


Иван Андреев

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

129.71kb.

17 12 2014
1 стр.


А- аксёнов Иван Романович, 1914 г р., сержант, в июле 1943 г погиб на фронте. Алексеев Александр Родионович

Андреев Василий Григорьевич, 1900 г р., рядовой, 17 августа 1943 г погиб в бою, захоронен: д. В. Сорочки, Орловская область

285.52kb.

16 12 2014
1 стр.


-

Парк. По аллее навстречу друг другу идут Андреев и Коротков. Оба одеты по-осеннему, у Андреева через плечо сумка для ноутбука. Коротков выглядит жизнерадостным, Андреев бледным и у

546.43kb.

12 10 2014
4 стр.


Повесть о том, как поссорился Иван Иванович с Иваном Никифоровичем Глава I иван Иванович и Иван Никифорович

Описать нельзя: бархат! серебро! огонь! Господи Боже мой! Николай Чудотворец, угодник Божий! отчего же у меня нет такой бекеши! Он сшил ее тогда еще, когда Агафия Федосеевна не езд

572.52kb.

10 09 2014
4 стр.


Иван Михайлов Радко: Аз съм българин от Македония

Непубликувано интервю на легендарния лидер на вмро иван Михайлов, направено преди 11 години в Рим от скопския журналист Борис Вишински

161.54kb.

02 10 2014
1 стр.