Cloud Rendering Problem

Started by
10 comments, last by DragonJoker 3 years, 1 month ago

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.

my cloud rendering

I think my Noise texture are not right. I dont have any experience on this. So any thoughts?

shape texture r channel
shape texture b channel
shape texture g channel
shape texture a channel
detail texture b channel
detail texture g channel
detail texture r channel

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);
	}

}
Advertisement

hmmm… this looks pretty involved -lol-, is it probably worth starting with a small volm cloud, like this one:

http://graphicsrunner.blogspot.com/2008/03/volumetric-clouds.html

and if it is volume ray casting aka ray marching that u want to try on a small scale, try this one:

http://graphicsrunner.blogspot.com/2009/01/volume-rendering-101.html

See how he's done it ?

Until then ?

Don't have any resources to point to offhand, but like pointed out above. Start small and work your way up. You will need to understand the basics of ray-marching a volume( lookup volume rendering). With that, you should be able to at least trace the cloud representation to see what they look like. Then move on to the atmospheric scattering computation while doing the marching. I haven't read the implementation you are trying to accomplish, but I'm assuming they are just tracing the volume ( which defines your cloud shape) an evaluating single/multiple scattering at each voxel along the ray.

The above code is my implementation. I have also written the atmospheric scattering code which is also ray marching.

The problem is not the 3d noise textures. It's how i sample the texture. This is what I got after I made some changes to the sampling code. What do you think? I know it's ugly.

This is far I could get

The above image seems to be showing what appears to be point sampling artifacts if that is the issue you are trying to show with the above screenshot. Without having to resort to reading the article ( though a link to it would be nice ), can you outline the algorithm you are using in your implementation at least from a high level perspective? This way its probably easier for someone to follow along and can also potentially highlight issues in other areas that may need attention.

here's the link. https://www.guerrilla-games.com/read/nubis-authoring-real-time-volumetric-cloudscapes-with-the-decima-engine

I think I got it right…

at sunset

This topic is closed to new replies.

Advertisement