Original Post
Hello,
I am trying to implement atmospheric scattering effect based on O'Neils implementation from GPUGems2. Almost everything seems to be fine, but the sky colour is too dark. I already spent several hours trying to understand the problem and I am running out of ideas.
To render the effect, I create a sphere of a radius of 6370997.0f * 1.025f (radius of earth with atmosphere) and then I move it down by 6370997.0f in the shader (earth radius). It means that my camera is located on the North Pole.
From my experience I know that this atmospheric scattering algorithm is very fragile to input parameters which need to be exactly in the right range or otherwise the sky will look completely wrong. Here are my parameters:
This is how I pass all parameters to the shader:
Functions used by the shader:
Vertex shader:
Pixel shader:
And a picture showing my sky (120deg FOV to show more sky):
I also should mention that I render my sky to HDR texture and I use following equation to convert colors to LDR:
Did anyone who implemented atmospheric scattering based on O'Neils algorithm have the same problem? Do you have any ideas how to fix my sky?
The easiest way to make the sky brighter is to increase sun brightness parameter (fSunBrightness in my code), but for values above 20.0f it creates very strong "halo" effect around the horizon.
I am trying to implement atmospheric scattering effect based on O'Neils implementation from GPUGems2. Almost everything seems to be fine, but the sky colour is too dark. I already spent several hours trying to understand the problem and I am running out of ideas.
To render the effect, I create a sphere of a radius of 6370997.0f * 1.025f (radius of earth with atmosphere) and then I move it down by 6370997.0f in the shader (earth radius). It means that my camera is located on the North Pole.
static const float EARTH_RADIUS = 6370997.0f;static const float EARTH_ATMOSPHERE_RADIUS = EARTH_RADIUS * 1.025f;From my experience I know that this atmospheric scattering algorithm is very fragile to input parameters which need to be exactly in the right range or otherwise the sky will look completely wrong. Here are my parameters:
float fRayleighScatteringConstant = 0.0025f;float fMieScatteringConstant = 0.0015f;float fSunBrightness = 18.0f;float fRayleighScaleDepth = 0.25f;float fMiePhaseAsymmetryFactor = -0.98f;Vector3 vWaveLength = Vector3( 0.650f, 0.570f, 0.475f );This is how I pass all parameters to the shader:
const float fKr = cAtmosphericScattering.fRayleighScatteringConstant; // Rayleigh scattering constantconst float fKr4PI = fKr * 4.0f * SkMath::Pi();const float fKm = cAtmosphericScattering.fMieScatteringConstant; // Mie scattering constantconst float fKm4PI = fKm * 4.0f * SkMath::Pi();const float fScale = 1 / (EARTH_ATMOSPHERE_RADIUS - EARTH_RADIUS);Vector3 vInvWavelength;vInvWavelength.x = 1.0f / SkMath::Pow( cAtmosphericScattering.vWaveLength.x, 4.0f ); // 650 nm for redvInvWavelength.y = 1.0f / SkMath::Pow( cAtmosphericScattering.vWaveLength.y, 4.0f ); // 570 nm for greenvInvWavelength.z = 1.0f / SkMath::Pow( cAtmosphericScattering.vWaveLength.z, 4.0f ); // 475 nm for bluepEffect->SetVector( "v3InvWavelength", vInvWavelength );pEffect->SetFloat( "fInnerRadius", EARTH_RADIUS );pEffect->SetFloat( "fKrESun", fKr*cAtmosphericScattering.fSunBrightness );pEffect->SetFloat( "fKmESun", fKm*cAtmosphericScattering.fSunBrightness );pEffect->SetFloat( "fKr4PI", fKr4PI );pEffect->SetFloat( "fKm4PI", fKm4PI );pEffect->SetFloat( "fScale", fScale );pEffect->SetFloat( "fScaleDepth", cAtmosphericScattering.fRayleighScaleDepth );pEffect->SetFloat( "fScaleOverScaleDepth", fScale / cAtmosphericScattering.fRayleighScaleDepth );pEffect->SetFloat( "g", cAtmosphericScattering.fMiePhaseAsymmetryFactor );pEffect->SetFloat( "g2", cAtmosphericScattering.fMiePhaseAsymmetryFactor * cAtmosphericScattering.fMiePhaseAsymmetryFactor );float fCameraHeight = EARTH_RADIUS + vCameraPosition.y; // vCameraPosition is my camera position in meterspEffect->SetVector( "v3CameraPos", SkVector3( 0.0f, fCameraHeight, 0.0f ) );pEffect->SetVector( "v3LightPos", vSunDirection ); // vSunDirection is my light vectorpEffect->SetFloat( "fCameraHeight", fCameraHeight );Functions used by the shader:
float ExpScale(float fCos){ float x = 1.0 - fCos; return fScaleDepth * exp(-0.00287 + x*(0.459 + x*(3.83 + x*(-6.80 + x*5.25))));}float GetRayleighPhase( float fCos2 ){ return 0.75 * (1.0 + fCos2);}float GetMiePhase( float fCos, float fCos2 ){ return 1.5 * ((1.0 - g2) / (2.0 + g2)) * (1.0 + fCos2) / pow(1.0 + g2 - 2.0*g*fCos, 1.5);}Vertex shader:
#define INTEGRAL_EQUATION_NUM_SAMPLES 2 // Number of sample rays to use in integral equationSVSOutput VS_Sky( float4 i_Position : POSITION ){ SVSOutput sOutput; // This is a vertex position center earth center at (0,0,0) float3 vPositionEarthCenter = i_Position.xyz; // Transform vertex to the screen space i_Position.y -= fInnerRadius; // Here I move the mesh down sOutput.vPosition = mul( i_Position, g_WorldViewProjection ); // Get the ray from the camera to the vertex, and its length (which is the far point of the ray passing through the atmosphere) float3 v3Ray = vPositionEarthCenter - v3CameraPos; float fFar = length(v3Ray); v3Ray /= fFar; // Calculate the ray's starting position, then calculate its scattering offset float3 v3Start = v3CameraPos; float fHeight = length(v3Start); float fDepth = exp(fScaleOverScaleDepth * (fInnerRadius - fCameraHeight)); float fStartAngle = dot(v3Ray, v3Start) / fHeight; float fStartOffset = fDepth*ExpScale(fStartAngle); // Initialize the scattering loop variables float fSampleLength = fFar / float( INTEGRAL_EQUATION_NUM_SAMPLES ); float fScaledLength = fSampleLength * fScale; float3 v3SampleRay = v3Ray * fSampleLength; float3 v3SamplePoint = v3Start + v3SampleRay * 0.5; // Now loop through the sample rays float3 v3FrontColor = float3(0.0, 0.0, 0.0); for(int i=0; i< INTEGRAL_EQUATION_NUM_SAMPLES; i++) { float fHeight = length(v3SamplePoint); float fDepth = exp(fScaleOverScaleDepth * (fInnerRadius - fHeight)); float fLightAngle = dot(v3LightPos, v3SamplePoint) / fHeight; float fCameraAngle = dot(v3Ray, v3SamplePoint) / fHeight; float fScatter = (fStartOffset + fDepth*(ExpScale(fLightAngle) - ExpScale(fCameraAngle))); float3 v3Attenuate = exp(-fScatter * (v3InvWavelength * fKr4PI + fKm4PI)); v3FrontColor += v3Attenuate * (fDepth * fScaledLength); v3SamplePoint += v3SampleRay; } // Finally, scale the Mie and Rayleigh colours and set up the varying variables for the pixel shader sOutput.vMieColor = v3FrontColor * fKmESun; sOutput.vRayleighColor = v3FrontColor * (v3InvWavelength * fKrESun); sOutput.vCameraToVertexDir = v3CameraPos - vPositionEarthCenter;}Pixel shader:
SPSOutput PS_Sky( SVSOutput sInput ){ SPSOutput sOutput; float fCos =dot(v3LightPos, sInput.vCameraToVertexDir) / length(sInput.vCameraToVertexDir); float fCos2 = fCos * fCos; float fRayleighPhase = GetRayleighPhase( fCos2 * fff ); float fMiePhase = GetMiePhase( fCos, fCos2 ); sOutput.vColor = float4( fRayleighPhase * sInput.vRayleighColor + fMiePhase * sInput.vMieColor, 1.0f );}And a picture showing my sky (120deg FOV to show more sky):
I also should mention that I render my sky to HDR texture and I use following equation to convert colors to LDR:
vColor = 1.0 - exp( -2.0 * vColor )Did anyone who implemented atmospheric scattering based on O'Neils algorithm have the same problem? Do you have any ideas how to fix my sky?
The easiest way to make the sky brighter is to increase sun brightness parameter (fSunBrightness in my code), but for values above 20.0f it creates very strong "halo" effect around the horizon.