Game Development Community

Fog of War-type Transparency Effect/Dynamic Lighting Alternative

by Chase Webb · in Torque 2D Beginner · 07/28/2013 (12:19 am) · 22 replies

Hey all, I'm looking for ideas on how to do something. Basically, I have a black sprite overlay over everything in SceneLayer 1. I want to do a kind of reverse transparency effect around my player unit in a circle. Where the circle touches the black sprite I want the transparency applied so that the person playing can see what is underneath.

If that's too hard to understand, this old Warcraft 2 image should make it easier to understand:
static.giantbomb.com/uploads/original/0/24/10712-fog1.gif
Where the unit travels, the area underneath is unveiled. I don't even need the "already been there" portion of the fog of war, just black and not black is fine with me.

Any ideas on how to accomplish this?
Page «Previous 1 2
#1
07/28/2013 (1:20 am)
Perhaps use a composite sprite over your level map and then set the "fog" values appropriately. That "partial fog" effect would probably have to be matched to the map below it to allow you to mask any underlying changes that the player would be unaware of.
#2
07/28/2013 (1:23 am)
I'm afraid I don't understand what you are saying. :P
#3
07/28/2013 (1:48 am)
I don't have a "this is how you do it" type answer unfortunately, but I would look at the blending modes for your map and the black overlay. So, the SrcBlendFactor and DstBlendFactor. There might be a combination that gives you the effect you are looking for. See the blending guide on the wiki for more information on the subject.
#4
07/28/2013 (2:14 am)
This definitely seems to be the right track for what I want... hopefully someone out there has some experience with the values for setSrcBlendFactor and setDstBlendFactor, as I'm not sure how to use them from looking at the source. Thanks for the help so far!
#5
07/28/2013 (9:40 am)
Chase, my idea would avoid having to try to set some kind of splatter on a single FOW image. Basically you would use a CompositeSprite (or set of them) for your map. Then use another on top of your map to use as the FOW. When a unit "sees" an area, just change the sprite for that map cell of the FOW CompositeSprite to the "seen" sprite(transparent). After the unit leaves you can then replace the "seen" sprite with a "stale" sprite that is not transparent but looks like the map beneath with the "grayed out" bit added. I admit that generating those would be nasty hard - it would probably be better to test for them and just not render units in those areas.

Hmm, I'm going to have to find time to work up an example - I need to learn more about CompositeSprites anyway....
#6
07/29/2013 (6:18 am)
I've toyed a little with Composite Sprites, but I've had zero luck figuring out how to call an individual piece of a Composite sprite so that I can alter it.

Oh, for further clarification, I really just want the "Dark" and "Not Dark" layers, no middle layer. The screenshot above was just a random example I pulled from the net... maybe not the best. My goal "Fog of War" is really more like darkness that you can light up. As I don't know a thing about Shaders and such, here are the two things I'm envisioning trying at the moment:

1- (Optimal Solution) A black square is transplanted over everything, and has it's own Scene Layer and Scene Group. Another "light" image is attached to the player that has a circular shape and a transparency effect so it fades at the edges. Where the image contacts the black square it does a kind of inverted transparency effect - the transparent parts of the "light" image don't affect the black square, but the colored portion of the "light" image reacts to the black square causing it to become transparent where they overlap. I suspect this might be possible with SrcBlendFactor and DstBlendFactor, but I have no idea.

2- (Less optimal solution) I implement a grid system of black circles (faded at the edges) set as Sensor objects and whenever something is in contact the black circles fade to transparent until the user has moved out of its range. I dislike this because it means less freedom for light source sizes.

3- Give up on the darkness effect.
#7
07/29/2013 (9:43 am)
Quote:but I've had zero luck figuring out how to call an individual piece of a Composite sprite so that I can alter it

It's trivial. Use one of the "SelectXXX" methods on CompositeSprite. You can select by Id (SelectSpriteId), logical-position (selectSprite) or name (selectSpriteName). When it's selected you can call one of the many methods that act on the currently selected sprite. In this context you can select the blending methods such as setSpriteBlendMode(), setSpriteSrcBlendFactor() or setSpriteDstBlendFactor().

Note that when you add a sprite, it is automatically the selected sprite that's why the per-sprite methods work when you're adding them. For pre-existing ones though, just select the sprite then make the appropriate call.

By having a "selected sprite" it makes all the sprite methods simpler/faster as you don't have to have lots of overloads of them and continually pass the sprite you want.

If it were me however I'd write this in C++ and derive from CompositeSprite and render each sprite appropriately using either a separate map of fog-o-war or a dynamically calculated one based upon the players position ... really depends on how you want the fog to work.

#8
07/29/2013 (10:36 am)
Well, I took some time out of my evening to solve this. So how about in return you look through the blending guide and give me feedback on what parts you don't understand Chase, so I can improve it. Fair trade? :)

The secret is to think a bit outside the box and completely reverse how the scene is setup. I have the Canvas set as a black color, which acts as your fog. The mask is simply a white/alpha sprite like the hollow arrow I used from ToyAssets. Note I put it down at layer 30. The background map is on layer 20 and has the blending factors set up such that it "pulls" the black color of the Canvas up and where the white color of the mask is, it keeps the texture colors of the map.

Here is a snippet of the script:

function BlendToy::createBackground( %this )
{    
    // Create the sprite.
    %object = new Sprite();
   
    %object.Position = "0 0";
    %object.SceneLayer = 20;
    %object.Size = "100 75";

    %object.Image = "ToyAssets:JungleSky";
    
    // Set up the blending factors
    %object.SrcBlendFactor = DST_COLOR;
    %object.DstBlendFactor = ZERO;
            
    // Add the sprite to the scene.
    SandboxScene.add( %object ); 
   
}

//-----------------------------------------------------------------------------

function BlendToy::createMask( %this )
{    
    // Create the sprite.
    %object = new Sprite();
   
    %object.Position = "0 0";
    %object.SceneLayer = 30;
    %object.Size = 20;

    %object.Image = "ToyAssets:HollowArrow";
        
    // Add the sprite to the scene.
    SandboxScene.add( %object );    
}
#9
07/29/2013 (12:25 pm)
Mike's solution works really well

www.s01l.com/repository/fogofwar.png
However, as soon as you use gradients for your mask (even though you said you wanted 100% visible or black), overlaps look weird. A more thorough understanding and testing of blending modes will be required.

As Alien Cabbage points out, the ideal solution would be a custom C++ fog-of-war implementation but the simplicity of Mike's solution really makes it an attractive one. I'm saying the solution is attractive, not the cabbage.
#10
07/29/2013 (1:32 pm)
@Chase - Wait, it sounds like you want a "torch" effect, only showing the area around the player while the rest of the "world" remains in darkness. If the camera follows the player, just make the overlay part of the GUI. If not, just attach the overlay as a sprite to the player, making sure that the overlay is large enough to always extend beyond the edges of the camera.

I do like Mike's solution - or Alien Cabbage's - for an actual "Fog of War" thing.
#11
07/30/2013 (6:17 am)
From Simon's picture that looks like exactly what I want - a dark effect that I can use with multiple light source-type objects. I'm going to have to play with these commands more to see what they can do. Looking at the source code I see a lot of values for Scene Objects, I just wish I knew how they all worked:

static EnumTable::Enums srcBlendFactorLookup[] =
                {
                { GL_ZERO,                  "ZERO"                  },
                { GL_ONE,                   "ONE"                   },
                { GL_DST_COLOR,             "DST_COLOR"             },
                { GL_ONE_MINUS_DST_COLOR,   "ONE_MINUS_DST_COLOR"   },
                { GL_SRC_ALPHA,             "SRC_ALPHA"             },
                { GL_ONE_MINUS_SRC_ALPHA,   "ONE_MINUS_SRC_ALPHA"   },
                { GL_DST_ALPHA,             "DST_ALPHA"             },
                { GL_ONE_MINUS_DST_ALPHA,   "ONE_MINUS_DST_ALPHA"   },
                { GL_SRC_ALPHA_SATURATE,    "SRC_ALPHA_SATURATE"    },
                };

EnumTable srcBlendFactorTable(sizeof(srcBlendFactorLookup) / sizeof(EnumTable::Enums), &srcBlendFactorLookup[0]);

//-----------------------------------------------------------------------------

static EnumTable::Enums dstBlendFactorLookup[] =
                {
                { GL_ZERO,                  "ZERO" },
                { GL_ONE,                   "ONE" },
                { GL_SRC_COLOR,             "SRC_COLOR" },
                { GL_ONE_MINUS_SRC_COLOR,   "ONE_MINUS_SRC_COLOR" },
                { GL_SRC_ALPHA,             "SRC_ALPHA" },
                { GL_ONE_MINUS_SRC_ALPHA,   "ONE_MINUS_SRC_ALPHA" },
                { GL_DST_ALPHA,             "DST_ALPHA" },
                { GL_ONE_MINUS_DST_ALPHA,   "ONE_MINUS_DST_ALPHA" },
                };

EnumTable dstBlendFactorTable(sizeof(dstBlendFactorLookup) / sizeof(EnumTable::Enums), &dstBlendFactorLookup[0]);
(From SceneObject.cc)

I'll post more here when I get time to play with these things. I appreciate the help you're all giving me!
#12
08/02/2013 (8:04 pm)
Ok, I've been busy playing with all of this. I made a toy that combined Mike's script, the MoveToy toy, and a random sprite. It's not polished and it's pretty sloppy, but it allows you to play with the BlendFactor values of the background and the sprite, and with the MoveTo target sprites you can see the difference between colors and alpha values on the ability to view the background image. You can look at it here: https://dl.dropboxusercontent.com/u/18965446/LightOverlayToy.zip

It's set up like this:

SceneLayer 9 - Arrow sprite, which is not blended or anything and acts regularly as it sits above the background layer.
SceneLayer 10 - Background with BlendFactor values altered.
SceneLayer 30 - The MoveTo Toy target sprites, which blend with the background layer.
SceneLayer 31 - A Planetoid sprite from the PointForceController

Here's my conclusion so far: You can make things above the background layer (from Mike's script) appear regularly without blending so long as they stay above that layer. Everything below it either gets blended or is hidden completely, depending on the BlendFactor values you give it.

What I think would basically solve everything would be an additional value for objects below the background layer (aka the layer with the BlendFactor values altered for transparency) that allows it to be ignored for blending purposes and rendered regularly, except that it also acts like any ordinary object underneath another object.

Or in other words, a value that makes an object ignore the BlendFactor values of the objects above it and render regularly underneath the upper layer. In the case of the toy, the planetoid would be hidden by the black background, until you brought one of the transparent objects (like the targets) over the top of it.

Unless this is already possible and I haven't figured out how to do it yet? In my mind this would make it easier to create virtual lighting systems without full dynamic lighting.
#13
08/03/2013 (3:16 pm)
Quote:What I think would basically solve everything would be an additional value for objects below the background layer (aka the layer with the BlendFactor values altered for transparency) that allows it to be ignored for blending purposes and rendered regularly, except that it also acts like any ordinary object underneath another object.

Or in other words, a value that makes an object ignore the BlendFactor values of the objects above it and render regularly underneath the upper layer. In the case of the toy, the planetoid would be hidden by the black background, until you brought one of the transparent objects (like the targets) over the top of it.

@Chase - I'm not sure if you already played around with this or not, but you can set your blend and source factors on your lightmap and place your objects underneath the "shroud" layer (a little opposite from Mike's solution).

So something like this:
layer 4 = background image (in this case mountain sky)
layer 3 = all your other sprites you want hidden under shroud but revealed under light source.
layer 2 = black image map covering screen
layer 1 = your whitemap

Set the lightmap's blend factors to SrcBlendFactor = "DST_Color" and DstBlendFactor = SRC_Alpha";



A toy allowing us to adjust and test these settings will definitely but useful, thank you.
#14
08/03/2013 (6:33 pm)
Hello Caleb! I'm trying your idea, but I can't get it to work like you expect it to. Unless I'm just clueless as to what you mean by lightmap and black imagemap... I'm using a black square that covers the screen and just using the images in the above toy as my whitemap - the layers are as you suggested. At best with fiddling I can make the whitemap layer make holes in the darkness, but it doesn't reveal what is beneath it.
#15
08/03/2013 (9:40 pm)
Sorry, it sounds like you figured it out...black image / lightmap = white image.

Well, I played around with this and I had to set the blendalpha down on the black shroud image. I also made a full white image for the whitemap.

Hmmm...not sure if it's at all the results your looking for but here is what I did. I also added an additional whitemap to increase the effect.

// Create the background.  
	%object = new Sprite();  
	%object.Position = "0 0";  
	%object.SceneLayer = 10;  
	%object.Size = "100 75";  
	%object.Image = "ToyAssets:JungleSky";  
	SandboxScene.add( %object ); 
   
	// Create a underlying sprite.  
	%object = new Sprite();   
	%object.Position = "0 0";  
	%object.SceneLayer = 9;  
	%object.Size = 20;  
	%object.Image = "ToyAssets:HollowArrow";  
	SandboxScene.add( %object );
    
	//create our shroud...set the blend alpha
	%object = new Sprite(shroud);
	%object.Position = "0 0";
	%object.Size = "100 75"; 
	%object.SceneLayer = 8;  
	%object.Image = "ToyAssets:shroud";
	SandboxScene.add( %object ); 
	
	shroud.setblendalpha(0.95);
	
	// Create the whitemap.
	%object = new Sprite();
	%object.Position = "0 0";
	%object.Size = 25;
	%object.SceneLayer = 7;  
	%object.Image = "ToyAssets:Planetoid2";
	%object.SrcBlendFactor = "DST_Color";//LightOverlayToy.PlanetoidSrcBlendFactor;  
	%object.DstBlendFactor = "SRC_Alpha";//LightOverlayToy.PlanetoidDstBlendFactor;  
	SandboxScene.add( %object );
		
	// Create the whitemap.
	%object = new Sprite();
	%object.Position = "0 0";
	%object.Size = 25;
	%object.SceneLayer = 7;  
	%object.Image = "ToyAssets:Planetoid2";
	%object.SrcBlendFactor = "DST_Color";//LightOverlayToy.PlanetoidSrcBlendFactor;  
	%object.DstBlendFactor = "SRC_Alpha";//LightOverlayToy.PlanetoidDstBlendFactor;  
	SandboxScene.add( %object );



#16
08/03/2013 (11:00 pm)
Yeah, I appreciate the effort, but this is definitely different from what I've been trying. Simon's post above (number 9) where you can see holes in the darkness to the stuff between, that's like what I'm aiming for, but also with sprites coming out from under the darkness. Also, multiple "light" sources, so it's not a problem solved by having a single image over your player sprite that extends beyond the edge of the screen (since that gets recommended to me repeatedly).

I still think this might require additional blending options added to the code, to make it possible. Unfortunately I'm not that talented with C++. :/
#17
08/03/2013 (11:15 pm)
Just an additional update, this is the closest thing I can get to my goal:

https://dl.dropboxusercontent.com/u/18965446/LightOverlayToy2.zip

The target and other sprites create a hole in the layer of darkness to reveal the stuff beneath it. The planetoid sprite even hides for the most part, with the exception of the edges. Here are the problems with this system:

ANYTHING with ANY Alpha blending beneath the layer of darkness makes the alpha layer create a hole in the darkness. You can't use any cool transparency effects on things beneath the darkness. The second problem is that it's not a proper bright hole, but more of a faded grey version of whatever is beneath it.

Here's my idea, if anyone knows how to make it work:
1- Create a BlendFactor option that only blends with things above it, rather than below it. This way you can have the transparency/fake lighting effects in the top two layers of the scene and still have everything else beneath it.
2- An alternative mode of DstBlendFactor that is like ONE_MINUS_DST_ALPHA but is more effective than how it appears in my second version of the above toy in that it completely shows the contents beneath, rather than a faded version.

...of course, I'm still hoping this might be possible and I just haven't figured out how to do it yet. ;)
#18
08/06/2013 (6:27 am)
This probably is going towards the Advanced T2D Forum type content, but I was just curious if anyone is experienced enough with the source code to give me an idea of how to add the above-mentioned additional option to the BlendFactor code. I took a look but I ended up at some 0x values and no idea where to go from there.
#19
08/06/2013 (2:21 pm)
This is a perfect example of something that should be done with stencil buffer, not just with blending.
#20
08/10/2013 (7:14 am)
Well, I'm still working on this, since I'm kind of a one-problem-at-a-time kind of guy lately. I'm also still REALLY hoping someone can explain where to go from here.

First, I see code referencing the stencil buffer, but I can't find anything about it in Torquescript. Are there any functions exposed that allow for something like post # 9 above?

Back to the original problem: I found this post for the old Torque2D engine that seemed like it might be useful if I could replicate the formula: www.garagegames.com/community/forums/viewthread/123915/1#comment-800758 . The problem is I can't figure out for the life of me where the formulas are set up in the source code (searching using Visual Studio Express through the entire project). I figured I could copy ZERO and ONE, alter to the formula in the above link for a SUBTRACT_ZERO and SUBTRACT_ONE, and then try it out. However, like I said before I can't figure out where to do that.

Last thing: In gltypes.h I can see this:
/* Blending */
#define GL_BLEND				0x0BE2
#define GL_BLEND_SRC				0x0BE1
#define GL_BLEND_DST				0x0BE0
#define GL_ZERO					0x0
#define GL_ONE					0x1
#define GL_SRC_COLOR				0x0300
#define GL_ONE_MINUS_SRC_COLOR			0x0301
#define GL_SRC_ALPHA				0x0302
#define GL_ONE_MINUS_SRC_ALPHA			0x0303
#define GL_DST_ALPHA				0x0304
#define GL_ONE_MINUS_DST_ALPHA			0x0305
#define GL_DST_COLOR				0x0306
#define GL_ONE_MINUS_DST_COLOR			0x0307
#define GL_SRC_ALPHA_SATURATE			0x0308

However, I'm not experienced with this and have no idea where it's supposed to be pointing at. I'm aware that it's a memory address of some sort, and my guess is that it's supposed to point to where those things are defined (and thus where I might be able to edit them), but I've no idea how to tell where they go.

*crosses fingers that someone can point him in the right direction now*
Page «Previous 1 2