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
![Image](http://oi54.tinypic.com/dzbl5.jpg)
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.