Any idea on how to create an sandstorm effect in Unreal Engine 4

Hi, I am creating a game for my graduation class and one of the things I want to include in my game is an realistic sandstorm since a part of the game is in the desert.
My question is if there is any insightful tutorial on how to create this effect.
Any help is appreciated and if you have question please ask.
Thanks in advance!

Hi. You could check out Eat 3D. They have a UDK tutorial that you might be able to work through. Its in Free Stuff

In general terms, you could use gpu emitters to produce specs of sand in great numbers and then mix that with gpu sprite emitters that emit sort of a sand fog effect. You can probably also abuse the atmospheric fog to reinforce the “sand fog” as well.

I’ve been toying with the idea for a Sandstorm in my little demo game. I managed to get something pretty nice with a fog effect however it didnt feel right :frowning: not sure if it was the lighting or the effect it self

There is a blizzard effect in theVFX weather Pack which could be turned into sand particles pretty easily however I dont get paid for a few more days so i’ll let you know :smiley:

There’s actually some big sand particle effects in the Vehicle Example level.

Yeah, I would use an exponential heightfog to take care of the light scattering in the distance and GPU sprites to render the individual sand particles up close, with scene depth collisions! If you want, you can even use giant clouds of dust in your particle effects, to give some shape to your sandstorm, but is very expensive to render.

It would be awesome if you could use vector fields to make the particles swirl around in multiple vortexes, to give the sandstorm some shape, but as it stands there’s not much in the way of making the swirling effect other than possibly throwing out the GPU particles around a central point gravity, and get the settings just right so it doesn’t look like a bunch of electrons. You can attempt to do something with the orbit module, but that may or may not work for you.

Here’s something to get you started. I’m playing with a tornado post process material, so it didn’t take long to modify. This isn’t cheap, it adds 1-5ms on my 280x, but it stays consistently above 60 fps and doesn’t look bad. Doesn’t play nice with Temporal AA or Motion Blur.

To avoid raymarching everywhere, this raytraces a cylinder to get near and far intersection points. Then it raymarches between the intersection points, sampling a rotating noisefield combined with a noisefield moving in x. Uses a directional derivative for lighting, so unfortunately it doesn’t cast shadows.

Here’s the content of the custom nodes:

//CRotateAboutAxis CMOT Float 3

return RotateAboutAxis (MaterialFloat4 (MaterialFloat3 (NormalizedRotationAxis.x,
NormalizedRotationAxis.y, NormalizedRotationAxis.z), (RotationAngle*6.28318548)),
MaterialFloat3 (PivotPoint.x, PivotPoint.y, PivotPoint.z),
MaterialFloat3 (Position.x, Position.y, Position.z));

//RayTraceMarch CMOT Float 4

float3 CylinderOrigin={25000-5000t,0,0};
float CylinderRadius1 = 35000;
float CylinderRadius2 = 25000;
float3 CylinderD = ro - CylinderOrigin;
float CylinderA = dot( rd.xz, rd.xz );
float CylinderArec=1/CylinderA;
float CylinderB = dot( rd.xz, CylinderD.xz );
float CylinderC = dot( CylinderD.xz, CylinderD.xz ) - CylinderRadius1
CylinderRadius1;
float CylinderDiscrim= CylinderBCylinderB - CylinderACylinderC;
float CylinderDiscrimsqrt=sqrt( CylinderDiscrim );
float far=lerp(0,min(scenedepth0.99,max(-(CylinderB-CylinderDiscrimsqrt)CylinderArec,0)),CylinderDiscrim>0);
float near=lerp(0,min(scenedepth
0.99,max(-(CylinderB+CylinderDiscrimsqrt)CylinderArec,0)),CylinderDiscrim>0);
float3 NoiseScale1={20000,10000,20000};
float3 lightvec = normalize(float3(0.687334,0.506979,-0.520138));
float stepsize=1000;
float numsteps=clamp((far-near)/stepsize,0,1000);
float mul=0.0;
float4 sum={0,0,0,0};
for(int i=0;i<numsteps;i++)
{
if( sum.a > 0.99 ) continue;
float rt=near+stepsize
mul
(1+dither);
float3 pos=rtrd+ro;
float3 rotpos=CustomExpression0(Parameters,float3(0,1,0),-t/10,float3(0,pos.y,0)+CylinderOrigin,pos)+pos;
if(pos.z<0||distance(pos,CylinderOrigin)>250000||rt>scenedepth) break;
float cylinderfade=clamp((CylinderRadius2-distance(pos,float3(0,pos.y,0)+CylinderOrigin))/CylinderRadius2,0,1);
float heightfade=pow((CylinderRadius1-pos.z)/CylinderRadius1,3)+0.5;
float spherefade=clamp((250000-distance(pos,CylinderOrigin))/250000,0,1);
float dens=clamp(SimplexNoise3D_TEX((pos+float3(-5000
t,0,0))/NoiseScale1)+0.5SimplexNoise3D_TEX(rotpos/(NoiseScale1/2))+heightfade,0,1)cylinderfadespherefade;
float4 col=float4(dens,dens,dens,dens)float4(0.62,0.45,0.19,1);
float dif=0.1+0.4
(col.w-clamp(SimplexNoise3D_TEX((pos+lightvec
1000+float3(-5000t,0,0))/NoiseScale1)+0.5SimplexNoise3D_TEX((rotpos+lightvec1000)/(NoiseScale1/2))+heightfade,0,1))cylinderfadespherefade;
col.xyz+=dif;
col.rgb
=col.a;
sum=sum+col*(1.0-sum.a);
mul+=1.0;
}
return clamp(sum,0,1);

The dither texture is just 2048x2048 of random noise.

Here’s what it looks like:

Let me know if you have any questions.

Thanks for the screenshot, I have to mention that I am fairly new to Unreal Engine so I am not familiar with coding the nodes but I tried my best.
Now I am left with these errors: Error [SM5] Only transparent or postprocess materials can read from scene depth. Error [SM5] SceneTexture expressions cannot be used in opaque materials.
I don’t know what the error refers to so I am kind of lost here.


Here is my material.
Thanks for the help!

It’s a post process material, you have to set the material domain to post process, should work after that. Then, add a post process volume to your level. In the post process volume settings, look for blendables, and add an element to the array. Choose asset reference, and then select the sandstorm material. Check the unbound box. In your project settings, under rendering, default post processing settings, disable motion blur and set anti-aliasing method to fxaa or you’ll get some ugly edges on foreground objects.

Thanks for your fast reply!
It seemed to have fixed the above errors but now I came across the following error: Error [SM5] error X3004: undeclared identifier ‘ro’
and it seems there are more code errors coming from the custom nodes
Like I said, I am new to UE4 and the coding so I don’t know what exactly is wrong.
Thanks for your help!

First two inputs to RayTraceMarch are ro and rd(ray origin, ray direction), case sensitive. Looks like you’ve got IO and id.

Ah thank you, seems to have removed the errors. And it seems to be working!
Now, what I achieve is that the sandstorm approaches you but doesnt go anywhere but stays active while walking. What happens now is the sandstorm comes and goes. It is the desired effect but like I said I want it to stay with the player. Thanks for all help!

Hey I inserted the material, inserted the code, set the material domain to post process, added sandstorm material to blendables.

I have had a (syntax error: unexpected integer constant) in the material. Not sure how to fix the error in the material to make the sandstorm work.


Also I had sand edges on the screen.

Anyone have any luck rotating the sandstorm effect? I’ve made some progress optimizing the shader but I’m having trouble getting it to move any way other than on the x axis.

Hey James Steininger,

I believe in order to do that you would have to pass in a vector value from a Material parameter collection (and update that vector every frame), then feed that vector into CylinderOrigin as that determines the center of the sandstorm mass.

Right, I figured that was the case. I’ll probably just use that to move the storm rather than use the time variable.

racerindiekid: that 1 on the raymarch is actually a t

Thanks its working now!

im a little disappointed that you didnt put the song in your sandstorm video. i 100% expected it.