Legacy:Bloom - Unreal Wiki

# Legacy:Bloom

### From Unreal Wiki, The Unreal Engine Documentation Site

A bloom is a lenticular halo. This page shows how to implement this visual effect in UT2004.

Blooms are another type of effect seen around lights. While coronas form the fuzzy glow you see around a light at night, or the rays which seem to shoot out from the light of the sun, blooms are a color-banded halo which is usually visible surrounding the corona. They are only seen when a person's pupils are dilated enough. (And lens flares are a natural phenomena that occurs when light is refracted many times by some sort of lens, creating several images of geometric shapes to appear from a single light source. Given that fact, you'll find that lens flares are often over used and appear where they ought not to in 3D graphics. In other words, you need a compund lens system for a lens flare. They aren't seen purely with the naked eye.)

See the result on the Legacy:Bloom/Screenshots page, or Download here (don't post public, please). Use for whatever you want, but it would be nice if you let me know and add me in the credits (at least for the idea).

## Code

At first, let take a look at the mutator:

    //-----------------------------------------------------------
    // Bloom Mutator
    // Creates Interaction and handles
    // rendering of the bloom-texture
    //-----------------------------------------------------------
    #exec OBJ LOAD FILE=DreamTex.utx
    class Bloom extends Mutator;

    var bool bAffectSpectators; // If this is set to true, an interaction will be created for spectators
    var bool bAffectPlayers; // If this is set to true, an interaction will be created for players
    var bool bHasInteraction;
    var ScriptedTexture st;
    var Actor bMesh;
    var BloomInteraction BI;

    function PreBeginPlay()
    {
        //Log("ICU Mutator Started"); // Always comment out your logs unless they're errors
    }

    simulated function Tick(float DeltaTime)
    {
        local PlayerController PC;

        // If the player has an interaction already, exit function.
        if (bHasInteraction)
            Return;
        PC = Level.GetLocalPlayerController();

        // Run a check to see whether this mutator should create an interaction for the player
        if ( PC != None && ((PC.PlayerReplicationInfo.bIsSpectator && bAffectSpectators) || (bAffectPlayers && !PC.PlayerReplicationInfo.bIsSpectator)) )
        {
            st=ScriptedTexture(Level.ObjectPool.AllocateObject( class'ScriptedTexture' ) );
            st.Client=self;
            //change this for different 'bloom-map' qualities
            st.SetSize(256,256);

            PC.Player.InteractionMaster.AddInteraction("Dreamland.BloomInteraction", PC.Player); // Create the interaction
            bHasInteraction = True; // Set the variable so this lot isn't called again
            bMesh=Spawn(class'BloomMesh');
            //A FinalBlend with FB_Brighten
            FinalBlend(bMesh.Skins[0]).Material=st;

            BloomInteraction(PC.Player.LocalInteractions[PC.Player.LocalInteractions.Length-1]).St=st;
            BloomInteraction(PC.Player.LocalInteractions[PC.Player.LocalInteractions.Length-1]).BMesh=bMesh;
            BI=BloomInteraction(PC.Player.LocalInteractions[PC.Player.LocalInteractions.Length-1]);
        }
    }

    event RenderTexture(ScriptedTexture Tex)
    {
        local PlayerController PC;
        local vector camloc;
        local Rotator camrot;
        local Actor camactor;
        local Color tAlpha;

        talpha.R=238;
        talpha.G=238;
        talpha.B=238;

        PC = Level.GetLocalPlayerController();
        PC.PlayerCalcView(camactor,camloc,camrot);
        //log("RenderEvent");

        Tex.DrawPortal(0,0,Tex.USize,Tex.VSize,camactor,camloc,camrot,PC.DefaultFOV,false);
        //Lightfilter is a FinalBlend with FB_Darken
        //The Light-threshold is controlled via talpha;
        //Colored Bloom should be possible with unequal RGB values (untested)
        Tex.DrawTile(0,0,Tex.USize,Tex.VSize,0,0,16,16,FinalBlend'DreamTex.Lightfilter',talpha);

    }

    DefaultProperties
    {
         bAffectSpectators=true
         bAffectPlayers=true
         RemoteRole=ROLE_SimulatedProxy
         bAlwaysRelevant=true
         FriendlyName="Bloom"
         Description="Adds Bloom to the gameplay."
    }

This mutator creates an interaction, which i'll discuss later. Additionally a simple rectangle mesh is spawned. The only spcial thing aboput this mesh is, that its UV-Coordinates do not cover the whole range, but only a horizontal 4:3 part of it. It took some time to figure out that a mesh with a full UV-Range will squeeze the bloom.

This mutator also is responsible for rendering the ScriptedTexture which is uses as the material for the FinalBlend which is in turn the skin of the bloom mesh.

The basic idea is to render the whole frame again into the ScriptedTexture and then filter the bright parts with a FinalBlend (FB_Darken). But that just the first part.

    //-----------------------------------------------------------
    // Renders the bloom-mesh several times to create a blur effect
    //-----------------------------------------------------------
    class BloomInteraction extends Interaction;

    var ScriptedTexture St;
    var Actor BMesh;
    var float dist;
    var float fov;
    var float dev;
    var bool bEnableBloom,bXBloom;
    var float devX;
    var vector CamPos,X,Y,Z;
    var Rotator CamRot;

    event NotifyLevelChange()
    {
            Master.RemoveInteraction(self);
            assert( St != None );
            ViewportOwner.Actor.Level.ObjectPool.FreeObject( St );

        //  (destroy any actors spawned by this interaction)
        //  (clean up any uscript objects that have been loaded)
    }

    function bool KeyEvent(EInputKey Key, EInputAction Action, FLOAT Delta )
    {

        if ((Key == IK_PageUp) && (Action == IST_Release)) devX+=0.25;
        if ((Key == IK_PageDown) && (Action == IST_Release) && (devX>0)) devX-=0.25;
        if ((Key == IK_Home) && (Action == IST_Release))bEnableBloom=!bEnableBloom;
        if ((Key == IK_End) && (Action == IST_Release))bXBloom=!bXBloom;
        if ((Key == IK_Insert) && (Action == IST_Release)) dev+=0.05;
        if ((Key == IK_Delete) && (Action == IST_Release) && (dev>0)) dev-=0.05;
        log("dev");
        log(dev);
    }

    //not used currently
    function RenderBloomLayer(Canvas c, float devX, float mZ, float mY, float dev)
    {
        bMesh.SetLocation(campos+(dist-devX)*vector(camrot)+(mZ*Z+mY*Y)*dev);
        C.DrawActor(bMesh,false,true);
    }

    function PostRender(canvas Canvas)
    {

        local Font UsedFont;
        if(!bEnableBloom)return;
            if(UsedFont==None){
            UsedFont = Font(DynamicLoadObject("SECFonts.Arial", class'Font'));
        }
            Canvas.Font = UsedFont;
            Canvas.FontScaleX = 1;
            Canvas.FontScaleY = 1;
        Canvas.Style=ViewportOwner.Actor.ERenderStyle.STY_Normal;

        Canvas.DrawColor.R=255;
        Canvas.DrawColor.G=255;
        Canvas.DrawColor.B=255;
        //calc this frame
        st.Revision++;

        Canvas.GetCameraLocation(campos,camrot);

        bMesh.SetRotation(camrot);
        ViewportOwner.Actor.GetAxes(camrot,X,Y,Z);

        //moved to init
        //FinalBlend(bMesh.Skins[0]).Material=st;

        Canvas.SetPos(10,240);

        if(!bXBloom)
        {
        bMesh.SetLocation(campos+(dist-devX)*vector(camrot)+Z*dev);
        Canvas.DrawActor(bMesh,false,true);
        bMesh.SetLocation(campos+(dist-devX)*vector(camrot)-Z*dev);
        Canvas.DrawActor(bMesh,false,true);
        bMesh.SetLocation(campos+(dist-devX)*vector(camrot)+Y*dev);
        Canvas.DrawActor(bMesh,false,true);
        bMesh.SetLocation(campos+(dist-devX)*vector(camrot)-Y*dev);
        Canvas.DrawActor(bMesh,false,true);
        Canvas.DrawText("XBloom");
        }
        else
        {
        bMesh.SetLocation(campos+(dist-devX)*vector(camrot)+(Z+Y)*dev);
        Canvas.DrawActor(bMesh,false,true);
        bMesh.SetLocation(campos+(dist-devX)*vector(camrot)+(-Z+Y)*dev);
        Canvas.DrawActor(bMesh,false,true);
        bMesh.SetLocation(campos+(dist-devX)*vector(camrot)+(-X-Y)*dev);
        Canvas.DrawActor(bMesh,false,true);
        bMesh.SetLocation(campos+(dist-devX)*vector(camrot)+(-Y+Z)*dev);
        Canvas.DrawActor(bMesh,false,true);
        Canvas.DrawText("DoubleBloom");
        }

        Canvas.SetPos(10,200);
        Canvas.DrawText("Bloom Active");

        //innerglow

        if(!bXBloom)
        {
        bMesh.SetLocation(campos+(dist-devX/2)*vector(camrot)+Z*dev/2);
        Canvas.DrawActor(bMesh,false,true);
        bMesh.SetLocation(campos+(dist-devX/2)*vector(camrot)-Z*dev/2);
        Canvas.DrawActor(bMesh,false,true);
        bMesh.SetLocation(campos+(dist-devX/2)*vector(camrot)+Y*dev/2);
        Canvas.DrawActor(bMesh,false,true);
        bMesh.SetLocation(campos+(dist-devX/2)*vector(camrot)-Y*dev/2);
        Canvas.DrawActor(bMesh,false,true);

        }
        else
        {
        bMesh.SetLocation(campos+(dist-devX/2)*vector(camrot)+(Z+Y)*dev/2);
        Canvas.DrawActor(bMesh,false,true);
        bMesh.SetLocation(campos+(dist-devX/2)*vector(camrot)+(-Z+Y)*dev/2);
        Canvas.DrawActor(bMesh,false,true);
        bMesh.SetLocation(campos+(dist-devX/2)*vector(camrot)+(-Y-Z)*dev/2);
        Canvas.DrawActor(bMesh,false,true);
        bMesh.SetLocation(campos+(dist-devX/2)*vector(camrot)+(-Y+Z)*dev/2);
        Canvas.DrawActor(bMesh,false,true);

        }
    }

    DefaultProperties
    {
        bVisible=true
        bActive=true
        dist=40
        fov=90
        dev=0.28
        devX=1.6
        bEnableBloom=true
        bXBloom=false
    }

Well, as it says, it renders the mesh at different positions to create a blur effect. The "Dev"-Property will just move the mesh the blur along the axis of the screen, while "DevX" will create a perspective-blur.

This is the BloomMesh-class:

    class BloomMesh extends Actor;

    DefaultProperties
    {
        DrawType =              DT_StaticMesh
        StaticMesh =            StaticMesh'DreamStat.BlurMesh'
        bUnlit =                true
        bStatic =               false
        bAlwaysRelevant =       true
        bNoDelete =             false
        bHardAttach =           true
        bHidden=                true
        Skins[0]=               FinalBlend'DreamTex.BloomFB'
        DrawScale =             1;
        bAcceptsProjectors =    false;
        RemoteRole =            ROLE_None;
    }

The Material "BloomFB" is a FinalBlend with FB_Brighten, but no material assigned to it, this is done in the code. "Lightfilter" is a Finalblend with FB_Darken an a pure white, or bright gray texture assigned to it.

## Notes

Recently I had the idea to use the BloomMesh for both filtering the lights and for the bloom-effect itself. Maybe this could solve the terrain, banding and overlit problems, as no redundant RGB data is stored in the texture. This method would apply the Lightfilter to the BloomMesh and not to the ScriptedTexture itself, as long as RenderTexture is called, after this, the material is switched back to BloomFB.

For larger mods, or TC's this could be made map, or even zone-dependant, to create individual effects. It could be even possibly to create distant-dependant bloom-effects, by changing the zone fog values to a near black before rendering and applying the scripted texture several times.

Objects nearer at the camera can receive a larger glow than objects more away.

I'm honestly not the one who is made to understand UScript-Replication, so if you see any issues that make my code network-incompatible, please let me know here.

### Problems

Sometimes, a ScriptedTexture does not render ParticleSystems correctly, displaying them boxes. Maybe this is an Z-Buffer Issue, or, the ScriptedTexture does not like certain Rendertypes of Particles, Subtractive, f.i.

It seems, a scripted Texture renders Terrain completely different as normally, making terrain extremely blurry with the bloom-effect.

Because the ScriptedTexture, rendering all the level again(and filtering the bright parts), has to be applied several times on canvas, dealing a hard punch on the framerate.

Actors, that are rendered via PostRender/DrawOverlay calls, are not visible for any calls of DrawPortal, of course. They still show up when the bloom is active, but they become transparent. I think i can solve this problem by creating a 'real' actor, giving it the same mesh/animation as the desired 1stPV-Actor(most commonly weapons models)

## Comments

j3rky: Interesting idea, and the screenshot looks promising... i'd like to see some code snippets :)

MythOpus: Looks rather beautiful. Good Job! :tup:

El Muerte: looks nice, but, is it worth it ?

Jarronis, The Vampiric Unicorn: Well, if you have around 80FPS and you don't know where to put them, why not? Of course, my hardware isn't good enough to manage this, but i'm working on a 'light versions' too.

Foxpaw: Hmm, I like the way that looks. It's almost like high dynamic range lighting.

DJPaul: Can I/we get the code for this? It'll be useful for a project that I am working on.

Jarronis, The Vampiric Unicorn: Of course, i'll make this open source, once i cleaned up the code, and added enough configurable properties.

j3rky: Actually, I think it would be more usefull to see some code that doesn't feature a lot of extras. That way the basic idea behind the implementation becomes more obvious. It shouldn't be your goal to provide a working does-it-all library - that's something the individual programmer can and should do.

Foxpaw: I'm finding it somewhat difficult to understand how this works from your code. What is the code for the BloomMesh class? It apparently must have some as it has something in it's Skins array which doesn't appear to be put there by the interaction or the mutator.

As best I can tell, you render the world onto your scripted texture, then LightFilter is drawn on top of it. Lightfilter I'm guessing is an all-white texture, so since it has FB_Darken, and is drawn mostly opaque, all but the most brightly lit parts of the scripted texture will be wiped out and made completely black.

Then, you draw the mostly-black-but-dim-in-areas-that-should-get-brightened mesh with FB_Brighten, to brighten only the areas that remain on the scriptedtexture. The mesh appears to be drawn several times with slight offsets, which I'm guessing is so that instead of directly brightening only the brighter areas, (with hard edges) the bloommesh is shifted around to make the lightening "spill over" onto nearby areas.

Is that correct? The BloomMesh class is missing and of course there's little information on what the materials used contain, so I'm guessing at how it works.

Graphik: I'm drooling. Added this to Featured Pages.

El Muerte: A little note: never use the `assert()` function. It will trigger an engine shutdown when the expression is false. UnrealScript should never do this.

Jarronis, The Vampiric Unicorn: Of course, i wouldn't do that normally, but interactions sometimes show strange behavior when handling Null-Objects. Sometimes they tend to crash anyway if you even attempt to check if they are "None" with an if expression.

Foxpaw is right concerning how the code works. The BloomMesh(derived from Actor) class has no code, just some defaultproperties, that make sure this thing isn't replicated, unlit, etc. It's Skin is a predefined FinalBlend with FB_Brighten, but no Material assigned, because i'm doing that in the code.

I'll add Material descriptions and the missing class later, of course.

Tarquin: This is now on Featured Pages – so could we bring it out of walled garden space please and into the main project namespace? Thanks :)

DJPaul: The BloomMesh is client-side only, I presume? Also, have you made any progress fixing this from a first person view? And whilst I plan to add this to my project in the next few days, is there any chance of screenshots that demonstrate the transparent-meshes-whilst-in-first-person so that I can appreciate how distracting this effect is a s a signifcant part of my mod is first-person.

Foxpaw: Unfortunately, I don't see how the first-person could be fixed without resorting to a hack. The issue is that the first person weapon is not drawn in DrawPortal because it is not drawn normally as part of the world - it is itself drawn in PostRender(). So, it would be more accurate to say that your first person weapon is like a part of the HUD, rather than being a part of the world.

This can be solved, as Jarronis mentioned, by having the first person weapon be part of the world. However, that of course is going to be a hack and won't work for mods that use something different for their first person weapons. It would also involve replacing some other classes, which would be somewhat invasive and prone to incompatibilities with other mods.

You might be able to work around it by copying the same code that draws the weapon onto the canvas, to have the weapon itself drawn onto the scriptedtexture after drawing the world onto it. However, now that I think about it some more, the bloommesh should be getting drawn beneath the weapon, and so should not be visible over top of it, so maybe the cause is soemthing different from what I was thinking. Hrmm. I'll see if I can get this working and see if I can figure out what's going on there.

Also, what mesh did you use for the bloommesh, or did you create your own? Also, does the bloommesh compensate in any way for different resolutions (and hence, different aspect ratios on the monitor) or different POV settings? If not it seems like it might look funny when zoomed in on the sniper rifle, for example.

Jarronis, The Vampiric Unicorn: I created my own mesh, though it is nothing more than a rectangle with an 4:3, so it should work on resolution that have the same ratio. Not that the UV-Coords must be map in the same way, this means, not the whole UV-"Square" but the biggest 4:3 part of it.

As long as you zoom in(decreasing the FOV), everything is just fine, because the mesh floats in front of the camera as a virtual canvas. Increasing FOV will make the bordes become visible.

Drawing the weapon directly onto the ScriptedTexture is not possible i think, because ScriptedTextures unfortunately don't have a DrawActor-Function. As said, another solution can be an actor that has the same Mesh, Animation und position like the FPV-Weapon, but this one is only visible during the RenderTexture event of the texture. So, copying code from the Postrender/DrawOverlay-Function might be a good idea for determining the position, the rest must be done via animation-replication.

In general, i would suggest mod-authors to modify the code, as is fit to them, such as implenting the routines in a HUD instead of Interaction, which always draws ontop of the HUD.

DJPaul: I've been away too long to remember how these functions interact and how things are rendered; for simplicity's sake, on my part, I think I will wait to see if anyone comes up with a fix for this and otherwise leave it out of my code.

Foxpaw: Hrmm, I tried the code above, substituting in a mesh of my own and making the appropriate materials, but the effect I get isn't as nice. The "bloom" effect has visible contours on my setup, like the kind you get when you convert a colorful image to 256 color palettized. Is there any other settings you changed on the materials that are pertinent to making the effect look good?

Foxpaw: Actually it looks like I spoke too soon. It only does that in my mod. In UT2004 it doesn't do that. Wierd. (Actually, I would speculate that it probrably still exists in UT2004, but UT2004 doesn't have the same smooth gradient background that would make such an effect noticeable. Hmmm.

Also it should be mentioned that the bloommesh has to have bHidden=true, otherwise it will look wierd when moving - because otherwise the bloommesh from the last frame gets rendered onto the scriptedtexture.

Also I see what is going on with the weapons. My initial speculation was correct. Your bloommesh is getting drawn after the weapon, so it gets drawn on top of it. This causes it to appear "translucent" when in reality a translucent version of the world is being applied to it. I believe this because it seems logical and the transparency is greater over brighter objects, which would be the objects doing more brightening on the bloommesh. The least invasive workaround would be to draw the weapon on after drawing the bloommesh - so it would end up getting drawn twice, which is less than ideal but that way you don't have to have a custom controller or anything to work around it.

DJPaul: What sort of function would you use for that? HUD.Postrender/DrawOverlay as was mentioned in Jarronis' post above?

Foxpaw: PostRender. The exact same place as the bloommesh is drawn in - just draw it after drawing the bloommesh. This won't prevent the first drawing, but then you can draw over the "translucent" version with a new one that won't have the bloommesh on top of it. This may look a bit strange for weapons with transparent bits, though, I haven't tried.

Also, I haven't had any problems with emitters, like you had Jarronis. I had them at first, but setting bHidden to true on the bloommesh fixed them.

DJPaul: It would be appreciated if someone would upload the missing class that is mentioned, and the bloomesh as I cannot model anything. Thanks.

Foxpaw: Also I found a line that I think is a typo. One of the bloom offset thingies references X, when I think you meant to type Z.

The comment about zooming in with the sniper rifle is, the "zoom" on the sniper rifle does not affect the location of the camera - it alters the FOV. However, I tried that and it doesn't seem to hurt anything when the FOV is tightened. I did notice that the overlay when zooming in with the lightning gun ends up being very bright, but again, there's not much that can be done about that.

I've also noticed some glitchy behaviour when moving with the bloom on - sometimes the apparent light level makes a sudden shift. I'm guessing that this is related to the "deviation" of the glow.

Okay, I'm also changing my stance on the emitters again - Emitters seem fine, but some xEmitters seem to have a kind of square around them. This is probrably simply an imperfect texture, and the imperfection is amplified. (Most modulated textures, for example, start to look wierd if you put a lot of them on top of each other.)

Also, it turns out my speculation was right in that UT2004 does suffer from the same issue of looking like an indexed image, it's just not as noticeable because there isn't much in the way of color gradients. However, I did notice a color gradient (the sky in DM-Antalus) and it had the same "banding" issue.

Also, I've noticed that the bloom effect varies a lot on different levels. I don't really know if anything can be done about that as it is likely related to the level's general brightness. For instance, in DM-Antalus it's very bright, kind of overdone-looking. However, on DM-TrainingDay, it's barely visible. (Except for some wierd artifacts visible out the windows.) On ONS-Torlan, the distance fog is lit up like a Christmas tree.

Also, I optimized the code in the interaction slightly, which may also increase readability somewhat. Here's what I changed it to:

    function PostRender(canvas Canvas)
    {
      local vector BaseDisplacement;

      if (!bEnableBloom)
        return;

      st.Revision++;

      Canvas.Reset();
      Canvas.GetCameraLocation(campos,camrot);

      BMesh.SetRotation(Rot(0,0,-16384) CoordRot Camrot);
      BMesh.GetAxes(Camrot,X,Y,Z);

      BaseDisplacement = CamPos + (dist-devX) * vector(CamRot);
      Y *= dev;
      Z *= dev;

      if(!bXBloom)
      {
        bMesh.SetLocation(BaseDisplacement+Z);
        Canvas.DrawActor(bMesh,false,true);
        bMesh.SetLocation(BaseDisplacement-Z);
        Canvas.DrawActor(bMesh,false,true);
        bMesh.SetLocation(BaseDisplacement+Y);
        Canvas.DrawActor(bMesh,false,true);
        bMesh.SetLocation(BaseDisplacement-Z);
      }
      else
      {
        bMesh.SetLocation(BaseDisplacement+Z+Y);
        Canvas.DrawActor(bMesh,false,true);
        bMesh.SetLocation((BaseDisplacement-Z)+Y);
        Canvas.DrawActor(bMesh,false,true);
        bMesh.SetLocation((BaseDisplacement-Z)-Y);
        Canvas.DrawActor(bMesh,false,true);
        bMesh.SetLocation((BaseDisplacement-Y)+Z);
      }

      BaseDisplacement = CamPos + (Dist-(DevX/2))*Vector(CamRot);
      Y *= 0.5;
      Z *= 0.5;

      if(!bXBloom)
      {
        bMesh.SetLocation(BaseDisplacement+Z);
        Canvas.DrawActor(bMesh,false,true);
        bMesh.SetLocation(BaseDisplacement-Z);
        Canvas.DrawActor(bMesh,false,true);
        bMesh.SetLocation(BaseDisplacement+Y);
        Canvas.DrawActor(bMesh,false,true);
        bMesh.SetLocation(BaseDisplacement-Y);
        Canvas.DrawActor(bMesh,false,true);
      }
      else
      {
        bMesh.SetLocation(BaseDisplacement+Z+Y);
        Canvas.DrawActor(bMesh,false,true);
        bMesh.SetLocation((BaseDisplacement-Z)+Y);
        Canvas.DrawActor(bMesh,false,true);
        bMesh.SetLocation((BaseDisplacement-Z)-Y);
        Canvas.DrawActor(bMesh,false,true);
        bMesh.SetLocation((BaseDisplacement-Y)+Z);
        Canvas.DrawActor(bMesh,false,true);
      }
    }

Graphik: See tarquin's comment above. May I please move this to a public page (a subpage of Lighting perhaps) rather than your personal page?

Jarronis, The Vampiric Unicorn: Of course move it into project name space, or into Lighting as you desire.

Thank you, Foxpaw, for the typo. I mixed them up before, so i probably just forgot to change that.

I noticed the color-banding and overlit issue too, if you you look close to screenshots you can see them there too. That's why i mentionend that especially talpha(the light treshold) cannot be the same maps. Dark maps can handle a lower talpha, resulting in brighter bloom-maps, but especially outdoor maps are overlit easily, so the threshhold must be higher.

A general problem is that FB_Darken don't work that i want too, i think. It seems that the textures stores kind of 'negative' RGB-Values. So when you add them up, by drawing them ontop of each other several times, the colors that should be 'zero' show up again. That's also where the banding-effect takes place.

Jarronis, The Vampiric Unicorn: I found a hack for the FPV Problem. Just add `ViewportOwner.Actor.Pawn.Weapon.RenderOverlays(Canvas);` at the end of the posternder - routine of the interaction. This will prevent the weapon becoming translucent. However, the weapon still won't be included into the bloom-lighting. I tried to set the weapons bHidden to false, when calling RenderTexture, because everytime RenderOverlay is called, the weapon's Rotation/Location is recalculated and drawn directly. In theory, when calling RenderOverlay in the interaction(although is shouldn't be necessary because the HUD already did it the same frame), setting bHidden to false, and then rendering the texture, the weapon should be visible in the bloom layer.

My result was that the weapon actually shows up but in a wrong position. For some strange reasons, the weapons position seems to be reset, after RenderOverlay has been called.

Graphik: Moved.

Jarronis, The Vampiric Unicorn:Thank you. I found the problem. I was wrong, the position stays same, but the FPV-Weapons are drawn at a different FOV, which is weird for my purpose.

I tried it with this:

    event RenderTexture(ScriptedTexture Tex)
    {
        local PlayerController PC;
        local vector camloc;
        local Rotator camrot;
        local Actor camactor;
        local Color tAlpha;

        talpha.R=238;
        talpha.G=238;
        talpha.B=238;

        PC = Level.GetLocalPlayerController();
        PC.PlayerCalcView(camactor,camloc,camrot);
        //log("RenderEvent");
        //make weapons visible
        PC.Pawn.Weapon.bHidden=false;
        Tex.DrawPortal(0,0,Tex.USize,Tex.VSize,camactor,camloc,camrot,PC.DefaultFOV,false);
        //Lightfilter is a FinalBlend with FB_Darken
        //The Light-threshold is controlled via talpha;
        //Colored Bloom should be possible with unequal RGB values (untested)
        Tex.DrawTile(0,0,Tex.USize,Tex.VSize,0,0,16,16,FinalBlend'DreamTex.Lightfilter',talpha);
        //hide weapon
        PC.Pawn.Weapon.bHidden=true;

    }

It works just fine, except when the weapon-bloom disappears when you are too close to a wall, because of the z-buffer, but that's another problem. Well, in the RenderOverlay-function, DrawActor is called with a weapon-specific FOV which lies around 60 to 70. Well, Standard-Player-FOV is around 90, and that's the whole problem. So mod-makers: Match you FPV-Weapon's FOV to the Player-FOV and it will work fine.

Foxpaw: Did some more experimentation today. I was noticing a slight glitchiness, but when you posted your BloomMesh I thought I'd compare it to mine to see if there was any differences. I had forgotten to make mine bUnlit. This resolved some glitchiness with the apparent amount of bloom varying at different areas in the level.

I had an idea regarding the banding but it was a no-go. I was thinking to myself. I thought, "It looks like what you get when you convert a 24 bit color image to 8-bit palletized." I responded by thinking, "maybe that's because the color compression of the scripted texture is 8-bit palletized, seing as how that's the default compression type." I deftly replied with "Holy Compression Batman, you're right!", while doing my best mental impression of err. that guy that played batman.

Long story short, I changed the texture compression type, I tried a bunch of the options, but it didn't fix the banding. It seems like that might be a piece of the puzzle though, so to speak.

I'm also a little unclear on what exactly you are planning with the lightfilter in your recent edit. Do you mean as in, putting the lightmap on the scripted texture, making it visible, then doing DrawPortal again with the filter over top?

Jarronis, The Vampiric Unicorn: I used this code as an alternative RenderTexture:

    PC = Level.GetLocalPlayerController();
        PC.PlayerCalcView(camactor,camloc,camrot);
        bMesh.bHidden=false;
        bMesh.Skins[0]=FinalBlend'DreamTex.Lightfilter';
        Tex.DrawPortal(0,0,Tex.USize,Tex.VSize,camactor,camloc,camrot,PC.DefaultFOV,false);
        bMesh.Skins[0]=FinalBlend'DreamTex.BloomFB';
        FinalBlend(bMesh.Skins[0]).Material=st;
        bMesh.bHidden=true;

Unfortunately it didn't work. It seems that DrawPortal has problems with rendering FinalBlends.

I don't think that the band-problem can be resolved completely. Because all colors are added up more or less, the difference between to colors become greater.

Foxpaw: Hrm.. That actually worked fairly well on my mod. The sun was nicely brightened as was my white spacecraft, but the nebula background wasn't all bandy. It doesn't seem to work well in UT2004 though. I have to go to class now, but I'll experiment more when I return.

Jarronis, The Vampiric Unicorn: Cool. Let me know if you got it working in UT2004, unless i don't get it. This solution don't creates redundant RGB-Data, which add up and create the banding-effect.

It think it might be a z-buffer issue, because if Standard-UT2004 clears the z-buffer while rendering the HUD.

When you use this solution you should'nt use a pure white texture, because then the FB would suctract everything, all light gray should do the trick, but i guess you already figured this out.

Mosquito: could this effect be intensified, sorta like the blur effect in farcry? If it causes such a performance hit, it could just be a hit effect or a shell effect.

Jarronis, The Vampiric Unicorn: Variations of this effect are nearly limitless. You can play with different Blendmodes, Filters and offsets to achieve different effects.

Foxpaw: Actually, I was using an all-white texture. Now I know why the scriptedtexture was ending up completely black in UT2004. ;)

However, there's a new problem posed by using this method. Because you are using something that exists in world space as your light filter, it can get clipped by other objects. That means that if you are really close to some grass or something, the grass doesn't get filtered and as such is blindingly bright. This turned out to be why my mod was working.. because my entire ship was between the clipping plane and the bloom mesh. This problem should be fixable by simply reducing the drawscale of the bloommesh and moving it closer to the camera, until it's right next to the clipping plane.

However, erm... All of that is fairly irrelevant, as now that I got it working using the bloommesh as a light filter, it still has the banding. :(

I did get a strange insight though - the darkening light filter doesn't appear to actually push all pixels down a set amount (thus causing anything below a certain threshold to bottom out at 0, as one would think.) I set the scriptedtexture's FinalBlend to FB_Overwrite instead of FB_Brighten, and noticed that, while the scriptedtexture is darker than the scene, it still seems to have all of the same detail, even at very high "light thresholds." I now believe that the entire scene is being patterned around when the bloommesh is drawn, but is 'most noticeable' around brighter objects, as they cause the most brightening. I think we need to find a new way of filtering out the bright parts, but I'm not sure how we can go about doing that. It's possible but unlikely that the CO_Subtract of a combiner would behave the way we though that FB_Darken was. Maybe that's worth a try.

Curses, now you've got me thinking about all kinds of things I might try with these filtering techniques. Previously I couldn't think of any way to implement a pixel-shader-esque effect like this in UScript because I didn't know of any way to get color data of pixels - now that I see how you've done this the possibilities seem endless. Hrmm.

And Mosquito, I've already nicknamed the bloom effect, "drunken mode" or "hangover mode" because I have the blur and the light threshold fairly high. ;) The effect at high levels of dev is quite comparable to far cry's hit effect.

Foxpaw: Using Combiners for any texture math is probrably a good idea regardless as they are handled by the video card and will somewhat reduce the speed penalty.

But erm... Combiners apparently do have different behaviour on their subtract than does Darken. But, err.. it appears that large subtractions (subtracting 240 for instance) actually produces a 'negative' of the view.

Foxpaw: Scratch that. Apparently CO_Subtract subtracts the FIRST material from the SECOND, so the order is reversed. No more negatives ;)

Jarronis, The Vampiric Unicorn: If you want to know, what a "drunken" effect is, combine bloom with motionblur...

I had the same idea on using a combiner, but i wasn't sure about it, because a Combiner demands at least one constant texture. Using combiners also produces more 'overhead', it think, because the materials have more complex hierarchy.

By the way, this "project" just was meant to be hobby-coding-project from me, and now it turned this large thing... I'm drooling....

Foxpaw: Err, well, you see, using Combiners actually produces drastically less overhead. To make a long story short, the overhead of combining the LightFilter and the scriptedtexture is effectively trivial. The transformation is reliably predictable, so the video card can be fed a little program that executes very quickly on purpose built hardware (the shader pipeline) instead of interpreting instructions and blitting textures in RAM.

Plus the Combiner method actually does the filtering we want, completely wiping out any light below a certain threshold. :D

However, we have a new problem: Now that we have something that's actually subtracting 238 from each pixel and bottoming out at 0, even the brightest of bright things is now only a brightness of 17, which is pretty dim. So now, I can't even see the bloom effect. (Though if I set the finalblend to FB_Overwrite I can see that it's there.) We now need a way to increase the brightness of non-zero pixels without increasing the brightness of zero pixels. Unfortunately, CO_Multiply won't work, as it interprets 255 as 1.0 and everything less as some fraction. We could just draw the additive texture a bunch of times, but that's probrably not the ideal solution. Any ideas on how we can control the brightness of the bloom effect, without changing the lightfilter?

Foxpaw: Another idea, we might be able to get better control over the effects and reduce the performance hit to almost nothing if we can find a way to prevent the world from being rendered by default, and then drawing our DrawPortal result + the Bloom effect as FB_Overwrite. Highly modified TCs like mine might be able to do this as I can have the playercontroller reside in a box way out in the middle of nowhere so everything will be occluded, and then have the playercontroller basically "watch TV" which would be the DrawPortal taken from the Pawn's first person view. However, that won't work for any mod using UT2004s default Pawn behaviour.

Jarronis, The Vampiric Unicorn: We could reverse the texture using another combiner, then multiply, and then reverse again... "negative multiplication."

Foxpaw: Hrrmmm. I don't think that will work. If we take the negative of the texture using another combiner, the 0s become 255s, which will then get multiplied by some fraction, so when we take the negative again, they won't be zeros anymore..

Foxpaw: Err.. hemm. I've hit a brick wall. Apparently, the Unreal Engine doesn't like certain combinations of Materials. If I put the scriptedtexture directly into a finalblend I can use FB_Brighten with no problems. If I put it into a combiner with CO_Subtract, then put that into a final blend, all the blend modes except for FB_Brighten and FB_Darken work. I tried to get around this by simply using DrawTile to put the Combiner onto a second scriptedtexture, but that seems to cause GPFs.

FB_Translucent does work, but it's not quite the same effect. I've tried using a combiner to add the combiner to the original st, and then displaying that with FB_Overwrite, which works, but there doesn't appear to be any way to get the "blur" effect, which is of course essential, otherwise it just makes the bright areas look washed out. I tried just using a ton of combiners and TexOscillators to get the blur effect, but that just gave me the default texture, which says to me that the maximum length of a shader program has been reached. (255 lines in Shader Model 2.0, which is what UnrealEngine2 uses.)

So, I'm somewhat at a loss for what to do there. The FB_Brighten is important to get the desired effect. FB_Translucent gives hard edges and some of the detail on the light for instance (detail textures and such) spill over onto the "glow" area.

Foxpaw: Blech. I had an idea to work around the previous issue by taking another DrawPortal of the partially filtered image.. but that didn't work out. It appeared to be working, but it turns out that only the coronas were working because the coronas take time to fade out and so were getting in the frame unintentionally.

Jarronis, The Vampiric Unicorn: Eureka, i got the Combiner working by turning on Modulate2X and Modulate4X. The effect is certainly there even with FB_Brighten, but it is quite dim.

Jarronis, The Vampiric Unicorn: Hm, strike that maybe, i'm not sure if it was this, or some other changes in the code, that made it running.

Tarquin: it's best to generall avoid subpages. 'Bloom' is a perfectly good title – 'lighting bloom' or 'bloom effect' if you guys think that's better. Could someone with a decent text editor please promote all the headings by one level? thanks :)

DJPaul: Clever chaps, if I implement this code as is from the top of the page, if you come up with any changes to improve it or fix issues, will I end up ripping it all out or will it most likely be a change of just one class? I'm deciding whether to implement it now or put it in later.

Foxpaw: The current system I've been using is quite different from the original code, but the original code is enough to see how it is done. The original code used a finalblend with FB_Darken, which doesn't work the way we expected, and produced bands of color on areas that should have had smooth contours. The Combiner method I've used so far didn't seem to be working, and a slightly different method "worked" but made the colors look washed out. So, I'm still experimenting.

I'll try playing around with Modulate2X and Modulate4X. I don't see why that would make any difference to the not drawing, but it might be useful anyway. The UDN says that it only works with CO_Multiply, and makes the result brighter. So, we may be able to multiply the filtered out light with a straight white texture and Modulate4X to make it brighter. I'll give it a shot.

Jarronis, The Vampiric Unicorn: I'm currently cleaning up my code, so i can make an demonstration mutator. In case major spread is desired, someone should upload this to an official mirror, because my ISP's have limited bandwith.

Foxpaw: Modulate2X and Modulate4X was perfect for increasing the brightness of the filtered image, but I still can get it to actually draw onto the screen. Turns out that the problem I was having before that I attributed to the max shader length being reached was actually due to something else - I was using a ConstantColor material instead of a white bitmap, and mistakenly assumed that a ConstantColor still counted as a constant bitmap material. Now that I've fixed that I got a stage further, but FB_Brighten still doesn't seem to want to work. FB_Darken works though, so I'm left wondering if this is some bizarre behaviour of FB_Brighten (as in, remember how FB_Darken didn't actually directly subtract RGB values like we thought? Maybe FB_Brighten is doing something funny that's making it impossible to see.)

Did you get your working using a combiner for the filter? If so I'd be interested to see where your code differs from mine.

Foxpaw: Huzzah! FinalBlend wouldn't draw FB_Brighten for me but using a Shader with OB_Brighten seems to. Now I'll see if I can tweak the dev and such to get the desired effect.

Jarronis, The Vampiric Unicorn: Yeah, i got it working with a combiner(and without a shader). Well maybe your bitmap is just to bright, did you try a darker one?

Foxpaw: Well, when you upload the demo mutator I'll see how that effect compares to the one I got. The final effect that I got with the combiner and shader seems technically correct but looks a little exact for my liking, so I'm eager to see what you came up with.

Jarronis, The Vampiric Unicorn: Well, used both approaches. The brighter one(which formerly uses only the FB) i called PowerBloom, but it also used the combiner with a black texture, which should make no difference, than using the FB alone. For a strange reason, the combiner eliminated all particle-errors, even if used with the pure black texture.

The other method used the combiner with a very light gray, which makes a more "selective" bloom.

I'd plan to implement a configuration menu for the mutator, where you can choose which method shall be used, along other options.

Foxpaw: I'm confused. You used only the FinalBlend, no combiners? How did you get rid of the banding?

Jarronis, The Vampiric Unicorn: Of course i used combiners, but, when using PowerBloom, they are "disabled", by using a black texture. Yet, PowerBloom should only be used on maps where the banding-effect is not clearly visible, as DM-CBP2-Achilles (as in the screenshots.)

Foxpaw: So.. err, if you use a black texture with the combiner, no filtering would take place at all, so wouldn't you then end up with just a really blurry and very bright view?

Jarronis, The Vampiric Unicorn: Yeah, but i still used the old Lightfilter on the ScriptedTexture too, so the bloom does not look "capped". So by using the both techniques it's a matter of the right individual settings for each map to look good.

T1: Up there it says something about needing a lens for a lens flare. It's true you can't see lens flares with the naked eye, but eyes still have lenses, so that's kind of inaccurate. Sorry for being nitpicky.

Foxpaw: Fixed. :D

Graphik: Glad you added a link to a demo mutator, but why so hidden in the page?

Jarronis, The Vampiric Unicorn: Well, i didn't want it to post here in the comment area...

Foxpaw: Hmm. I like the demonstration mutator, though it looks a lot like the initial finalblend method you posted up here. The mutator still has the "see-through" weapons and has the banding, even with the "power bloom" turned off.

As best I can tell, any FinalBlend using FB_Darken is going to cause that banding, as FB_Darken appears to do more of a division instead of a subtraction. This results in rounding making similar colors appear to be the same color. Maybe the banding could be fixed by hiding the skybox when you do the DrawPortal, that might cause HOMs, but it might not. Skyboxes seem to be the area where the banding is most noticeable as they often have smooth color gradients.

Also the bloommesh doesn't adjust for different aspect ratios. This is probrably easy to accomplish, as I think that the Canvas has a function to get the resolution of the screen, so it'd be something like BloomMesh.SetDrawScale3D( Canvas.SizeX / 800, Canvas.SizeY / 600, 1 ); or something very similar. 800x600 is of course, only an example resolution, I don't know what resolution you designed it for.

Also I noticed with the PowerBloom off the terrain gets all blotchy. (I noticed this with my own experimentations as well.) Maybe you could keep a linked list of terraininfos and make them all bHidden before doing the drawportal? That might cause HOMs or other undesireable behaviour though, but it's a shot.

Except for the terrain issue and the banding in the skybox though, I think the bloom is very cool.

Jarronis, The Vampiric Unicorn: Thanks. Hm, i think i can solve the transluent weapon problem, by disabling general HUD-Rendering and then use the interaction to call the HUD's PostRender() function. The Mesh was designed 1024*768.

Mosquito: With powerbloom it seems to had a level of desaturation and realism to the image, and the unsual effect on the terrain is actually very interesting, Could it could be recreated to create a kind of NPR (Non-PhotoRealistic Rendering) effect?

Jarronis, The Vampiric Unicorn: Interresting idea. I'm sure, NPR could be done this way. But i think the (usually) terrain issue is a bug of the DrawPortal-function of ScriptedTexture. Maybe it will be fixed in an future patch.

Foxpaw: The stuff on that NPR quake example would probrably be a little trickier to do. Pretty much all three methods rely on drawing things in wireframe mode (though with occlusion) and then tweaking the way that the wireframes are drawn. You might be able to do something like that with materials, but I'm not sure if you'd be able to do it on-the-fly like this, primarily as I don't know any way that you could get both wireframes drawn and still have proper z-buffering. The rendering methods used there look relatively simple to do, if you had access to the renderer, but unfortunately we don't. It might be possible to do that if you had the native headers.

Not that I'm saying for sure that it's not possible, as it's always possible that someone might come up with a clever scheme to get a visual effect without accessing the renderer directly. (as was done here.)

T1: Is there anyway to make it so that those screenshots need a link to open up, here on 56k I hate to wait for them to load.

Graphik: The majority have broadband and would likely prefer things as they are. If you have a 56k you're going to be doing a lot of waiting. Get used to it. ;)

Tarquin: you could feasibly crop the screenshots to show just a portion of the image. would say about 300x300 pixels, and perhaps glue the before / after images side by side. The big advantage of this page not being a subpage any more is that we can make a Legacy:Bloom/Screenshots page. :) I'll repeat my request for the heading levels to be corrected :)

T1: Thanks.

Graphik: Heading levels corrected.

Uncommon: The download link seems broken.

Wormbo: Works fine for me.

Uncommon: Ah, I see. My downloads window was in back, and the link also took me to an incomplete-looking page, so I got confused. Cool effects :) I'm kind of surprised how little impact the motion blur has on performance.

OlympusMons: Havent read all of this, is it all relevant could use a Category:Legacy Refactor Me

Moron: This is pretty good, I use a setting of 20 for the threshold and turn on the power bloom (it has nasty FX on the terrain for me otherwise). The only problem is that it seems to blur the terrain layers, regardless of whether whether they are bright or not. Has there been a fix for this yet (I'm not a coder, I only downloaded the mutator) or is it not possible?

Jan: Is there an alternate download?

SuperApe: Added category custom class tag. The download link is broken. ("404 page not found") It looks like we'd only need some specific info on the BloomMesh and DreamTex to reproduce the latest version. On Jarronis' page, it's mentioned the BloomMesh is a rectangle, so I assume a plane mesh. It would be nice to see a download of the latest version. This effect sure looks like what is being done in the next gen games; all the launch titles for XBox360 seems to have this effect (Call of Duty2, Quake4, Killzone, etc) Really neat stuff. :)

Jan: Reproduced version:

http://files.ego-creations.de/ut2004/BloomEffect_Mutator.zip

Olga Updated download link. Original Link

GG-Xtreme: For some reason, I can only use XBloom and DoubleBloom, but I would like to use the bloom shown in the screenshots. Is it the same one with a different name, or is that a different version of the mutator? I would really like the bloom in the screenshots because XBloom and DoubleBloom don't look anything like it.

Ambershee: It's pretty impressive. I find it amusing how it also applies it to the GUI though =p

Xaklse: Just a note to let you all know that colored Bloom is possible and looks nice; just modify tAlpha RGB values of RenderTexture function. Thanks for your effort, Jarronis.

Categories: Legacy Refactor Me | Legacy Custom Class (UT2004)

##### Views

- Legacy
- Discussion
- Current revision

##### Search