# aerial perspective

Hey everyone,

this afternoon I’ve implemented aerial perspective (rayleigh and mie scattering in the atmosphere) as described in
http://ati.amd.com/developer/dx9/ATI-LightScattering.pdf. I am not sure if I understood all of it, but here is what I got:

sunset (0 degree light angle):

afternoon (40 degree light angle):

noon (80 degree light angle):

aerial_perspective.py:

``````from math import sin, pi

from pandac.PandaModules import AmbientLight, DirectionalLight, Filename
import direct.directbase.DirectStart

# camera
base.disableMouse()
base.camera.setPos(0, 6000, 3500)
base.camera.lookAt(0, 0, 2000)

# sun
dlight = DirectionalLight('sun')
dlight.setColor((0.9, 0.9, 0.9, 1))
dlnp = render.attachNewNode(dlight)
dlnp.setHpr(0, -80, 0)
render.setLight(dlnp)

# ambient light
alight = AmbientLight('ambient')
# make ambient stronger with angle
ambient_strength = abs(0.2 * sin(dlnp.getP() * pi / 180))
alight.setColor((ambient_strength, ambient_strength, ambient_strength, 1.0))
alnp = render.attachNewNode(alight)
render.setLight(alnp)

# terrain
terrain = GeoMipTerrain("Terrain")
terrain.setHeightfield(Filename("perlin-noise.png"))
terrain.setBruteforce(True)
root = terrain.getRoot()
root.reparentTo(render)
root.setScale(128, 128, 5000)    # make it biiig, so that the blue-shift appears
root.setPos(-10240, -10240, 0)
root.setColor((0.8, 0.8, 0.8, 0.8))
terrain.generate()

run()``````

aerial_perspective.sha

``````//Cg
//
//Cg profile arbvp1 arbfp1

#define BETA_RAYLEIGH_RED 0.00000695
#define BETA_RAYLEIGH_GREEN 0.0000118
#define BETA_RAYLEIGH_BLUE 0.0000244

#define BETA_MIE_RED 0.0000004
#define BETA_MIE_GREEN 0.0000006
#define BETA_MIE_BLUE 0.0000024

#define PHASE_ECC 0.0
#define PI 3.1416
#define SUN_IRR 0.3

float3 Fex(float distance)
{
float3 fex;
fex.x = exp(-(BETA_RAYLEIGH_RED + BETA_MIE_RED) * distance);
fex.y = exp(-(BETA_RAYLEIGH_GREEN + BETA_MIE_GREEN) * distance);
fex.z = exp(-(BETA_RAYLEIGH_BLUE + BETA_MIE_BLUE) * distance);
return fex;
}

float3 betaRayleigh(float angle)
{
float3 betarayleigh;
float k = 0.1875 * PI * (1 + pow(cos(angle), 2));
betarayleigh.x = k * BETA_RAYLEIGH_RED;
betarayleigh.y = k * BETA_RAYLEIGH_GREEN;
betarayleigh.z = k * BETA_RAYLEIGH_BLUE;
return betarayleigh;
}

float3 betaMie(float angle)
{
float3 betamie;
float k = 0.25 * PI * pow((1 - PHASE_ECC), 2) / (1 + pow(PHASE_ECC, 2) - 2 * PHASE_ECC * pow(cos(angle), 1.5));
betamie.x = k * BETA_MIE_RED;
betamie.y = k * BETA_MIE_GREEN;
betamie.z = k * BETA_MIE_BLUE;
return betamie;
}

float3 Lin(float distance, float angle)
{
float3 lin;
float3 betarayleigh = betaRayleigh(angle);
float3 betamie = betaMie(angle);
float3 fex = Fex(distance);
lin.x = (betarayleigh.x + betamie.x) / (BETA_RAYLEIGH_RED + BETA_MIE_RED) * SUN_IRR * (1 - fex.x);
lin.y = (betarayleigh.y + betamie.y) / (BETA_RAYLEIGH_GREEN + BETA_MIE_GREEN) * SUN_IRR * (1 - fex.y);
lin.z = (betarayleigh.z + betamie.z) / (BETA_RAYLEIGH_BLUE + BETA_MIE_BLUE) * SUN_IRR * (1 - fex.z);
return lin;
}

in float3 vtx_position : POSITION,
in float3 vtx_normal : NORMAL,
in float4 vtx_color : COLOR,
in uniform float4x4 mat_modelproj,
in uniform float4x4 trans_model_to_view,
in uniform float4x4 dlight_sun_to_world,
out float4 l_color : COLOR,
out float3 l_fex,
out float3 l_lin,
out float4 l_position  : POSITION)
{
l_position = mul(mat_modelproj, float4(vtx_position,1));

// scattering
float distance = mul(trans_model_to_view, float4(vtx_position,1)).y * 2;   // make the bluish effect bigger
float angle = asin(normalize(dlight_sun_to_world[2]).z);
l_fex = Fex(distance);
l_lin = Lin(distance, angle);

// diffuse lightening
vtx_normal.z /= 39;         // make up for scaling
float3 normal = normalize(vtx_normal);
float3 lightvector = normalize(dlight_sun_to_world[2]).xyz;
float brightness = saturate(dot(normal, lightvector));

l_color = vtx_color * brightness;
}

in float4 l_color : COLOR,
in float3 l_fex,
in float3 l_lin,
in uniform float4 alight_ambient,
out float4 o_color : COLOR)
{
o_color.xyz = saturate(l_color.xyz * l_fex.zyx + l_lin.zyx + alight_ambient.xyz);
o_color.a = 1.0;
}``````

A few notes:

• I know it’s not optimized or anything, I just got it to work. Any helpful comment is very welcome!
• It assumes viewpoint and object close to the ground (won’t work for flight simulation or planet views)
• I don’t guarantee that it is correct, it just looks ok
• Play around with the constants to get it to look like you want.

I leave it as an exercise to the inclined reader to make Ralph roam through the terrain

Nice! Ive never attempted something like this myself. Thanks for sharing!