TGE VolumeLight Distance Fade
by Chris Haigler · 09/30/2010 (2:35 pm) · 3 comments
In stock TGE, VolumeLights do not handle fogging causing them to "pop" in and out of view. This resource causes VolumeLights to automatically alpha fade based on the distance from the camera. The fade occurs linearly between the fog start range and the camera's max visible distance (in other words, the fade effect doesn't begin until the VolumeLight is actually "in" the fog plane and ends with the VolumeLight being totally transparent at the max visible distance).
On to the changes!
Step 1: Open engine/lightingSystem/volumeLight.h. In the public section of the volumeLight class (just below the sgRenderY() declaration) add:
In the protected section of the volumeLight class (just below S32 mLastRenderTime) add:
Save and close volumeLight.h.
Step 2: Open engien/lightingSystem/volumeLight.cc. Find volumeLight::initPersistFields(). Just above this method, add:
Find volumeLight::renderObject(). Inside this method, find renderGL(state, image). Just above this line, add:
Replace the volumeLight::sgRenderY() and volumeLight::sgRenderX() methods with the following:
Replace the volumeLight::renderGL() method with the following:
Save volumeLight.cc. Recompile. :]
On to the changes!
Step 1: Open engine/lightingSystem/volumeLight.h. In the public section of the volumeLight class (just below the sgRenderY() declaration) add:
F32 getRenderFadeValue(){return mLastRenderFadeValue;}In the protected section of the volumeLight class (just below S32 mLastRenderTime) add:
void calcRenderFadeValue(SceneState *state); F32 mLastRenderFadeValue;
Save and close volumeLight.h.
Step 2: Open engien/lightingSystem/volumeLight.cc. Find volumeLight::initPersistFields(). Just above this method, add:
void volumeLight::calcRenderFadeValue(SceneState *state)
{
mLastRenderFadeValue = 1.0f;
F32 fadeDistStart = state->getFogDistance();
F32 fadeDistEnd = state->getVisibleDistance();
F32 distToCam = (state->getCameraPosition() - getRenderPosition()).len();
mLastRenderFadeValue = 1.0f - ((distToCam - fadeDistStart) / (fadeDistEnd - fadeDistStart));
mLastRenderFadeValue = mClampF(mLastRenderFadeValue, 0.0f, 1.0f);
}Find volumeLight::renderObject(). Inside this method, find renderGL(state, image). Just above this line, add:
// Calc render fade value to simulate fogging calcRenderFadeValue(state);
Replace the volumeLight::sgRenderY() and volumeLight::sgRenderX() methods with the following:
void volumeLight::sgRenderY(const Point3F &near1, const Point3F &far1, const Point3F &far2,
const ColorF &nearcol, const ColorF &farcol, F32 offset)
{
glBegin(GL_QUADS);
glColor4f(nearcol.red, nearcol.green, nearcol.blue, nearcol.alpha * getRenderFadeValue());
glTexCoord2f(offset, 0.0);
glVertex3f(near1.x, -near1.y, 0.0f);
glColor4f(nearcol.red, nearcol.green, nearcol.blue, nearcol.alpha * getRenderFadeValue());
glTexCoord2f(offset, 0.5);
glVertex3f(near1.x, 0.0f, 0.0f);
glColor4f(farcol.red, farcol.green, farcol.blue, farcol.alpha * getRenderFadeValue());
glTexCoord2f(offset, 0.5);
glVertex3f(far1.x, 0.0, far1.z);
glColor4f(farcol.red, farcol.green, farcol.blue, farcol.alpha * getRenderFadeValue());
glTexCoord2f(offset, 0.0);
glVertex3f(far1.x, far1.y, far1.z);
glEnd();
glBegin(GL_QUADS);
glColor4f(farcol.red, farcol.green, farcol.blue, farcol.alpha * getRenderFadeValue());
glTexCoord2f(offset, 0.5);
glVertex3f(far1.x, 0.0, far1.z);
glColor4f(nearcol.red, nearcol.green, nearcol.blue, nearcol.alpha * getRenderFadeValue());
glTexCoord2f(offset, 0.5);
glVertex3f(near1.x, 0.0f, 0.0f);
glColor4f(nearcol.red, nearcol.green, nearcol.blue, nearcol.alpha * getRenderFadeValue());
glTexCoord2f(offset, 1.0);
glVertex3f(near1.x, near1.y, 0.0f);
glColor4f(farcol.red, farcol.green, farcol.blue, farcol.alpha * getRenderFadeValue());
glTexCoord2f(offset, 1.0);
glVertex3f(far2.x, far2.y, far2.z);
glEnd();
}
void volumeLight::sgRenderX(const Point3F &near1, const Point3F &far1, const Point3F &far2,
const ColorF &nearcol, const ColorF &farcol, F32 offset)
{
glBegin(GL_QUADS);
glColor4f(nearcol.red, nearcol.green, nearcol.blue, nearcol.alpha * getRenderFadeValue());
glTexCoord2f(0.0, offset);
glVertex3f(-near1.x, near1.y, 0.0f);
glColor4f(nearcol.red, nearcol.green, nearcol.blue, nearcol.alpha * getRenderFadeValue());
glTexCoord2f(0.5, offset);
glVertex3f(0.0f, near1.y, 0.0f);
glColor4f(farcol.red, farcol.green, farcol.blue, farcol.alpha * getRenderFadeValue());
glTexCoord2f(0.5, offset);
glVertex3f(0.0, far1.y, far1.z);
glColor4f(farcol.red, farcol.green, farcol.blue, farcol.alpha * getRenderFadeValue());
glTexCoord2f(0.0, offset);
glVertex3f(far1.x, far1.y, far1.z);
glEnd();
glBegin(GL_QUADS);
glColor4f(farcol.red, farcol.green, farcol.blue, farcol.alpha * getRenderFadeValue());
glTexCoord2f(0.5, offset);
glVertex3f(0.0, far1.y, far1.z);
glColor4f(nearcol.red, nearcol.green, nearcol.blue, nearcol.alpha * getRenderFadeValue());
glTexCoord2f(0.5, offset);
glVertex3f(0.0f, near1.y, 0.0f);
glColor4f(nearcol.red, nearcol.green, nearcol.blue, nearcol.alpha * getRenderFadeValue());
glTexCoord2f(1.0, offset);
glVertex3f(near1.x, near1.y, 0.0f);
glColor4f(farcol.red, farcol.green, farcol.blue, farcol.alpha * getRenderFadeValue());
glTexCoord2f(1.0, offset);
glVertex3f(far2.x, far2.y, far2.z);
glEnd();
}Replace the volumeLight::renderGL() method with the following:
void volumeLight::renderGL(SceneState *state, SceneRenderImage *image)
{
glEnable(GL_TEXTURE_2D);
glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
glEnable(GL_BLEND);
glDepthMask(GL_FALSE);
glBlendFunc(GL_SRC_ALPHA, GL_ONE);
glBindTexture(GL_TEXTURE_2D, mLightHandle.getGLName());
Point3F lightpoint;
// This is where the hypothetical point source of the spot will be
// The volume slices are projected along the lines from
lightpoint.x = lightpoint.y = 0.0f;
lightpoint.z = -mlpDistance;
F32 ax = mXextent / 2;
F32 ay = mYextent / 2;
glEnable(GL_DEPTH_TEST);
glDepthMask(GL_TRUE);
// Draw the bottom foot... this is basically the glowing region.
glBegin(GL_QUADS);
glColor4f(mfootColour.red, mfootColour.green, mfootColour.blue, getRenderFadeValue());
glTexCoord2f(0.0f, 0.0f);
glVertex3f(-ax, -ay, 0.0f);
glColor4f(mfootColour.red, mfootColour.green, mfootColour.blue, getRenderFadeValue());
glTexCoord2f(1.0f, 0.0f);
glVertex3f(ax, -ay, 0.0f);
glColor4f(mfootColour.red, mfootColour.green, mfootColour.blue, getRenderFadeValue());
glTexCoord2f(1.0f, 1.0f);
glVertex3f(ax, ay, 0.0f);
glColor4f(mfootColour.red, mfootColour.green, mfootColour.blue, getRenderFadeValue());
glTexCoord2f(0.0f, 1.0f);
glVertex3f(-ax, ay, 0.0f);
glEnd();
S32 i;
glDepthMask(GL_FALSE);
// Slices in X/U space
for(i = mSubdivideU; i >= 0; i--)
{
F32 k = ((F32)i) / mSubdivideU; // use for the texture coord
F32 bx = i * mXextent / mSubdivideU - ax; // use for point positions
// These are the two endpoints for a slice at the foot
Point3F end1(bx, -ay, 0.0f);
Point3F end2(bx, ay, 0.0f);
end1 -= lightpoint; // get a vector from point to lightsource
end1.normalize(); // normalize vector
end1 *= mShootDistance; // multiply it out by shootlength
end1.x += bx; // Add the original point location to the vector
end1.y -= ay;
// Do it again for the other point.
end2 -= lightpoint;
end2.normalize();
end2 *= mShootDistance;
end2.x += bx;
end2.y += ay;
sgRenderY(Point3F(bx, ay, 0.0f), end1, end2, mfootColour, mtailColour, k);
}
// Slices in Y/V space
for(i = 0; i <= mSubdivideV; i++)
{
F32 k = ((F32)i) / mSubdivideV; // use for the texture coord
F32 by = i * mXextent / mSubdivideV - ay; // use for point positions
// These are the two endpoints for a slice at the foot
Point3F end1(-ax, by, 0.0f);
Point3F end2(ax, by, 0.0f);
end1 -= lightpoint; // get a vector from point to lightsource
end1.normalize(); // normalize vector
end1 *= mShootDistance; // extend it out by shootlength
end1.x -= ax; // Add the original point location to the vector
end1.y += by;
// Do it again for the other point.
end2 -= lightpoint;
end2.normalize();
end2 *= mShootDistance;
end2.x += ax;
end2.y += by;
sgRenderX(Point3F(ax, by, 0.0f), end1, end2, mfootColour, mtailColour, k);
}
glDepthMask(GL_TRUE);
}Save volumeLight.cc. Recompile. :]
About the author
#2
The pop-in/clipping issue may not be a problem depending on your game. If your VolumeLights are typically small or in areas where they're easily occluded (say, inside interiors) you may not see much benefit from having them autofade. Cases where pop-in can occur are typically with large VolumeLights (think giant spotlight/Bat Signal :]) where the VLight could be seen from across the map.
Also, there's still plenty of room for improvement with this. One trivial thing to add would be an "on/off" switch that could be used to disable the fading effect for each VLight.
Another thing to note is the fading effect loses accuracy as the VLight grows in size. Currently the fade is applied uniformly to all of the vertices in the VolumeLight but in most cases certain pairs of vertices will be farther away from the camera than others. In those situations it'd be more accurate to have the fade occur per-vertex. This is something I may implement if I find some time to do so but anyone is welcome to give it a shot!
10/01/2010 (5:10 am)
Thanks!The pop-in/clipping issue may not be a problem depending on your game. If your VolumeLights are typically small or in areas where they're easily occluded (say, inside interiors) you may not see much benefit from having them autofade. Cases where pop-in can occur are typically with large VolumeLights (think giant spotlight/Bat Signal :]) where the VLight could be seen from across the map.
Also, there's still plenty of room for improvement with this. One trivial thing to add would be an "on/off" switch that could be used to disable the fading effect for each VLight.
Another thing to note is the fading effect loses accuracy as the VLight grows in size. Currently the fade is applied uniformly to all of the vertices in the VolumeLight but in most cases certain pairs of vertices will be farther away from the camera than others. In those situations it'd be more accurate to have the fade occur per-vertex. This is something I may implement if I find some time to do so but anyone is welcome to give it a shot!
#3
10/09/2010 (11:44 am)
I've got to echo the thanks for these three resources - great to see someone still tweaking and improving TGE! 
Torque Owner CSMP
MP Studios