5-Light Phong Shader Material

You are an experienced programmer and have a problem with the engine, shaders, or advanced effects? Here you'll get answers.
No questions about C++ programming or topics which are answered in the tutorials!
Post Reply
slavik262
Posts: 753
Joined: Sun Nov 22, 2009 9:25 pm
Location: Wisconsin, USA

5-Light Phong Shader Material

Post by slavik262 »

I noticed that while Irrlicht supports normal and parallax mapping, it doesn't just have a standard per-pixel lighting material. I'm sure you could hack the normal and or parallax materials to get the intended effect, but since I'm learning shader stuff, I decided to create a Phong shader material type. It supports up to 5 lights. :D

Image

Right now it just does point lights and there's no attenuation, but I plan on adding attenuation and support for all the different light types. What do you guys think of it?

I'm having one issue though. Sometimes when moving the camera (it's just an FPS camera), the teapot goes black for a single frame. Does anyone with shader experience know why this might occur? My assumption is that it has to do with the engine reordering the lights, but I don't know why that would make it go black for a second, and the lights are all fixed. Here are my callbacks:

EDIT: I've confirmed that the material drops to black for a second is when the lights reorder due to camera position change, but why would this affect the shader?

Code: Select all

void DirectionalPhong::OnSetConstants(IMaterialRendererServices *services, s32 userData)
{
	worldViewProjection = vd->getTransform(video::ETS_PROJECTION);
	worldViewProjection *= vd->getTransform(video::ETS_VIEW);
	worldViewProjection *= vd->getTransform(video::ETS_WORLD);
	services->setVertexShaderConstant("worldViewProjection", worldViewProjection.pointer(), 16);

	world = vd->getTransform(video::ETS_WORLD);
	services->setVertexShaderConstant("world", world.pointer(), 16);
	
	world.getTransposed(worldTranspose);

	worldTranspose.getInverse(worldInverseTranspose);
	services->setVertexShaderConstant("worldInverseTranspose", worldInverseTranspose.pointer(), 16);

	vd->getTransform(video::ETS_VIEW).getInverse(viewInverse);
	services->setVertexShaderConstant("viewInverse", viewInverse.pointer(), 16);

	s32 numLights = vd->getDynamicLightCount();
	
	for(s32 c = 0; c < 5; c++)
	{
		numChar = '0' + c;
		ltBuff[10] = numChar;
		if(c >= numLights)
		{
			services->setPixelShaderConstant(ltBuff, (f32*)&kNoLight, 1);
			continue;
		}

		light = vd->getDynamicLight(c);

		switch(light.Type)
		{
		case(video::ELT_POINT):
			lightType = 1;
			break;
			
		case(video::ELT_DIRECTIONAL):
			lightType = 2;
			break;

		case(video::ELT_SPOT):
			lightType = 3;
		}

		services->setPixelShaderConstant(ltBuff, (f32*)&lightType, 1);

		arr[0] = light.Position.X;
		arr[1] = light.Position.Y;
		arr[2] = light.Position.Z;
		lpBuff[14] = numChar;
		services->setVertexShaderConstant(lpBuff, arr, 3);

		arr[0] = light.AmbientColor.r;
		arr[1] = light.AmbientColor.g;
		arr[2] = light.AmbientColor.b;
		acBuff[13] = numChar;
		services->setPixelShaderConstant(acBuff, arr, 3);

		arr[0] = light.DiffuseColor.r;
		arr[1] = light.DiffuseColor.g;
		arr[2] = light.DiffuseColor.b;
		dcBuff[13] = numChar;
		services->setPixelShaderConstant(dcBuff, arr, 3);

		arr[0] = light.SpecularColor.r;
		arr[1] = light.SpecularColor.g;
		arr[2] = light.SpecularColor.b;
		scBuff[14] = numChar;
		services->setPixelShaderConstant(scBuff, arr, 3);
	}

	services->setPixelShaderConstant("specularAmount",
		&specularAmount, 1);

	services->setPixelShaderConstant("specularExponent",
		&specularExponent, 1);
}

void DirectionalPhong::OnSetMaterial(const SMaterial& mat)
{
	specularAmount = mat.Shininess;
	specularExponent = mat.MaterialTypeParam;
}
And the shader itself:

Code: Select all

//Transform vertices to world space
float4x4 world;
//Transform normals to world space
float4x4 worldInverseTranspose;
//transform view coordinates to world space
float4x4 viewInverse;
//Transform coordinates to screen space
float4x4 worldViewProjection;

struct VertexInput
{
	float3 position: POSITION;
	float4 normal : NORMAL;
	float4 uv : TEXCOORD0;
	float4 color : COLOR0;
};

struct VertexOutput
{
	float4 screenPos : POSITION;
	float2 uv : TEXCOORD0;
	float3 worldNormal : TEXCOORD1;
	float3 worldView : TEXCOORD2;
	float3 lightVector[5] : TEXCOORD3;
	float4 color : COLOR0;
};

float3 lightPosition[5];

VertexOutput ColorPhongVS(VertexInput IN)
{
	VertexOutput OUT = (VertexOutput)0;
	OUT.worldNormal = mul(IN.normal, worldInverseTranspose).xyz;

	float4 worldPosition = mul(float4(IN.position.xyz, 1), world);	

	int c;
	for(c = 0; c < 5; c++)
	{
		//Even if there aren't 5 valid lights, we'll just ignore the invalid ones
		//In the pixel shader
		OUT.lightVector[c] = lightPosition[c] - worldPosition.xyz;
	}
		
	OUT.worldView = normalize(viewInverse[3].xyz - worldPosition.xyz);
	OUT.screenPos = mul(float4(IN.position, 1), worldViewProjection);
	OUT.color = IN.color;
	OUT.uv = IN.uv.xy;
	
	return OUT;
}


float3 ambientColor[5];
float3 diffuseColor[5];
float3 specularColor[5];
//Types:  Off (0), point (1), directional (2), spot(3)
int lightType[5];
float specularAmount;
float specularExponent;

float4 ColorPhongPS(VertexOutput IN) : COLOR
{
	float3 lightVec;
	float3 view = normalize(IN.worldView);
	float3 norm = normalize(IN.worldNormal);
	float3 halfAngle;
	float4 litResult;
	float3 diffuseContrib;
	float3 specularContrib;
	float4 matDiffuse = IN.color;
	float4 OUT = float4(0, 0, 0, 1);

	int c;
	for(c = 0; c < 5; c++)
	{
		if(lightType[c] > 0)
		{
			lightVec = normalize(IN.lightVector[c]);
			halfAngle = normalize(view + lightVec);
			litResult = lit(dot(lightVec, norm), 
				dot(halfAngle, norm), specularExponent);
			diffuseContrib = litResult.y * diffuseColor[c];
			specularContrib = litResult.y * litResult.z * 
				specularAmount * specularColor[c];
			OUT += float4(specularContrib +
				(matDiffuse * (diffuseContrib + ambientColor[c])), 0);
		}
	}
	OUT.a = matDiffuse.a;
	OUT = saturate(OUT);
	return OUT;
}

Any explanation as to why this could be happening would be greatly appreciated.
slavik262
Posts: 753
Joined: Sun Nov 22, 2009 9:25 pm
Location: Wisconsin, USA

Post by slavik262 »

Upon testing on another computer (with integrated graphics as opposed to my GTX260), the teapot is black the entire time. I'm really confused now... the console doesn't give any shader compilation errors. Does anyone have any idea why this is happening?
ent1ty
Competition winner
Posts: 1106
Joined: Sun Nov 08, 2009 11:09 am

Post by ent1ty »

I'm not sure, but maybe integrated graphics does not support shaders very well? I was not able to run XEffects on integrated graphic card, so that's why i think so.
irrRenderer 1.0
Height2Normal v. 2.1 - convert height maps to normal maps

Step back! I have a void pointer, and I'm not afraid to use it!
slavik262
Posts: 753
Joined: Sun Nov 22, 2009 9:25 pm
Location: Wisconsin, USA

Post by slavik262 »

It's funny that you should mention that. I just tested XEffects on this machine to check that exact theory and it works fine - slowly, but fine.
Viking86
Posts: 24
Joined: Tue Apr 20, 2010 2:29 pm

Post by Viking86 »

It may be similar to the problem i had recently, I get shader linking errors on my new pc, but on my laptop with an old radeon the errors are not there and it runs. In my case the reason was an old opengl texture function, so it may be a driver issue with you as well.
slavik262
Posts: 753
Joined: Sun Nov 22, 2009 9:25 pm
Location: Wisconsin, USA

Post by slavik262 »

The issues with another PC aside, why would the shader flicker black when the lights reorder?
BlindSide
Admin
Posts: 2821
Joined: Thu Dec 08, 2005 9:09 am
Location: NZ!

Post by BlindSide »

For the light re-ordering issue, you could look at the ILightManager example in the SDK, you may be able to make a better ordering scheme.

What video card is the Intel integrated graphics? Possibly you are missing a D3D9 dll or similar. If you are using OpenGL then I am not surprised, Intel have very buggy OpenGL support.
ShadowMapping for Irrlicht!: Get it here
Need help? Come on the IRC!: #irrlicht on irc://irc.freenode.net
Post Reply