Board index FlightGear Development Effects and shaders

Revisiting clouds

An exciting "new" option in FlightGear, that includes reflections, lightmaps, the particle system etc.. A lot is yet to be discovered/implemented!

Re: Revisiting clouds

Postby Thorsten » Thu Sep 06, 2018 12:28 pm

Actually, I don't really see how this should be feasible without substantial changes to FG architecture / GLSL version being used.

Quick test of a not too complicated procedurally generated volumetric cloud (a number of 'blobs' modulated with 3d Perlin noise).

Image

You get quite compelling shading that way, but you need to solve a nested integral equation somehow (and I fail how that could be done without sampling the integrand reasonably often) - so the end result drives a GeForce 1080 into single digits. For this one cloud.

Granted, the algorithm isn't optimized at all, but it'd have to be a factor 10 - 100 faster to really matter if we want Stratocumulus layers or so. Probably it's faster if you have a 3d texture pre-assembled - but then again, it needs to be held in memory.
Thorsten
 
Posts: 10843
Joined: Mon Nov 02, 2009 8:33 am

Re: Revisiting clouds

Postby Icecode GL » Thu Sep 06, 2018 2:58 pm

You get quite compelling shading that way, but you need to solve a nested integral equation somehow (and I fail how that could be done without sampling the integrand reasonably often) - so the end result drives a GeForce 1080 into single digits. For this one cloud.


I don't know the exact details, but the presentation I linked earlier mentions it solves the clouds in the span of 16 frames, and the cloud volume is re-projected every frame by approximating the cloud's position.

Probably it's faster if you have a 3d texture pre-assembled - but then again, it needs to be held in memory.


The document mentions 128x128x128 3D textures, that's a negligible amount of memory.

Quick test of a not too complicated procedurally generated volumetric cloud (a number of 'blobs' modulated with 3d Perlin noise).


I assume you are using the billboarded cloud geometry, are you raymarching it, using a texture modulated by a noise function or something else? Looks very good!
Icecode GL
 
Posts: 525
Joined: Thu Aug 12, 2010 12:17 pm
Location: Spain
Callsign: icecode
Version: GIT
OS: Arch Linux

Re: Revisiting clouds

Postby Thorsten » Thu Sep 06, 2018 3:22 pm

I don't know the exact details, but the presentation I linked earlier mentions it solves the clouds in the span of 16 frames, and the cloud volume is re-projected every frame by approximating the cloud's position.


Yeah, by distributing things like shading across a couple of frames, you'd win in a major way.... I just have no idea how to do that in a shader.

I assume you are using the billboarded cloud geometry, are you raymarching it, using a texture modulated by a noise function or something else? Looks very good!


No, this is 'the genuine thing' - like the thruster flame, a 3-dim structure function inside a bounding box with a numerical solver of the approximate light diffusion equations.

Aka, a physics solver of the problem...
Thorsten
 
Posts: 10843
Joined: Mon Nov 02, 2009 8:33 am

Re: Revisiting clouds

Postby Icecode GL » Thu Sep 06, 2018 3:30 pm

Yeah, by distributing things like shading across a couple of frames, you'd win in a major way.... I just have no idea how to do that in a shader.


OSG automatically passes the frame number to every shader so you could make a switch statement whose argument is 'osg_FrameNumber % 16' and do something different on each frame. Branching logic in shaders has improved a lot lately, so the performance hit of having a switch/if will be negligible.

No, this is 'the genuine thing' - like the thruster flame, a 3-dim structure function inside a bounding box with a numerical solver of the approximate light diffusion equations.


That explains the performance.
Icecode GL
 
Posts: 525
Joined: Thu Aug 12, 2010 12:17 pm
Location: Spain
Callsign: icecode
Version: GIT
OS: Arch Linux

Re: Revisiting clouds

Postby Thorsten » Thu Sep 06, 2018 4:54 pm

OSG automatically passes the frame number to every shader so you could make a switch statement whose argument is 'osg_FrameNumber % 16' and do something different on each frame.


Still need to be able to store the things somewhere when I use them from past frames.

That explains the performance.


I think not really. If you can get by computing stuff every 10th frame, then with the same algorithm you get 9x the speed.
Thorsten
 
Posts: 10843
Joined: Mon Nov 02, 2009 8:33 am

Re: Revisiting clouds

Postby Icecode GL » Thu Sep 06, 2018 7:40 pm

Still need to be able to store the things somewhere when I use them from past frames.


You can output textures now with the Compositor. :) Another optimization is to render to a downsampled buffer and then upscale it using dithering.


May I have a look at the code that generated that cloud? I'd like to experiment a bit with it.
Icecode GL
 
Posts: 525
Joined: Thu Aug 12, 2010 12:17 pm
Location: Spain
Callsign: icecode
Version: GIT
OS: Arch Linux

Re: Revisiting clouds

Postby Thorsten » Fri Sep 07, 2018 5:07 am

You can output textures now with the Compositor. Another optimization is to render to a downsampled buffer and then upscale it using dithering.


That sounds about what's needed, yes. If the compositor can do such things, we might be back in business after all.


May I have a look at the code that generated that cloud? I'd like to experiment a bit with it.


Sure - but let me clean it just a bit (it still has plenty of uniforms from the flame shader in and doesn't do changes in daylight so far).
Thorsten
 
Posts: 10843
Joined: Mon Nov 02, 2009 8:33 am

Re: Revisiting clouds

Postby Mihajlo » Fri Sep 07, 2018 8:15 am

In case anyone is interested there is a newer presentation available here: https://www.guerrilla-games.com/read/nu ... ima-engine
The one that Icecode GL posted is from 2015. This one is from 2017.
It is large (807 MB), but it contains some code examples. They've really improved it. Some images of clouds (mostly in daytime) look photorealistic to me. Can't discern them from reality.
Mihajlo
 
Posts: 53
Joined: Tue Aug 28, 2018 5:47 pm

Re: Revisiting clouds

Postby Thorsten » Fri Sep 07, 2018 8:56 am

I suspect looking at non-GPL code examples and then coding the same thing may not be entirely un-problematic.
Thorsten
 
Posts: 10843
Joined: Mon Nov 02, 2009 8:33 am

Re: Revisiting clouds

Postby Mihajlo » Fri Sep 07, 2018 9:27 am

Yes, I know you can't just use someone else's code if it's not GPL compatible. I didn't post this so you could use the exact same code, but the code might give you an insight on how developers managed to make those volumetric clouds and perhaps give you an idea how to proceed further.
Mihajlo
 
Posts: 53
Joined: Tue Aug 28, 2018 5:47 pm

Re: Revisiting clouds

Postby Mihajlo » Fri Sep 07, 2018 9:49 am

This also got me thinking whether their own custom noises are GPL compatible. I am guessing Worley noise aside from Perlin is compatible, but not sure about their layered noises.
Mihajlo
 
Posts: 53
Joined: Tue Aug 28, 2018 5:47 pm

Re: Revisiting clouds

Postby Icecode GL » Fri Sep 07, 2018 9:55 am

I suspect looking at non-GPL code examples and then coding the same thing may not be entirely un-problematic.


It's common practice to show small code snippets to provide context and readability. They don't usually show more than 5 lines of code together.

Which also got me thinking whether their own custom noises are GPL compatible. I am guessing Worley noise aside from Perlin is compatible, but not sure about their layered noises.


Sebastien Hillaire from Frostbite released their noise generator under the MIT license.
Icecode GL
 
Posts: 525
Joined: Thu Aug 12, 2010 12:17 pm
Location: Spain
Callsign: icecode
Version: GIT
OS: Arch Linux

Re: Revisiting clouds

Postby Mihajlo » Fri Sep 07, 2018 10:02 am

True, there is not much code. Most of the code is in slide 40.
Mihajlo
 
Posts: 53
Joined: Tue Aug 28, 2018 5:47 pm

Re: Revisiting clouds

Postby Johan G » Sun Sep 09, 2018 12:17 pm

Some posts was moved to the new topic Shadows without clouds above.
Low-level flying — It's all fun and games till someone looses an engine. (Paraphrased from a YouTube video)
Improving the Dassault Mirage F1 (Wiki, Forum, GitLab. Work in slow progress)
Johan G
Moderator
 
Posts: 5490
Joined: Fri Aug 06, 2010 5:33 pm
Location: Sweden
Callsign: SE-JG
IRC name: Johan_G
Version: 3.0.0
OS: Windows 7, 32 bit

Re: Revisiting clouds

Postby Thorsten » Sun Sep 09, 2018 5:56 pm

May I have a look at the code that generated that cloud? I'd like to experiment a bit with it.



Vertex shader

Code: Select all
// -*-C++-*-

#version 120

varying vec3 vertex;
varying vec3 viewDir;

void main()
{

vec4 ep = gl_ModelViewMatrixInverse * vec4(0.0,0.0,0.0,1.0);

vertex = gl_Vertex.xyz;
viewDir = normalize(vertex - ep.xyz);

gl_Position = ftransform();
gl_TexCoord[0] = gl_TextureMatrix[0] * gl_MultiTexCoord0;

gl_FrontColor = vec4 (1.0,1.0,1.0,1.0);
gl_BackColor = gl_FrontColor;
}


Fragment shader

Code: Select all
// -*-C++-*-

#version 120

varying vec3 vertex;
varying vec3 viewDir;

uniform float osg_SimulationTime;
uniform float thrust_collimation;
uniform float flame_radius_fraction;
uniform float thrust_density;
uniform float base_flame_density;
uniform float shock_frequency;
uniform float noise_strength;
uniform float noise_scale;
uniform float deflection_coeff;
uniform float random_seed;




uniform int use_shocks;
uniform int use_noise;

float Noise2D(in vec2 coord, in float wavelength);
float Noise3D(in vec3 coord, in float wavelength);
float rand3D(in vec3 coord);
float rand2D(in vec2 coord);


const int n_steps = 25;
const int n_steps_shade = 10;
const int n_blobs = 5;
const int n_bblobs = 3;


const vec3 seed = vec3 (10.0, 30.0, 50.0);
int n_rand = 0;


float spherical_smoothstep (in vec3 pos, in vec3 ref, in float rad, in float z_squish, in float sharpness )
{

vec3 delta = pos - ref;
delta.z *= (1.0/z_squish);
float l = length(delta );
return (1.0 - smoothstep((1.0 - sharpness) * rad, rad, l));
}


float coarse_density (in vec3 pos)
{

//return 5.0 * spherical_smoothstep(pos, vec3 (0.0, 0.0, 0.0) , 0.5, 0.5 , 0.2);

float sum = 0.0;

for (int i=0; i < n_blobs; i++)
   {
   
    float ref_x = 0.6 * (rand3D(seed * n_rand) - 0.5);
   n_rand++;
   float ref_y = 0.6 * (rand3D(seed * n_rand) - 0.5);
   n_rand++;
   float ref_z = 0.6 * (rand3D(seed * n_rand) - 0.5);
   n_rand++;
   
   float size = 0.2 + 0.2 * rand3D(seed * n_rand);
   n_rand++;
   
   vec3 ref = vec3 (ref_x, ref_y, ref_z);
   
   sum += spherical_smoothstep(pos, ref, size, 0.5 , 0.5);
   
   //if (sum > 2.0) {  break;}
   
   }
   
   float noise = Noise3D(pos, 0.05) + 0.5 * Noise3D(pos, 0.025) +  0.25 * Noise3D(pos, 0.01);

return 10.0 * clamp(sum * (0.4 + 1.2 * noise), 0.0,2.0);


}




void main()
{

vec3 vDir = normalize(viewDir);

float x_E, y_E, z_E;

if (vDir.x > 0.0) {x_E = 1.0;} else {x_E = -1.0;}
if (vDir.y > 0.0) {y_E = 1.0;} else {y_E = -1.0;}
if (vDir.z > 0.0) {z_E = 1.0;} else {z_E = -1.0;}

float t_x = (x_E - vertex.x) / vDir.x;
float t_y = (y_E - vertex.y) / vDir.y;
float t_z = (z_E - vertex.z) / vDir.z;

float t_min = min(t_x, t_y);
t_min = min(t_min, t_z);

vec3 lightDir = normalize((gl_ModelViewMatrixInverse * gl_LightSource[0].position).xyz);


float dt = t_min  / float(n_steps);
float dt1 = 2.0/float(n_steps_shade);

vec3 step = viewDir * dt;
vec3 light_step = lightDir * dt1;

float light_view = dot (viewDir, lightDir);

vec3 pos = vertex;

float density = 0.0;

float lighting = 0.0;
float norm = 0.0;

for (int i = 0; i < n_steps; i++)
   {
   pos = pos + step;
   float local_density = coarse_density(pos);
   density += local_density * dt;
   
   vec3 shade_pos = pos;
   float opacity = 0.0;

   
   for (int j=0; j< n_steps_shade; j++)
      {
      shade_pos += light_step;
      opacity += coarse_density(shade_pos) * dt1;
      
      }
   
   lighting += (1.0 - 0.15 * opacity) * local_density * exp(-density);
   norm += local_density * exp(-density);
   }

lighting/= norm;

if (lighting < 0.5) {lighting = 0.5 - 0.1 * (0.5-lighting);}


density = 1.0 - exp(-density);



vec3 color = vec3 (1.0, 1.0, 1.0) * lighting;

vec4 finalColor = vec4 (color.rgb, density);
gl_FragColor = finalColor;
}


Render onto a cube from [-1,-1,-1] to [1,1,1] wihich you scale to the desired size (a few thousand meters). The effect file is pretty much like for the flame shader, so far no real light changes, just sun position is used.

Just a rough proof of concept really, there's still unused stuff in. Can likely be optimized by simplifying either the integrand or the integral.

Have fun and feel free to ask what's unclear.
Thorsten
 
Posts: 10843
Joined: Mon Nov 02, 2009 8:33 am

PreviousNext

Return to Effects and shaders

Who is online

Users browsing this forum: No registered users and 3 guests