Atmospheric Scattering
- by Lawrence Kok
I'm trying to implement atmospheric scattering based on Sean O`Neil algorithm that was published in GPU Gems 2. But I have some trouble getting the shader to work. My latest attempts resulted in: http://img253.imageshack.us/g/scattering01.png/
I've downloaded sample code of O`Neil from: http://http.download.nvidia.com/developer/GPU_Gems_2/CD/Index.html. Made minor adjustments to the shader 'SkyFromAtmosphere' that would allow it to run in AMD RenderMonkey.
In the images it is see-able a form of banding occurs, getting an blueish tone. However it is only applied to one half of the sphere, the other half is completely black. Also the banding appears to occur at Zenith instead of Horizon, and for a reason I managed to get pac-man shape.
I would appreciate it if somebody could show me what I'm doing wrong.
Vertex Shader:
uniform mat4 matView;
uniform vec4 view_position;
uniform vec3 v3LightPos;
const int nSamples = 3;
const float fSamples = 3.0;
const vec3 Wavelength = vec3(0.650,0.570,0.475);
const vec3 v3InvWavelength = 1.0f / vec3( Wavelength.x * Wavelength.x * Wavelength.x * Wavelength.x,
Wavelength.y * Wavelength.y * Wavelength.y * Wavelength.y,
Wavelength.z * Wavelength.z * Wavelength.z * Wavelength.z);
const float fInnerRadius = 10;
const float fOuterRadius = fInnerRadius * 1.025;
const float fInnerRadius2 = fInnerRadius * fInnerRadius;
const float fOuterRadius2 = fOuterRadius * fOuterRadius;
const float fScale = 1.0 / (fOuterRadius - fInnerRadius);
const float fScaleDepth = 0.25;
const float fScaleOverScaleDepth = fScale / fScaleDepth;
const vec3 v3CameraPos = vec3(0.0, fInnerRadius * 1.015, 0.0);
const float fCameraHeight = length(v3CameraPos);
const float fCameraHeight2 = fCameraHeight * fCameraHeight;
const float fm_ESun = 150.0;
const float fm_Kr = 0.0025;
const float fm_Km = 0.0010;
const float fKrESun = fm_Kr * fm_ESun;
const float fKmESun = fm_Km * fm_ESun;
const float fKr4PI = fm_Kr * 4 * 3.141592653;
const float fKm4PI = fm_Km * 4 * 3.141592653;
varying vec3 v3Direction;
varying vec4 c0, c1;
float scale(float fCos)
{
float x = 1.0 - fCos;
return fScaleDepth * exp(-0.00287 + x*(0.459 + x*(3.83 + x*(-6.80 + x*5.25))));
}
void main( void )
{
// Get the ray from the camera to the vertex, and its length (which is the far point of the ray passing through the atmosphere)
vec3 v3FrontColor = vec3(0.0, 0.0, 0.0);
vec3 v3Pos = normalize(gl_Vertex.xyz) * fOuterRadius;
vec3 v3Ray = v3CameraPos - v3Pos;
float fFar = length(v3Ray);
v3Ray = normalize(v3Ray);
// Calculate the ray's starting position, then calculate its scattering offset
vec3 v3Start = v3CameraPos;
float fHeight = length(v3Start);
float fDepth = exp(fScaleOverScaleDepth * (fInnerRadius - fCameraHeight));
float fStartAngle = dot(v3Ray, v3Start) / fHeight;
float fStartOffset = fDepth*scale(fStartAngle);
// Initialize the scattering loop variables
float fSampleLength = fFar / fSamples;
float fScaledLength = fSampleLength * fScale;
vec3 v3SampleRay = v3Ray * fSampleLength;
vec3 v3SamplePoint = v3Start + v3SampleRay * 0.5;
// Now loop through the sample rays
for(int i=0; i<nSamples; i++)
{
float fHeight = length(v3SamplePoint);
float fDepth = exp(fScaleOverScaleDepth * (fInnerRadius - fHeight));
float fLightAngle = dot(normalize(v3LightPos), v3SamplePoint) / fHeight;
float fCameraAngle = dot(normalize(v3Ray), v3SamplePoint) / fHeight;
float fScatter = (-fStartOffset + fDepth*( scale(fLightAngle) - scale(fCameraAngle)))/* 0.25f*/;
vec3 v3Attenuate = exp(-fScatter * (v3InvWavelength * fKr4PI + fKm4PI));
v3FrontColor += v3Attenuate * (fDepth * fScaledLength);
v3SamplePoint += v3SampleRay;
}
// Finally, scale the Mie and Rayleigh colors and set up the varying variables for the pixel shader
vec4 newPos = vec4( (gl_Vertex.xyz + view_position.xyz), 1.0);
gl_Position = gl_ModelViewProjectionMatrix * vec4(newPos.xyz, 1.0);
gl_Position.z = gl_Position.w * 0.99999;
c1 = vec4(v3FrontColor * fKmESun, 1.0);
c0 = vec4(v3FrontColor * (v3InvWavelength * fKrESun), 1.0);
v3Direction = v3CameraPos - v3Pos;
}
Fragment Shader:
uniform vec3 v3LightPos;
varying vec3 v3Direction;
varying vec4 c0;
varying vec4 c1;
const float g =-0.90f;
const float g2 = g * g;
const float Exposure =2;
void main(void){
float fCos = dot(normalize(v3LightPos), v3Direction) / length(v3Direction);
float fMiePhase = 1.5 * ((1.0 - g2) / (2.0 + g2)) * (1.0 + fCos*fCos) / pow(1.0 + g2 - 2.0*g*fCos, 1.5);
gl_FragColor = c0 + fMiePhase * c1;
gl_FragColor.a = 1.0;
}