Spotlights

Spotlights

一个spotlight是一个directional light和point light的组合。在world space中有一个坐标位置,但是光线只能照射到某个特定方向。另外,spotlight与point light一样,也是根据距离而衰减,但spotlight还会围绕光线的原始方向而衰减。可以把spotlight看作一个虚拟的手电筒,形成一个focus beam(聚焦光束)随着光线距离光源中心越远而衰减。
模拟一个spotlight,需要一个坐标位置,方向,辐射半径,颜色和强度,以及用于描述focus beam辐射范围的inner和outer角度的浮点值。图7.4描述了这些元素。
Spotlights_第1张图片
图7.4 An illustration of a spotlight.
列表7.3列出了一种简单的spotlight effect的代码。与之前一样,把该代码拷贝到NVIDIA FX Composer中。接下来将详细讲解这些代码。
列表7.3 Spotlight.fx
#include "include\\Common.fxh"

/************* Resources *************/

cbuffer CBufferPerFrame
{
	float4 AmbientColor : AMBIENT <
		string UIName =  "Ambient Light";
		string UIWidget = "Color";
	> = {1.0f, 1.0f, 1.0f, 0.0f};

	float4 LightColor : COLOR <
		string Object = "LightColor0";
		string UIName =  "Spot Light Color";
		string UIWidget = "Color";
	> = {1.0f, 1.0f, 1.0f, 1.0f};

	float3 LightPosition : POSITION <
		string Object = "SpotLightPosition0";
		string UIName =  "Spot Light Position";
		string Space = "World";
	> = {0.0f, 0.0f, 0.0f};

	float3 LightLookAt : DIRECTION <
		string Object = "SpotLightDirection0";
		string UIName =  "Spot Light Direction";
		string Space = "World";
	> = {0.0f, 0.0f, -1.0f};

	float LightRadius <
		string UIName =  "Spot Light Radius";
		string UIWidget = "slider";
		float UIMin = 0.0;
		float UIMax = 100.0;
		float UIStep = 1.0;
	> = {10.0f};

	float SpotLightInnerAngle <
		string UIName =  "Spot Light Inner Angle";
		string UIWidget = "slider";
		float UIMin = 0.5;
		float UIMax = 1.0;
		float UIStep = 0.01;
	> = {0.75f};

	float SpotLightOuterAngle <
		string UIName =  "Spot Light Outer Angle";
		string UIWidget = "slider";
		float UIMin = 0.0;
		float UIMax = 0.5;
		float UIStep = 0.01;
	> = {0.25f};
	
	float3 CameraPosition : CAMERAPOSITION < string UIWidget="None"; >;
}

cbuffer CBufferPerObject
{
	float4x4 WorldViewProjection : WORLDVIEWPROJECTION < string UIWidget="None"; >;
	float4x4 World : WORLD < string UIWidget="None"; >;
	
	float4 SpecularColor : SPECULAR <
		string UIName =  "Specular Color";
		string UIWidget = "Color";
	> = {1.0f, 1.0f, 1.0f, 1.0f};

	float SpecularPower : SPECULARPOWER <
		string UIName =  "Specular Power";
		string UIWidget = "slider";
		float UIMin = 1.0;
		float UIMax = 255.0;
		float UIStep = 1.0;
	> = {25.0f};
}

Texture2D ColorTexture <
	string ResourceName = "default_color.dds";
	string UIName =  "Color Texture";
	string ResourceType = "2D";
>;

SamplerState ColorSampler
{
	Filter = MIN_MAG_MIP_LINEAR;
	AddressU = WRAP;
	AddressV = WRAP;
};

RasterizerState DisableCulling
{
	CullMode = NONE;
};

/************* Data Structures *************/

struct VS_INPUT
{
	float4 ObjectPosition : POSITION;
	float2 TextureCoordinate : TEXCOORD;
	float3 Normal : NORMAL;
};

struct VS_OUTPUT
{
	float4 Position : SV_Position;
	float3 Normal : NORMAL;
	float2 TextureCoordinate : TEXCOORD0;
	float3 WorldPosition : TEXCOORD1;
	float Attenuation : TEXCOORD2;
	float3 LightLookAt : TEXCOORD3;	
};

/************* Vertex Shader *************/

VS_OUTPUT vertex_shader(VS_INPUT IN)
{
	VS_OUTPUT OUT = (VS_OUTPUT)0;
	
	OUT.Position = mul(IN.ObjectPosition, WorldViewProjection);
	OUT.WorldPosition = mul(IN.ObjectPosition, World).xyz;
	OUT.TextureCoordinate = get_corrected_texture_coordinate(IN.TextureCoordinate); 
	OUT.Normal = normalize(mul(float4(IN.Normal, 0), World).xyz); 
		
	float3 lightDirection = LightPosition - OUT.WorldPosition;	
	OUT.Attenuation = saturate(1.0f - length(lightDirection) / LightRadius);	
	
	OUT.LightLookAt = -LightLookAt;
	
	return OUT;
}

/************* Pixel Shader *************/

float4 pixel_shader(VS_OUTPUT IN) : SV_Target
{
	float4 OUT = (float4)0;
	
	float3 lightDirection = normalize(LightPosition - IN.WorldPosition);   
	float3 viewDirection = normalize(CameraPosition - IN.WorldPosition);
	
	float3 normal = normalize(IN.Normal);
	float n_dot_l = dot(normal, lightDirection);	
	float3 halfVector = normalize(lightDirection + viewDirection);
	float n_dot_h = dot(normal, halfVector);
	float3 lightLookAt = normalize(IN.LightLookAt);

	float4 color = ColorTexture.Sample(ColorSampler, IN.TextureCoordinate);	
	float4 lightCoefficients = lit(n_dot_l, n_dot_h, SpecularPower);
	
	float3 ambient = get_vector_color_contribution(AmbientColor, color.rgb);
	float3 diffuse = get_vector_color_contribution(LightColor, lightCoefficients.y * color.rgb) * IN.Attenuation;
	float3 specular = get_scalar_color_contribution(SpecularColor, min(lightCoefficients.z, color.w)) * IN.Attenuation;
	
	float spotFactor = 0.0f;
	float lightAngle = dot(lightLookAt, lightDirection);	
	if (lightAngle > 0.0f)
	{
		spotFactor = smoothstep(SpotLightOuterAngle, SpotLightInnerAngle, lightAngle);
	}
	
	OUT.rgb = ambient + (spotFactor * (diffuse + specular));
	OUT.a = 1.0f;

	return OUT;
}

/************* Techniques *************/

technique10 main10
{
	pass p0
	{
		SetVertexShader(CompileShader(vs_4_0, vertex_shader()));
		SetGeometryShader(NULL);
		SetPixelShader(CompileShader(ps_4_0, pixel_shader()));
			
		SetRasterizerState(DisableCulling);
	}
}

Spotlight Preamble

CBufferPerFrame模块中包含了LightColor,LightPosition以及LightRadius,从这些变量的名称就可以知道它们的作用。其中LightLookAt定义了focus beam的方向。之所以使用这个变量名,是为了避免与lightDirection产生混淆,而lightDirection表示光源相对于object表面的方向。
CBufferPerFrame中还包含了SpotLightOuterAngle和SpotLightInnerAngle变量,并通过它们的annotations把这两个变量值分别限制到范围[0.0, 0.5]和[0.5 1.0]。在pixel shader,你将会看到这些值表示spotlight系数的最小值和最大值,spotlight系数由focus beam和lightDirection的夹角确定。
Shader输入中唯一的修改是在VS_OUTPUT结构体中新增了一个LightLookAt向量。这是一个用于值传递的变量,存储了全局的shader常量LightLookAt的取反值。与directional lights一样,也要对全局的LightLookAt取反,因为NVIDIA FX Composer传递到shader中的light方向是以光源为起点,但真正需要的是以表面为起点。如果是自己编写CPU程序,可以传递正确的light方向而不用取反操作。

Spotlight Vertex and Pixel Shader

Spotlight的vertex shader与point light effect第二个版本(即PointLightModifications.fx)基本相同,除了新增的对LightLookAt取反的语句。同样,pixel shader与point light的pixel shader也大部分相同,除了新增的计算spotFactor的代码语句。实际上,就是使用point light的方法来计算diffuse和specular。Spotlight的look at 向量LightLookAt以及该向量与lightDirection的夹角确定这些光照模型的最终颜色值。
在pixel shader中,lightAngle是lightLookAt和lightDirection两个向量的dot-product值。如果lightAngle大于0,spotFactor介于spotlight的inner和outer angle值之间。其中调用了HLSL的内置函数smoothstep(),该函数使用lightAngle作为SpotLightOuterAngle和SpotLightInnerAngle渐变的插值。把SpotLightInnerAngle和SpotLightOuterAngle值限制到范围[0.5, 1.0]和[0.0, 0.5],现在就有用了。如果指定outer angle为0.0,inner angle为1.0,随着表面与focus beam距离越远,spotFactor值就从1.0逐渐减小到0.0。
在生成最终的pixel cololr时,使用spotFactor值来调制diffuse和specular。而环境光(ambient term)不会受到任何新增光源的影响。

Spotlight Output


Spotlight Output
图7.5显示了spotlight effect的输出结果。在这个例子中,使用了一个带checkerborad纹理的平面。其中禁用了ambient和specular(把它们的强度设为0.0),并把spotlight设为纯白色,full-intensity(强度值为1.0)。左图中,spotlight的inner和outer angle分别为0.0和1.0。右图中,inner和outer angle都为0.5。注意这两种不同设置下的光照区域边界。
Spotlights_第2张图片
图7.5 Spotlight.fx applied to a plane with a checkerboard texture. To the left, the outer-to-inner range is [0.0, 1.0]; to the right, both values are set to 0.5.


你可能感兴趣的:(Spotlights)