LensFlareSceneNode with occlusion query
Posted: Sun Sep 04, 2011 6:14 am
Hi,
for my tech demo I needed a sun with lensflare. Though there is a lensflare scene node in irrExt I was a little bit unhappy with it.
For large camera far values the flares are hopping (I assume that this is a rounding issue in getRayFromScreenCoordinates).
So I decided to create a new one. Here is the result.
sample download with sources
http://www.van-helsing-band.de/irrlicht/lensflare.zip

you are able to custimize the flares:
Intances of SFlareData are stored in an array
with the corresponding accessor method
you can modify the flares from outside the class.
the render method is straight forward
using
gives the possibility to use an occlusion query result to set the strength of the flares.
In the example I included a SceneNodeAnimatorFollowCamera class that keeps the relative position of the sun constant.
The sample is compiled against a newer trunk of irrlicht (which includes the occlusion query feature)
Thats it, I hope you enjoy it.
for my tech demo I needed a sun with lensflare. Though there is a lensflare scene node in irrExt I was a little bit unhappy with it.
For large camera far values the flares are hopping (I assume that this is a rounding issue in getRayFromScreenCoordinates).
So I decided to create a new one. Here is the result.
sample download with sources
http://www.van-helsing-band.de/irrlicht/lensflare.zip

you are able to custimize the flares:
Code: Select all
// enum with different flare types (used by SFlareData)
enum E_FLARE_TYPE
{
EFT_SUN = 0,
EFT_GLOW,
EFT_LENS,
EFT_STREAKS,
EFT_RING,
EFT_HALO,
EFT_CIRCLE,
EFT_POLY,
EFT_COUNT
};
// struct holding the flare specification
struct SFlareData
{
public:
// constructor
SFlareData(E_FLARE_TYPE type, float position, float scale, video::SColor& color)
{
Type = type;
Position = position;
Scale = scale;
Color = color;
}
// flare type
E_FLARE_TYPE Type;
// position
f32 Position;
// flare scale
f32 Scale;
// flare color
video::SColor Color;
};
Code: Select all
protected:
// flare data array
core::array<SFlareData> FlareData;
Code: Select all
// returns the flare data array
core::array<SFlareData>& getFlareData();
the render method is straight forward
Code: Select all
void CLensFlareSceneNode::render()
{
// get the videodriver and the active camera
video::IVideoDriver* driver = SceneManager->getVideoDriver();
ICameraSceneNode* camera = SceneManager->getActiveCamera();
// return if we don't have a valid driver or a valid camera
// or if we have no texture attached to the material
if (!camera || !driver || !Material.getTexture(0))
return;
// get screencenter
const core::vector2d<s32> screenCenter = core::vector2d<s32>(
SceneManager->getVideoDriver()->getScreenSize().Width,
SceneManager->getVideoDriver()->getScreenSize().Height)/2;
// get screencoordinates of the node
const core::vector2d<s32> lightPos = SceneManager->getSceneCollisionManager()->getScreenCoordinatesFrom3DPosition(
getAbsolutePosition(),
camera);
// store old projection matrix
core::matrix4 oldProjMat = driver->getTransform(video::ETS_PROJECTION);
// store old view matrix
core::matrix4 oldViewMat = driver->getTransform(video::ETS_VIEW);
// clear the projection matrix
driver->setTransform(video::ETS_PROJECTION, core::IdentityMatrix);
// clear the view matrix
driver->setTransform(video::ETS_VIEW, core::IdentityMatrix);
// set the transform
driver->setTransform(video::ETS_WORLD, core::IdentityMatrix);
// set the material
driver->setMaterial(Material);
// calculate some handy constants
f32 texPos = 1.0f/EFT_COUNT;
s32 texHeight = s32(Material.getTexture(0)->getSize().Height*0.5f);
f32 screenWidth = f32(driver->getScreenSize().Width);
f32 screenHeight = f32(driver->getScreenSize().Height);
// render the flares
for (u32 i=0; i<FlareData.size(); ++i)
{
// get the flare element
SFlareData& flare = FlareData[i];
// calculate center of the flare
core::vector2d<s32>flarePos = screenCenter.getInterpolated(lightPos, -2.0*flare.Position);
// calculate flareposition in vertex coordinates using the scalefactor of the flare
s32 flareScale = s32((texHeight*flare.Scale));
core::rect<f32> flareRect = core::rect<f32>(
-1.f + 2.f * f32(flarePos.X-flareScale) / screenWidth,
-1.f + 2.f * f32(screenHeight-flarePos.Y-flareScale) / screenHeight,
-1.f + 2.f * f32(flarePos.X+flareScale) / screenWidth,
-1.f + 2.f * f32(screenHeight-flarePos.Y+flareScale) / screenHeight);
// calculate flarecolor in dependence of occlusion
f32 flareAlpha = f32(flare.Color.getAlpha()) / 255.f;
video::SColor flareColor(255,
(u32)(Strength * flareAlpha * flare.Color.getRed()),
(u32)(Strength * flareAlpha * flare.Color.getGreen()),
(u32)(Strength * flareAlpha * flare.Color.getBlue()));
// set vertex colors
Vertices[0].Color = flareColor;
Vertices[1].Color = flareColor;
Vertices[2].Color = flareColor;
Vertices[3].Color = flareColor;
// set texture coordinates
Vertices[0].TCoords.set( flare.Type * texPos, 1.0f);
Vertices[1].TCoords.set( flare.Type * texPos, 0.0f);
Vertices[2].TCoords.set((flare.Type+1) * texPos, 0.0f);
Vertices[3].TCoords.set((flare.Type+1) * texPos, 1.0f);
// set vertex positions
Vertices[0].Pos.set(flareRect.UpperLeftCorner.X, flareRect.UpperLeftCorner.Y, 0);
Vertices[1].Pos.set(flareRect.UpperLeftCorner.X, flareRect.LowerRightCorner .Y, 0);
Vertices[2].Pos.set(flareRect.LowerRightCorner.X, flareRect.LowerRightCorner.Y, 0);
Vertices[3].Pos.set(flareRect.LowerRightCorner.X, flareRect.UpperLeftCorner.Y, 0);
//draw the mesh
driver->drawIndexedTriangleList(Vertices, 4, Indices, 2);
}
// restore view matrix
driver->setTransform(video::ETS_VIEW, oldViewMat);
// restore projection matrix
driver->setTransform(video::ETS_PROJECTION, oldProjMat);
}
Code: Select all
// sets the strength (visibility) of the flares
void setStrength(f32 strength) { Strength = core::clamp(strength, 0.0f, 1.0f); }
In the example I included a SceneNodeAnimatorFollowCamera class that keeps the relative position of the sun constant.
The sample is compiled against a newer trunk of irrlicht (which includes the occlusion query feature)
Thats it, I hope you enjoy it.