Jump to content
  • Advertisement
Sign in to follow this  

Atmospheric Scattering from Space, yet again

This topic is 3802 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

Hello there! Today I was trying to finally get the Atmospheric Shader working. I've been testing it into RenderMonkey but well it really doesn't work. Here's the output from Rendermonkey. Image Hosted by ImageShack.us<br/> Here is the shader code I'm using. For testing purposes, the initialization of the constants is inside the vertex shader (!). "LightDirection" is 1000,1000,1000 (as in O'Neil source code) "CameraPosition" is 0,0,25; "ESun" is 15.0f; "InnerRadius" is 10.0f; Vertex Shader
[source lang = "C"]
float cameraHeight; // The camera's current height
float innerRadius; // The inner (planetary) radius
float ESun;
float kr =0.0025;
float km =0.0015f;
float krESun; // Kr * ESun
float kmESun; // Km * ESun
float kr4PI; // Kr * 4 * PI
float km4PI; // Km * 4 * PI
float scale; // 1 / (fOuterRadius - fInnerRadius)
float scaleDepth = 0.25f; // The scale depth (i.e. the altitude at which the atmosphere's average density is found)
float scaleOverScaleDepth; // fScale / fScaleDepth
float g = -0.95f;
int samples=4;

float4 waveLength;
float4 lightDirection;
float4 cameraPosition;
float4 invWavelength

float cameraHeight2;
float outerRadius;
float outerRadius2;

float4x4 WorldViewProj;

struct vpconn
float4 Position : POSITION;
float3 t0 : TEXCOORD0;
float3 c0 : COLOR; // The Rayleigh color
float3 c1 : COLOR1; // The Mie color

void SetupValues() {
float Pi = 3.14159265359;
lightDirection = normalize(lightDirection);
outerRadius = innerRadius * 1.025f;
outerRadius2 = pow(outerRadius,2);
scale = 1.0f / (outerRadius - innerRadius);
scaleOverScaleDepth = scale/scaleDepth;

float4 waveLength4 = pow(waveLength,4);

cameraHeight = length(cameraPosition);
cameraHeight2 = pow(cameraHeight,2);

krESun = kr *ESun;
kmESun = km*ESun;
kr4PI = kr *4.0f * Pi;
km4PI = km *4.0f * Pi;

invWavelength = 1 / waveLength4;

// The scale equation calculated by Vernier's Graphical Analysis
float expScale (float fCos)
float x = 1.0 - fCos;
return scaleDepth * exp(-0.00287 + x*(0.459 + x*(3.83 + x*(-6.80 + x*5.25))));


// Calculates the Mie phase function
float getMiePhase(float fCos, float fCos2, float g, float g2)
return 1.5 * ((1.0 - g2) / (2.0 + g2)) * (1.0 + fCos2) / pow(1.0 + g2 - 2.0*g*fCos, 1.5);

// Calculates the Rayleigh phase function
float getRayleighPhase(float fCos2)
return 0.75 + (1.0 + fCos2);

// Returns the near intersection point of a line and a sphere
float getNearIntersection(float3 v3Pos, float3 v3Ray, float fDistance2, float fRadius2)
float B = 2.0 * dot(v3Pos, v3Ray);
float C = fDistance2 - fRadius2;
float fDet = max(0.0, B*B - 4.0 * C);
return 0.5 * (-B - sqrt(fDet));

// Returns the far intersection point of a line and a sphere
float getFarIntersection(float3 v3Pos, float3 v3Ray, float fDistance2, float fRadius2)
float B = 2.0 * dot(v3Pos, v3Ray);
float C = fDistance2 - fRadius2;
float fDet = max(0.0, B*B - 4.0 * C);
return 0.5 * (-B + sqrt(fDet));

vpconn AtmosphereFromSpaceVS(float4 vPos : POSITION )


float3 ray = vPos.xyz - cameraPosition.xyz;

float far = length (ray);
ray /= far;

float near = getNearIntersection (cameraPosition, ray,
cameraHeight2, outerRadius2);

float3 start = cameraPosition + ray * near;
far -= near;

float startAngle = dot (ray, start) / outerRadius;
float startDepth = exp (scaleOverScaleDepth * (innerRadius - cameraHeight));
float startOffset = startDepth * expScale (startAngle);

float sampleLength = far / samples;
float scaledLength = sampleLength * scale;
float3 sampleRay = ray * sampleLength;
float3 samplePoint = start + sampleRay * 0.5f;

float3 frontColor = float3 (0,0,0);

for (int i = 0; i < samples; i++)
float height = length (samplePoint);
float depth = exp (scaleOverScaleDepth * (innerRadius - height));
float lightAngle = dot (lightDirection, samplePoint) / height;
float cameraAngle = dot (ray, samplePoint) / height;
float scatter = (startOffset + depth * (
expScale (lightAngle) - expScale (cameraAngle)));

float3 attenuate = exp (-scatter * (invWavelength.xyz * kr4PI + km4PI));

frontColor += attenuate * (depth * scaledLength);

samplePoint = samplePoint + sampleRay;


vpconn OUT;

OUT.t0 = cameraPosition.xyz - vPos.xyz;
OUT.Position = mul(vPos, WorldViewProj);
OUT.c0.xyz = frontColor * (invWavelength.xyz * krESun);
OUT.c1.xyz = frontColor * kmESun;

return OUT;

Pixel Shader
float4 AtmosphereFromSpacePS(vpconn IN) : COLOR

float4 color;
float g2 = pow(g,2);
float cos = dot (lightDirection, IN.t0) / length (IN.t0);
float cos2 = cos*cos;

float fMiePhase = 1.5 * ((1.0 - g2) / (2.0 + g2)) * (1.0 + cos2) / pow(1.0 + g2 - 2.0*g*cos, 1.5);
float fRayleighPhase = 0.75 * (1.0 + cos*cos);

float a = fRayleighPhase * IN.c0;
float b =  fMiePhase * IN.c1;
color.rgb = (fRayleighPhase * IN.c0 + fMiePhase * IN.c1);
color.a = color.b;

return color;

As you can see from the image.. It seems to be somewhat working. There are faint shades of blue .... at least, there is hope :D But the white areas? Where could the error be? If I try to set the innerRadius (currently 10) to bigger values, the sphere becomes all white. Thanks in advance.

Share this post

Link to post
Share on other sites
I'm checking and double checking the code.. I used a sphere that matches the outerRadius length. As you can see from the following image, the shader seems to be working nicely on the edges (there's a faint blue outline). The problem is in the front vertices.. everything is white.

Image Hosted by ImageShack.us<br/>
(there is just that one sphere being rendered in the scene)

By using PIX I found out that the problem lies in the "startAngle" calculation which in my shader code is:

float startAngle = dot (ray, start) / outerRadius;

for those front vertices, the result is always very close to -1 (like ~ -0.980). Then the resulting call to the "expScale" function returns values of astronomical scale (float startOffset = startDepth * expScale (startAngle);). Subsequently the resulting color value from the vertex shader is always too big and gets therefore clamped to one.

The code seems to be identical to all the other implementations that I've seen on the forums.. So the problem must lie somewhere else. Does anyone have any idea about what might be going wrong? Surely it's something stupid...

I've some doubts about the "cameraHeight" value being passed.. BY looking at O'Neil source code, I'm passing the length of the vector). Is that correct?
If anyone is interested in looking at the C# solution I can upload it somewhere...

Thanks in advance!

Share this post

Link to post
Share on other sites
When I plug in fCos = -1 for your expScale function, I get astonomical values also. Do you mean to set x = 1-fCos?? For fCos=-1, that sets x to ~2 which is pretty large I would think.

Perhaps x = 1-fabs(fCos)?

EDIT: a dot()= -1 implies the vectors are anti-parallel. Is that what you intend?

Share this post

Link to post
Share on other sites
I plotted the expScale function on a graph (with x being 1- (cos)):
Image Hosted by ImageShack.us<br/>

from the image, the result of the startAngle expression discussed previously shouldn't be bigger than -0.5, otherwise the values will skyrocket. The x = 1-fCos expression is what I found on the dozens of implementation that I found here on gamedev. About the rays being antiparallel.. well honostely I don't know. I'm just wondering what is wrong.

Here is my parameter listing:

case FXType.AtmosphericScattering:
fxDescriptor = new EffectDescriptor("Atmosphere.fx");

float innerRadius = 10.0f;
float outerRadius = innerRadius * 1.025f;

float ESun = 15.0f;
float kr = 0.0025f;
float km = 0.0015f;
float scale = 1.0f / (outerRadius-innerRadius);
float scaleDepth = 0.25f; //(outerRadius - innerRadius) / 2.0f;
float scaleOverScaleDepth = scale/scaleDepth;
float g = -0.95f;

Vector4 wavelenght = new Vector4(0.650f, 0.570f, 0.450f, 1.0f);
Vector4 invWavelenght = new Vector4(
(float)(1.0 / Math.Pow(wavelenght.X,4.0)),
(float)(1.0 / Math.Pow(wavelenght.Y,4.0)),
(float)(1.0 / Math.Pow(wavelenght.Z,4.0)),

FloatOp cameraHeight = delegate() {
return Game.CurrentScene.Camera.PositionV4.Length();};
EffectParameter epCameraHeight = EffectParameter.CreateCustomParameter("cameraHeight", fxDescriptor.Effect, cameraHeight);

FloatOp cameraHeight2 = delegate()
return Game.CurrentScene.Camera.PositionV4.LengthSquared();

EffectParameter epCameraHeight2 = EffectParameter.CreateCustomParameter("cameraHeight2", fxDescriptor.Effect,cameraHeight2);

EffectParameter epInnerRadius = EffectParameter.CreateCustomParameter("innerRadius", fxDescriptor.Effect, innerRadius);

EffectParameter epOuterRadius = EffectParameter.CreateCustomParameter("outerRadius", fxDescriptor.Effect, outerRadius);

EffectParameter epOuterRadius2 = EffectParameter.CreateCustomParameter("outerRadius2", fxDescriptor.Effect, outerRadius * outerRadius);

EffectParameter epkrESun = EffectParameter.CreateCustomParameter("krESun", fxDescriptor.Effect, kr*ESun);

EffectParameter epkmESun = EffectParameter.CreateCustomParameter("kmESun", fxDescriptor.Effect, km * ESun);

EffectParameter epkr4Pi = EffectParameter.CreateCustomParameter("kr4PI", fxDescriptor.Effect, (float)(kr * 4 * Math.PI));

EffectParameter epkm4Pi = EffectParameter.CreateCustomParameter("km4PI", fxDescriptor.Effect, (float)(km * 4 * Math.PI));

EffectParameter epScale = EffectParameter.CreateCustomParameter("scale", fxDescriptor.Effect, scale);

EffectParameter epScaleDepth = EffectParameter.CreateCustomParameter("scaleDepth", fxDescriptor.Effect, scaleDepth);

EffectParameter epScaleOverScaleDepth = EffectParameter.CreateCustomParameter("scaleOverScaleDepth", fxDescriptor.Effect, scaleOverScaleDepth);

EffectParameter epG = EffectParameter.CreateCustomParameter("g", fxDescriptor.Effect, g);

EffectParameter epG2 = EffectParameter.CreateCustomParameter("g2", fxDescriptor.Effect, g*g);

EffectParameter epInvWavelength = EffectParameter.CreateCustomParameter("invWavelength", fxDescriptor.Effect, invWavelenght);

return fxDescriptor;

Share this post

Link to post
Share on other sites
omg I cant wait to learn this kind of stuff :D:D:D.....

sorry my response was more out of excitement and not very useful : /

Share this post

Link to post
Share on other sites
the startAngle expression discussed previously shouldn't be bigger than -0.5

DISCLAIMER: I'm not familiar with the algoritm; I'm just looking at the math.

dot(ray,start) = |ray|*|start|*cos(angle)

where |value| means absolute magnitude.

You normalize "ray" but "start" appears to be a position, not a direction. Should it be normalized?

dot(ray,normalize(start)) ??

Share this post

Link to post
Share on other sites
I've tried and tried but I cannot seem to get it to worki. I uploaded the source code on my website... so if anyone could be so kind to have a look at it, I'll greatly appreciate it.

here's the link!

It's in C# and comes complete with the slimDX dlls. The rendering code is in the "Application/Scenes/GridRenderer.cs" file (it once had a grid :D).

While the Effect setup code is in the Odyssey/Engine/Objects/Effects/EffectManager.cs" file. The first method: "CreateEffect". The shader is in the Application/bin/Debug/Effects directory.

Thanks in advance :)

Share this post

Link to post
Share on other sites
Sign in to follow this  

  • Advertisement

Important Information

By using GameDev.net, you agree to our community Guidelines, Terms of Use, and Privacy Policy.

GameDev.net is your game development community. Create an account for your GameDev Portfolio and participate in the largest developer community in the games industry.

Sign me up!