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):

We implemented a quick hack to set the edges of the volume texture to be pure black:

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.
Here's a screenshot of the bug(I masked the bottom and exaggerated the green to highlight the issue):

We implemented a quick hack to set the edges of the volume texture to be pure black:

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.
#2
Replace the sgGenerateDynamicLightingTexture function with this version
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);
}
Associate Ross Pawley
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:
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.