OPEN GL Lens Flare Effect
My lens flare effect uses several bitmaps I made in photoshop, applied along an axis from the camera to the light source. The blending modes and subtle color modulations I apply give the flare a realistic and cinematic effect. The code is quite efficient in OpenGL and does not need to offload any operations onto the gpu to get a decent framerate in the game. Here is some sample code from my implementation:
#include "LensFlare.h"
int LensFlare::useMipmaps = 0;
GLuint LensFlare::flareTex[6], LensFlare::shineTex[6];
//Flare LensFlare::flare[20];
//int LensFlare::num_flares = -1;
void LensFlare::UpdateAABB()
{
static Vec3 offset(5,5,5);
AABB temp;
boundingBox.Reset();
temp.AddPoint(light - offset);
temp.AddPoint(light + offset);
AddAABBWithModelview(boundingBox, temp);
}
LensFlare::LensFlare(){
from[0] = 0.0;
from[1] = 0.0;
from[2] = 20.0;
at[0] = 0.0;
at[1] = 0.0;
at[2] = 0.0;
near_clip = 1.0;
//useMipmaps = 0;
//num_flares = 15;
shine_tic = 0;
//float tic = 3.66;
//random light pos that looks good
light[0] = sin(3.66 * 0.73) * 6;
light[1] = 4.0f + 8.0 * sin(3.66 * 0.678);
light[2] = sin(3.66 * 0.895) * 6;
UpdateAABB();
}
LensFlare::LensFlare(const Vec3& CamPos, const Vec3& CamAt, const
Vec3& LightPos, GLfloat n){
from = CamPos;
at = CamAt;
near_clip = n;
//useMipmaps = 0;
//num_flares = 0;
shine_tic = 0;
//tic = 3.66;
light = LightPos;
UpdateAABB();
}
void LensFlare::SetLightPos( const Vec3& pos ){
light = pos;
UpdateAABB();
}
void LensFlare::SetCamFrom( const Vec3& camera ){
from = camera;
}
void LensFlare::SetCamAt( const Vec3& atpos ){
at = atpos;
}
void LensFlare::SetNearClip( GLfloat nearclip ){
near_clip = nearclip;
}
const Vec3& LensFlare::GetLightPos(){
return light;
}
const Vec3& LensFlare::GetCamFrom(){
return from;
}
const Vec3& LensFlare::GetCamAt(){
return at;
}
double LensFlare::GetNearClip(){
return near_clip;
}
Flare LensFlare::set_flare(int type, float location, float scale, Vec3 color,
float colorscale){
Flare Bob;
Bob.type = type;
Bob.loc = location;
Bob.scale = scale;
Bob.color[0] = color[0] * colorscale;
Bob.color[1] = color[1] * colorscale;
Bob.color[2] = color[2] * colorscale;
return Bob;
}
void LensFlare::InitFlares(){
// Shines
//6 shines - colors modulated to blue, yellow, pink tints
flare.push_back(set_flare(-1, 1.0f, 0.25f, lightblue, 1.0));
flare.push_back(set_flare(-1, 1.0f, 0.2f, lightyellow, 1.0));
flare.push_back(set_flare(-1, 1.0f, 0.15f, lightpink, 1.0));
flare.push_back(set_flare(-1, 1.0f, 0.1f, lightblue, 1.0));
flare.push_back(set_flare(-1, 1.0f, 0.3f, lightyellow, 1.0));
flare.push_back(set_flare(-1, 1.0f, 0.17f, lightpink, 1.0));
// Flares
//offset these...la la la
flare.push_back(set_flare(5, 1.3f, 0.04f, lightyellow, 0.6));
flare.push_back(set_flare(4, 1.1f, 0.1f, lightyellow, 0.4));
flare.push_back(set_flare(3, 0.5f, 0.15f, lightyellow, 0.3));
flare.push_back(set_flare(2, 0.2f, 0.05f, lightyellow, 0.3));
flare.push_back(set_flare(1, 0.0f, 0.04f, lightyellow, 0.3));
flare.push_back(set_flare(2, -0.25f, 0.07f, lightyellow, 0.5));
flare.push_back(set_flare(3, -0.4f, 0.02f, lightyellow, 0.6));
flare.push_back(set_flare(4, -0.6f, 0.04f, lightyellow, 0.4));
flare.push_back(set_flare(5, -1.0f, 0.03f, lightyellow, 0.2));
}
void LensFlare::DoFlares(){
Vec3 view_dir, tmp, light_dir, lightTemp, position, dx, dy,
center, axis, sx, sy;
GLfloat FranktheDotProd, global_scale = 1.5;
int i;
lightTemp = light;
// view vector
view_dir = normalize(at - from);
// world-position at center of view frustum near plane
center = from + (view_dir * 1.0);
// vector from camera to light
light_dir = normalize(light - from);
lightTemp = from + light_dir * 1.0 / dot(light_dir, view_dir) ;
// axis from flare to center
axis = lightTemp - center;
dx = normalize(axis);
dy = cross(dx, view_dir);
glPushAttrib(GL_ENABLE_BIT | GL_TEXTURE_BIT);
glEnable(GL_TEXTURE_2D);
glDisable(GL_DEPTH_TEST);
glEnable(GL_BLEND);
glDisable(GL_LIGHTING);
glDisable(GL_CULL_FACE);
glBlendFunc(GL_ONE, GL_ONE);
glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
for (i = 1; i < flare.size(); i++) {
sx = dx * flare[i].scale * global_scale;
sy = dy * flare[i].scale * global_scale;
glColor3f(flare[i].color[0], flare[i].color[1], flare[i].color[2]);
if (flare[i].type < 0) {
glBindTexture(GL_TEXTURE_2D, shineTex[shine_tic]);
shine_tic = (shine_tic + 1) % 5;
} else {
glBindTexture(GL_TEXTURE_2D, flareTex[flare[i].type]);
}
tmp = flare[i].loc * axis;
Vec3 offset_dir;
offset_dir = normalize(tmp + center - from);
position = tmp + center + offset_dir*i*0.02;
glBegin(GL_QUADS);
glTexCoord2f(0.0, 0.0);
tmp = position + sx + sy;
glVertex3f(tmp[0], tmp[1], tmp[2]);
glTexCoord2f(1.0, 0.0);
tmp = position - sx + sy;
glVertex3f(tmp[0], tmp[1], tmp[2]);
glTexCoord2f(1.0, 1.0);
tmp = position - sx - sy;
glVertex3f(tmp[0], tmp[1], tmp[2]);
glTexCoord2f(0.0, 1.0);
tmp = position + sx - sy;
glVertex3f(tmp[0], tmp[1], tmp[2]);
glEnd();
}
glPopAttrib();
}
void LensFlare::setup_texture(char *filename, GLuint texobj,
GLenum minFilter, GLenum maxFilter){
glBindTexture(GL_TEXTURE_2D, texobj);
glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, minFilter);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, maxFilter);
}
void LensFlare::load_textures(){
char filename[256];
GLenum minFilter, maxFilter;
GLuint textureId = 1, i;
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
/* if (useMipmaps) {
minFilter = GL_LINEAR_MIPMAP_LINEAR;
maxFilter = GL_LINEAR;
} else {
minFilter = GL_LINEAR;
maxFilter = GL_LINEAR;
}*/
//if (!useMipmaps) {
minFilter = GL_NEAREST;
maxFilter = GL_NEAREST;
//}
for (i = 0; i < 6; i++) {
sprintf(filename, "Shine%d.bmp", i+1);
BitmapFile FloydtheBitmap;
FloydtheBitmap.SetBmp(filename);
textureId = FloydtheBitmap.MakeTexture();
shineTex[i] = textureId;
setup_texture(filename, shineTex[i], minFilter, maxFilter);
}
for (i = 0; i < 6; i++) {
sprintf(filename, "Flare%d.bmp", i+1);
BitmapFile HanktheBitmap;
HanktheBitmap.SetBmp(filename);
textureId = HanktheBitmap.MakeTexture();
flareTex[i] = textureId;
setup_texture(filename, flareTex[i], minFilter, maxFilter);
}
}