Game Development Community

Clamp bug in sgLightingModel.cc/sgGenerateDynamicLightingTexture

by Ross Pawley · in Torque Game Engine Advanced · 07/27/2007 (3:17 pm) · 2 replies

There's a bug with with the volume texture generated in sgGenerateDynamicLightingTexture. It generates values on the outer edges that are non-black, which causes the lighting to repeat across the terrain.

Here's a screenshot of the bug(I masked the bottom and exaggerated the green to highlight the issue):
farm2.static.flickr.com/1139/920783358_018ffe93d0.jpg?v=0
We implemented a quick hack to set the edges of the volume texture to be pure black:
farm2.static.flickr.com/1119/919816251_7ccb30b102.jpg?v=0
The issue here is that the texture is used as a mask, and since the edge values have a *small* amount of value (rather than black), it repeats the mask across the entire terrain square(the reason this happens is because the texture repeat mode is set to CLAMP).

NOTE: This is using the default sgLightingModelGLBase, rather than any of the other models.

#1
07/27/2007 (4:30 pm)
Update: I've put in a hack that subtracts a constant value (which is greater than the amount I observed at the edges when debugging) and it creates a somewhat acceptable appearance. However, this is a gross hack, and does not address the fundamental issue of the bugged lighting model.

In sgLightingModel.cc in sgGenerateDynamicLightingTexture in the loop to construct the volume texture for omni lights (starts with // fill out the omni... ) right after this:
sgLightingLM(pos, SG_STATIC_SPOT_VECTOR_NORMALIZED, diff, attn, norm);

add

attn.red -= 0.065;
   if ( getMax( attn.red, 0.0f ) == 0.0f )
      attn.red = 0.0f;

This does not completely fix the issue, but it's better than the previous hack or the glaring bug. It may be acceptable for some projects. I'd much rather have the generation itself working correctly.
#2
07/30/2007 (4:54 pm)
Here's a proper fix for this issue from John Kabus:

Replace the sgGenerateDynamicLightingTexture function with this version
void sgLightingModel::sgGenerateDynamicLightingTexture()
{
   LightInfo *oldlight = sgLight;
 
   // setup basic light...
   LightInfo light;
   light.mType = LightInfo::Point;
   light.mRadius = 10.0;
   light.mColor = ColorF(1.0, 1.0, 1.0);
   light.sgLocalAmbientAmount = 1.0;
   light.sgDoubleSidedAmbient = true;
   light.sgUseNormals = false;
 
   // set state...
   sgResetState();
   sgSetState(&light);
   sgInitStateLM();
 
   // get the basics...
   F32 scale = F32(SG_DYNAMIC_LIGHTING_TEXTURE_HALFSIZE + 1) / F32(SG_DYNAMIC_LIGHTING_TEXTURE_HALFSIZE);
   F32 dist = sgGetMaxRadius(true) * scale;
   F32 maxlensq = (SG_DYNAMIC_LIGHTING_TEXTURE_HALFSIZE - 1) * (SG_DYNAMIC_LIGHTING_TEXTURE_HALFSIZE - 1);
   Point3F halfamount(SG_DYNAMIC_LIGHTING_TEXTURE_HALFSIZE,
      SG_DYNAMIC_LIGHTING_TEXTURE_HALFSIZE, SG_DYNAMIC_LIGHTING_TEXTURE_HALFSIZE);
   Point3F invhalfamount(1 / SG_DYNAMIC_LIGHTING_TEXTURE_HALFSIZE,
      1 / SG_DYNAMIC_LIGHTING_TEXTURE_HALFSIZE, 1 / SG_DYNAMIC_LIGHTING_TEXTURE_HALFSIZE);
   Point3F radiusdist(dist, dist, dist);
 
 
   // misc...
   ColorF diff, attn;
   Point3F norm;
 
   Point3F pos;
   pos = Point3F(SG_DYNAMIC_LIGHTING_TEXTURE_HALFSIZE - 1, SG_DYNAMIC_LIGHTING_TEXTURE_HALFSIZE - 1, SG_DYNAMIC_LIGHTING_TEXTURE_HALFSIZE - 1) +
      Point3F(0.5f, 0.5f, 0.5f);
   pos -= halfamount;
   pos *= invhalfamount;
   pos *= radiusdist;
   diff = attn = ColorF(0.0f, 0.0f, 0.0f);
   norm = VectorF(0.0f, 0.0f, 0.0f);
   sgLightingLM(pos, SG_STATIC_SPOT_VECTOR_NORMALIZED, diff, attn, norm);
 
   F32 max = attn.red;
   if(max > 0.0f)
      scale = 1.0f / max;
   else
      scale = 1.0f;
 
 
   pos = Point3F(0, SG_DYNAMIC_LIGHTING_TEXTURE_HALFSIZE - 1, SG_DYNAMIC_LIGHTING_TEXTURE_HALFSIZE - 1) +
      Point3F(0.5f, 0.5f, 0.5f);
   pos -= halfamount;
   pos *= invhalfamount;
   pos *= radiusdist;
   diff = attn = ColorF(0.0f, 0.0f, 0.0f);
   norm = VectorF(0.0f, 0.0f, 0.0f);
   sgLightingLM(pos, SG_STATIC_SPOT_VECTOR_NORMALIZED, diff, attn, norm);
   F32 min = attn.red;
 
 
   // fill out the omni...
   U8 bits[SG_DYNAMIC_LIGHTING_TEXTURE_SIZE][SG_DYNAMIC_LIGHTING_TEXTURE_SIZE][SG_DYNAMIC_LIGHTING_TEXTURE_SIZE];
   for(U32 x=0; x<SG_DYNAMIC_LIGHTING_TEXTURE_SIZE; x++)
   {
      for(U32 y=0; y<SG_DYNAMIC_LIGHTING_TEXTURE_SIZE; y++)
      {
         for(U32 z=0; z<SG_DYNAMIC_LIGHTING_TEXTURE_SIZE; z++)
         {
            Point3F pos(x, y, z);
            pos += Point3F(0.5f, 0.5f, 0.5f);
            pos -= halfamount;
            if(pos.lenSquared() >= maxlensq)
            {
               bits[x][y][z] = 0;
               continue;
            }
 
            pos *= invhalfamount;
            pos *= radiusdist;
 
            diff = attn = ColorF(0.0f, 0.0f, 0.0f);
            norm = VectorF(0.0f, 0.0f, 0.0f);
            sgLightingLM(pos, SG_STATIC_SPOT_VECTOR_NORMALIZED, diff, attn, norm);
 
            attn.red = (attn.red - min) * scale + min;
 
            bits[x][y][z] = mClamp(S32(attn.red * 255.f), S32(0), S32(255));
         }
      }
   }
 
   // upload...
	sgDynamicLightingTextureOmni = GFX->mTextureManager->createTexture(SG_DYNAMIC_LIGHTING_TEXTURE_SIZE,
      SG_DYNAMIC_LIGHTING_TEXTURE_SIZE, SG_DYNAMIC_LIGHTING_TEXTURE_SIZE,
      bits, GFXFormatL8, &GFXDefaultStaticDiffuseProfile);
 
   // change to spot...
   light.mDirection = VectorF(-1.0, 0.0, 0.0);
   light.mType = LightInfo::SGStaticSpot;
   light.sgSpotAngle = 90;
 
   // set lighting model to spot...
   sgResetState();
   sgSetState(&light);
   sgInitStateLM();
 
   // fill out the spot...
   for(U32 x=0; x<SG_DYNAMIC_LIGHTING_TEXTURE_SIZE; x++)
   {
      for(U32 y=0; y<SG_DYNAMIC_LIGHTING_TEXTURE_SIZE; y++)
      {
         for(U32 z=0; z<SG_DYNAMIC_LIGHTING_TEXTURE_SIZE; z++)
         {
            Point3F pos(x, y, z);
            pos += Point3F(0.5f, 0.5f, 0.5f);
            pos -= halfamount;
            if(pos.lenSquared() >= maxlensq)
            {
               bits[x][y][z] = 0;
               continue;
            }
 
            pos *= invhalfamount;
            pos *= radiusdist;
 
            diff = attn = ColorF(0.0f, 0.0f, 0.0f);
            norm = VectorF(0.0f, 0.0f, 0.0f);
            sgLightingLM(pos, SG_STATIC_SPOT_VECTOR_NORMALIZED, diff, attn, norm);
 
            attn.red = (attn.red - min) * scale + min;
 
            bits[x][y][z] = mClamp(S32(attn.red * 255.f), S32(0), S32(255));
         }
      }
   }
 
	// upload...
	sgDynamicLightingTextureSpot = GFX->mTextureManager->createTexture(SG_DYNAMIC_LIGHTING_TEXTURE_SIZE,
		SG_DYNAMIC_LIGHTING_TEXTURE_SIZE, SG_DYNAMIC_LIGHTING_TEXTURE_SIZE,
		bits, GFXFormatL8, &GFXDefaultStaticDiffuseProfile);
 
 
#ifdef SG_TEST_LIGHTING_TEXTURE
	for(U32 x=0; x<SG_DYNAMIC_LIGHTING_TEXTURE_SIZE; x++)
	{
		for(U32 y=0; y<SG_DYNAMIC_LIGHTING_TEXTURE_SIZE; y++)
		{
			for(U32 z=0; z<SG_DYNAMIC_LIGHTING_TEXTURE_SIZE; z++)
			{
				bits[x][y][z] = 255;
			}
		}
	}
	sgTestLightingTexture = GFX->mTextureManager->createTexture(SG_DYNAMIC_LIGHTING_TEXTURE_SIZE,
		SG_DYNAMIC_LIGHTING_TEXTURE_SIZE, SG_DYNAMIC_LIGHTING_TEXTURE_SIZE,
		bits, GFXFormatL8, &GFXDefaultStaticDiffuseProfile);
 
	sgDynamicLightingTextureOmni = sgTestLightingTexture;
	sgDynamicLightingTextureSpot = sgTestLightingTexture;
#endif
 
 
	// back to the original state...
	sgResetState();
	sgSetState(oldlight);
}