Real time atmospheric scattering shader -- Space view

I may have got this working a bit better, by fine-tuning the input parameters, and changing a bit of the shader code.
However, it only seems to work with the solar_sky_sphere model from the Solar-System sample. This could be due to the fact that I don’t know how to properly create .egg models. Also, the earth scale must be 1.0 and the atmo scale must be around 1.025. It fails at other sizes unfortunately (even when using the 2.5% ratio specified by O’Neil)

from pandac.PandaModules import loadPrcFileData 

loadPrcFileData( '', 'frame-rate-meter-scale 0.035' ) 
loadPrcFileData( '', 'frame-rate-meter-side-margin 0.1' ) 
loadPrcFileData( '', 'show-frame-rate-meter 1' ) 
loadPrcFileData( '', 'window-title Atmosphere Demo' ) 
loadPrcFileData('', "sync-video 0") 

from direct.directbase.DirectStart import * 
from direct.task import Task 
from panda3d.core import Shader, PointLight 
import math 
base.setBackgroundColor(0.0, 0.0, 0.0) 

light = render.attachNewNode( PointLight( "sunPointLight" ) ) 
light.setPos(1000,0,0) 
render.setLight( light ) 
sun = loader.loadModel("models/solar_sky_sphere") 
sun.reparentTo(render) 
sun.setPos(1050,0,0) 
sun.setScale(5) 

earth = loader.loadModel("models/planet_sphere") 
earth.reparentTo(render) 

# This is a sphere with the normals flipped 
atmo = loader.loadModel("models/solar_sky_sphere") 
#atmo.reparentTo(earth) 
atmo.reparentTo(render) 
atmo.setScale(1.025)


#outerRadius = abs((earth.getScale().getX() * atmo.getScale().getX()))   # ?????
outerRadius = atmo.getScale().getX()
scale = 1/(outerRadius - earth.getScale().getX()) 
atmo.setShaderInput("fOuterRadius", outerRadius) 
atmo.setShaderInput("fInnerRadius", earth.getScale().getX()) 
atmo.setShaderInput("fOuterRadius2", outerRadius * outerRadius) 
atmo.setShaderInput("fInnerRadius2", earth.getScale().getX() * earth.getScale().getX()) 

atmo.setShaderInput("fKr4PI", 0.000055 * 4 * 3.14159) 
atmo.setShaderInput("fKm4PI", 0.000015 * 4 * 3.14159) 

atmo.setShaderInput("fScale", scale) 
atmo.setShaderInput("fScaleDepth", 0.5) 
atmo.setShaderInput("fScaleOverScaleDepth", scale/0.5) 

# Currently hardcoded in shader 
atmo.setShaderInput("fSamples", 10.0) 
atmo.setShaderInput("nSamples", 10) 

# These do sunsets and sky colors 
# Brightness of sun 
ESun = 15
# Reyleight Scattering (Main sky colors) 
atmo.setShaderInput("fKrESun", 0.000055 * ESun) 
# Mie Scattering -- Haze and sun halos 
atmo.setShaderInput("fKmESun", 0.000015 * ESun) 
# Color of sun 
atmo.setShaderInput("v3InvWavelength", 1.0 / math.pow(0.650, 4), 
                                  1.0 / math.pow(0.570, 4), 
                                  1.0 / math.pow(0.465, 4)) 
                                  
atmo.setShaderInput("v3CameraPos", base.camera.getPos().getX(), 
        base.camera.getPos().getY(), 
        base.camera.getPos().getZ()) 
# Light vector from center of planet.        
lightv = light.getPos() 
lightdir = lightv / lightv.length()

atmo.setShaderInput("v3LightPos", lightdir[0], lightdir[1], lightdir[2]) 
      
atmo.setShaderInput("fCameraHeight", base.camera.getPos().length()) 
atmo.setShaderInput("fCameraHeight2", base.camera.getPos().length()*base.camera.getPos().length()) 

atmo.setShaderInput("g", 0.90) 
atmo.setShaderInput("g2", 0.81) 
atmo.setShaderInput("float", 2) 

atmoShader = Shader.load("shader.cg") 
atmo.setShader(atmoShader) 

def shaderUpdate(task): 
    atmo.setShaderInput("v3CameraPos", base.camera.getPos().getX(), 
        base.camera.getPos().getY(), 
        base.camera.getPos().getZ()) 
    atmo.setShaderInput("fCameraHeight", base.camera.getPos().length()) 
    atmo.setShaderInput("fCameraHeight2", base.camera.getPos().length()*base.camera.getPos().length()) 
    return task.cont 

taskMgr.add(shaderUpdate, 'shaderUpdate') 
run()

shader.cg

//Cg 
// http://stainlessbeer.weebly.com/planets-9-atmospheric-scattering.html 
// http://http.developer.nvidia.com/GPUGems2/gpugems2_chapter16.html 
// http://sponeil.net/ 
float g =-0.90f; 
float g2 = 0.81f; 
float fExposure =.6; 

float scale(float fCos) 
{ 
    float x = 1.0 - fCos;  

	return 0.25 * exp(-0.00287 + x*(0.459 + x*(3.83 + x*(-6.80 + x*5.25))));
	  
 
} 
  
void vshader(float4 vtx_position:POSITION, 
	float4 vtx_color: COLOR,
    in uniform float4 k_v3LightPos,          // The light's current position 
    in uniform float4 k_v3CameraPos,         // The camera's current position 
    in uniform float4 k_v3InvWavelength,     // 1 / pow(wavelength, 4) for RGB 
    in uniform float4 k_fCameraHeight,        // The camera's current height 
    in uniform float4 k_fCameraHeight2,       // fCameraHeight^2 
    in uniform float4 k_fOuterRadius,         // The outer (atmosphere) radius 
    in uniform float4 k_fOuterRadius2,        // fOuterRadius^2 
    in uniform float4 k_fInnerRadius,         // The inner (planetary) radius 
    in uniform float4 k_fInnerRadius2,        // fInnerRadius^2 
    in uniform float4 k_fKrESun,              // Kr * ESun 
    in uniform float4 k_fKmESun,              // Km * ESun 
    in uniform float4 k_fKr4PI,               // Kr * 4 * PI 
    in uniform float4 k_fKm4PI,               // Km * 4 * PI 
    in uniform float4 k_fScale,               // 1 / (fOuterRadius - fInnerRadius) 
    in uniform float4 k_fScaleOverScaleDepth, // fScale / fScaleDepth 
    in uniform float4 k_fScaleDepth,          // The scale depth (i.e. the altitude at which the atmosphere's average density is found) 
    in uniform float4 k_fSamples, 
    in uniform float4 k_nSamples, 
    uniform float4x4 mat_modelproj, 
    out float4 l_position : POSITION, 
    out float4 l_color0   : COLOR0, 
    out float4 l_color1   : COLOR1, 
    out float3 l_v3Direction: TEXCOORD0 ) 
{ 
    float3 v3Pos = vtx_position.xyz; 
    float3 v3Ray = v3Pos - k_v3CameraPos.xyz; 
    float fFar = length(v3Ray); 
    v3Ray /= fFar; 
  
    // Calculate the closest intersection of the ray with the outer atmosphere 
    // (which is the near point of the ray passing through the atmosphere)    
    float B = 2.0 * dot(k_v3CameraPos.xyz, v3Ray); 
    float C = k_fCameraHeight2 - k_fOuterRadius2; 
    float fDet = max(0.0, B*B - 4.0 * C); 
    float fNear = 0.5 * (-B - sqrt(fDet)); 
      
    // Calculate the ray's start and end positions in the atmosphere, 
    // then calculate its scattering offset 
    float3 v3Start = k_v3CameraPos.xyz + v3Ray * fNear; 
    fFar -= fNear; 
    
  
    // Initialize the scattering loop variables 
    float fSampleLength = fFar / 15.0; 
	//float fSampleLength = fFar / 1.0
    float fScaledLength = fSampleLength * k_fScale; 
    float3 v3SampleRay = v3Ray * fSampleLength; 
    float3 v3SamplePoint = v3Start + (v3SampleRay * 0.5); 
    
    float fHeight = length(v3SamplePoint); 
    //float fStartAngle = dot(v3Ray, v3Start) / k_fOuterRadius.x; 
    float fStartAngle = dot(v3Ray, v3Start) / fHeight; 
    //float fStartDepth = exp(-1.0 / k_fScaleDepth.x); 
	float fStartDepth = exp(k_fScaleOverScaleDepth * (k_fInnerRadius - fHeight)); 
    float fStartOffset = fStartDepth * scale(fStartAngle); 
	
    
    // Now loop through the sample points 
    float3 v3FrontColor = float3(0.0, 0.0, 0.0); 
    for(int i=0; i<14; i++)  
    { 
        float fHeight = length(v3SamplePoint); 
		float fDepth = exp(k_fScaleOverScaleDepth * (k_fInnerRadius - fHeight));
        float fLightAngle = dot(k_v3LightPos.xyz, v3SamplePoint) / fHeight; 
        float fCameraAngle = dot(v3Ray, v3SamplePoint) / fHeight; 
        float fScatter = (fStartOffset + fDepth * (scale(fLightAngle) - scale(fCameraAngle))); 
        float3 v3Attenuate = exp(-fScatter *(k_v3InvWavelength.xyz * k_fKr4PI.x + k_fKm4PI.x)); 
        v3FrontColor += v3Attenuate * (fDepth * fScaledLength); 
        v3SamplePoint += v3SampleRay; 
    } 
	l_position=mul(mat_modelproj, vtx_position);  
    l_color0.rgb = v3FrontColor.xyz * k_v3InvWavelength.xyz * k_fKrESun.x; 
    l_color1.rgb=v3FrontColor*k_fKmESun.x; 
    l_v3Direction =k_v3CameraPos.xyz - v3Pos; 
} 
  


void fshader(in float4 l_position : POSITION, 
    in float4 l_color0   : COLOR0, 
    in float4 l_color1   : COLOR1, 
    in float3 l_v3Direction : TEXCOORD0, 
    uniform float4 k_v3LightPos, 
    out float4 o_color:COLOR) 
{ 
    float fCos = dot(k_v3LightPos, l_v3Direction) / length(l_v3Direction); 
    float fRayleighPhase = 0.75 * (1.0 + fCos*fCos); 
	//float fRayleighPhase = .9 * (1.0 + fCos*fCos);
    float fMiePhase= 1.5 * ((1.0 - g2) / (2.0 + g2)) * (1.0 + fCos*fCos) / pow(1.0 + g2 - 2.0*g*fCos, 1.5); 
    //float fMiePhase = 	1.5 * 1.0 / (0.0015 * 4 * 3.14159) * (1.0 - g2) * pow(1.0 + (g2) - 2.0*g*fCos, -3.0/2.0) * (1.0 +fCos * fCos) / (2.0 + g2);
    //This causes a brightness reduction when viewing at 90 degrees 
    float4 col=(fRayleighPhase * l_color0) + (fMiePhase * l_color1); 
    //float4 col=(l_color0) + (fMiePhase * l_color1); 
    //col.a = col.b; 
	col.a=max(col.b,col.r);
    //In some versions, not in others. Not sure what impact it has 
    o_color = 1 - exp(-fExposure * col); 
    //o_color = col; 
}   

Be sure to zoom out if you get it running. The sky is a faint blue. I guess higher-res sphere models may solve the remaining issues, but it’s just a guess.