Horizon:zero Dawn Cloud System

Started by
78 comments, last by IlPresidente 5 years, 8 months ago

recently i read the gpu pro 7 article <<Real-Time Volumetric Cloudscapes>>, i want to implement a volumetric cloud system myself.

i create the 3d noise texture accord to the article.

and write the shader, test it. but i found it's hard to model the cloud.

i use the sample cloud density code from the article, and a weather texture from the video which embed in the siggraph 2015 ppt.

and the result is not good.

the article didn't mention the mapping method which mapping the weather texture to the sky dome.

i use a plane projection, just use the position.xy to calculate the weather texture uv.

but the uv is mostly distribute in the horizon direction, distortion is very obvious.

so how to mapping the weather texture to the sky dome?

and how to generate the weather?

i found the left bottom weather texture in the video maybe is not the original weather texture, it's just used to show some info.

has someone do the same thing?

any suggestion will be so appreciate.

thanks.

hehe.
Advertisement

i kind of coded clouds similar to theirs(well based on their noise texture ideas), so the mapping is done by raymarching through the 3d noise texture and doing the density, coverage,refinement and lighting on each of the raymarch sample.

You can start raymarching by intersecting a lower cloud "plane" with the eye-vector at a pixel, then you march in steps and do your calculations.

Later on you can do an intersection with a sphere around your map so you get a nice curvature at the horizon.

So reading up on raymarching and volume rendering is a good idea.

About how one could generate the weather texture, that's something i need ideas for aswell. You could precompute/prepaint some weather textures for a single cloud.

Then based on the weather you want, pack some of the premade textures in a fbo, add some magic and use that in the final raymarch shader.

The cloud shape depends a lot on the baked 3D noise texture and how you combine it later on. I mean you have 4 channels with different octaves and only need a float density value. For me it was a lot of experimenting.

i kind of coded clouds similar to theirs(well based on their noise texture ideas), so the mapping is done by raymarching through the 3d noise texture and doing the density, coverage,refinement and lighting on each of the raymarch sample.

You can start raymarching by intersecting a lower cloud "plane" with the eye-vector at a pixel, then you march in steps and do your calculations.

Later on you can do an intersection with a sphere around your map so you get a nice curvature at the horizon.

So reading up on raymarching and volume rendering is a good idea.

About how one could generate the weather texture, that's something i need ideas for aswell. You could precompute/prepaint some weather textures for a single cloud.

Then based on the weather you want, pack some of the premade textures in a fbo, add some magic and use that in the final raymarch shader.

The cloud shape depends a lot on the baked 3D noise texture and how you combine it later on. I mean you have 4 channels with different octaves and only need a float density value. For me it was a lot of experimenting.

thanks for your reply.

i already implement the raymarch and volume rendering.

problem is how to get the cloud density, i think need to use the weather texture to get cloud base shape and empty sky regions.

so every raymarch step, we need a weather data to calculate the cloud density, so we need use position to calculate a uv to sample the weather texture.

my problem is that : how to calculate the weather texture uv?how to mapping the weather texture to sky dome?

for me the key point is the noise texture, weather texture, the mapping method, and the compose algorithm.

i already generated the noise texture .

below is the gpu pro 7 compose algorithm:


// Fractional value for sample position in the cloud layer .
float GetHeightFractionForPoint ( float3 inPosition , float2 inCloudMinMax )
{
    // Get global fractional position in cloud zone .
    float height_fraction = (inPosition.z ? inCloudMinMax.x ) / ( inCloudMinMax.y ? inCloudMinMax.x ) ;
    return saturate ( height_fraction ) ;
}

// Utility function that maps a value from one range to another .
float Remap ( float original_value , float original_min , float original_max , float new_min , float new_max )
{
    return new_min + ( ( ( original_value ? original_min) / ( original_max ? original_min ) ) ? ( new_max ? new_min ) );
}

float SampleCloudDensity ( float3 p , float3 weather_data )
{
    // Read the low?frequency Perlin?Worley and Worley noises.
    float4 low_frequency_noises = tex3Dlod ( Cloud3DNoiseTextureA , Cloud3DNoiseSamplerA , float4 ( p , mip_level) ).rgba;

    // Build an FBM out of the low frequency Worley noises
    // that can be used to add detail to the low?frequency
    // Perlin?Worley noise.
    float low_freq_FBM = ( low_frequency_noises.g ? 0.625 )
    + ( low_frequency_noises.b ? 0.25 )
    + ( low_frequency_noises.a ? 0.125 );

    // define the base cloud shape by dilating it with the
    // low?frequency FBM made of Worley noise.
    float base_cloud = Remap ( low_frequency_noises.r , ?( 1.0 ? low_freq_FBM ) , 1 .0 , 0 .0 , 1 .0 );

    // Get the density?height gradient using the density height
    // function explained in Section 4.3.2.
    float density_height_gradient = GetDensityHeightGradientForPoint ( p , weather_data );
    
    // Apply the height function to the base cloud shape.
    base_cloud ?= density_height_gradient ;

    // Cloud coverage is stored in weather data’s red channel.
    float cloud_coverage = weather_data.r;
    
    // Use remap to apply the cloud coverage attribute.
    float base_cloud_with_coverage = Remap ( base_cloud , cloud_coverage, 1. 0 , 0. 0 , 1.0);

    // Multiply the result by the cloud coverage attribute so
    // that smaller clouds are lighter and more aesthetically
    // pleasing.
    base_cloud_with_coverage ?= cloud_coverage;


    // Add some turbulence to bottoms of clouds.
    p.xy += curl_noise.xy ? ( 1 . 0 ? height_fraction );

    // Sample high?frequency noises.
    float3 high_frequency_noises = tex3Dlod ( Cloud3DNoiseTextureB , Cloud3DNoiseSamplerB , float4 ( p ? 0.1 , mip_level) ).rgb;

    // Build?high frequency Worley noise FBM.
    float high_freq_FBM = ( high_frequency_noises.r ? 0.625 )
    + ( high_frequency_noises.g ? 0.25 )
    + ( high_frequency_noises.b ? 0.125 );

    // Get the height fraction for use with blending noise types
    // over height.
    float height_fraction = GetHeightFractionForPoint ( p , inCloudMinMax );

    // Transition from wispy shapes to billowy shapes over height.
    float high_freq_noise_modifier = mix ( high_freq_FBM ,
    1 .0 ? high_freq_FBM , saturate ( height_fraction ? 10.0 ) );

    // Erode the base cloud shape with the distorted
    // high?frequency Worley noises.
    float final_cloud = Remap ( base_cloud_with_coverage ,
    high_freq_noise_modifier ? 0.2 , 1.0 , 0.0 , 1.0 );


    return final_cloud;
}

so the last problem is the mapping method, and how to create the weather texture.

especially the coverage, the cloud type is easy to create.

hehe.

For mapping the weather texture i use as simple planar projektion, or more xy position of the samplepos with some scaling and wind offset. Works quite well as long as the curvature of you cloudsphere is small.
As i said i use a ray-sphere intersection as the starting point, but with a quite big sphere wich is centered in xy at the camera's position and at a height so that above the cam the clouds start at the height you want.

For the weathertexture what you can try out, is using 2 premade tiling perlin noise textures(different noise in r,g,b,a) , scroll them, combine them and in my case apply a global coverage value to it(remapping with some exp magic).
It gives changing patterns , you have a global coverage value and as a result also local covarage values.

float t = 255.0-min(255.0,(inCoverage*255.0));//inCoverage is a global coverage value
float c = max(0.0,cloud_sampled*255.0-t);//some remapping i found somewhere years ago
float cld_cov = 1.0-saturate(pow(0.99325,c));//exponent contolls sharpness of the result(higher, more soft)

That's what i use to remap the combines perlin noises to build a cheap but animated weathertexture

4f630f497179338.jpg

Right Top image shows the weather texture. As you see it's quite simple but works.

For a more realistic outcome it could also be possible to use some texture just as a "heat and vapor" source and let a cellular automata evolve some nice shapes out of it.

But yeah as a start, don't worry too much about the weather texture as simple perlin noise textures work good for a beginning.

For mapping the weather texture i use as simple planar projektion, or more xy position of the samplepos with some scaling and wind offset. Works quite well as long as the curvature of you cloudsphere is small.
As i said i use a ray-sphere intersection as the starting point, but with a quite big sphere wich is centered in xy at the camera's position and at a height so that above the cam the clouds start at the height you want.

For the weathertexture what you can try out, is using 2 premade tiling perlin noise textures(different noise in r,g,b,a) , scroll them, combine them and in my case apply a global coverage value to it(remapping with some exp magic).
It gives changing patterns , you have a global coverage value and as a result also local covarage values.

float t = 255.0-min(255.0,(inCoverage*255.0));//inCoverage is a global coverage value
float c = max(0.0,cloud_sampled*255.0-t);//some remapping i found somewhere years ago
float cld_cov = 1.0-saturate(pow(0.99325,c));//exponent contolls sharpness of the result(higher, more soft)

That's what i use to remap the combines perlin noises to build a cheap but animated weathertexture

4f630f497179338.jpg

Right Top image shows the weather texture. As you see it's quite simple but works.

For a more realistic outcome it could also be possible to use some texture just as a "heat and vapor" source and let a cellular automata evolve some nice shapes out of it.

But yeah as a start, don't worry too much about the weather texture as simple perlin noise textures work good for a beginning.

thanks for your share.

if i understand correctly, you use a octave perlin noise as source coverage, and remap it to get a new coverage.

right?

i implement this remap function in mathematica:

[sharedmedia=gallery:images:7600]
apply to octave perlin noise
[sharedmedia=gallery:images:7601]

mathematica file is in attach files.

hehe.

Excactly, along with some code to make it look as needed.

Also notice i only use coverage and coudtype(rg) as i don't need that fine control, but still want a "random" look all over the map

this is my result.

[sharedmedia=gallery:images:7602]

but it looks very fuzzy, lack of details, but the detail noise texture repeat count is very high.

your cloud top have very rich details, how to do this?

my cloud bottom and top high is 1500 and 4000, planet radius is 640000, horizon start fade distance is 30000.

base frequency is 1e-5, base noise repeat is 5, and detail noise repeat is 60.

base noise position uv scale is 1e-5*5, the entire sky dome repeat 3-4 times.

is these data something wrong?

thanks.

hehe.

From my experiments the way you combine the base noise octaves have the most impact on how detailed the clouds are. But well i'm not using perlin-worley , just worley noise with 3 octaves per channel with a size of 123^3.

So the way i combine them is totally different from what they do, some kind of fbm+inbetween remapping with coverage.

And ofc the powder effect adds a lot.

For the cloud top, think the main difference is, i don't modify the density of the clouds with the height signal, but modify the coverage.

Then sure, the smaller high frequency noise adds the very fine detail and is applied like that


base_cloud = base_cloud * high_freq_noise *(1.0-base_cloud);

You see i simply assume that an edge is where the cloud got a density below 1

From my experiments the way you combine the base noise octaves have the most impact on how detailed the clouds are. But well i'm not using perlin-worley , just worley noise with 3 octaves per channel with a size of 123^3.

So the way i combine them is totally different from what they do, some kind of fbm+inbetween remapping with coverage.

And ofc the powder effect adds a lot.

For the cloud top, think the main difference is, i don't modify the density of the clouds with the height signal, but modify the coverage.

Then sure, the smaller high frequency noise adds the very fine detail and is applied like that


base_cloud = base_cloud * high_freq_noise *(1.0-base_cloud);

You see i simply assume that an edge is where the cloud got a density below 1

thanks.

to get a more detailed base cloud noise, need a high octaves? or need other things?

this is my base noise texture.

[sharedmedia=gallery:images:7603]
i found the r channel looks fuzzy indeed, i will change the noise arithmetic to test, thanks.
the noise texture you use rgba fp16 texture or rgba8?
some kind of fbm+inbetween remapping with coverage.
something like Remap(low_freq_FBM, coverage, 1, 0, 1)?

modify coverage accord to the height, the top get less coverage?

use this formula, when base_cloud is 1.0, means it's inside the cloud.

base_cloud = base_cloud * high_freq_noise *(1.0-base_cloud) = 0.0?

hehe.

Really great results guys! I wish I could achieve at least something relatively close to what you have for my game.

That GPU 7 Pro article mostly describes ways to create a realistic clouds in terms of density functions and overall weather simulation, but it somehow assumes that reader is already familiar with ray marching techniques. While I can understand the algorithm behind a single ray cast from a given point to sample various density functions and so on, I can't get my head over the more basic stuff like - what render pass does it all happen? Is it during rendering the skybox? Or do you render some special shape and then in it's shader you do all this? What does it mean to do raycasts in this cast? Is it per pixel? Per point in a world space? They say in the article they assume some spherical shell around the camera of some thickness, but how do you pick points on that sphere for actual raycast? And how is the result from such raycast used to actually shade the sky?

Are there some papers on basics like this? If you could provide some pseudocode used to actually "draw" and execute this shader that's supposed to create clouds, including what are you rendering, and what's the input for raymarching algorithm, it would greatly help me understand this concept.


Where are we and when are we and who are we?
How many people in how many places at how many times?

This topic is closed to new replies.

Advertisement