Wikis / Unreal Wiki / Legacy:Lens Flare

A lens flare is a visual effect assembled of several circles/hexagons that – when the person is moving – move opposite of the light source and don't stay on one spot. So they move in the direction that the person is moving. The color of lens flares is generally related to the color of the light. A lens flare is a result of compound lens systems used in cameras and other optical devices, and so some consideration should be given to their use - lens flares do not occur on light sources viewed with the naked eye. Also, lens flares can make it harder to see, so be careful that their use does not adversely impact gameplay.

The textures that are displayed around light sources, but stick at the place where the light is are coronas. For some reason they're frequently erroneously called "lens flare" as well.

Legacy corona.gif

Lens Flare in the Unreal Engine

No support for lens flares is built in to the Unreal Engine, but lens flares can be created using UnrealScript. This should be possible with any version of the Unreal Engine, though the specific Unrealscript code required will vary. Lens flares can be created two ways - by drawing them directly onto the screen using the Canvas, or by creating non-colliding actors in the world. Using actors allows you to make better use of hardware graphics acceleration, though drawing to the Canvas will require marginally less memory. Drawing to the Canvas may also produce slightly better results, as actors may be clipped by nearby meshes. Both solutions should have negligible impact on performance. With either solution, Unrealscript will be needed to compute the correct positions for the lens flares.

Making Lens Flares

Here's the code I used for a proof of concept I threw together a short while back. References can be found in the comments.

The idea is that a light source is creating a glare against the "lens" through which we are viewing the world – in this case the canvas. The flare effect is then created by drawing a series of coronas along a vector on the canvas which passes through both the light source and the center of the screen.

//-----------------------------------------------------------
// FlareInteraction.
// Adds 1337 lens flares for lights w/ coronas.
// (c) 2002 jasonyu
//-----------------------------------------------------------
class FlareInteraction extends Interaction;
 
#exec OBJ LOAD FILE=Textures\FlareTextures.utx PACKAGE=LensFlare
 
var Actor PlayerOwner;
const HLSMAX = 240;
const RGBMAX = 255;
 
 
event Initialized()
{
	PlayerOwner = ViewportOwner.Actor;
	log("Lensflare interaction initialized.");
}
 
// adapted from:
// http://plaza27.mbn.or.jp/~satomii/design/win32/hls2rgb.html
final simulated function float HueToRGB(float n1, float n2, float hue)
{
	if ( hue < 0 ) hue += HLSMAX;
	if ( hue > HLSMAX ) hue -= HLSMAX;
 
	/* return r,g, or b value from this tridrant */
	if ( hue < (HLSMAX/6) )
		return ( n1 + (((n2-n1)*hue + (HLSMAX/12))/(HLSMAX/6)) );
	if ( hue < (HLSMAX/2) )
		return n2;
	if ( hue < ((HLSMAX*2)/3) )
		return ( n1 + (((n2-n1)*(((HLSMAX*2)/3)-hue) + (HLSMAX/12))/(HLSMAX/6)) );
	else
		return n1;
}
 
 
final simulated function Color hls2rgb(byte hue, byte lum, byte sat)
{
	local Color C;
	local float Magic1, Magic2;
	local int R,G,B;
 
	if ( sat==0)
	{     /* achromatic case */
		C.R = (lum*RGBMAX) / HLSMAX;
		C.G = C.R;
		C.B = C.R;
	}
	else
	{       /* chromatic case */
		/* set up magic numbers */
		if (lum <= (HLSMAX/2))
			Magic2 = (lum*(HLSMAX+sat)+(HLSMAX/2)) / HLSMAX;
		else
			Magic2 = lum+sat - ((lum*sat)+(HLSMAX/2)) / HLSMAX;
 
		Magic1 = 2*lum - Magic2;
 
		/* get RGB, change units from HLSMAX to RGBMAX */
		R = (HueToRGB(Magic1, Magic2, hue+(HLSMAX/3))
				* RGBMAX + (HLSMAX/2)) / HLSMAX;
		G = (HueToRGB(Magic1, Magic2, hue)
				* RGBMAX + (HLSMAX/2)) / HLSMAX;
 
		B = (HueToRGB(Magic1, Magic2, hue-(HLSMAX/3))
				* RGBMAX + (HLSMAX/2)) / HLSMAX;
	}
 
	C.R = R;
	C.G = G;
	C.B = B;
	C.A = 255;
	return C;
}
 
 
 
// adapted from:
// http://www.gamedev.net/reference/articles/article813.asp
simulated function PostRender( canvas Canvas )
{
	local Light Other;
	local float Dist;
	local Vector X,Y,Z, Dir;
 
	local Vector V, C, L;
	local float Length;
	local float ScaleX, ScaleY;
 
 
	if ( ViewportOwner.Actor.Pawn != None ) PlayerOwner = ViewportOwner.Actor.Pawn;
	else PlayerOwner = ViewportOwner.Actor;
 
	if ( PlayerOwner != None )
	{
		ScaleX = Canvas.SizeX / 640.0;
		ScaleY = Canvas.SizeY / 480.0;
 
		C.X = Canvas.ClipX/2;
		C.Y = Canvas.ClipY/2;
		GetAxes(PlayerOwner.Rotation, X,Y,Z);
		ForEach PlayerOwner.RadiusActors(class'Light', Other, 2000)
		{
			Dir = Other.Location - PlayerOwner.Location;
			Dist = VSize(Dir);
			Dir = Dir/Dist;
 
			// find an appropriate light source (bright, in FOV)
			if ( (Dir Dot X) > 0.7 && Other.bCorona &&
				(
				(PlayerOwner.IsA('Pawn') &&  Pawn(PlayerOwner).LineOfSightTo(Other) ) ||
				(PlayerOwner.IsA('Controller') && Controller(PlayerOwner).LineOfSightTo(Other) )
				) &&
				Other.LightRadius >= 64 && Other.DrawScale > 0.2 )
			{
				//Convert 3d location to 2d for display on the Canvas
				L = WorldToScreen(Other.location);
 
				V.X = L.X - C.X;
				V.Y = L.Y - C.Y;
				Length = VSize(V);
				V = Normal(V);
 
				Canvas.Style = PlayerOwner.ERenderStyle.STY_Additive;
 
				// tint the corona textures to match the hue of the light
				Canvas.DrawColor = hls2rgb(Other.LightHue, 15, 75);
 
				// place flares along directional vector
				Canvas.SetPos( (V.X*Length*1.2)-32*ScaleX+C.X, (V.Y*Length*1.2)-32*ScaleY+C.Y );
				Canvas.DrawTile(Texture'LensFlare3', 64*ScaleX,64*ScaleY,0.0, 0.0, 128, 128);
 
				Canvas.SetPos( (V.X*Length)-32*ScaleX+C.X, (V.Y*Length)-32*ScaleY+C.Y );
				Canvas.DrawTile(Texture'LensFlare1', 64*ScaleX,64*ScaleY,0.0, 0.0, 128, 128);
 
				Canvas.SetPos( (V.X*Length*0.66)-64*ScaleX+C.X, (V.Y*Length*0.66)-64*ScaleY+C.Y );
				Canvas.DrawTile(Texture'LensFlare1', 128*ScaleX,128*ScaleY,0.0, 0.0, 128, 128);
 
				Canvas.SetPos( (V.X*Length*0.33)-64*ScaleX+C.X, (V.Y*Length*0.33)-64*ScaleY+C.Y );
				Canvas.DrawTile(Texture'LensFlare0', 128*ScaleX,128*ScaleY,0.0, 0.0, 128, 128);
 
				Canvas.SetPos( (V.X*Length*0.125)-64*ScaleX+C.X, (V.Y*Length*0.125)-64*ScaleY+C.Y );
				Canvas.DrawTile(Texture'LensFlare2', 128*ScaleX,128*ScaleY,0.0, 0.0, 128, 128);
 
				Canvas.SetPos( (V.X*Length*-0.21)-64*ScaleX+C.X, (V.Y*Length*-0.21)-64*ScaleY+C.Y );
				Canvas.DrawTile(Texture'LensFlare3', 128*ScaleX,128*ScaleY,0.0, 0.0, 128, 128);
 
				Canvas.SetPos( (V.X*Length*-0.30)-32*ScaleX+C.X, (V.Y*Length*-0.30)-32*ScaleY+C.Y );
				Canvas.DrawTile(Texture'LensFlare4', 64*ScaleX,64*ScaleY,0.0, 0.0, 128, 128);
 
				Canvas.SetPos( (V.X*Length*-0.5)-90*ScaleX+C.X, (V.Y*Length*-0.5)-90*ScaleY+C.Y );
				Canvas.DrawTile(Texture'LensFlare4', 180*ScaleX,180*ScaleY,0.0, 0.0, 128, 128);
			}
		}
	}
}
 
defaultproperties
{
   bVisible=true
   bActive=true
}

A compiled .u with textures and activating mutator may be located at http://www.boilingpoint.com/~jasonyu/ut/LensFlare.zip

Enjoy.

capt.

Links

http://www.wildtangent.com/developer/howtos/LensFlareHowTo/ - Explanation of how to calculate positions for lens flare effects.

http://www.blackpawn.com/texts/lensflare/default.html - Information on creating good textures for use with lens flares.

Understanding Lens Flare - Background on what causes lens flare and techniques to reduce it in photography.


Ghost3021: Wow! Lets combine this with realtime SquirrelZ's realtimeshadows.... :D

Wormbo: Lense flares only appear when you use an optical device like a camera, that's why they are called lense flares. You will never see them with bare eyes, so why combine them with realistic real-time shadows? Both work independantly and that's a good thing, actually.

GG-Xtreme: It doesn't seem to come with the textures, and it doesn't use a default texture package. Does anyone have a download to LensFlares.utx?

Page Information

2022-11-18T09:42:05.523850Z 2007-03-03T00:30:48Z GG-Xtreme * https://wiki.beyondunreal.com/Legacy:Lens Flare Attribution-NonCommercial-ShareAlike 3.0