Elemar DEV

Negócios, tecnologia e desenvolvimento

Vamos aprender XNA? – Parte 11 – HLSL e Point Light Effect

Olá pessoal, como estamos?!

HLSL é bacana, embora tenha tornado essa série um pouco mais difícil de acompanhar. Isso ocorre devido ao fato de que estamos programando em um nível um pouco mais baixo. Estamos abrindo mão de algumas facilidades para atingir resultados superiores. No caso dessa série, isso significa fazer jogos com gráficos mais atraentes.

Você pode acompanhar essa série desde o início. Ou ainda, pode ver o que já foi escrito sobre HLSL.

Até aqui, mostrei como trabalhar com luzes direcionais, que são interessantes para representar luzes como a do sol, por exemplo. Agora, começo a mostrar como representar luzes provenientes de uma área específica. Hoje, vamos falar sobre Point Light Effect.image

Uma vista aérea previlegiada

image

O código-fonte está disponível em https://github.com/ElemarJR/VamosAprenderXNA

O que queremos dizer com Point Light?

Point light é uma luz que emana para todas as direções em torno de sí (formato esférico), como uma lâmpada, e perde intencidade conforme a distância aumenta. Considere:

 

image

image

Todo o código-fonte está disponível em https://github.com/ElemarJR/VamosAprenderXNA

A matemática para Point Light

Uma forma simples de pensar a implementação point light seria dividir a distância entre a luz e o objeto pela distância máxima que desejamos atingir. Depois, inverter o resultado (subtraindo de 1) e multiplicar o Lambertian Lighting por este.

Entretanto, no mundo real, o comportamento seria um pouco difente. Observe:

image

Ou seja, há atenuação da luz ocorre com menor intensidade próximo a fonte, mas, vai aumentando gradativamente a medida que a distância aumenta. A fórmula pode ser melhor descrita por:

Intensidade = 1 – (d / a)f

Onde:

  • d é a distância entre o vértice e a fonte de luz;
  • a é a distância em que a luz deve parar de afetar o ambiente;
  • f é a potencialização do fall-off que determina o “formato” da curva exposta no gráfico.

Parâmetros para PointLightEffect

A implementação do effect para point light é relativamente simples (dado toda a fundamentação que já desenvolvemos no post anterior). Comecemos pela definição dos parâmetros:

float4x4 World;
float4x4 View;
float4x4 Projection;

float3 AmbientLightColor = float3(.05, .05, .05);
float3 DiffuseColor = float3(.85, .85, .85);

float3 LightPosition = float3(0, 1500, 1500);
float3 LightColor = float3(1, 1, 1);
float LightAttenuation = 3000;

texture BasicTexture;

sampler BasicTextureSampler = sampler_state { 
	texture = <BasicTexture>; 
};

float SpecularPower = 32;
float3 SpecularColor = float3(1,1,1);
float3 CameraPosition;
bool SpecularEnabled = true;

bool TextureEnabled = true;

A novidade, com relação aos effects que desenvolvemos até aqui, fica por conta de LightPosition, LightColor e LightAttenuation. Penso que os significados estejam claros.

Duas considerações são necessárias:

  1. Optei por utilizar esses valores default por eles atenderem adequadamente a minha cena. Para utilização em outras cenas, esse effect poderia ser “parametrizado” na inicialização;
  2. Estou considerando que haverá apenas uma fonte de luz no meu projeto. Em posts futuros mostro como trabalhar com várias fontes de luz.

Consideremos, por exemplo, a modificação da cor da luz para vemelho.

var pointLightEffect = Content.Load<Effect>("PointLightEffect");
pointLightEffect.Parameters["LightColor"].SetValue(new Vector3(1,0,0));

Vejamos o resultado

image

Se fosse amarelo:

image

Estruturas para PointLightEffect

As estruturas de dados para PointLightEffect são bem simples. Observe:

struct VertexShaderInput
{
	float4 Position : POSITION0;
	float2 UV : TEXCOORD0;
	float3 Normal : NORMAL0;
};

struct VertexShaderOutput
{
	float4 Position : POSITION0;
	float2 UV : TEXCOORD0;
	float3 Normal : TEXCOORD1;
	float4 WorldPosition : TEXCOORD2;
	float3 ViewDirection : TEXCOORD3;
};

O elemento novo, comparado ao Effect desenvolvido no post anterior, é WorldPosition. Essa informação será “calculada” no Vertex Shader e utilizada no Pixel Shader.

Vertex Shader para PointLightEffect

O pixel shader para esse effect também é bem semelhante ao desenvolvido no final do post anterior. Observe:

VertexShaderOutput VertexShaderFunction(VertexShaderInput input)
{
	VertexShaderOutput output;

	float4 worldPosition = mul(input.Position, World);
	float4 viewPosition = mul(worldPosition, View);
	output.Position = mul(viewPosition, Projection);

	output.WorldPosition = worldPosition;

	output.UV = input.UV;

	output.Normal = mul(input.Normal, World);
	output.ViewDirection = worldPosition - CameraPosition;
	return output;
}

Como havia informado na explicação para as estruturas, a novidade é a “passagem a diante” da informação relacionada a posição real do vértice na matriz World.

Pixel Shader para PointLightEffect

Por fim, mas de forma alguma menos importante, temos o nosso PixelShader. Observe:

float4 PixelShaderFunction(VertexShaderOutput input) : COLOR0
{
	float3 color = DiffuseColor;

	if (TextureEnabled)
		color *= tex2D(BasicTextureSampler, input.UV).rgb;
	
	float3 lighting = float3(0, 0, 0);
	
	lighting += AmbientLightColor;
	
	float3 direction = normalize(LightPosition - input.WorldPosition);
	float diffuse = saturate(dot(normalize(input.Normal), direction));
	
	float d = distance(LightPosition, input.WorldPosition);
	float att = 1 - pow(clamp(d / LightAttenuation, 0, 1), 2); 

	lighting += diffuse * att * LightColor;

	if (SpecularEnabled)
	{
		float3 refl = reflect(direction, normalize(input.Normal));
		float3 view = normalize(input.ViewDirection);
		lighting += pow(saturate(dot(refl, view)), SpecularPower) * SpecularColor;
	}

	return float4(color * lighting, 1);
}

Como você pode perceber, basicamente, calculei a luz usando a equação de Lambert e depois apliquei a atenuação conforme equação descrita no início desse post.

Considere que esse Effect deixa o cálculo do brilho specular (Phong) como um opcional. Na minha implementação, desliguei intencionalmente o cálculo de Specular para o piso.

 

Por hoje, era isso.

Smiley piscando

Um comentário em “Vamos aprender XNA? – Parte 11 – HLSL e Point Light Effect

  1. Gilberto
    17/05/2013

    Caro Elemar,
    Seu curso de XNA é muito legal, porém eu queria saber como fazer para ter dois pontos de luz na mesma cena, pois como aplica o SetEffect por objeto. Aparecer no ground(piso) duas fontes de luz.
    Grato

Deixe um comentário

Informação

Publicado às 26/07/2011 por em Sem categoria e marcado , , .

Estatísticas

  • 921.697 hits