Original Post
Hi everybody, I'm trying to work out why my atmospheric single scattering solution is showing annoyingly off colours. At midday, at height zero, you'd expect an even, light blue sky dome, brightening up at the horizon (mie scattering ignored for now), wheras my simulation produces a reasonable effect near the horizon (but maybe getting ever so slightly reddish), and fading to a dark blue directly overhead. At height >= 1, I get a very bright white band instantly fading to black, with very little of the characteristic bluish fading glow you should see. My method is based on the paper "Real-Time Rendering Of Planets With Atmospheres" (Schafhitzel...), in that I store the results of the standard atmospheric scattering equation (not including rayleigh or mie phase factors, which I apply later in the fragment shader) in a 64x64x64 3d texture, parameterized by height, view direction and sun direction. Upping the sampling rate helps, but not much; I'm currently using 40 samples for each integral. I'll add the code if anybody is interested. The constants I am currently using are: Planet radius: 6360km Atmosphere thickness: 60km Rayleigh coefficients: { 5.8x10-6, 13.5x10-6, 33.1x10-6 } I guess my question is: Has anyone come across this darkening effect before? (appears to be around blue wavelengths?). fwiw, here's the code that calculates the scattering terms: Thanks in advance, coderfish [Edited by - CoderFish on July 1, 2009 6:30:34 PM]
/// <summary>
/// Computes the single scattering equation for a given sun angle, viewer orientation, and height. Stores
/// the result in a 4-component voxel
/// </summary>
private unsafe void ComputeScattering( Vector3 viewDir, Vector3 sunDir, float height, byte* voxel )
{
Point3 viewPos = new Point3( 0, height, 0 );
Point3 viewEnd;
GetRayAtmosphereIntersection( viewPos, viewDir, out viewEnd );
Vector3 vecToPt = viewEnd - viewPos;
Vector3 sampleStep = vecToPt / m_AttenuationSamples;
float mul = sampleStep.Length * m_InscatterDistanceFudgeFactor;
if ( mul < 0.00001f )
{
voxel[ 0 ] = voxel[ 1 ] = voxel[ 2 ] = voxel[ 3 ] = 0;
return;
}
float* rAccum = stackalloc float[ 3 ];
float* mAccum = stackalloc float[ 3 ];
float* rViewOutScatter = stackalloc float[ 3 ];
float* mViewOutScatter = stackalloc float[ 3 ];
float* rSunOutScatter = stackalloc float[ 3 ];
float* mSunOutScatter = stackalloc float[ 3 ];
Point3 samplePos = viewPos + sampleStep;
for ( int sampleCount = 1; sampleCount < m_AttenuationSamples; ++sampleCount, samplePos += sampleStep )
{
// Cast a ray to the sun
Point3 sunPt;
if ( !GetRayAtmosphereIntersection( samplePos, sunDir, out sunPt ) )
{
continue;
}
float sampleHeight = Height( samplePos );
float pRCoeff = Functions.Exp( sampleHeight * m_InvRH0 ) * mul; // m_InvRH0 is 1 / -rayleigh scale height (~8km)
float pMCoeff = Functions.Exp( sampleHeight * m_InvMH0 ) * mul; // m_InvMH0 is 1 / -mie scale height
// Calculate (wavelength-dependent) out-scatter terms
Vector3 viewStep = ( viewPos - samplePos ) / m_AttenuationSamples;
Vector3 sunStep = ( sunPt - samplePos ) / m_AttenuationSamples;
CalculateOutScatter( samplePos, sunStep, rSunOutScatter, mSunOutScatter );
CalculateOutScatter( samplePos, viewStep, rViewOutScatter, mViewOutScatter );
for ( int i = 0; i < 3; ++i )
{
float outScatterR = Functions.Exp( ( -rViewOutScatter[ i ] - rSunOutScatter[ i ] ) );
float outScatterM = Functions.Exp( ( -mViewOutScatter[ i ] - mSunOutScatter[ i ] ) );
mAccum[ i ] += pMCoeff * outScatterM;
rAccum[ i ] += pRCoeff * outScatterR;
}
}
float tR = rAccum[ 0 ] * m_RayleighCoefficients[ 0 ];
float tG = rAccum[ 1 ] * m_RayleighCoefficients[ 1 ];
float tB = rAccum[ 2 ] * m_RayleighCoefficients[ 2 ];
float tA = ( ( mAccum[ 0 ] * m_MieCoefficients[ 0 ] ) + ( mAccum[ 1 ] * m_MieCoefficients[ 1 ] ) + ( mAccum[ 2 ] * m_MieCoefficients[ 2 ] ) ) / 3;
voxel[ 0 ] = ( byte )Utils.Clamp( tR * 255, 0, 255 );
voxel[ 1 ] = ( byte )Utils.Clamp( tG * 255, 0, 255 );
voxel[ 2 ] = ( byte )Utils.Clamp( tB * 255, 0, 255 );
voxel[ 3 ] = ( byte )Utils.Clamp( tA * 255, 0, 255 );
}
private float Height( Point3 pt )
{
return Utils.Clamp( pt.DistanceTo( Point3.Origin ) - m_InnerRadius, 0, m_OuterRadius - m_InnerRadius );
}
private unsafe void CalculateOutScatter( Point3 startPt, Vector3 step, float* rOutScatter, float* mOutScatter )
{
float mul = step.Length;
Point3 samplePt = startPt;
float rAccum = 0;
float mAccum = 0;
for ( int sample = 0; sample < m_AttenuationSamples; ++sample )
{
float samplePtHeight = Height( samplePt );
float samplePtRCoeff = Functions.Exp( samplePtHeight * m_InvRH0 );
float samplePtMCoeff = Functions.Exp( samplePtHeight * m_InvMH0 );
rAccum += samplePtRCoeff * mul;
mAccum += samplePtMCoeff * mul;
samplePt += step;
}
for ( int i = 0; i < 3; ++i )
{
rOutScatter[ i ] = m_RayleighCoefficients[ i ] * rAccum;
mOutScatter[ i ] = m_MieCoefficients[ i ] * mAccum;
}
}