[Official] Changing how Fog is done in Unity shaders?

We’re thinking about changing the way Fog is done in Unity shaders (for Unity 5 or so).

Right now, fog calculations are “patched into” actual shaders, at runtime, on demand.
Advantage: avoids situation of “ship 4x more shader variants” (none, linear, exp, exp2 fog types).
Disadvantages: lots of complicated code implemented for each platform (d3d9, d3d11, opengl, gles); and some other platforms don’t support fog at all (all consoles, WinPhone, iOS8 Metal).

We’d like to change it so that fog calculations are done directly in shader code. Good things out of that:

  • Fog starts working on WinPhone, Metal, consoles.
  • Makes it easier to add better fog modes (like height-based fog).

Practical implications for users / shader writers:

  • Fog command in shaderlab would stop working, except for cases like “Fog { Mode Off }” (which just disabled fog for this shader). In all other cases, most likely result is that fog as specified in render settings will be used. TLDR: no changing of fog modes / parameters per-shader, unless you’re willing to implement your own fog calculations.

  • For vertex+fragment program shaders (i.e. not surface shaders), you’ll have to add some macros / function calls to make fog work.

  • In order to avoid “well now you ship & load 4x more shader variants”, we’ll probably look at which fog modes are used in the scenes, and only include these shader variants into build.

  • Which would mean, don’t change fog modes at runtime.

  • In case you do need that, we’ll provide some UI somewhere to explicitly tell “hey I’ll need these fog modes in the game”.

Does that make sense? Any comments?

This makes a lot of sense. It’ll be good to have a fog implementation that isn’t opaque (lol) to the user.

Not much activity here, but quite some discussion (& approval) on the beta testing list. So here’s the current plan; quite likely to become reality in Unity 5.0:

  • For surface shaders, nothing needs to be done; fog variants & code will be generated. You can add “nofog” to #pragma surface line, if you really don’t want fog.

  • For vertex/fragment shaders, if you want fog you have to do this:

  • Add #pragma multi_compile_fog

  • Add UNITY_FOG_COORDS(n) to your vertex-to-fragment struct

  • Add UNITY_TRANSFER_FOG(o,o.vertex); to your vertex shader. “o” is output struct name, and “o.vertex” is position in clip space.

  • Add UNITY_APPLY_FOG(i.fogCoord, col); to end of your pixel shader. “i” is input struct name, and “col” is the color computed in your pixel shader. This applies standard fog color; if you want custom fog color (as some particle/additive shaders do, for example), you can do UNITY_APPLY_FOG_COLOR(i.fogCoord, col, fixed4(0,0,0,0)); to fog towards black for example.

  • For fixed function shaders, nothing needs to be done. “Fog { … }” command in shaderlab still works, but now it only affects fixed function shaders (for non-fixed function, see point 2 above).

  • By default, fog modes used by scenes are included into game data build. If you know you’ll want to change them at runtime, you can choose “Custom fog modes” (default is “automatic”) under project’s Graphics Settings, and tick checkboxes you need.

I have all the above working, and now fog “just works” on WP8/WinRT/consoles. Also, Unity codebase got smaller by 3500 lines of code. So there, yay.

3 Likes

Thats just like light attenuation, kinda of a hassle the first time but then it’s all gravy

Does this means i can have different object getting affected by fog in a different way, say color for example? I can see some usefulness in there

You can do that right now (using ShaderLab’s “Fog” command - each shader can have totally arbitrary fog). The plan is to remove support for that. However, since fog would be done “manually”, you could just as well do “whatever you want” calculations there. There’s nothing stopping you from not using our built-in fog macros.

1 Like

i see, thx for the explanation, i’ve gotta say i never did anything serious with the current fog system, hence my confusion

I’ve noticed this current system uses a textcoord, light attenuation already uses 2, aren’t we getting a bit short on these? I remember having created a complex shader that used all of the available ones, does using arrays of them help or solve the problem?

It sounds good, but the thing is there is not much to play with fog except what unity already provides, we just need an additional height based fog.
While we can write our own, it would be nice to have an option on the editor for global settings rather than adjusting fog for individual materials.
Soo, just add the height based macro there and thats done.
Or, just expose a fog function somewhere which we can overwrite with our own(if needed)?

Like I said in the first post - this is a prerequisite for adding fancier (atmospheric/height) based fog modes.

2 Likes

Ohh…this is interesting, so this will change/remove the FinalColor in surface shader??

If you’re using FinalColor in surface shader, then we’ll assume you want 100% control over your, well, final color – and will not generate any built-in fog code in that case.

I see…Hmmm oh hey about changing the built in fog into the shader as long as it make it better then i vote yes.
Sure we have to adapt and if it’s give better result then why not…:slight_smile:

You can’t change built-in fog “into” a shader, since it needs to be done as part of other shaders while rendering.

Of course you can do fog as a post-processing effect, and do whatever fancy calculations you want there! In fact, that’s exactly what GlobalFog effect does (which I’m also changing for 5.0 to do something more sensible - but anyone can do that at any time; it’s just a script+shader with full source).

Woops…i mean “in the shader” not “into the shader”, sorry
Problem with GlobalFog is it doesn’t work well with some transparent object ,out of the box of course except it’s already changed in 5 :).
Alright i don’t want to derail this thread into GlobalFog discussion .
Except if it is included in this thread topic.

Wooh seems like a great chance Aras, the less shader patching the better. What kind of unity specific shader patching is still being done? Any improvments to lower memory and prewarming times are very welcome (does 5.0 still store shaders in hex instead of binary format?)

I do not agree with Aubergine, Fog also needs to be used as complex fog volume if we want to have a seamless transition between over the water to under the water the way Crytek and Ubi does it. What Aras proposes is a good thing in order to make fog matching any type of mesh for example.

@Smartwater3D : I don’t see how my proposed change helps with implementing fog volumes, to be honest…

I just made some tests. It seems OK but Legacy Shaders should use the old way. Otherwise porting 4.x projects to 5.x will be a nightmare if we have to add these macros in every shader we’ve already created.

If you’re using surface shaders, then nothing needs to be done. If you aren’t, then yes, you should now add these macros.

The problem with “legacy shaders” - there’s no good way of knowing if a shader is legacy or not.

“It sounds good, but the thing is there is not much to play with fog except what unity already provides, we just need an additional height based fog.”: Aubergine.

I do not agree about the fact we “only need additional height based fog”… That’s what I mean. Fog can be used in a lot of different ways, and if we want to have a fog matching a moving surface for example, we need to access to how the fog is calculated and possibly change it.
Also having access to the Grabpass code and being able to modify it would be great (but it is another subject)

Well, I already do fog in shader as it’s a lot faster and looks practically the same as Unity fog (we just do it per vert and pass in few floats globally).

Since fog is the job of shaders, having it outside of shaders is just misleading at this point. We need less mystery and more tweakability :slight_smile: just my two cents.

5 will change a lot of things. Physics, code, shaders. This is the best time to make changes. Some people will moan about changing their shaders, but really is that more than 5 mins work? I don’t see it as a reason to worry.

I’d like it to be explicitly asked for in the shader via a macro or whatever to avoid shipping 4x the amount, of course since not all my shaders use fog. Obviously :slight_smile: