Need fogging alpha equation!

Miscellaneous Forums/General Discussion/Need fogging alpha equation!

I'm trying to add fog to my sprite system, and I'm having a hard time coming up with the right equations to use.

Here's the problem:

To blit a sprite with fog, I first blit the sprite, then I blit a solid color version of the sprite over it. The alpha of the solid version is set accoring to how much fog is desired.

Sounds simple enough and works just fine when the sprite's alpha is 1.

But when the sprite's alpha is 0.5, sprite should be 50% transparent regardless of whether the fog is set to 1.0, or 0.5.

But if I simply multiply the fog's value by the alpha value and use that for my fog, then this is what happens:

Alpha = 0.5
Fog = 0.5
Fog*Alpha = Adjusted Fog = 0.25

Alpha + Adjusted Fog = 0.75

0.75 is how much the end result would cover up the pixels behind the sprite. One sprite blitted with 0.5 alpha, and one with 0.25 alpha comes to a total coverage of 0.75.

That not the desired level of transparency of the sprite though. It should only have a total alpha of 0.5.

I tried scaling the alpha values for both by a scaling factor to get them into the range I wanted:

factor = alpha/totalalpha
If totalalpha = 0 Then factor = 0

alpha*factor
fog*alpha*factor

But the result was that if the alpha was 1.0, and the fog was 0.25, then the total was 1.25, and this scaled down the alpha from 1.0 where it should have remained, meaning the end result was a sprite whose alpha was 1, but which was being drawn semi transparent.

Clamping the total to 1.0 would be the obvious next thing to try, but I'm pretty sure that that won't get me the result I want either.

In short, this can't be done.

In long, if fogfactor = 0..1, alpha = 0..1

if you just go setalpha( alpha ) for the image and setalpha( fogfactor * alpha) for the fog, the total alpha applied is fogfactor * alpha^2. You want to solve for:

useralpha = drawalpha^2 * fogfactor
or drawalpha = Sqr( useralpha / fogfactor )

That will get the alpha values correct, but not the result you're looking for (at least not perfect).

At least I think that is right, I haven't tested it.

Can't be done isn't in in my vocabulary. :-)

Hm... Maybe I'm going about this the wrong way.

First, I need to know what I want the resulting pixel color to be. And that is governed by this equation:

ResultingPixelR = BackgroundColorR*(1-Alpha) + Alpha*(SpriteColorR*(1-FogAlpha) + FogColorR*FogAlpha)

Sooo...

I have no idea where to go from here. :-)

The problem with the above equation is that I'm not multiplying the background color by 1-Alpha, or adding Alpha*(SpriteColorR*(1-FogAlpha) + FogColorR*FogAlpha) to the background. That stuff is handled by the card.

So hm...

I can't do Alpha*(SpriteColorR*(1-FogAlpha) + FogColorR*FogAlpha) before adding it to the background.

But this is what I can do:
ResultingPixelR = (BackgroundColorR*(1-Alpha) + Alpha*SpriteColorR)*(1-FogAlpha) + FogColorR*FogAlpha

That represents blitting an alpha sprite over the background, then blitting another alpha sprite over the result.



SO...

What I need to do is find a way to get the results of this:
ResultingPixelR = (BackgroundColorR*(1-Alpha) + Alpha*SpriteColorR)*(1-FogAlpha) + FogColorR*FogAlpha


To match this:
ResultingPixelR = BackgroundColorR*(1-Alpha) + Alpha*(SpriteColorR*(1-FogAlpha) + FogColorR*FogAlpha)


And DO that by tweaking ONLY the values of Alpha and FogAlpha with another set of equations based on their input values.

Hm... What if I write it another way...

BC = BackgroundColor


The result I want:

BC = BC * 1-Alpha
BC = BC + Alpha*(SpriteR*(1-FogAlpha) + FogR*FogAlpha)

What I can do:

BC = BC * 1-Alpha
BC = BC + SpriteR*Alpha
BC = BC * 1-FogAlpha
BC = BC + FogR*FogAlpha

Argh. This isn't helping!

Hm... If I remember anything from my algebra classes it's that you can distribute certain things through equations in parentheses.... Like so:

2*(2 + 3) = 10

2*2 = 4
+
2*3 = 6

4+6= 10


Yep, that works.


So...

BC = BC * 1-Alpha
BC = BC + Alpha*(SpriteR*(1-FogAlpha) + FogR*FogAlpha)

Could be converted to:

BC = BC * 1-Alpha
BC = BC + Alpha*SpriteR*(1-FogAlpha) + Alpha*FogR*FogAlpha


This gives me some options. I COULD set the fog sprite to add blend to get the result I want.

So, if I set the sprite's alpha to Alpha, and blit it, that satisfies this part of the equation:

BC = BC * 1-Alpha
BC = BC + Alpha*SpriteR

But that's not the result I want after the first step, I need this:

BC = BC + Alpha*SpriteR*(1-FogAlpha)

So, my first step is to set the sprite's alpha to Alpha*(1-FogAlpha) and blit it.

The remainder of the equation is this:
+ Alpha*FogR*FogAlpha

So my second step should be to multiply the color of the fog sprite by Alpha and FogAlpha, and then add blend that over the result of the first operation!

And that should give me the result I want!

I think.

Off to try it I guess. :-)

Can't be done isn't in in my vocabulary. :-)

Then add it. It really can't be done with two image draws and two alphas. It is easy to figure this out. Don't be a dick.

So my second step should be to multiply the color of the fog sprite by Alpha and FogAlpha, and then add blend that over the result of the first operation!

Additive blending the fog sprite after alpha blending the first image can't be the solution, because if the background pixels and the image pixels are both brighter than the fog, there's no way to alpha blend them together to get a result that's darker than the desired result.

I got my pen and paper out to try to figure out possible solutions, but man, you sure don't know how to ask for help.

Yay, it works!

Can't be done my arse. :-)

It can't be done. You've created an approximation.


Additive blending the fog sprite after alpha blending the first image can't be the solution, because if the background pixels and the image pixels are both brighter than the fog, there's no way to alpha blend them together to get a result that's darker than the desired result.



Okay you got me there... It seemed to work, but when I set the background to white and the fog to black, and the fog alpha to 1, all I get is white, where I should be seeing a black sprite.

I don't know why that is occuring since according to the equations it should work, unless I accidentalyl dropped a term somewhere.

I still think it can be done though... Hm...


but man, you sure don't know how to ask for help.



What? I asked and you said it couldn't be done. Of course I'm not gonna just roll over and admit defeat that easily. I'm gonna try to prove you wrong. :-)


It can't be done. You've created an approximation.



Everything on a computer is an approximation. When I set alpha to 0.1, I don't get an alpha of 0.1. As long as it is close enough, it's good enough. Why would I care if it was off by a teensy bit?

Ah I think this is where I screwed up:

BC = BC + Alpha*SpriteR*(1-FogAlpha) + Alpha*FogR*FogAlpha

I set the sprite alpha to Alpha*(1-FogAlpha). But it gets confusing what is going on because I have to take into account what the card does with alpha and stuff when blitting it.

I think the correct thing to do is to set the sprite's alpha to alpha, but multiply the sprite's COLOR by 1-fogalpha.

What? I asked and you said it couldn't be done. Of course I'm not gonna just roll over and admit defeat that easily. I'm gonna try to prove you wrong. :-)


No, what I'm saying that you can't manipulate the equations to come up with the exact values to use that will do the effect. It's not possible (chuck it into an equation solver, and the result is dependent per pixel per channel), so don't try. It may be possible to do something that comes close, given the range of blend modes available (multiply blend not supporting alpha is annoying), so do try that.

What's wrong with just multiplying the sprite by a color calculated depending on the fog distance?

Woo! Multiplying the sprite color by 1-FogAlpha was the solution.

And I disagree with you saying it is approximate. The equation I used is exactly the same equations used when the card blits sprites with alpha on the screen. There's nothing approximte about my solution. It's exactly how the card would fog something internally. And it looks exactly the way you would expect it to and the way I want it to.

(Assuming you don't take the alpha channel of the sprite into account... I did not qhen I wrote those equations. I don't know how that affects it. But the results with my antialiased sprites with alpha channels appear to be correct, so I think in the end it didn't matter.)


What's wrong with just multiplying the sprite by a color calculated depending on the fog distance?



Because that would produce entirely the wrong result, if the fog color is not a shade of gray. I have red birds with white wings. My fog is blue. If I multiplied the bird's color with blue, then I'd get bright blue wings, and a black body. That's the first thing I tried. :-)

Also, I wanted fog to work right cause other people use my sprite system and I had implemented this kludgy solid_glow thing to allow you to make sprites flash white. Now you can do fog, and do the flashing in a non-kludgy way.

I hate to bring this up... But fog isn't transparent.

Why not take sprite A sans alpha, then sprite B (the fog) with alpha and cover sprite A, to net you sprite C, which has no alpha, and give it sprite A's origional alpha figures...

so.. if you have a bottle with a top. The bottle itself might be 50% transparent, and the top is solid. You cover with fog, and the top becomes more grey, and the bottles colors become more grey, and you can then still see through the bottle by 50%.

I don't see why you can't do that.


In other words... Do whatever you want to make the fog look. But replace the ultimate alpha with the origional sprite's alpha.

Well here's why I don't do that.

Let's say I'm using white fog, on a black background.

If I have objects that are fogged by 50%, and I don't alter the transparency of the fog as I alter the transparency of the object, then what I'll get is objects fading to a white outline as I fade their alpha out.

Now, if my background was white, which it would be if we were talking about real fog, this would look right.

Or would it?

Sure, it seems like it would work correctly on first glance, but consider what would happen if you had other objects behind that object you were fading out.

They would have 50% fog applied to them as well already, but now your object is fading out, but the fog affecting its color is not faded out with it, so now you have a 50% transparent white siloughette of your object affecting objects behind it even though it is completely invisible.

And that's obviously wrong.

So to make fog look right with semi-transparent objects, you have to alter the amount of fog on the object as it becomes more transparent, such that the colors will remain at 50% saturation throughout the whole fadeout process. And that is what I've done here.

Post your code, sswift?

' -------------------------------------------------------------------------------------------------------------------------------------------------------
	' This method draws the sprite.
	' -------------------------------------------------------------------------------------------------------------------------------------------------------

		Method Draw()
			
			Local A#			
			
			
				' BC = BC * 1-Alpha
				' BC = BC + Alpha*(SpriteR*(1-FogAlpha) + FogR*FogAlpha)
				' OR
				' BC = BC + Alpha*SpriteR*(1-FogAlpha) + Alpha*FogR*FogAlpha			
			
				' IE: 
				' 1. Set the sprite's alpha to Alpha*(1-FogAlpha)
				' 2. Multiply the color of the fog sprite by Alpha and FogAlpha, and then add blend that over the result of the first operation.
			
			
			' If the sprite has an image...
			If _Image <> Null
			
				' If the sprite is not hidden, and none of its parents are hidden...
				If Hidden(True) = False
			
			
					' Store the current visual properties so we can reset them when we are done.
						RenderState.Push()
					
					' Set color, alpha, rotation, blending mode, etc.
						
						A# = 1.0-Fog(True)
						
						.SetAlpha(Alpha#(True))
						.SetBlend(_Blend)
						.SetColor(R#(True)*A#, G#(True)*A#, B#(True)*A#) 
						.SetRotation(Rotation#(True))
						.SetScale(ScaleX#(True), ScaleY#(True))
						.SetViewport(ViewportX#(True), ViewportY#(True), ViewportWidth#(True), ViewportHeight#(True))

								
					' If the sprite has a shadow, draw it.			
					
						If (Alpha#(True) * _ShadowAlpha#) > 0
					
							RenderState.Push()	
					
							.SetAlpha(Alpha#(True) * _ShadowAlpha#)
							.SetColor(0, 0, 0)
							
							' Transform shadow vector into global space, and draw shadow relative to sprite's global position.
							
								TFormVector(_ShadowVx#, _ShadowVy#, Parent(), Null)
								DrawImage(_Image, X#(True)+Sprite.TFormedX#(), Y#(True)+Sprite.TFormedY#(), Frame#(True))
								
							RenderState.Pop()
							
						EndIf			
						
																																								
					' Draw sprite.
						DrawImage(_Image, X#(True), Y#(True), Frame#(True))	
						
												
					' If the sprite's color is over 255, make it "glow" by drawing a second copy at the same location with add blend.
					
						If (R#(True) > 255)	Or (G#(True) > 255) Or (B#(True) > 255)

							.SetColor(R#(True)-255, G#(True)-255, B#(True)-255)
							.SetBlend(LIGHTBLEND)
							
							DrawImage(_Image, X#(True), Y#(True), Frame#(True))
											
						EndIf
						
					
					' If the sprite's fog is greater than 0, fog the sprite by drawing a second solid-color copy at the same location with alpha blend.	
						
						If Fog#(True) > 0

							EnableSolidColor()
							
							A# = Alpha#(True)*Fog#(True)
											
							.SetAlpha(1.0)
							.SetColor(FogR#(True)*A#, FogG#(True)*A#, FogB#(True)*A#)
							.SetBlend(LIGHTBLEND)
							
							DrawImage(_Image, X#(True), Y#(True), Frame#(True))
							
							DisableSolidColor()							

						EndIf
						
						
					' Restore visual properties.
						RenderState.Pop()
						
					
				EndIf
				
			EndIf
		
		End Method



EnableSolidColor() makes the sprite render using alpha, but the sprite's color rather than the color*texture color. So the sprite will be say, a solid blue siloughette instead of a normal sprite with texture colors multiplied by the sprite color.

Oh right, that's what EnableSolidColor does. It definitely looks like it works in all situations.

I'm not sure I agree.

First. Fog doesn't effect a single object. It will have to effect the entire scene. Otherwise.. well.. that is retarded.

you can't have a black background, with an object effected by white fog. You'll have a grey background. That white fog will lighten up the background.

Lets say. You have green glass, at the edges you'll have very transparent green glass as it AAs into the background. Cover that with white fog, and you now have lighter-more-muted-green nearly-transparent glass... Over a grey background. Or- grey with a very slight green tinge... which is exactly what you need.

In the center of the glass, you'll have lightened up, and also more muted green glass at 50% transparency over a grey background. Which is again... what you need.

Unless we are talking about some un-realistic magic fog that doesn't do what real fog does... in which case... why are you bothering with any kind of realism in the first place?

You could even. And this is getting really trippy. give each sprite a 'fog map' which is a set of alpha values specific to how fog should cover the object... Could be useful if there is a fan, or something else (as it could animate and create whisps of fog) Then, the fog blanket combines with that fog-map to create the color and relative thickness of the fog.. thickness being analogue to alpha.

that would enable you to kill 2 birds with one stone. You set anything that isn't the sprite to 100% transparency. And AA in the fog around the object. And you can then push out and take in the fog around the fictional geometery of the sprite. Since fog will get less transparent the thicker it is and the more of it you are looking through.

In other words. A closed bottle will lock out all the fog. While an open one will have a lower transparency around where you can see the 'inside' of the bottle... So it 'fills' with fog. And can even be animated to make it slosh about inside the bottle in the fictional current that runs through the map.

The fog map would take into account the 'back' of the sprite as well.. And anywhere there isn't a sprite, the engine adds in filler-fog... based on a giant fog-map that is defined by the background tiles/image.

I mean... if you are going to do fog.. Might as well make it dynamic fog..


Regardless.. I don't see how what you said effects anything.

as long as you are creating an alternate sprite, with the transparency of the origional sprite and the color changes of the new sprite. It should work.

If your sprites have outlines.. like cartoons.. you would have to have a fog-map to define the areas less effected by the fog. so you don't bleach out the outlines which defy logic.

Dampe:
It's not retarded. :-)

I have stars in the background of my space scene. I don't want them to be fogged. Nor do I want my nebulas to be fogged. Or my scoreboard.

But I do want aliens which have just spawned and are rendered in front of the stars to take on a bluish fog effect so that the player will be able to tell that they are not part of the foreground, and thus, not yet shootable.



you can't have a black background, with an object effected by white fog. You'll have a grey background. That white fog will lighten up the background.



But you also don't want an invisible object to look like a white cloud moving over objects that are behind it because fog is still affecting it 100%.

The way I have set up fog is correct in this respect. You can set the fog of your sprites according to the distance from the camera, and the effect will be 100% correct. If you did it the way you're saying then any sprite with alpha near 0 would render like a nearly solid white cloud and look all wrong.

The only way to get a "proper" fog effect would be to, for every Z level a sprite is drawn at, render a solid fog square covering the whole scene and that sprite and all sprites at that Z distance with the transparency set to get the appropriate amount of additional fog. This would add on top of all the layers that were drawn behind it with fog as well. Otherwise you'd only get this additive effect where the individual sprites are and that wouldn't be correct, unless you give up completely on allowing any transparency.



Unless we are talking about some un-realistic magic fog that doesn't do what real fog does... in which case... why are you bothering with any kind of realism in the first place?




There was a very speciifc effect I wanted to get. I wanted to get the effect of a semitransparent solid color version of a sprite rendered over a semitransparent regular version of a sprite, and I wanted the overall effect to have the same level of transparency as the sprite was supposed to have in the first place.

In other words, I wanted to recreate the effect of a photoshop layer with my sprite, with another layer colored blue grouped with that layer. Both have alpha you can set, but the alpha of the parent layer is what sets the overall alpha. The alpha of the child only affects the color of the parent layer, not the overall transparency of it.

I also wanted this effect to be useful for simulating fog. And it is entirely useful for that. With the fog getting more transparent as the sprite does, the overall effect is fairly realistic. It may not be 100% accurate, (or maybe it is, I don't know.) but it's close enough.

Is it better to simulate fog perfectly, or simulate it closely, but also give the user the ability to make artistic decisions about how objects in the world are affected by it, and for them to be able to use it to make objects flash white momentarily? I say the latter is more important.

Making a game fun doesn't mean making it make perfect sense. When mario gets that cape and you want him to interpolate from normal to pure white over the course of a second to indicate he is powering up, you'll be glad I did it this way. :-)



If your sprites have outlines.. like cartoons.. you would have to have a fog-map to define the areas less effected by the fog. so you don't bleach out the outlines which defy logic.



I don't think I've seen a cartoon where they don't bleach out the lines as a character moves into fog. That would look really weird.

Ok.. but what you want ISN'T fog...

as far as the cartoon goes. Bleaching the line. Yes. But it wouldn't react exactly the same as the rest of the object... or wouldn't HAVE to react the same.

sure it could and does in a lot of animations. but much like eyes pushing through hair in anime. You might want the lines to always be at least a little visible through fog. even when everything else is blocked out.

Point was, black lines around an object aren't realistic. So you can't always rely on logic for things to look 'right'.


My thing was assuming a 2D space, perhaps isometric. Where there really isn't any distance from the camera to speak of. Or at least relative to the other objects in the scene.

What I described does FOG. Not weird magic glows.

Sure. My thing won't work for you... cause you don't need fog.. haha. :P

I hate eyes that show though hair in anime. So no! I might definitely not want that! :-)

haha. Yeah. But SOMEONE might.