OGRE Lens Flare 镜头眩光

本文介绍了一种在2D和3D环境中实现镜头光晕效果的方法。通过使用多个透明图像叠加,模拟真实世界中强光源在相机镜头上产生的光晕现象。文章详细解释了如何通过计算光源位置和屏幕中心的距离来放置光晕元素,并提供了不同元素的位置和缩放比例建议。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

葫芦画瓢

 

 

 

 Reference

http://www.ogre3d.org/wiki/index.php/Displaying_LensFlare

http://www.gamedev.net/reference/articles/article874.asp

http://www.gamedev.net/reference/articles/article813.asp

 

Lens Flare Tutorial

Published Oct 19 1999 06:50 PM in Lens Flares -----
By Alan Gordie

Objective

To explain how to create the over-used, totally hyped "Lens Flare" effect that everyone seems to be talking about these days.

"Did you see the screenshots for , they had the coolest Lens Flares!!!"


Assumptions

I assume that you are familiar with 2d coordinate systems.

I assume that you are capable of using blit to compose the image.


Explanation of the Concepts

The basic idea is that when a bright light passes in front of a camera in real life, it typically creates a glare on each of the lenses within your camera. The physics of why and how the optics of lens flares work are irrelevant to achieving a believable effect.

Now, in your typical polygon-based 3d engine, you don't actually use multiple lenses in your calculations so we need to find a way to "fake" the illusion that the light is glaring off of several lenses within your virtual camera.

Ingredients:
The position of a light in screen coordinates(lx,ly).
The position of the center of the screen in screen coordinates(cx,cy).

Now that we have these two positions, we can calculate the direction vector from the center of the screen to the position of the light.

Attached Image: fig1.png
// find the direction vector from the center to the light
vx = cx - lx;
vy = cy - ly;
Then normalize the direction vector so we can use it for scaling purposes.

NOTE: STORE THE LENGTH OF THE VECTOR...we'll call ours "length"

You'll also need several images to use as components of the effect. One of these images is typically a star-burst type of image. The others are usually halo type images. See below.

Attached Image: fig2.png
OK, so now we have three vectors:
cx,cy = center of the screen
lx,ly = position of the light
vx,vy = normalized direction vector from the center to the light

and the four images from above...

Here comes the easy part...

Simply scale the normalized direction vector by the length to get the center coordinate of where to draw the primary "star-burst" image.

vx = vx * distance;
vy = vy * distance;

If you are using blits, then you infer your top, left, right and bottom coordinates from this new center vector...for example an image that has dimensions of 128x128 you would do something like this...(plenty of room for optimizations here...)

top = vy - 64 + cy;
left = vx - 64 + cx;
right = vx + 64 + cx;
bottom = vy + 64 + cy;
// NOTE: MAKE SURE TO OFFSET BY THE CENTER POINT

Now, for each of the other elements of the lens flare, simply use variations of the length variable in the scaling of the direction vector:

I've found the following values to work quite well...

FlareLength ScaleImage Scale
Primary Flarelength1.0
First Halolength/2.5
Small Burstlength/3.25
Next Halolength/81.0
Next Burst-(length/2).5
Next Halo-(length/4).25
Next Burst-(length/5.5).25
Try playing around with different images, length scales and image scales. FYI...don't use divides, instead calculate the reciprocals and multiply by those...MUCH FASTER. (e.g. DIV2 = 1.0f/2.0f; newlength = length*DIV2;...this is the same as newlength = length / 2.0f, but around 15 times faster to process...)

Below you can see a shot of the effect at work...Notice the lack of proper alpha-blending, this was due to the fact that I slapped the lens flare test project together with C++ Builder using straight SRCAND-style "blits"...gets the point across and only took about a half-hour to code...

Attached Image: lens-flare-tut-shot1.jpg
For an actual high-quality example of this algorithm at work, be sure to download the Venom demo on March 2, 1998.

I hope you have enjoyed this short tutorial, if you have any questions or would like the source code to the C++ Builder project that this screen shot is from, send me an email.

- Alan Gordie


Version: 1.0
Date: February 20, 1999
Copyright © 1999


Introduction

A few authors recently have discussed methods of alpha blending. For those of you who are clueless as to what alpha blending is, I'd suggest reading some of the other alpha blending tutorials on GameDev.net for a more extensive tutorial. This will be a tough article to work through, and will require a lot of effort on your part to learn everything involved with creating beautiful flares, but the results will be well worth the effort.


Alpha Blending

The basic break-down of the effect is that you create a new color by splitting up two colors, source1 and source2, into their RGB components. You then take percentages of each color component (usually on a 0..255 scale), and add them up to create a destination color component:

Figure 1.

Dest_r = ((source1_r * alpha) + (source2_r * (255-alpha))/255;
Dest_g = ((source1_g * alpha) + (source2_g * (255-alpha))/255;
Dest_b = ((source1_b * alpha) + (source2_b * (255-alpha))/255;
When done correctly, you can overlay one semi-translucent image over another image. The next section will assume that you already have at least attempted to perform alpha blending.


Alpha Blending Speed-ups

Now if one were to attempt to perform fast alpha blending, you may find that the equations in Figure 1 are not too efficient. Some common speed-ups involve storing the inverse alpha value into a new variable so that it doesn't have to be recalculated each time, ie:

Figure 2.

int inverse_alpha = 255 - alpha;

Dest_r = ((source1_r * alpha) + (source2_r * inverse_alpha))/255;
Dest_g = ((source1_g * alpha) + (source2_g * inverse_alpha))/255;
Dest_b = ((source1_b * alpha) + (source2_b * inverse_alpha))/255;
Well, this gets rid of at least two subtractions. Still not good enough.. we may not even find a fast enough implementation of alpha blending in this article, but we definitely can beat Figure 1. On to new optimizations..

Examining Figure 2, we can clearly see that the biggest drawbacks in speed lie in the fact that 6 multiplications and 3 divides are performed. But one thing that really jumps out is the value 255. We won't assume that the compiler optimizes this code in any form and replace the three divides by bit shift operations.

Figure 3.

int inverse_alpha = 255 - alpha;

Dest_r = ((source1_r * alpha) + (source2_r * inverse_alpha)) >> 8;
Dest_g = ((source1_g * alpha) + (source2_g * inverse_alpha)) >> 8;
Dest_b = ((source1_b * alpha) + (source2_b * inverse_alpha)) >> 8;
Ahh, so we've reduced those divides into simple instructions which will execute quite fast. But those 6 muls still jump straight out at us.

Here is where things get interesting, we can eliminate at least three multiplications and one subtract instruction by storing our bitmap differently. The rest of these optimizations will apply only to those who don't need to constantly modify their alpha values.

Let's check out some sample data:

Figure 4.

Attached Image: alphamap.jpg
The color data is simply a composite of the red, green, and blue channels. You can see what those channels would look like in the lower left corner of Figure 4. I've added the coloring so it is easier to distinguish between the three channels, but normally they appear in grayscale format. The alpha data represents the level of translucency that the corresponding pixel in the color data should maintain when blitted onto another color surface.

Okay, so here is what we can do. We can use the same amount of space as before with minimal-to-no loss in quality. Simply pre-divide each pixel in the color data by it's corresponding alpha component FIRST.

Figure 5 illustrates how this can be done.

Figure 5.

/*

In mathematics, you learn something known as an distributive property of real numbers. We can illustrate that property as:

(A + B) C = AC + BC

Where A, B, and C are real numbers.

Now lets see what that looks like in code by expanding the source below :

*/

int inverse_alpha = 255 - alpha;

Dest_r = (source1_r * alpha)/255 + (source2_r * inverse_alpha)/255;
Dest_g = (source1_g * alpha)/255 + (source2_g * inverse_alpha)/255;
Dest_b = (source1_b * alpha)/255 + (source2_b * inverse_alpha)/255;
As you can see, Figure 5 is virtually identical to Figure 1. We simply store the values (source1_r * alpha)/255, (source1_g * alpha) / 255, and (source2_b * alpha) / 255 in our original bitmap format.

Also, our original bitmap format will include some sort of alpha channel. As you can see in the previous code samples, we are only using it in the line:

int inverse_alpha = 255 - alpha;
Well, we can simply replace every value in the alpha map by the inverse alpha. Thereby eliminating the need to calculate the value for the inverse alpha.

The equation then gets reduced to:

Figure 6.

// Remember that alpha now contains the inverse value (255-alpha)

Dest_r = source1_r + (source2_r * alpha) >> 8;
Dest_g = source1_g + (source2_g * alpha) >> 8;
Dest_b = source1_b + (source2_b * alpha) >> 8;
Are we done yet? For all intents and purposes, yes. I have additional methods of optimization that I haven't tested yet, so I'd prefer not to introduce them at this point in time. Don't forget that you may additionally examine the assembly output of your alpha blending routine, and optimize it by hand.


Lens Flare Translucency

Well, I just explained to you everything you'll need to know about alpha blending. Now put that aside, because for lens flares we will utilize additive blending. Blending in this manner is what causes flares to appear purely white over bright surfaces.

Additive blending is done in unoptimized form as follows:

Figure 7.

Dest_r = source1_r + source2_r 
Dest_g = source1_g + source2_g
Dest_b = source1_b + source2_b

if (Dest_r > 255) Dest_r = 255;
if (Dest_g > 255) Dest_g = 255;
if (Dest_b > 255) Dest_b = 255;
There is an assumption made in the above equation that may cause problems if you try to implement this directly.

We assume that Dest_r, Dest_g, and Dest_b are capable of handling numbers greater than 255. One solution could be to leave those values as short 16-bit integers. Though, there may be a better brute-force solution which will allow us to use 8-bit integers and get rid of those nasty "if" statements.

Create a ( 256+256) byte look-up TABLE as follows:

Figure 8.

unsigned char alut [512];

for (int i = 0; i < 512; i++)
{
alut [i] = i;
if (i > 255) alut [i] = 255;
}
Here is how the TABLE looks:

alut [0] = 0;
alut [1] = 1;
alut [2] = 2;
...
alut [255] = 255;
alut [256] = 255;
...
alut [511] = 255;
Our additive blending equation then is rewritten as follows:

Figure 9.

Dest_r = alut [source1_r + source2_r];
Dest_g = alut [source1_g + source2_g];
Dest_b = alut [source1_b + source2_b];
If anybody finds a more reduced implementation of additive blending, please let me know.

I have left out some rather vital information for putting the destination pixel back together, but this can be commonly found in most alpha blending tutorials for various pixel formats.


Parts of a Flare

1. Main Flare - The main flare is the most prominent portion of a lens flare. It is what should typically come to mind when you think of lens flares. See below for several examples

Attached Image: mainfl.jpg
The main flare can be broken down into four distinct parts:

DescriptionWhat it looks likeHow it can be done manually
GlowAttached Image: part_glow.jpgThe glow portion of the main flare is simply a gradient from a bright color to a darker color. Use the following code to calculate a good intensity falloff for the glow if you plan on doing it by "hand":
   for (int i = 0; i < max_radius; i++) {
Intensity = exp(-i*i*0.006)*0.50 + exp(-i*0.03)*(1-0.50);
}
RingAttached Image: part_ring.jpgThe ring is simply an antialiased circle of some dark color. Here is where you can get creative by using gradients of color for the ring. See the above examples of main flares for some rings illustrating this concept. The variable portion of this ring is its radius and color.
StreaksAttached Image: part_streak.jpgNot the best example of a streak, but it is basically the primary "streak" of light that radiates out from the center of the flare. Note that all streaks are evenly spaced out. So, for example, 4 streaks are spread apart at angles of 90 degrees. Bright white colors usually work best.
RaysAttached Image: part_rays.jpgThe rays are also antialiased. They are simply lines of random length that radiate out from the center of the flare.
CompositeAttached Image: part_total.jpgJust a crude flare I made with a composite of the quickly (and I do mean quickly) drawn flare illustrations in the columns above. Not bad. But what is important is that you can see how the components interact with each other. Note that additive blending is used to combine the components.
2. Secondary Parts

The rest of the flare varies widely between flares. These parts usually appear as small, very translucent, circles.

Attached Image: secfl.jpg
Here, we don't really see too much variation in relation to how the circles are created. The diagram below gives you a basic insight as to how they are created. Generally, you create a ring of any color, and smoothly transition to another color using smaller and smaller rings until your final ring has a radius of 0. Note that you'll need to utilize a blur filter to make it look the most correct.

Figure 11.

Attached Image: secdemo.jpg
The optimal way to create the secondary rings is to create a function which takes at least a radius, an outer ring color, and an inner ring color. You then interpolate the R, G, B values starting from the outer ring color and continuing until you reach the final inner ring color. Looking at figure 11, we could say that the outer ring's color is some shade of purple, while the inner ring color is pure black.


Breaking the Screen Down : Cartesian Coordinates

In order to easily draw lens flares, we'll need to take notice of a property that they all share.

Examine the flare below, and see if you can determine a trend in the positions of the primary flare in relation to the secondary parts of the flare.

Attached Image: fig12.png
If you haven't noticed yet, all parts of the flare pivot around the center of the image. So as such, we will utilize a coordinate system that is conducive to this sort of pivoting. The Cartesian coordinate system, as demonstrated in figure 10, is perfect for the creation of lens flares because all points are centered around the middle of the graph.

Figure 10.

Attached Image: graph.gif
In order to utilize a Cartesian coordinate system on-screen, we simply set our origin to (X = SCREEN_WIDTH / 2, Y = SCREEN_HEIGHT / 2). We then plot all points on screen in Cartesian coordinates, with the x-extents being +/-(SCREEN_WIDTH / 2) and the y-extents being +/-(SCREEN_HEIGHT / 2).

Converting back to screen coordinates is also easy. We simply add our (X, Y) values for the origin to each (X, Y) pair in Cartesian coordinates.


The Lens Flare Vector

The lens flare we create will have parts that all lie on a straight line. The method that I like to utilize when positioning parts of the lens flare is to set position coordinates only for the main flare using a standard (h, k) center, plotted in cartesian coordinates. All other secondary portions are inverted and/or scaled (h, k) coordinates. While you may use a 2d vector, or a 3d vector if you want the flare to have capabilities of being hidden by geometry (such as a sun behind a star), I find this to be the easiest.

Figure 11.

Attached Image: flarevect.gif
Remember, (h, k) are simply points in the cartesian coordinate system you set up. They more specifically represent the CENTER of the main flare. The other coordinates are simply scaled and/or inverted h, k coordinates. Note that you can pick out virtually any number to scale the coordinates, and invert any coordinate set as you wish.

What happens when you invert the h, k coordinates is that if your main flare is in the top right corner, the new coordinates would be in the bottom left corner of the screen.

Main Flarehk
Secondary Flare-h-k
Secondary Flare-h / 2-k / 2
Secondary Flare-h / 1.2-k / 1.2
Secondary Flareh / 3.2k / 3.2
Converting the (h, k) coordinates to screen coordinates is exactly as stated before. You simply add:

Screen_coord_x = h + SCREEN_W / 2;
Screen_coord_y = k + SCREEN_H / 2;
Where SCREEN_W is the screen width and SCREEN_H is the screen height.

When blitting the flare portions to the screen using additive blending, don't forget to blit the flare you generate with the new screen coordinates being in the absolute center of the sprite, rather than the top right corner like most blitting functions work.


Using Graphics Programs to Create Flares

Well, we just spent a considerable amount of time talking about how you can graphically generate your very own lens flares. Well, it may not be very practical for most programmers to spend the time writing routines to generate lens flares. So what you need to do is grab a good graphics program like Photoshop and create your own using filters!

Simply render them to a black background and use a crop tool to pick out only the parts you want.

But, for those of you who can't afford an expensive graphics program like Photoshop, I've taken the liberty of creating a few flares for you.

Just click on the flare you like. They are stored in 32-bit TARGA files.

Attached Image: fig1.png
Good Luck!

The tutorial and the code are written by User:Sphinkie.

If you implement this effect in your project, please send me a screenshot, I'll add it here.

A lens-flare effect is an optical effect that appears on the lens of a camera when a strong light is in the field of view.

Lensflare.jpg

The effect is composed with several elements: halos (like a shining circle) or "burst" (like a shining star). You need images with transparency for this (tga or png files usually).

lensflare5.jpg

lensflare6.jpg

On computers, this effect can be rendered with 2D or with 3D.

The 2D way

A plane is created in front of the camera. Halos and bursts will be drawn on this plane.

  • C : The point at the X-Y center of the screen.
  • L : The point at the X-Y position of the light source (projected on the screen).
  • Now, draw a line between C and L with a distance d. The halos are on this line, at d/2 (for instance).

 
The article that help me for the theory can be found on gamedev.net (Austin Game Conference).

The 3D way

A 3D line is created between a point in front of the camera and the light source. The halos and burst are drawn along this line.

  • L: The Light source Position
  • C: The Camera position
  • R: The Distance between C and L.
  • F: This point is in fromt of the camera, at the same distance thna the light. (Draw a line following the Camera direction, with a distance R).

 

  • Now you can draw a line between L and F with a distance d. Halos will be drawn on this line, at d/2, d/3, etc.

 
A good rendering will be obtain by placing the following elements:

Type  Position Scale 
 Main  d        1    
 halo  d/2      1/2    
 burst d/3      1/4    
 halo  d/5      1
 burst -d/2     1/2
 halo  -d/4     1/4
 burst -d/5     1/4

 

Code

 

The following code will use the 3D method.

This code is not perfect, because the effect is a little bit far from the camera and some 3D objects can pass between the camera and the effect. (And the effect is supposed to take place, on the camera lens itself (!).

This code is not very difficult, but need a lot of try, and adjustement to have a nice rendering.

Usage

This is the LensFlare class.

You create the lensflare with the createLensFlare function, and then call update in your frameListener, to update the effect according to the camera position, and direction.

LensFlare *lensflare;
 
 bool frameStarted(const FrameEvent& evt)
 {
  ..
  ..
  lensflare->update();
 }
 
 CreateScene(void)
 {
   ...
   ...
   lensflare = new LensFlare(Vector3(0,10,0),mCamera, mSceneMgr);
 }

This code will also need a dedicated material file.

Source files

LensFlare.h

LensFlare.cpp

lensflare.material


Alias: Displaying_LensFlare


Contributors to this page: jacmoe111147 points  and OgreWikiBot .
Page last modified on Wednesday 22 of December, 2010 15:49:04 UTC by jacmoe111147 points .


The content on this page is licensed under the terms of the Creative Commons Attribution-ShareAlike License.
As an exception, any source code contributed within the content is released into the Public Domain.

 

Click this link to toggle the Report Page Issue Form

Search by Tags

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值