Hi, I started to build a cloud rendering system based on “the Real-Time Volumetric Cloudscapes of Horizon Zero Dawn”. The problem is I haven't been able to render clouds, I use a compute shader because it is easy to integrate into sky module. I don't have much experience on ray-marching and 3d textures other than I successfully created a simple sky shader based on Nishita et al's paper.
This is what I get. below.
I think my Noise texture are not right. I dont have any experience on this. So any thoughts?
My compute shader
static float PI = 3.141592653;
static float InnerRadius = 6320000;
static float OutterRadius = 6420000;
static const float2 inMinMaxCloudLayer = float2(InnerRadius+1500, InnerRadius+4000);
static int NumSamples = 50;
static const float4 STRATUS_GRADIENT = float4(0.0, 0.07, 0.08, 0.15);
static const float4 STRATOCUMULUS_GRADIENT = float4(0.0, 0.2, 0.42, 0.6);
static const float4 CUMULUS_GRADIENT = float4(0.0, 0.08, 0.75, 0.98);
float GetHeightFractionForPoint(float3 inPos)
{
float Height_Fraction = ((length(inPos) - inMinMaxCloudLayer.x)/(inMinMaxCloudLayer.y-inMinMaxCloudLayer.x));
return saturate(Height_Fraction);
}
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));
}
float4 MixGradients(float cloudType)
{
float stratus = 1.0f - saturate(cloudType * 2.0f);
float stratocumulus = 1.0f - abs(cloudType - 0.5f) * 2.0f;
float cumulus = saturate(cloudType - 0.5f) * 2.0f;
return STRATUS_GRADIENT * stratus + STRATOCUMULUS_GRADIENT * stratocumulus + CUMULUS_GRADIENT * cumulus;
}
float GetDensityHeightGradientForPoint(float3 inPos, float3 inWeatherData)
{
float4 cloudGradient = MixGradients(inWeatherData.b);
float heightFrac = GetHeightFractionForPoint(inPos);
return smoothstep(cloudGradient.x, cloudGradient.y, heightFrac) - smoothstep(cloudGradient.z, cloudGradient.w, heightFrac);
}
float3 SampleWeather(float3 inPos)
{
float2 unit = inPos.xz * 10000 / 30000.0;
float2 uv = unit * 0.5 + 0.5;
return txWeather.SampleLevel(ssClouds, uv, 0).rgb;
}
float SampleCloudDensity(float3 inP, float3 inWeatherData)
{
const float baseFreq = 1e-5;
float3 coord = inP * baseFreq * 10000;
float4 low_frequency_noises = txShape.SampleLevel(ssClouds, coord, 0).rgba;
float low_freq_fbm = (low_frequency_noises.g * 0.625f) + (low_frequency_noises.b * 0.25f) + (low_frequency_noises.a * 0.125f);
float base_cloud = Remap(low_frequency_noises.r, -(1.0f - low_freq_fbm), 1.0f, 0.0f, 1.0f);
float density_height_gradient = GetDensityHeightGradientForPoint(inP, inWeatherData);
base_cloud *= density_height_gradient;
float cloud_coverage = inWeatherData.r;
float base_cloud_with_coverage = Remap(base_cloud, cloud_coverage, 1.0f, 0.0f, 1.0f);
base_cloud_with_coverage *= cloud_coverage;
float3 high_frequency_noises = txDetail.SampleLevel(ssClouds, coord * 0.1f, 0).rgb;
float high_freq_fbm = (high_frequency_noises.r * 0.625f) + (high_frequency_noises.g * 0.25f) + (high_frequency_noises.b * 0.125f);
float height_fraction = GetHeightFractionForPoint(inP);
float high_freq_noise_modifier = lerp(high_freq_fbm, (1.0f - high_freq_fbm), saturate(height_fraction * 10.0f));
float final_cloud = Remap(base_cloud_with_coverage, high_freq_noise_modifier * 0.2f, 1.0f, 0.0f, 1.0f);
return saturate(final_cloud);
}
float LightTransfer(float OpticalDepth, float cosTheta)
{
// float powder_sugar_effect = (1.0f - exp(-OpticalDepth * 2));
float beerz_law = exp(-OpticalDepth);
float light_energy = /*2 * */beerz_law * /*powder_sugar_effect **/ HenyeyGreensteinCornetteShankPhaseFunction(0.2f, cosTheta);
return light_energy;
}
static const float3 RandomUnitSphere[6] =
{
{0.3f, -0.8f, -0.5f},
{0.9f, -0.3f, -0.2f},
{-0.9f, -0.3f, -0.1f},
{-0.5f, 0.5f, 0.7f},
{-1.0f, 0.3f, 0.0f},
{-0.3f, 0.9f, 0.4f}
};
[numthreads(32, 32, 1)] Dispatch (8,8,1)
void ComputeSky(uint3 DTID : SV_DispatchThreadID)
{
float X = ((2 * (float)DTID.x) / 255) - 1;
float Y = 1 - ((2 * (float)DTID.y) / 255);
float r = sqrt(((X*X)+(Y*Y)));
static float3 Eye = float3(0, InnerRadius+10, 0);
float3 SunDir = normalize(-LightDirection), I;
if (r<=1)
{
float Theta = r * ((PI+.05)/2);
float Phi = atan2(Y, X);
float3 ViewDir = normalize(float3(sin(Theta)*cos(Phi), cos(Theta), sin(Theta)*sin(Phi)));
float ViewRay_A = RaySphereIntersection(Eye, ViewDir, float3(0, 0, 0), inMinMaxCloudLayer.x);
float ViewRay_B = RaySphereIntersection(Eye, ViewDir, float3(0, 0, 0), inMinMaxCloudLayer.y);
float3 Pos_A = Eye + ViewRay_A * ViewDir;
float SampleLength = (ViewRay_B - ViewRay_A) / NumSamples;
float3 tmpPos = Pos_A + .5f * SampleLength * ViewDir;
float cosTheta = dot(SunDir, ViewDir);
float CloudViewDensity = 0, CloudLightDensity = 0;
float SOD = 0, VOD = 0;
for (int i=0;i<NumSamples;i++)
{
float SunRayLength = RaySphereIntersection(tmpPos, SunDir, float3(0, 0, 0), inMinMaxCloudLayer.y);
float LightSampleLength = SunRayLength / 6;
float3 tmpLightPos = tmpPos + .5f * LightSampleLength * SunDir;
for (int k=0; k<6;k++)
{
float3 randomOffset = RandomUnitSphere[k] * LightSampleLength * 0.25 * ((float)(k + 1));
tmpLightPos += randomOffset;
float3 WeatherData = SampleWeather(tmpLightPos);
CloudLightDensity = SampleCloudDensity(tmpLightPos, WeatherData);
SOD += CloudLightDensity*LightSampleLength;
tmpLightPos += LightSampleLength * SunDir;
}
I += ASTM_SI * LightTransfer(SOD, cosTheta);
float3 WeatherData = SampleWeather(tmpPos);
CloudViewDensity = SampleCloudDensity(tmpPos, WeatherData);
if (CloudViewDensity>0)
{
VOD += CloudViewDensity*SampleLength;
I += ASTM_SI * LightTransfer(VOD, cosTheta);
}
tmpPos += SampleLength * ViewDir;
}
SkyColors[DTID.xy] = float4(I, 1);
}
}