Multiple Spot Lights with HLSL

Started by
1 comment, last by ddamicoAVI 16 years, 2 months ago
I've been testing XNA/HLSL for the last few weeks. I was able to render a model I created along with some simple lighting effects. I want to add multiple spots lights to my scene. After reviewing the Materials And Lights tutorial from I saw how they added two light sources. I attempted to replicate their procedures and ran out of arithmetic instructions in my Pixel Shader. So what I tried is use two passes. However, after the scene renders it only shows the second spotlight. Is there a better way to do this? I posted my FX file below.

struct VS_SpotOut
{
	float4 position : POSITION;
	float2 texCoord : TEXCOORD0;
	float3 interPos : TEXCOORD1;
	float att : TEXCOORD2;
	float3 lightVec : TEXCOORD4;
	float3 lightDir : TEXCOORD6;
};

struct VS_SpotIn
{
	float4 position : POSITION0;
	float2 texCoord : TEXCOORD0;

};

float CalculateFallOff(float alpha)
{
	float result;
	
	float nom = cos(alpha) - cos(phi);
	float donom = cos(theta) - cos(phi);
	result = pow(nom/donom,1);
	
	return result;
};
VS_SpotOut VS_Spot(VS_SpotIn input, uniform int CurrentLight)
{
	VS_SpotOut output;
	float4x4 wvp = mul(mul(World, View), Projection);
	output.position = mul(input.position, wvp);
	output.interPos = mul(input.position, World);
	output.lightDir = mul(-lightDirection, World);
	output.texCoord = input.texCoord;

	output.lightVec = lightPosition[CurrentLight] - output.interPos.xyz;
	output.att = 1-(1/pow(length(output.lightVec),4));
		
	return output;
}

float4 PS_Spot(VS_SpotOut input) : COLOR
{
	float4 FinalColor = {0.0f,0.0f,0.0f,1.0f};
	
	float4 TextureColor = tex2D(ColoredTextureSampler,input.texCoord);
	
	float alpha;

	alpha = acos(dot(input.lightVec,input.lightDir)/(length(input.lightVec)*length(input.lightDir)));  
		
	if (alpha < phi && alpha > theta)
	{
		FinalColor =  input.att * (CalculateFallOff(alpha) * (TextureColor)) ;
	}
	else if (alpha < theta)
	{
		FinalColor =  input.att * (TextureColor) ;
	}
	
	FinalColor.a = 1.0;
	return FinalColor ;
}
technique SpotLight
{
	
    pass Pass0
    {
        VertexShader = compile vs_2_0 VS_Spot(0);
        PixelShader = compile ps_2_0 PS_Spot();
    }
    pass Pass1
    {
        VertexShader = compile vs_2_0 VS_Spot(1);
        PixelShader = compile ps_2_0 PS_Spot();
    }
    
}
 
Advertisement
You need to enable additive blending before rendering your second pass. Basically this means that the output of your second pass will be summed with the contents of the frame buffer, which should contain the results of your first pass. Unfortunately I have no idea how this is done in XNA, as I haven't so much as looked at the API. In Direct3D9 it's done by calling IDirect3DDevice9::SetRenderState 3 times, specifying (D3DRS_ALPHABLENDENABLE, TRUE), (D3DRS_SRCBLEND, D3DBLEND_ONE), and (D3DRS_DESTBLEND, D3DBLEND_ONE). I have no idea if there's anything at all equivalent to that in XNA, but I thought I'd share anyway.

Also you should keep in mind that you can increase the limit on your arithmetic instructions by using a higher pixel shader version. These include ps_2_a (Nvidia-optimized), ps_2_b (ATI X-series optimized), and ps_3_0. Of course you'll have to ensure the device supports these before you use your shader, and again I have no idea how that's done in XNA.
Thanks for the reply. I found how to do it in XNA and how to do it in HLSL both yield the same result. Both lights show up but the scene is somewhat transparent. Any ideas?

XNA: I put this code in my Draw method
graphics.GraphicsDevice.RenderState.AlphaBlendEnable = true;graphics.GraphicsDevice.RenderState.SourceBlend = Blend.One;graphics.GraphicsDevice.RenderState.DestinationBlend = Blend.One;


One Light:

Free Image Hosting at www.ImageShack.us

Two Lights:
Free Image Hosting at www.ImageShack.us

This topic is closed to new replies.

Advertisement