Jump to content
  • Advertisement
Sign in to follow this  
db123

how to render multiple lights in the same area?

This topic is 2035 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

if there are multiple lights in one area, for example, three point lights, their lighting area are overlapped.

should i create two rendertarget, and render one light to the first buffer, and then add to second buffer?

like this:

create rendertarget 0

create rendertarget 1

    for each light in lights

     {

           render light to rendertarget 0,

           render rendertarget 0 to rendertarget 1, use blend add.

     }

render render target 1 to frame buffer.

 

    but when i implement with this method, the overlapped was very brightness, and looks not unreal.

 

    what is the corret method to render multiple lights?

 

 

i found a demo :

http://www.dhpoware.com/demos/d3d9MultiplePointLights.html

but i can't see he create any render target or set blend state, why this demo has correct result?rolleyes.gif

Share this post


Link to post
Share on other sites
Advertisement

Hi,

 

No need to use multiple render targets. Just render to one render target and use additive blending. Are you programming a deferred renderer?

 

create rendertarget 0

set additive blending

 

    for each light in lights

     {

           render light to rendertarget 0,

     }

 

Cheers!

 

[edit] the demo is probably calculating multiple lights inside each shader pass. That is typical for a forward renderer. 

Edited by kauna

Share this post


Link to post
Share on other sites
You can calculate the lighting per point light in either the vertex or pixel shader, and then 'add them together' when calculating the final pixel color (using you possible ambient light, texture, directional light etc.). If you do this you can send the light's position, color, range and intensity to the shader.

It's a good idea to do simple bounding sphere radius/ distance check between the mesh you want to render and the point lights, and make sure you only pass the point lights to the shader that actually affect the mesh (or renderable/submesh). This way if your meshes/renderables are not to extremely large, you can get away with 4 or 8 max point lights in the shader easily.

Share this post


Link to post
Share on other sites

Hi,

 

No need to use multiple render targets. Just render to one render target and use additive blending. Are you programming a deferred renderer?

 

create rendertarget 0

set additive blending

 

    for each light in lights

     {

           render light to rendertarget 0,

     }

 

Cheers!

 

[edit] the demo is probably calculating multiple lights inside each shader pass. That is typical for a forward renderer. 

 

thanks for your reply, but there is a error when i use your method, could give me another advice?

 

first, i want to render four point light use traditional lighting(not deferred), so i create a render target, and set blend operation with:ADD

but i find when i render one mesh,  i can see some pixel which should be discard by depth test.

please see this picture:

[attachment=20071:1.png]

i guess if it is that the back pixel is put to the render target first, and then blend with the front pixel.

 

this is the d3d device state:

[attachment=20072:2.png]

[attachment=20073:3.png]

 

if i disable blend operation, and render only one light, it was correct.

smile.png

Share this post


Link to post
Share on other sites

You can calculate the lighting per point light in either the vertex or pixel shader, and then 'add them together' when calculating the final pixel color (using you possible ambient light, texture, directional light etc.). If you do this you can send the light's position, color, range and intensity to the shader.

It's a good idea to do simple bounding sphere radius/ distance check between the mesh you want to render and the point lights, and make sure you only pass the point lights to the shader that actually affect the mesh (or renderable/submesh). This way if your meshes/renderables are not to extremely large, you can get away with 4 or 8 max point lights in the shader easily.

thanks for you reply, 

i have a new question, could you help me again?smile.png

Share this post


Link to post
Share on other sites

If you add the different point lights within your one shader (effect), you don't have to enable blending. The reason is that the light calculations are added up already within the shader, therefor you don't need to blend the different lights 'on to the mesh'.

Here's a part of my lighting shader which basically does this:

// globals in the effect

#define MaxPointLights 8

extern float3		PointLightPos[MaxPointLights];
extern float2		PointLightRange[MaxPointLights];	// x = range, y = FP range
extern float3		PointLightColInt[MaxPointLights];				

// directional and point lights, calculation in the pixel shader

float4 PS_function(VS_OUTPUT input): COLOR0
{
	float4 textureColor = tex2D(textureSampler, input.TexCoord);

	float3 normalMap = normalize(2.0f * tex2D(normalMapSampler, input.TexCoord).xyz - 1.0f);
	normalMap = mul(normalMap, input.WorldToTangent);

	float3 diffuseAcc = 0.0f;
	float3 specularAcc = 0.0f;

/** 	DIRECTIONAL LIGHTS - PER PIXEL (DIFFUSE & SPECULAR) **/
	for(int i=0;i<MaxDirectionalLights;i++)
	{	
		diffuseAcc	= saturate(DirLightColInt[i] * dot(normalMap,  DirLightDir[i]) + diffuseAcc); 

		if(any(diffuseAcc) && any(MatSpec))
		{
			float3 lightdir = normalize(DirLightDir[i] - input.wPos);
			float3 h = normalize(lightdir + input.ViewDir);
			
			specularAcc = pow(saturate(dot(h, normalMap)), MatSpecPower) * DirLightColInt[i] + specularAcc;
		}
	}

/** 	POINT LIGHTS - PER PIXEL (DIFFUSE & SPECULAR) **/

	for(int i=0;i<MaxPointLights;++i)
	{
		float3 lightDir = (PointLightPos[i] - input.wPos);

		// PER PIXEL ATTENUATION
		float dist = length(lightDir);
		lightDir = lightDir / (dist + 0.001);

		float att = saturate(1 - ((dist - PointLightRange[i].y) / (PointLightRange[i].x - PointLightRange[i].y)));
		att *= att;	// optional, not correct for full power range !?!?

		// DIFFUSE

		float diffIntPoint = saturate(dot(normalMap, lightDir) * att);			
		diffuseAcc = diffIntPoint * PointLightColInt[i] + diffuseAcc;

		// SPECULAR; USING BLINN HALF ANGLE

		if(any(diffuseAcc) && any(MatSpec))
		{
			float3 h = normalize(lightDir + input.ViewDir);

			specularAcc = pow(saturate(dot(h, normalMap)), MatSpecPower) * att * PointLightColInt[i] + specularAcc;
		}
	}


Share this post


Link to post
Share on other sites

Hi,

 

No need to use multiple render targets. Just render to one render target and use additive blending. Are you programming a deferred renderer?

 

create rendertarget 0

set additive blending

 

    for each light in lights

     {

           render light to rendertarget 0,

     }

 

Cheers!

 

[edit] the demo is probably calculating multiple lights inside each shader pass. That is typical for a forward renderer. 

 

hi, how to fix the blend error, if i just use one render target?unsure.png

Share this post


Link to post
Share on other sites

 

Hi,

 

No need to use multiple render targets. Just render to one render target and use additive blending. Are you programming a deferred renderer?

 

create rendertarget 0

set additive blending

 

    for each light in lights

     {

           render light to rendertarget 0,

     }

 

Cheers!

 

[edit] the demo is probably calculating multiple lights inside each shader pass. That is typical for a forward renderer. 

 

hi, how to fix the blend error, if i just use one render target?unsure.png

 

prez-depth./....

Share this post


Link to post
Share on other sites

Nothing looks wrong with your depth states, except which pass is this? If you are rendering with multiple passes we need to see multiple states, one for each pass.
If you are using z-prepass then your depth function should be testing for “equal” on following passes, for one. Why is your alpha operation “max”?


L. Spiro

Edited by L. Spiro

Share this post


Link to post
Share on other sites


 

If you add the different point lights within your one shader (effect), you don't have to enable blending. The reason is that the light calculations are added up already within the shader, therefor you don't need to blend the different lights 'on to the mesh'.

Here's a part of my lighting shader which basically does this:

// globals in the effect

#define MaxPointLights 8

extern float3		PointLightPos[MaxPointLights];
extern float2		PointLightRange[MaxPointLights];	// x = range, y = FP range
extern float3		PointLightColInt[MaxPointLights];				

// directional and point lights, calculation in the pixel shader

float4 PS_function(VS_OUTPUT input): COLOR0
{
	float4 textureColor = tex2D(textureSampler, input.TexCoord);

	float3 normalMap = normalize(2.0f * tex2D(normalMapSampler, input.TexCoord).xyz - 1.0f);
	normalMap = mul(normalMap, input.WorldToTangent);

	float3 diffuseAcc = 0.0f;
	float3 specularAcc = 0.0f;

/** 	DIRECTIONAL LIGHTS - PER PIXEL (DIFFUSE & SPECULAR) **/
	for(int i=0;i<MaxDirectionalLights;i++)
	{	
		diffuseAcc	= saturate(DirLightColInt[i] * dot(normalMap,  DirLightDir[i]) + diffuseAcc); 

		if(any(diffuseAcc) && any(MatSpec))
		{
			float3 lightdir = normalize(DirLightDir[i] - input.wPos);
			float3 h = normalize(lightdir + input.ViewDir);
			
			specularAcc = pow(saturate(dot(h, normalMap)), MatSpecPower) * DirLightColInt[i] + specularAcc;
		}
	}

/** 	POINT LIGHTS - PER PIXEL (DIFFUSE & SPECULAR) **/

	for(int i=0;i<MaxPointLights;++i)
	{
		float3 lightDir = (PointLightPos[i] - input.wPos);

		// PER PIXEL ATTENUATION
		float dist = length(lightDir);
		lightDir = lightDir / (dist + 0.001);

		float att = saturate(1 - ((dist - PointLightRange[i].y) / (PointLightRange[i].x - PointLightRange[i].y)));
		att *= att;	// optional, not correct for full power range !?!?

		// DIFFUSE

		float diffIntPoint = saturate(dot(normalMap, lightDir) * att);			
		diffuseAcc = diffIntPoint * PointLightColInt[i] + diffuseAcc;

		// SPECULAR; USING BLINN HALF ANGLE

		if(any(diffuseAcc) && any(MatSpec))
		{
			float3 h = normalize(lightDir + input.ViewDir);

			specularAcc = pow(saturate(dot(h, normalMap)), MatSpecPower) * att * PointLightColInt[i] + specularAcc;
		}
	}


 

This is probably off-topic...

 

In the past I have used a similar solution but right now I'm using permutations. With permutations you have one specialized PS for 1 point light, another for two, another for 3... All of them index everything directly, so the 3rd light won't use PointLightColInt, where i == 2. It will use PointLightColInt[2].  I guess the same effect could be achieved with a for loop ending in different constant upper bounds and unrolled loops, but just to be safe everything is explicitly indexed. The code is auto-generated anyway. This solution works well and performs well, but it is kind of cumbersome.

 

Has anybody any experience regarding the performance difference between the two approaches? Is it significant or is my approach suffering from premature optimization? I'm using pixel shader 3 under DX9.

Share this post


Link to post
Share on other sites
Sign in to follow this  

  • Advertisement
×

Important Information

By using GameDev.net, you agree to our community Guidelines, Terms of Use, and Privacy Policy.

GameDev.net is your game development community. Create an account for your GameDev Portfolio and participate in the largest developer community in the games industry.

Sign me up!