The following is a quickly thrown together page on a lensflare shader. This has some corrections to the one posted in the newsgroup a while ago. (The corrections are mostly because of a quick conversion from noise() to cellnoise() I did and forgetting to change some of the numbers so that cellnoise would get different integer numbers. If you look in that code you'll see what I mean.)
I will put together a better web page for this later, but for now I'll just include the code below which was tested on PRMan and BMRT:
lensflare.h
/* * Copyright (c) 1999 Thomas E. Burge. All rights reserved. * * To make the light effects, a bilinear surface using the lensflare * shader must be placed infront of the camera and light shaders * that include lensflare.h in their parameter lists must be used. * * The surface represents the visible portion of the clip-near plane. * We will refer to it as the "FX plane" in this set of shaders. * Code for the lighting effects is located in lensflare.sl. The * parameters are set in spotlight. The parameters are then accessed * by lensflare.sl using messages. * * The term "ghost image" is used to describe the circles and hexagons * randomly placed along the line defined by the light's location * in screen space and the center point (with zero center offset, the * center point is the center in screen space). The term "ghost image" * is a bit long for parameter names so the term "spot" is used instead. * The term "spot" is from "flare spots" which is another name for * ghost images. * * When using this shader to create the look of a bright flare from an * almost off-camera light source, remember that the picture is most * likely to be an image of extreme contrast in light levels. When * using a real camera to capture the image of a flare and surrounding * areas, the areas of the image not in the glow of the flare are left * underexposed. If the picture were taken to get enough light for the * darker areas, the area around the flare would be overexposed and * become featureless. In computer rendering terms, the geometry in * the underexposed areas should be made darker than normal. In a * rendering it is possible to have forground geometry well lit and * have a background flare, but this lighting setup would ruin the high * contrast normally associated with such pictures. */ /* I'll put the parameters needed for a poor man's lensflare shader. * Add the following to the parameter list of a light shader that you * would like to have glow and lensflare effects support: * * #include "lensflare.h" * * I have included a modified spotlight shader that you can refer to. * Also refer to the lenflare.sl surface shader for RIB setup information. */ uniform float GlowIntensity = 0.0; uniform float GlowDecay = 1.0; uniform float GlowSpread = 1.0; uniform color GlowColor = color (1.0,1.0,1.0); uniform float LensFlareIntensity = 0.0; uniform color LensFlareSpotColor = color (0.62745,0.62745,1.0); uniform string LensFlareColorMap = ""; uniform float LensFlareColorMapOrientation = 0.0; uniform float LensFlareColorMapBlur = 0.0; uniform float LensFlareNSpots = 10.0; uniform float LensFlareMinSize = 0.10; uniform float LensFlareMaxSize = 1.0; uniform float LensFlareColorSpread = 0.5; uniform float LensFlareFocus = 0.6; uniform float LensFlareLength = 1.0; uniform float LensFlareCenterXOffset = 0.0; uniform float LensFlareCenterYOffset = 0.0; /* Added verbosity from another shader: * * About a year ago (begining of 1997) there were postings in * comp.graphics.rendering.renderman concerning lens flares. * I believe this was because of the 3.6 release of PRMan * which included a compiled shader that supported Alias-like * glow effects. Previously at the Birds of a Feather meeting * at SIGGRAPH '96, an anual meeting of many RenderMan users in * the industry, lens flares had also been discussed. In the * meeting it was mentioned that someone from ILM had cataloged * lens flares from various movies as a technique of creating * computer lens flares with a "look" that would be based * on a real model. Also mentioned was the lack of anything * really describing a physical model for lens flares and that * all that could be done was to simply draw a line of randomly * placed circles. Most books just say that lens flares are * things to avoid, but not how light rays going through a lens * assembly produce ghost images. * * As a little side note, when the subject of lens flares came * up it was pointed out that lens flares tend to be overused * and that everyone was just trying to imitate a scene in * "Blade Runner". I haven't rented "Blade Runner" to check * this out, but the notion of cataloging various real lens * flares appears to be the best approach. This is based on * the notion that any underlying simulation of a real lens * assembly during rendering would be much less efficient than * code that would simply use some random placements of flare * spots to create the look of a lens flare. * * Of course today someone assembling a catalog of lens flares * would need to be careful to refer to older movies as today's * lens flares maybe digitally composited in. Basically why * emulate an emulation. * * Using the technique of lining up a set of circles appears to * be a valid technique used in the industry and is the one used * here in this shader. This shader does not attempt to explain * how the rays going through a lens assemble at times to create * what used to be considered undesirable ghost images and flares. * Oddly these little flaws of aperture-shaped spots have become * highly sought after. They can create a nice effect, but it * seems that if a flare must be used in an image, the flare spots * should be avoided in favor of a flare's glow with a wide * contrast in lighting levels to create a more dramatic less * stereotypical computer image. */
lensflare.sl
/* * Copyright (c) 1999 Thomas E. Burge. All rights reserved. * * Poor man's lensflare shader. * * float GlowIntensity = 0.0; * Setting to zero disables glow effect. * * float GlowSpread = 1.0; * Controls the amount of light bleeding onto surrounding * areas of an image. * * color GlowColor = color (1.0,1.0,1.0); * GlowColor filters the output color by multiplying * each channel of the output color by its corresponding * channel value given to GlowColor. Think of GlowColor * as three percentage values for the three output * color channels. * * float LensFlareIntensity = 0.0; * If nonzero, sets an intensity value and enables * a lens flare effect with light sources that support * this shaders set of LensFlare parameters. The * LensFlare options describe a series of ghost images * randomly placed in a line connecting the light * source in screen space and the center screen point. * * color LensFlareSpotColor = (basically a blue-purple color) * The color that the ghost images (flare spots) will * center on. Variations are made to LensFlareSpotColor * based on LensFlareColorSpread. * * string LensFlareColorMap = ""; * Map an image to each ghost image (flare spot). * Could be used to add a highlight inside each ghost * image. Polar coordinates based on the center of each * circle are used to map the texture into place. * * float LensFlareColorMapOrientation = 0.0; * The number of degrees to rotate LensFlareColorMap. * * float LensFlareColorMapBlur = 0.0; * Amount to blur LensFlareColorMap using swidth * twidth. * * float LensFlareNSpots = 10.0; * The number of ghost images (flare spots) to appear * along the line of length LensFlareLength. * * float LensFlareMinSize = 0.10; * float LensFlareMaxSize = 1.0; * Sets the minimum and maximum radii to choose from when * creating the number of ghost images assigned to * LensFlareNSpots. The radii are measured in units in * screen space. * * float LensFlareColorSpread = 0.5; * The amplitude of the random variation allowed to be * applied equally to each color channel used to paint * the ghost images. * * float LensFlareLength = 1.0 * The length of the line that ghost images are placed on. * LensFlareLength is measured screen space units. * * float LensFlareCenterXOffset = 0.0; * Offset camera center in screen space by a plus * or minus value. In screen space the range * Y values in view is -1 to 1. * * float LensFlareCenterYOffset = 0.0; * Offset camera center in screen space by a plus * or minus value. In screen space the range of * X values in view is -1 to 1. * */ void Glow( uniform float glowIntensity; uniform float glowDecay; uniform float glowSpread; uniform color glowColor; point P; /* Point on FX plane being shaded . */ vector L; /* Light's L vector. */ point ps; /* P's position in screen space. */ point pl; /* Light's origin in screen space. */ float pr; /* P's radius from center of light. */ output color c; output color o; ) { float intensity; /* Add new glow features here. */ intensity = glowIntensity * glowSpread * max(0,1 - pr) / pow(length(L), glowDecay); c += glowColor * intensity; o += c; } void LensFlare( uniform float lfIntensity; uniform float glowDecay; uniform color lfSpotColor; uniform string lfColorMap; uniform float lfColorMapOrientation; uniform float lfColorMapBlur; uniform float lfNSpots; uniform float lfMinSize; uniform float lfMaxSize; uniform float lfColorSpread; uniform float lfFocus; uniform float lfLength; uniform float lfCenterXOffset; uniform float lfCenterYOffset; point P; /* Point on FX plane being shaded . */ vector L; /* Light's L vector. */ point ps; /* P's position in screen space. */ point pl; /* Light origin in screen space.*/ float pr; /* P's radius from center of light. */ output color c; output color o; ) { float distance,radius,nearness; vector direction; point origin,spot; color cc,spotcolor; float n,ss,tt,x,y,costheta,sintheta; float c1,c2,c3; float ds,dt; origin = point( lfCenterXOffset, lfCenterYOffset, 0 ); direction = normalize(origin-pl); cc = 0; for ( n=0; n<lfNSpots; n+=1 ) { /* Find ghost image's distance (the spot's distance) from * the light position. */ /* You could setup distance to also have a negative value which * will draw ghost images also in the direction away from center. * Most packages draw ghost images between the light * the camera center and beyond to off screen. Pixar in Toy * Story drew spots anywhere along the line connecting the light * source position with center, not just on the ray starting at * the light source going towards center, I haven't seen anyone else * do it that way though. * Notice the center can be offset in this shader, so it is referred * to as the origin. */ distance = 3*lfLength*cellnoise(n*2); /* Find radius of spot. */ radius = 0.1*((lfMaxSize-lfMinSize)*cellnoise(n*3)+lfMinSize); /* Find position of nth spot. */ spot = pl + distance*direction; nearness = length( ps - spot ); if ( lfColorMap == "" ) { /* Find spot's color. */ c1 = comp(lfSpotColor,0) + lfColorSpread *(2*cellnoise(n*4)-1); c2 = comp(lfSpotColor,1) + lfColorSpread *(2*cellnoise(n*5)-1); c3 = comp(lfSpotColor,2) + lfColorSpread *(2*cellnoise(n*6)-1); spotcolor = color( clamp(c1,0,1), clamp(c2,0,1), clamp(c3,0,1) ); cc += spotcolor * lfIntensity * (1-smoothstep(max(0,radius-0.1*(1-lfFocus)),radius,nearness)) / pow(length(L), glowDecay); } else { ss = (xcomp(ps) - xcomp(spot))/radius + 1.0; tt = (ycomp(ps) - ycomp(spot))/radius + 1.0; ss /= 2.0; tt /= 2.0; /* Rotate (ss,tt) about the center (0.5,0.5). */ costheta = cos(lfColorMapOrientation); sintheta = sin(lfColorMapOrientation); x = ss - 0.5; y = tt - 0.5; ss = x * costheta - y * sintheta + 0.5; tt = x * sintheta + y * costheta + 0.5; spotcolor = texture( lfColorMap, ss,tt, /* (0,0) */ #ifdef LATER ss+ds,tt, /* (1,0) */ ss,tt+dt, /* (0,1) */ ss+ds,tt+dt, /* (1,1) */ #endif "swidth",lfColorMapBlur, "twidth",lfColorMapBlur ); } cc += spotcolor * lfIntensity * (1-smoothstep(max(0,radius-0.1*(1-lfFocus)),radius,nearness)) / pow(length(L), glowDecay); } c += cc; o += c; } surface lensflare() { uniform float GlowIntensity; uniform float GlowDecay; uniform float GlowSpread; uniform color GlowColor; uniform float LensFlareIntensity; uniform color LensFlareSpotColor; uniform string LensFlareColorMap; uniform float LensFlareColorMapOrientation; uniform float LensFlareColorMapBlur; uniform float LensFlareNSpots; uniform float LensFlareMinSize; uniform float LensFlareMaxSize; uniform float LensFlareColorSpread; uniform float LensFlareFocus; uniform float LensFlareLength; uniform float LensFlareCenterXOffset; uniform float LensFlareCenterYOffset; normal Nf; vector V; point pn; /* P's position in screen space. */ point pl; /* Light's position in screen space. */ float pr; /* P's radius from center of light. */ color c,o; Nf = faceforward( normalize(N), I ); V = -normalize(I); pn = transform( "screen", P ); setzcomp( pn, 0.0 ); c = 0.0; o = 0.0; illuminance( P, vector "NDC" (0,0,1), PI ) { pl = transform( "screen", P+L ); setzcomp( pl, 0.0 ); pr = length( pl - pn ); /* BMRT does not like the following combined if, but PRMan * has no problem with it. But to be compatible I'll * do two nested if's. */ #if 0 if ( lightsource( "GlowIntensity", GlowIntensity )!=0 && GlowIntensity!=0.0 ) #endif if ( lightsource( "GlowIntensity", GlowIntensity )!=0 ) { if ( GlowIntensity!=0.0 ) { GlowDecay = 1.0; GlowSpread = 1.0; GlowColor = color (1.0,1.0,1.0); lightsource( "GlowDecay", GlowDecay ); lightsource( "GlowSpread", GlowSpread ); lightsource( "GlowColor", GlowColor ); Glow( GlowIntensity, GlowDecay, GlowSpread, GlowColor, P, L, pn, pl, pr, c, o ); } } #if 0 if ( lightsource( "LensFlareIntensity", LensFlareIntensity )!=0 && LensFlareIntensity!=0.0 ) #endif if ( lightsource( "LensFlareIntensity", LensFlareIntensity )!=0 ) { if ( LensFlareIntensity!=0.0 ) { LensFlareSpotColor = color (0.62745,0.62745,1.0); LensFlareColorMap = ""; LensFlareColorMapOrientation = 0.0; LensFlareColorMapBlur = 0.0; LensFlareNSpots = 10.0; LensFlareMinSize = 0.10; LensFlareMaxSize = 1.0; LensFlareColorSpread = 0.5; LensFlareFocus = 0.6; LensFlareLength = 1.0; LensFlareCenterXOffset = 0.0; LensFlareCenterYOffset = 0.0; lightsource( "LensFlareSpotColor", LensFlareSpotColor ); lightsource( "LensFlareColorMap", LensFlareColorMap ); lightsource( "LensFlareColorMapOrientation", LensFlareColorMapOrientation ); LensFlareColorMapOrientation = radians(LensFlareColorMapOrientation); lightsource( "LensFlareColorMapBlur", LensFlareColorMapBlur ); lightsource( "LensFlareNSpots", LensFlareNSpots ); lightsource( "LensFlareMinSize", LensFlareMinSize ); lightsource( "LensFlareMaxSize", LensFlareMaxSize ); lightsource( "LensFlareColorSpread", LensFlareColorSpread ); lightsource( "LensFlareFocus", LensFlareFocus ); lightsource( "LensFlareLength", LensFlareLength ); lightsource( "LensFlareCenterXOffset", LensFlareCenterXOffset ); lightsource( "LensFlareCenterYOffset", LensFlareCenterYOffset ); LensFlare( LensFlareIntensity, GlowDecay, LensFlareSpotColor, LensFlareColorMap, LensFlareColorMapOrientation, LensFlareColorMapBlur, LensFlareNSpots, LensFlareMinSize, LensFlareMaxSize, LensFlareColorSpread, LensFlareFocus, LensFlareLength, LensFlareCenterXOffset, LensFlareCenterYOffset, P, L, pn, pl, pr, c, o ); } } } Oi = o; Ci = c; }
Example spotlightLF.sl
light spotlightLF( float intensity = 1; color lightcolor = 1; point from = point "shader" (0,0,0); point to = point "shader" (0,0,1); float coneangle = radians(30); float conedeltaangle = radians(5); float beamdistribution = 2; #include "lensflare.h" ) { float atten, cosangle; uniform vector A = (to-from) / length(to-from); illuminate (from, A, coneangle) { cosangle = (L.A) / length(L); atten = pow( cosangle, beamdistribution) / (L.L); atten *= smoothstep( cos(coneangle), cos(coneangle-conedeltaangle), cosangle ); Cl = atten * intensity * lightcolor ; } }
glow.rib
#Display "bmrt640x480.tif" "file" "rgba" Display "prman320x200.tif" "file" "rgba" #Display "a.tif" "framebuffer" "rgba" #Format 640 480 1 Format 320 200 1 #Format 8 5 1 Clipping 0.001 1000.1 #PixelSamples 1 1 Projection "perspective" WorldBegin LightSource "ambientlight" 2 "intensity" [1] #AttributeBegin # Surface "paintedplastic" "texturename" "grid2.tex" # Surface "seegrid" # ShadingRate 1.0 # CoordSysTransform "NDC" # Patch "bilinear" "P" [0 0 0 1 0 0 0 1 0 1 1 0] #AttributeEnd # # Another way based on what was given in SIGGRAPH '98 Course notes: # AttributeBegin # ShadingRate 1.0 # Surface "lensflare" # CoordSysTransform "NDC" # Polygon "P" [0 0 0 1 0 0 1 1 0 0 1 0] # AttributeEnd # # BMRT requires the following instead of the above patch: # # AttributeBegin # ShadingRate 1.0 # Surface "lensflare" # CoordSysTransform "camera" # # Let n = 1.0001 * ClippingNearValue # # w = n * tan(fov/2) * xres/yres, if xres >= yres # # w = n * tan(fov/2) * yres/xres, if yres > xres # # h = n * tan(fov/2) # Patch "bilinear" "P" [ -w h n w h n -w -h n w -h n ] # AttributeEnd # Declare "GlowIntensity" "uniform float" Declare "GlowDecay" "uniform float" Declare "GlowSpread" "uniform float" Declare "GlowColor" "uniform color" Declare "LensFlareIntensity" "uniform float" Declare "LensFlareSpotColor" "uniform color" Declare "LensFlareNSpots" "uniform float" Declare "LensFlareMinSize" "uniform float" Declare "LensFlareMaxSize" "uniform float" Declare "LensFlareColorSpread" "uniform float" Declare "LensFlareFocus" "uniform float" Declare "LensFlareLength" "uniform float" Declare "LensFlareCenterXOffset" "uniform float" Declare "LensFlareCenterYOffset" "uniform float" Declare "LensFlareColorMap" "uniform string" Declare "LensFlareColorMapOrientation" "uniform float" #Little test to show where light is: #Translate -5 2 10 #Sphere .1 -.1 .1 360 LightSource "spotlightLF" 1 "lightcolor" [1 1 1] "intensity" [1] "from" [-5 2 10] "GlowIntensity" [10.0] "GlowDecay" [2.0] "LensFlareIntensity" [10.0] # "LensFlareFocus" [1.0] "LensFlareFocus" [.90] # "LensFlareNSpots" [5.0] "LensFlareMaxSize" [5.0] # "LensFlareSpotColor" [0.75 0.75 0.75] # "LensFlareColorMap" "grid2.tex" # "LensFlareColorMapOrientation" [45] AttributeBegin ShadingRate 1.0 Surface "lensflare" CoordSysTransform "camera" Patch "bilinear" "P" [ -0.0016 0.0010001 0.0010001 0.0016 0.0010001 0.0010001 -0.0016 -0.0010001 0.0010001 0.0016 -0.0010001 0.0010001 ] AttributeEnd # # Clip far plane # #AttributeBegin #ShadingRate 1.0 #Surface "paintedplastic" "texturename" "../../mytextures/grid2.tiff" #Surface "paintedplastic" "texturename" "grid2.tex" #CoordSysTransform "camera" #Patch "bilinear" "P" [ -1600 1000 1000 # 1600 1000 1000 # -1600 -1000 1000 # 1600 -1000 1000 ] #AttributeEnd #AttributeBegin # Surface "paintedplastic" "texturename" "grid2.tex" # ShadingRate 1.0 # CoordSysTransform "NDC" # Patch "bilinear" "P" [0 0 1 1 0 1 0 1 1 1 1 1] #AttributeEnd WorldEnd