Bumpmapping - find appropriate ( best ) light or lights?

Started by
6 comments, last by Lopez 18 years, 4 months ago
Hi Recently I've started to carry on work on my engine, I've decided to add bumpmap support to my class working with Q3 Arena BSP map. I've had no experience with bumpmapping before, although I loved it. So somehow I've put it together and now - somehow - it is showing levels with bumpmapping, but problem I have is with lights. Particularly with finding best light for some face. What is best light? I thought,that it is closest one. No, it is not true. Then I thought that face's normal must face light directly and be closest one if posible. I've played around with few posibilities, but I am still in beginning. I cannot find best light source for bumpmapping. I should say, that I am looking for one light source, because I don't know how should I evaluate more lights for bumpmap purpose. I will show you my problem on few examples: In this one, the light is evaluated per whole face. I will find light closest to whole face, then I compute all vertices (bump shift - cubemap) to this perticular light. (notice lightchange on the floor between hall and corridor) Well, but what is problem - you can see on picture - that on border of two faces, the light is different. From corridor it is becoming dark, and from hall it is still light. So there is awful light jump. Code is about this:

void bspLevel::CalculateBumpVerticesLightShift ()
{
... some checks if light exist at all		

// go throught all faces and find closest light
// do bump map shift according to this light
	
Vertex lightVector;		// vector from vertices to light
Vertex stred;			// middle point in face
for (int i=0;i<m_nFaces;i++)
   {
   bspFace *f = &m_pFaces;
		
   // find middle point of face and make vector from it to light
   // take first and third for ease
   stred = m_pVertices[f->VertexIndex] + m_pVertices[f->VertexIndex + 2];
   stred /= 2;

   // find closest light and use face normal
   Vertex light = ((CLight *)lt.FindClosestLight ( stred,f->m_vNormal ))->m_vPos;
		
   for (int j=0;j<f->m_nVertices;j++)
	{
	int id = f->VertexIndex + j;
	lightVector = light - m_pVertices[id];

	m_pVertices[id].CubemapCoord.x = CMath::DotProduct (m_pVertices[id].STangent,  lightVector);
	m_pVertices[id].CubemapCoord.y = CMath::DotProduct (m_pVertices[id].TTangent,  lightVector);
	m_pVertices[id].CubemapCoord.z = CMath::DotProduct (f->m_vNormal, lightVector);			
	}
   }			
}

So .. I've decided to find closest light to every single vertex in face, it's solved something, but not all problems. See image below: You see the problem? Lights are not OK, because it makes weird effect on walls, somewhere there is too much light, for me it is random. Code was:

void bspLevel::CalculateBumpVerticesLightShift ()
{
... some checks if light exist at all		

// go throught all faces and find closest light
// do bump map shift according to this light
	
Vertex lightVector;		// vector from vertices to light
Vertex stred;			// middle point in face
for (int i=0;i<m_nFaces;i++)
   {
   bspFace *f = &m_pFaces;
		
		
   for (int j=0;j<f->m_nVertices;j++)
	{
	int id = f->VertexIndex + j;

	Vertex light = ((CLight *)lt.FindClosestLight (m_pVertices[id],m_pVertices[id].m_vNormal ))->m_vPos;

	lightVector = light - m_pVertices[id];
	m_pVertices[id].CubemapCoord.x = CMath::DotProduct (m_pVertices[id].STangent,  lightVector);
	m_pVertices[id].CubemapCoord.y = CMath::DotProduct (m_pVertices[id].TTangent,  lightVector);
	m_pVertices[id].CubemapCoord.z = CMath::DotProduct (f->m_vNormal, lightVector);			
	}
   }			
}

Code for lightmanagers FindClosestLight, is not smartest one, it goes trought all lights, find its closest two - one for really closest and second for closest, which also faces light. If there is no closest light which faces face, then use normal closest. Please take this function as temporary, not final, it is not for optimalisation, but for understanding etc..

CLight *CLightManager::FindClosestLight (Vertex v, Vertex normal)
{
	if (m_lLight.Count==0)
		return NULL;

	float vzdal = 99999;	// for regarding normal
	float vzdal2 = 99999;   // without normal
	float temp;
	float uhel;
	Vertex delta;
	Vertex lightpos;
	int id = 0;		// id of light with normal
	int id2 = 0;		// id of light without normal

	for (int i=0;i<m_lLight.Count;i++)
	{
 	  lightpos = m_lLight.m_vPos;
	  delta = lightpos - v;
	  temp = delta;	
	  CMath::Normalize (δ);	
	  uhel = CMath::DotProduct(delta, normal);
	  if (temp < vzdal && uhel>0)
		{
		vzdal = temp;
		id = i;
		}		
	if (temp < vzdal2)
		{
		vzdal2 = temp;
		id2 = i;			
		}
	}
   // if there is not closest light with normal, then use closest without normal
   if (vzdal == 99999)
	{
	id = id2;
	}

  // if vzdal1 light is too far, then use closest one
  if (vzdal > 2 * vzdal2)
	{
	id = id2;
	}
		
   return &m_lLight[id];
}

So and now, what I need. Firstly I am not decided to continue with only one light source per face, however I don't know how I should do it for more lights. Is there any tutorial on it? Well, let's say one light per face is enough. But how can I find "good" light? How can I avoid this light jumps and light marks on walls? If you know some tutorial, which solves this - if in BSP it will be great, let me know. If you know some open source engine, which uses and solves this, also let me know. If you have better idea/procedure how to find best light, also let me know. Any sugestions will be welcomed. Thanks Martin [Edited by - martinhoge on December 5, 2005 8:54:50 AM]
Advertisement
dont just have one light per surface it will look wrong and flicker during actual gameplay.
take into consideration all lights that affect the surface
Well, I agree with you, but how can I compute cubemap coords to all those
lights? I haven't found any materials on it.

And there is also another thing..how can I find lights who affects surface?
What if there will be some wall ( still in bsp ) between ligth and surface that affects it. How should I recognize it? I think it could be done be portals visibility, but if I will evaluate all those light it will take lot of CPU.
you can save light indices in each vertex and pre-calculate them into the static geometry on load time :
struct MyVertex{    float3 p;    float2 coord;    float4 lightIndices;};


"lightIndices" will hold 4 indices to the scene lights that you want to illuminate the vertex.
pass these vertices to the vertex shader and light them.

for more than 4 (less is even better) lights per vertex, you can do a simple algorithm like choosing the two lights that their angle between light position and destination vertex are wider.

hope this helps !

dark-hammer engine - http://www.hmrengine.com


Well,

I think, I won't use more than 4 lights at once,
but why you have indices in float? should it not be
just ordinary integers?

My problem is, that my graphic card - now I have
to work on notebook, does not support shaders at all.
So I must do it in old way and look for algorithm to
calculate it on CPU. What is source code for that
shader you've written me? May be I could put it to
C++. ? . Or do you now algorithm in c++ directly?

Thanks

Martin

Btw: please don't write me to buy new graphic card,
I know I should - however I want my engine run even
on older graphic cards.
Quote:but why you have indices in float? should it not be just ordinary integers?

this is because the GPU works only with floating point values, you can define an Integer , but in shader they are all converted into floating-point internally.

Quote:So I must do it in old way and look for algorithm to calculate it on CPU

I'm also developing my engine on my laptop graphic card which doesn't support shaders. but you can write your vertex shader and run it on software, buy passing D3DCREATE_SOFTWARE_VERTEXPROCESSING flag to "CreateDevice" . and it runs pretty fast, faster than program code for sure.
but you can't emulate Pixel shaders in driver, except in REF device, which is awefully slow.

I suggest you go with shaders anyway, cuz games are played on decent graphic cards and needs GPU power.

you can check out the demo of my engine that I'm working in my website, currently is supports per pixel + bump, and also vertex light + bump for older cards like gf3 , gf4, and does only per vertex lighting for crappy cards like mine(laptop).
but it works only on dynamic meshes, not static geometry due to performance reasons.

the algorithm is something like this :
you have a vertex that light indices are stored in, and you have couple of lights for the scene, the indices for each vertex points to each light in the scene.
when you want to lit each vertex you read each light by :

// input is a vertex ...for( int i = 0; i < MAX_INDICES; i++ ) {    Light& light = sceneLights[ vertex.lightIndices ];    // do the lighting/bump calculations for vertex ...}


anyway if you want to see the code, give me your mail and I will mail you my per vertex lighting + bump mapping shader, it supports two lights, and uses the indexing method that I told you.
I guess for bump mapping, in order to get the right results for multiple lights, you need at least pixel shader 1.1, but if you have only one light you can implement it with fixed function pipeline.

dark-hammer engine - http://www.hmrengine.com

Every light should affect every surface, of course.
First render a pass with only ambient lighting, and normal depthbuffering enabled. Then disable z-writes and render the scene once for each light, adding the results together. This may seem like it would be slow, but with depthwriting disabled it'll only render the visible pixels.
Once you start implementing shadow volumes, you'll have to render once for each light anyway.
Youre gonna get that sort of lighting with per vertex. If you subdivide all your polys it will look better, but with a greater rendering overhead.

This topic is closed to new replies.

Advertisement