Specular light color (float vs float3)

Started by
21 comments, last by cozzie 10 years, 8 months ago

Here's a bit more, you can get a bit of optimizations by reusing your length calculation for the direction normalization.


float3 lightDir = (PointLightPos[i] - input.wPos);

// PER PIXEL ATTENUATION
float dist = length(lightDir);
lightDir = lightDir / (dist + 0.001); // small bias to avoid division by 0 - check to see if it's necessary for you.

Also, to avoid the following:


att *= att;

You can use the squared distance instead of a linear one, it might give you a better falloff without this instruction (and is cheaper then length).


// PER PIXEL ATTENUATION
		float dist = dot(lightDir, lightDir);
lightDir /= sqrt(dist) + 0.001; // small bias to avoid division by 0 - check to see if it's necessary for you.

		float att = saturate(1 - ((dist - PointLightFPRange[i]) / (PointLightRange[i] - PointLightFPRange[i])));

The sqrt that's needed might be a bit heavier but you're still not doing the full normalize code, you don't need the att*att and you get the right falloff. This is something you'd need to try and see if it works for you. The first tip however is definitely a win.

The last thing is that the shader compiler is smart, but sometimes it misses stuff. So it's best to make sure your code is layed out properly. So for example your diffuse could be this instead, which should guarantee the use of a MAD in the compiled shader rather than an ADD and MUL.


diffuseAcc = diffIntPoint * PointLightColInt[i] + diffuseAcc;

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

This should maybe help a bit. Splitting multiplications based on vector count can also help, so multiply everything that is a scalar first then multiply it by the vector one, that way you don't mix the vector/scalar math together too much and the GPU has a bit of an easier time dealing with it.

Advertisement

Cool, thanks.

I've put through the first change for the lightdir normalization, the attenuation I left unchanged, because it became to 'round'.
Below are 2 screenshots with the difference.

I have also made 1 float2 for pointlight range (x) and fp range (y).

Gonna put through the changes to all my effect variants now (with 'x'/'y' numbers of lights) and work on a nice demo.

Here's the new PS:


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

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


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

		if(any(MatSpec))
		{
			float3 lightdir = normalize(DirLightDir[i] - input.wPos);
			float3 h = normalize(lightdir + input.ViewDir);
			
			// 1 MAD
			specularAcc = pow(saturate(dot(h, normal)), 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(normal, lightDir) * att);			
		diffuseAcc = diffIntPoint * PointLightColInt[i] + diffuseAcc;	// 1 MAD

		// SPECULAR; USING BLINN HALF ANGLE

		if(any(MatSpec))
		{
			float3 h = normalize(lightDir + input.ViewDir);
			
			// 1 MAD
			specularAcc = pow(saturate(dot(h, normal)), MatSpecPower) * att * PointLightColInt[i] + specularAcc;
		}
	}

/**	FINAL PIXEL COLOR **/

	return float4(textureColor.rgb * (MatDiff * diffuseAcc + AmbientColInt) + (MatSpec * specularAcc + MatEmi), textureColor.a);
}

Attenuation with only the normalization optimized (keeping this one :)):

range_ok.jpg

Attenuation with also the other change done (sqrt, no att*att):

range_sqrt.jpg

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

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

Cool, with this new knowledge I've also managed to kick my SM2 shader up to 4 point lights instead of 3 :)

No specular yet, maybe something to dig into for the future. But honestly, people should be able to have hardware running SM3 smile.png


/*******************************************************/
/**	VERTEX SHADER PROGRAM					**/
/*******************************************************/
                    
VS_OUTPUT VS_function(VS_INPUT input)
{
	VS_OUTPUT Out = (VS_OUTPUT)0;

	float4 worldPosition = mul(input.Pos, World);
	Out.Pos = mul(worldPosition, ViewProj);

	Out.Normal = mul(input.Normal, (float3x3)World);	// VS input = already normalized
	Out.TexCoord = input.TexCoord;
	Out.wPos = worldPosition.xyz;

/** 	DIRECTIONAL LIGHTS - PER VERTEX (DIFFUSE) **/
	float3 diffuseDir = 0.0f;

	for(int i=0;i<MaxDirectionalLights;i++)
	{	
		diffuseDir	= saturate(DirLightColInt[i] * dot(Out.Normal,  DirLightDir[i]) + diffuseDir);
	}
	Out.DiffuseDir = diffuseDir;
	
	return Out;
}

/*******************************************************/
/**	PIXEL SHADER PROGRAM					**/
/*******************************************************/

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

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

	float3 diffuseAcc = input.DiffuseDir;

	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)));
			
		// DIFFUSE

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

/**	FINAL PIXEL COLOR **/

	return float4(textureColor.rgb * (MatDiff * diffuseAcc + AmbientColInt), textureColor.a);
}

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

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

This topic is closed to new replies.

Advertisement