Cheapest (performance wise) fog for GLES2 mobile VR (LDR) - how?

How can I have a simple fog, as seen on the images below, in UE4 for GLES 2.0 mobile (VR) platform? Thanks!

cf48af30096986681c4deef7cab344daed697ca1.jpeg

6f9b6f76f1d71ad0af9175b27547a032c9ae1302.jpeg

9ee94e07453bb75dcd445835dde76d0510935508.jpeg

I’m pretty sure an exponential heightfog is cheap enough for mobile.

It doesn’t work on mobile, unless HDR is on and HDR is a no-go for VR.

Oh wow… next best thing would be a distance-based post process blend. Pixel depth / the distance you want the fog to reach full intensity, clamped 0-max opacity, say, 0.8, then lerped between the scene color (or post process 1) and the color. This goes in the blendables under post process. But it doesn’t work on translucent materials. You can use a similar function to blend any particle effects or translucent objects out by the same distance. I’m not too familiar with the mobile platform, but these methods are the only way I know to get fog.

Well, limitations of mobile VR are A. no HDR, B. no translucency (or very little of it), C. no post-process.

However, I know that Unity has fog for mobile platform that doesn’t affect performance (much). So I wonder why on Earth wouldn’t UE4 be capable of such basic fog :frowning:

I wonder if it would be easy to capture depth image as cubemap to a cube around player (and material of that cube would use depth image as texture) and blend (additively I guess) with the scene.

I am not that savvy with complex materials and post process stuff yet. If you could show me a node tree and how to set stuff up, I could test it on my Gear VR (and if performance doesn’t drop at all or by much, then the method could be considered valud and perhaps Epic could use it in their upcoming templates for Gear VR).

It’s not so much that it can’t be done as it is… time consuming. Somewhat.

Copy and paste this into a material function. Then, on every material in your scene, drag this material function in it and plug in your textures and your fog color. The depth controls the distance it takes for the fog to stop getting “foggier,” and the max opacity controls how “foggy” the entire scene is. The output should go to your base color.

This only requires 4 extra instructions. Not bad. But if for whatever reason the mobile renderer can’t handle Pixel Depth, try the [Distance] between [Absolute World Position] and [Camera Position], and use that instead of Pixel Depth.

EDIT: Pixel depth apparently DOES work on mobile, so yeah, you can get fog for 4 instructions.


Begin Object Class=MaterialGraphNode Name="MaterialGraphNode_2550"
   Begin Object Class=EdGraphPin Name="EdGraphPin_71274"
   End Object
   Begin Object Class=MaterialExpressionPixelDepth Name="MaterialExpressionPixelDepth_9"
   End Object
   Begin Object Name="EdGraphPin_71274"
      PinName="Output"
      PinFriendlyName=" "
      Direction=EGPD_Output
      PinType=(PinCategory="mask",PinSubCategory="red")
      LinkedTo(0)=EdGraphPin'MaterialGraphNode_2551.EdGraphPin_71276'
   End Object
   Begin Object Name="MaterialExpressionPixelDepth_9"
      MaterialExpressionEditorX=-688
      MaterialExpressionEditorY=480
      MaterialExpressionGuid=DCE0DC42412C63E585A3D6B39DBE3141
      Material=Material'/Engine/Transient.Material_25'
      Desc="Depth into the world"
      bCommentBubbleVisible=True
   End Object
   MaterialExpression=MaterialExpressionPixelDepth'MaterialExpressionPixelDepth_9'
   Pins(0)=EdGraphPin'EdGraphPin_71274'
   NodePosX=-688
   NodePosY=480
   NodeComment="Depth into the world"
   bCommentBubbleVisible=True
   NodeGuid=8D78435E4954865691900D919FE9E1D0
End Object
Begin Object Class=MaterialGraphNode Name="MaterialGraphNode_2551"
   Begin Object Class=EdGraphPin Name="EdGraphPin_71278"
   End Object
   Begin Object Class=EdGraphPin Name="EdGraphPin_71277"
   End Object
   Begin Object Class=EdGraphPin Name="EdGraphPin_71276"
   End Object
   Begin Object Class=MaterialExpressionDivide Name="MaterialExpressionDivide_9"
   End Object
   Begin Object Name="EdGraphPin_71278"
      PinName="Output"
      PinFriendlyName=" "
      Direction=EGPD_Output
      LinkedTo(0)=EdGraphPin'MaterialGraphNode_2553.EdGraphPin_71282'
   End Object
   Begin Object Name="EdGraphPin_71277"
      PinName="B"
      PinType=(PinCategory="optional")
      LinkedTo(0)=EdGraphPin'MaterialGraphNode_2561.EdGraphPin_71307'
   End Object
   Begin Object Name="EdGraphPin_71276"
      PinName="A"
      PinType=(PinCategory="optional")
      LinkedTo(0)=EdGraphPin'MaterialGraphNode_2550.EdGraphPin_71274'
   End Object
   Begin Object Name="MaterialExpressionDivide_9"
      A=(Expression=MaterialExpressionPixelDepth'MaterialGraphNode_2550.MaterialExpressionPixelDepth_9',Mask=1,MaskR=1)
      B=(Expression=MaterialExpressionFunctionInput'MaterialGraphNode_2561.MaterialExpressionFunctionInput_24')
      MaterialExpressionEditorX=-496
      MaterialExpressionEditorY=528
      MaterialExpressionGuid=800653994F5099D1C4B2419D9FA77D69
      Material=Material'/Engine/Transient.Material_25'
   End Object
   MaterialExpression=MaterialExpressionDivide'MaterialExpressionDivide_9'
   Pins(0)=EdGraphPin'EdGraphPin_71276'
   Pins(1)=EdGraphPin'EdGraphPin_71277'
   Pins(2)=EdGraphPin'EdGraphPin_71278'
   NodePosX=-496
   NodePosY=528
   NodeGuid=02ACDDF74E9C9E68B4133C8A890B249E
End Object
Begin Object Class=MaterialGraphNode Name="MaterialGraphNode_2553"
   Begin Object Class=EdGraphPin Name="EdGraphPin_71285"
   End Object
   Begin Object Class=EdGraphPin Name="EdGraphPin_71284"
   End Object
   Begin Object Class=EdGraphPin Name="EdGraphPin_71283"
   End Object
   Begin Object Class=EdGraphPin Name="EdGraphPin_71282"
   End Object
   Begin Object Class=MaterialExpressionClamp Name="MaterialExpressionClamp_9"
   End Object
   Begin Object Name="EdGraphPin_71285"
      PinName="Output"
      PinFriendlyName=" "
      Direction=EGPD_Output
      LinkedTo(0)=EdGraphPin'MaterialGraphNode_2554.EdGraphPin_71292'
   End Object
   Begin Object Name="EdGraphPin_71284"
      PinName="Max"
      PinType=(PinCategory="optional")
      LinkedTo(0)=EdGraphPin'MaterialGraphNode_2559.EdGraphPin_71311'
   End Object
   Begin Object Name="EdGraphPin_71283"
      PinName="Min"
      PinType=(PinCategory="optional")
   End Object
   Begin Object Name="EdGraphPin_71282"
      PinName="Input"
      PinFriendlyName=" "
      PinType=(PinCategory="required")
      LinkedTo(0)=EdGraphPin'MaterialGraphNode_2551.EdGraphPin_71278'
   End Object
   Begin Object Name="MaterialExpressionClamp_9"
      Input=(Expression=MaterialExpressionDivide'MaterialGraphNode_2551.MaterialExpressionDivide_9')
      Max=(Expression=MaterialExpressionFunctionInput'MaterialGraphNode_2559.MaterialExpressionFunctionInput_25')
      MaterialExpressionEditorX=-368
      MaterialExpressionEditorY=528
      MaterialExpressionGuid=176022B84C69447A88EBD7B5FB20E111
      Material=Material'/Engine/Transient.Material_25'
   End Object
   MaterialExpression=MaterialExpressionClamp'MaterialExpressionClamp_9'
   Pins(0)=EdGraphPin'EdGraphPin_71282'
   Pins(1)=EdGraphPin'EdGraphPin_71283'
   Pins(2)=EdGraphPin'EdGraphPin_71284'
   Pins(3)=EdGraphPin'EdGraphPin_71285'
   NodePosX=-368
   NodePosY=528
   NodeGuid=548F79294845BDD1EFF5898BC98227B8
End Object
Begin Object Class=MaterialGraphNode Name="MaterialGraphNode_2554"
   Begin Object Class=EdGraphPin Name="EdGraphPin_71293"
   End Object
   Begin Object Class=EdGraphPin Name="EdGraphPin_71292"
   End Object
   Begin Object Class=EdGraphPin Name="EdGraphPin_71291"
   End Object
   Begin Object Class=EdGraphPin Name="EdGraphPin_71290"
   End Object
   Begin Object Class=MaterialExpressionLinearInterpolate Name="MaterialExpressionLinearInterpolate_9"
   End Object
   Begin Object Name="EdGraphPin_71293"
      PinName="Output"
      PinFriendlyName=" "
      Direction=EGPD_Output
      LinkedTo(0)=EdGraphPin'MaterialGraphNode_2563.EdGraphPin_71272'
   End Object
   Begin Object Name="EdGraphPin_71292"
      PinName="Alpha"
      PinType=(PinCategory="optional")
      LinkedTo(0)=EdGraphPin'MaterialGraphNode_2553.EdGraphPin_71285'
   End Object
   Begin Object Name="EdGraphPin_71291"
      PinName="B"
      PinType=(PinCategory="optional")
      LinkedTo(0)=EdGraphPin'MaterialGraphNode_2560.EdGraphPin_71299'
   End Object
   Begin Object Name="EdGraphPin_71290"
      PinName="A"
      PinType=(PinCategory="optional")
      LinkedTo(0)=EdGraphPin'MaterialGraphNode_2558.EdGraphPin_71303'
   End Object
   Begin Object Name="MaterialExpressionLinearInterpolate_9"
      A=(Expression=MaterialExpressionFunctionInput'MaterialGraphNode_2558.MaterialExpressionFunctionInput_23')
      B=(Expression=MaterialExpressionFunctionInput'MaterialGraphNode_2560.MaterialExpressionFunctionInput_22')
      Alpha=(Expression=MaterialExpressionClamp'MaterialGraphNode_2553.MaterialExpressionClamp_9')
      MaterialExpressionEditorX=-80
      MaterialExpressionEditorY=320
      MaterialExpressionGuid=2F1FB01646359E9016C136806C0878F6
      Material=Material'/Engine/Transient.Material_25'
      Desc="Plug into base color/emissive"
      bCommentBubbleVisible=True
   End Object
   MaterialExpression=MaterialExpressionLinearInterpolate'MaterialExpressionLinearInterpolate_9'
   Pins(0)=EdGraphPin'EdGraphPin_71290'
   Pins(1)=EdGraphPin'EdGraphPin_71291'
   Pins(2)=EdGraphPin'EdGraphPin_71292'
   Pins(3)=EdGraphPin'EdGraphPin_71293'
   NodePosX=-80
   NodePosY=320
   NodeComment="Plug into base color/emissive"
   bCommentBubbleVisible=True
   NodeGuid=59133C9E445717A0E66DD0A0373C5D07
End Object
Begin Object Class=MaterialGraphNode Name="MaterialGraphNode_2560"
   Begin Object Class=EdGraphPin Name="EdGraphPin_71299"
   End Object
   Begin Object Class=EdGraphPin Name="EdGraphPin_71298"
   End Object
   Begin Object Class=MaterialExpressionFunctionInput Name="MaterialExpressionFunctionInput_22"
   End Object
   Begin Object Name="EdGraphPin_71299"
      PinName="Output"
      PinFriendlyName=" "
      Direction=EGPD_Output
      LinkedTo(0)=EdGraphPin'MaterialGraphNode_2554.EdGraphPin_71291'
   End Object
   Begin Object Name="EdGraphPin_71298"
      PinName="Preview"
      PinType=(PinCategory="optional")
   End Object
   Begin Object Name="MaterialExpressionFunctionInput_22"
      InputName="Fog Color"
      Description="Your fog color goes here"
      Id=AE96FAA845AC0719BC2EAFB189686B9D
      PreviewValue=(X=1.000000,Y=0.610000,Z=0.250000,W=1.000000)
      bUsePreviewValueAsDefault=True
      SortPriority=1
      MaterialExpressionEditorX=-384
      MaterialExpressionEditorY=336
      MaterialExpressionGuid=6545C43A47B1DEEBEC4E479439505277
      Material=Material'/Engine/Transient.Material_25'
   End Object
   MaterialExpression=MaterialExpressionFunctionInput'MaterialExpressionFunctionInput_22'
   Pins(0)=EdGraphPin'EdGraphPin_71298'
   Pins(1)=EdGraphPin'EdGraphPin_71299'
   NodePosX=-384
   NodePosY=336
   NodeGuid=0A8A85F5430D3384C780EFB3E60808F1
End Object
Begin Object Class=MaterialGraphNode Name="MaterialGraphNode_2558"
   Begin Object Class=EdGraphPin Name="EdGraphPin_71303"
   End Object
   Begin Object Class=EdGraphPin Name="EdGraphPin_71302"
   End Object
   Begin Object Class=MaterialExpressionFunctionInput Name="MaterialExpressionFunctionInput_23"
   End Object
   Begin Object Name="EdGraphPin_71303"
      PinName="Output"
      PinFriendlyName=" "
      Direction=EGPD_Output
      LinkedTo(0)=EdGraphPin'MaterialGraphNode_2554.EdGraphPin_71290'
   End Object
   Begin Object Name="EdGraphPin_71302"
      PinName="Preview"
      PinType=(PinCategory="optional")
   End Object
   Begin Object Name="MaterialExpressionFunctionInput_23"
      InputName="Texture"
      Description="Your material colors/textures go here"
      Id=C3207F284CC2B41929D0C7A7498B6C28
      bUsePreviewValueAsDefault=True
      MaterialExpressionEditorX=-384
      MaterialExpressionEditorY=160
      MaterialExpressionGuid=6545C43A47B1DEEBEC4E479439505277
      Material=Material'/Engine/Transient.Material_25'
   End Object
   MaterialExpression=MaterialExpressionFunctionInput'MaterialExpressionFunctionInput_23'
   Pins(0)=EdGraphPin'EdGraphPin_71302'
   Pins(1)=EdGraphPin'EdGraphPin_71303'
   NodePosX=-384
   NodePosY=160
   NodeGuid=74AD6BB949F2D4AC7D864EAF81946958
End Object
Begin Object Class=MaterialGraphNode Name="MaterialGraphNode_2561"
   Begin Object Class=EdGraphPin Name="EdGraphPin_71307"
   End Object
   Begin Object Class=EdGraphPin Name="EdGraphPin_71306"
   End Object
   Begin Object Class=MaterialExpressionFunctionInput Name="MaterialExpressionFunctionInput_24"
   End Object
   Begin Object Name="EdGraphPin_71307"
      PinName="Output"
      PinFriendlyName=" "
      Direction=EGPD_Output
      LinkedTo(0)=EdGraphPin'MaterialGraphNode_2551.EdGraphPin_71277'
   End Object
   Begin Object Name="EdGraphPin_71306"
      PinName="Preview"
      PinType=(PinCategory="optional")
   End Object
   Begin Object Name="MaterialExpressionFunctionInput_24"
      InputName="Depth"
      Description="Distance to max opacity"
      Id=9D209ADA4D478B5098C987A22BD72498
      InputType=FunctionInput_Scalar
      PreviewValue=(X=5000.000000,Y=0.610000,Z=0.250000,W=1.000000)
      bUsePreviewValueAsDefault=True
      SortPriority=2
      MaterialExpressionEditorX=-720
      MaterialExpressionEditorY=576
      MaterialExpressionGuid=6545C43A47B1DEEBEC4E479439505277
      Material=Material'/Engine/Transient.Material_25'
      bCollapsed=True
   End Object
   MaterialExpression=MaterialExpressionFunctionInput'MaterialExpressionFunctionInput_24'
   Pins(0)=EdGraphPin'EdGraphPin_71306'
   Pins(1)=EdGraphPin'EdGraphPin_71307'
   NodePosX=-720
   NodePosY=576
   NodeGuid=893B6BB0469DB4DD4048C38D47838EC5
End Object
Begin Object Class=MaterialGraphNode Name="MaterialGraphNode_2559"
   Begin Object Class=EdGraphPin Name="EdGraphPin_71311"
   End Object
   Begin Object Class=EdGraphPin Name="EdGraphPin_71310"
   End Object
   Begin Object Class=MaterialExpressionFunctionInput Name="MaterialExpressionFunctionInput_25"
   End Object
   Begin Object Name="EdGraphPin_71311"
      PinName="Output"
      PinFriendlyName=" "
      Direction=EGPD_Output
      LinkedTo(0)=EdGraphPin'MaterialGraphNode_2553.EdGraphPin_71284'
   End Object
   Begin Object Name="EdGraphPin_71310"
      PinName="Preview"
      PinType=(PinCategory="optional")
   End Object
   Begin Object Name="MaterialExpressionFunctionInput_25"
      InputName="Max Opacity"
      Description="Max opacity of the fog"
      Id=FC9837C94FC1CA02BA4560A7D5738BF8
      InputType=FunctionInput_Scalar
      PreviewValue=(X=0.800000,Y=0.610000,Z=0.250000,W=1.000000)
      bUsePreviewValueAsDefault=True
      SortPriority=3
      MaterialExpressionEditorX=-624
      MaterialExpressionEditorY=672
      MaterialExpressionGuid=6545C43A47B1DEEBEC4E479439505277
      Material=Material'/Engine/Transient.Material_25'
      bCollapsed=True
   End Object
   MaterialExpression=MaterialExpressionFunctionInput'MaterialExpressionFunctionInput_25'
   Pins(0)=EdGraphPin'EdGraphPin_71310'
   Pins(1)=EdGraphPin'EdGraphPin_71311'
   NodePosX=-624
   NodePosY=672
   NodeGuid=1E0007E84D4B1D84ED0C198BB10ED68B
End Object


Ah, nice. Thanks.

So, I tried it, but I am not sure if I am doing something wrong - it doesn’t look like fog :confused:

Also, I am guessing there is no way to tint sky sphere the same way as diffuse material ?

Just lerp the sky material with the fog color and in the alpha, raise it to the opacity you want. Since all parts of the sky are already at an infinite distance away from the player, there’s no need to perform the depth calculation, it will always be shaded at the maximum rate. You can leave it as a parameter or just color your sky texture in Photoshop and save the 2 instructions there.

Also, this code performs a basic fade on the textures, but in the real world fog scatters light, so it wouldn’t shade the way your textures do.

EDIT: OK, so make sure your textures go into base color untouched, but then use the function with the texture for emissive. In the pictures below, no fog was used. 5 extra instructions in the material for the fog. Use a material collection parameter to keep the fog consistent throughout your game. It shouldn’t be expensive at all.

ec5c2cf069d79ffd18d7541e76531cf242f26e35.jpeg
f04650f038f8afb13f4bcee69b96e44076d3525a.jpeg

I am not too concerned about skies. The lightmaps (shadows) aren’t being tinted. That’s why it doesn’t look like fog :confused:

Is it possible to tint shadows ?

It should tint absolutely everything. The fog is applied to emissive, so it’s lighting up the material, most noticably in the shadows, and removing the influence of direct lighting. Bring down your depth, and make your fog a little brighter. I should say, if your fog color is completely black, it won’t show up anywhere at all. This method is a brightening effect.

I see what I am doing wrong - I plug texture into fog function and then plug function into base color. Thanks for posting that image!

Most performant solution would be probably editing mobile forward pass shaders to handle simple fog. Fog has to main componet. Extinction: This make scene darker. This is happening because air volume absorb some amount of incoming light. This is isotropic if you assume uniform air density but it should be wavelength dependent.(this should tint scene towards red a bit).
Inscatter: This is incoming light from air volume itself. Color is based on lights color and it’s also highly view angle dependent. Because blue light scatter faster than red this should tint fog towards blue.
So making fast approximation of this you should at least calculate uniform inscatter color based on camera and sun direction. This can be calculated on vertex level and interpolated per pixel. There should be code path for translucent vertex fog that could be used for mobile opaque geometry. Performance should be really good.

Sounds overly complicated, Kalle_H :o

@mariomguy: I think it’s working now, thanks!

How can I do a non-liner interpolation, where fog is less dense between camera and point A and then from point A into infinity it gets progressively denser ? (or rather it’s linear, but from Cam to A it interpolates one way and from A to infinity - another way)

Never mind. I added Power node and it worked pretty much the way I wanted for it to work.

Now I got another question. How do I deal with emissive textures? For example, if my material already has emission going on (glowing parts of the model, texture that is already plugged into material’s emission socket), how do I make fog and emissive texture play along?

Found a major flaw of this technique - material becomes sort of fullbright, regardless of my fog settings, and thus skylight no longer affects it. That’s why it looks flat’ish on my video. The only light that affects it is suber bright direct light :frowning:

Is it fixable?

What about using scene depth instead of pixel depth ?

If you take the fog to a specific integer, just multiply the divided depth by itself. This way your “power” will only take 1 instruction, not 3 or 4 or 5.

The texture just shouldn’t go into emissive at all, geez what was I thinking? I’m trying to get an effect where eventually, at max opacity, your scene will just completely fade to the fog color, and initially I thought all your textures were emissive. If you want to do this the “right” way, you need to duplicate this function to both the base color and emissive. Base color should fade from the texture up close to 0 in the back, so 0 needs to be in the fog color here. Emissive needs to fade from 0 (texture color should be 0) up close to your fog color in the back. This ensures your shaded textures are replaced with whatever fog color of your choosing, darker or lighter. But I prefer to make fog colors lighter because fog scatters light, adding brightness to the entire scene. Try lighting up your scene as if there was no fog in it, and then layer this function on top. Mess with the fog color, depth, and max opacity settings until you get something that looks acceptable. The level should look appropriate when lit, and then fog out in the distance.

Total cost for everything: 5 instructions. But to your entire scene.

And no, you can’t use scene depth. Scene depth is only available to translucent materials.

For fully emissive materials, just use the fog function in emissive, there’s no need to mess with any lighting. For translucent materials, if you have any, the opacity of your material needs to blend to fully opaque (1) at the full fog strength.

I am not sure I am following :confused:

So my scene has standard type material, lit, no real-time lights (yet), baked lightmaps. Some of the materials will have emissive texture plugged into it emissive socket (think glowing eyes; but at this moment I don’t have any).

The issue is that when I bake lighting (skylight in particular), I get no difference in lighting at all. As if material is full bright. I removed all lights except tiny point lights I had as decorations, baked lighting - nothing. The level is all well lit. I added bright direct light, baked lighting - got shadows and some variations in how level is lit. Shadows however remained as they were when I had no lights in the scene. So then I disconnected fogging material function from emissive socket and bam! lighting looks right now, but of course I get no fog :frowning: Kinda catch 22.

Here is the current, fogget setup that makes fog looks really good, but interferes with the lighting :confused:

This is your fogging function, with Power node added to make fog look like what I want:

level geometry material with fogging function from above:

Alright, this is what unfogged scene looks like and what the same scene looks like when added fogging. See how “fog” washes lighting out? :frowning:

4b0d5c338eba2ae4a4ee44d3d922f1dbea6dda5b.jpeg

OK, this seems to be better, but can performance be improved along the way?