how to render multiple lights in the same area?

Started by
10 comments, last by cozzie 10 years, 1 month ago

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

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.

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.

Crealysm game & engine development: http://www.crealysm.com

Looking for a passionate, disciplined and structured producer? PM me

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

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

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


Crealysm game & engine development: http://www.crealysm.com

Looking for a passionate, disciplined and structured producer? PM me

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

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

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

I restore Nintendo 64 video-game OST’s into HD! https://www.youtube.com/channel/UCCtX_wedtZ5BoyQBXEhnVZw/playlists?view=1&sort=lad&flow=grid



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.

This topic is closed to new replies.

Advertisement