Jump to content
  • Advertisement
Sign in to follow this  
Formski

Nielsen Atmospheric Scattering

This topic is 3992 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

Hi all, I'm trying to implement Nielsen's Atmospheric Scattering to a skydome in my Direct3D9 program and am getting a black sky. I initialize the parameters to the effects file as follows:
float n = 0.9f;		// Refractive index of air
float N = 0.1f;		// Molecular Density
float K = 1.0f;
float T = 1.0f;
float c = (0.6544f*T - 0.6510f) * pow(10.0f,-16.0f);

D3DXVECTOR4 sunDir = D3DXVECTOR4(0.5f, 0.5f, 0.5f, 0.0f);
D3DXVec4Normalize(&sunDir, &sunDir);
D3DXVECTOR4 sunColourIntensity = D3DXVECTOR4(1.0f, 1.0f, 1.0f, 100.0f);
D3DXVECTOR4 BetaRayleigh;

BetaRayleigh.x = 8 * pow(PI,3.0f) * pow((pow(n,2.0f)-1),2.0f);
BetaRayleigh.y = BetaRayleigh.x;
BetaRayleigh.z = BetaRayleigh.y;
BetaRayleigh.x = BetaRayleigh.x / (3 * N * pow(650.0f,4.0f));	// Red (650nm)
BetaRayleigh.x = BetaRayleigh.x / (3 * N * pow(610.0f,4.0f));	// Green (610nm)
BetaRayleigh.x = BetaRayleigh.x / (3 * N * pow(475.0f,4.0f));	// Blue (475nm)

D3DXVECTOR4 BetaMie;
BetaMie.x = 0.434f * c* PI * 4*pow(PI,2.0f) * K;
BetaMie.y = BetaMie.x;
BetaMie.z = BetaMie.x;
BetaMie.x = BetaMie.x / pow(0.650f,2.0f);		// Red (650nm)
BetaMie.y = BetaMie.y / pow(0.610f,2.0f);		// Red (610nm)
BetaMie.z = BetaMie.z / pow(0.475f,2.0f);		// Red (475nm)

D3DXVECTOR4 BetaDashRayleigh = D3DXVECTOR4(
	BetaRayleigh.x * 3 / (16 * PI),	BetaRayleigh.y * 3 / (16 * PI),			BetaRayleigh.z * 3 / (16 * PI),	BetaRayleigh.w * 3 / (16 * PI));
D3DXVECTOR4 BetaDashMie = D3DXVECTOR4(	BetaMie.x * 1 / (4 * PI),
	BetaMie.y * 1 / (4 * PI),BetaMie.z * 1 / (4 * PI), 
	BetaMie.w * 1 / (4 * PI));
D3DXVECTOR4 BetaRM = BetaMie + BetaRayleigh;
D3DXVECTOR4 OneOverBetaRM;
OneOverBetaRM.x = 1 / BetaRM.x;
OneOverBetaRM.y = 1 / BetaRM.y;
OneOverBetaRM.z = 1 / BetaRM.z;

D3DXVECTOR4 eyePos = D3DXVECTOR4(objRenderer->GetCameraPos()->x,
	objRenderer->GetCameraPos()->y,
	objRenderer->GetCameraPos()->z, 1.0f);
// vHG = [1-g^2,1+g^2,-2g,insc]
float g=2.0f;
D3DXVECTOR4 HG = D3DXVECTOR4();
HG.x = 1 - pow(g, 2.0f);
HG.y = 1 + pow(g,2.0f);
HG.z = -2*g;

D3DXVECTOR4 DensityAlt = D3DXVECTOR4(0.0f, 0.0f, 1.0f, 1.0f);
D3DXVECTOR4 ToneMap = D3DXVECTOR4(1.0f, 1.0f, 1.0f, 1.0f);

These are then uploaded to the effect file, which is the same as the one in Nielsen's paper. Also I have loaded in the vertex normals to the skydome and the function for the optical depth in the texture coordinates of the vertices. To top it off as well PIX (from the June 07 SDK) won't let me debug the shaders on this project anymore, but that's another problem. I'd appreciate it if anyone can offer any guidance on this please? Thanks Marco

Share this post


Link to post
Share on other sites
Advertisement
I don't know anything about Nielsen's Atmospheric Scattering but shouldn't
BetaRayleigh.x = BetaRayleigh.x / (3 * N * pow(650.0f,4.0f));	// Red (650nm)
BetaRayleigh.x = BetaRayleigh.x / (3 * N * pow(610.0f,4.0f)); // Green (610nm)
BetaRayleigh.x = BetaRayleigh.x / (3 * N * pow(475.0f,4.0f)); // Blue (475nm)


be
BetaRayleigh.x = BetaRayleigh.x / (3 * N * pow(650.0f,4.0f));	// Red (650nm)
BetaRayleigh.y = BetaRayleigh.y / (3 * N * pow(610.0f,4.0f)); // Green (610nm)
BetaRayleigh.z = BetaRayleigh.z / (3 * N * pow(475.0f,4.0f)); // Blue (475nm)

Share this post


Link to post
Share on other sites
Just a guess: shouldn't the sunDir vector have the w component set to 1.0f if it is to work correctly in a homogeneous coordinate system?

Have you seen the source code snippets in Nielsens thesis?

Share this post


Link to post
Share on other sites
Hi Donnie,

I had, but went through alot more closely and rewrote most of my code.

I have got the vertex shader debugging through Pix again and am finding that the vBetaRayleigh constant is 0,0,0 in the shader. I set it as below:


float fRayMult = 1.0f;
float n = 0.9f; // Refractive index of air
float N = 0.00001f; // Molecular Density
vBetaR.x = 8 * pow(PI,3.0f) * pow((pow(n,2.0f)-1),2.0f);
vBetaR.y = vBetaR.x;
vBetaR.z = vBetaR.y;
vBetaR.x = vBetaR.x / (3 * N * pow(0.0000650f,4.0f)); // Red (650nm)
vBetaR.y = vBetaR.y / (3 * N * pow(0.0000610f,4.0f)); // Green (610nm)
vBetaR.z = vBetaR.z / (3 * N * pow(0.0000475f,4.0f)); // Blue (475nm)
vBetaR *= fRayMult;


My calcs are from Page 24 of the thesis, equation (2.2)

Does this look correct? My thinking is that the light wavelengths are not correct unit wise. Plus I'm not sure what the fRayMult value should be?

Thanks again
Marco

Share this post


Link to post
Share on other sites
I still can't seem to get this to work.

I've attached the effect file I'm using, which is basically the one in Nielsen's paper:

// this is the effect file for the skydome

matrix WorldViewProj : WORLDVIEWPROJECTION;
float3 LightDir : LIGHTDIRECTION;
vector vSunColorIntensity : SUNCOLORINTENSITY;
float3 vBetaRayleigh : BETARAYLEIGH;
float3 vBetaDashRayleigh : BETADASHRAYLEIGH;
float3 vBetaMie : BETAMIE;
float3 vBetaDashMie : BETADASHMIE;
float3 vOneOverBetaRM : ONEOVERBETARAYLEIGHMIE;
float4 vHG : HENYEYGG;
float3 vEyePos : EYEPOSITION;
float4 vDensityAlt : DENSDIST;
float3 vToneMap : TONEMAP;

float4 vConstants = {1.0f, -1.4426950f, 0.01f, 1000.0f}; // Constants

struct VS_OUTPUT
{
float4 Pos : POSITION;
float4 Diff : COLOR0;
float oFog : FOG;
};

VS_OUTPUT ScatteringSkyVS( float3 vPos : POSITION, float3 Norm: NORMAL, float2 Tex : TEXCOORD)
{
VS_OUTPUT Out = (VS_OUTPUT) 0 ;
// transform
Out.Pos = mul(float4(vPos, 1), WorldViewProj);
// determine angle between sun and view direction
float4 viewAngle ;
viewAngle.x = dot(LightDir, Norm) ;
viewAngle.y = (viewAngle.x * viewAngle.x)/2+2;
viewAngle.z = lerp(Tex.y, vDensityAlt.z, Tex.x) ;
viewAngle.w = Tex.y ;
viewAngle.w = lerp(viewAngle.w, vDensityAlt.w, Tex.x) ;

// Calculate Extinction terms for inscattered equation
float3 extinction;

extinction = vBetaRayleigh * viewAngle.z + vBetaMie * viewAngle.w;
extinction = exp(-extinction);

// Calculate mie scattering term (3.13)
// Phase2 (theta) = (10g^2)/(1+g^2-2g*cos(theta))^(3/2)

// vHG = [1-g^2,1+g^2,-2g,insc]
float4 phaseThetaMie;

phaseThetaMie.x = vHG.z * viewAngle.x + vHG.y;
phaseThetaMie.y = rsqrt(phaseThetaMie.x);
phaseThetaMie.z = pow(phaseThetaMie.y,3);
phaseThetaMie.w = phaseThetaMie.z * vHG.x;

// Inscattering (i) = (Beta R * Phase R(theta) + BetaM * PhaseM(Theta)) *
// [1-exp(-Beta R*s), exp(-Beta M*s) ] / (Beta R + Beta M)
float3 rayleigh;
rayleigh = vBetaDashRayleigh * viewAngle.y;
float3 mie;
mie = vBetaDashMie * phaseThetaMie.w;
float3 temp;
temp = vConstants.x - extinction;

float3 inscatter; // I(inscattering)
inscatter = (mie + rayleigh) * vOneOverBetaRM;
inscatter *= temp;
inscatter *= vSunColorIntensity.xyz;
inscatter *= vSunColorIntensity.w;

// color
Out.Diff.rgb = inscatter;
Out.Diff.b += 0.15f;
Out.Diff.a = 1.0f;
return (Out);
}

float4 ScatteringSkyPS(VS_OUTPUT In) : COLOR
{
return In.Diff;
}

technique ScatteringSky
{
pass P0
{
vertexShader = compile vs_2_0 ScatteringSkyVS();
pixelShader = compile ps_2_0 ScatteringSkyPS();
Lighting = FALSE;
ZWriteEnable = FALSE;
CullMode = NONE;
//FillMode = WireFrame;
}
}



The code for filling the constants to the effect is as follows:

float vEyey = -objRenderer->GetCameraPos()->y;

D3DXMATRIX matWVP;
D3DXMatrixMultiply(&matWVP, objRenderer->GetViewMatrix(), objRenderer->GetProjectionMatrix());
pSkydomeScatterFX->SetMatrix(m_pWorldViewProj, &matWVP);


float fRayMult = m_pAtmosphere->GetBetaRayleighMultiplier();
float fMieMult = m_pAtmosphere->GetBetaMieMultiplier();

D3DXVECTOR4 vSunDir = *m_pAtmosphere->GetSunDir();
vSunDir.y = vSunDir.y;
pSkydomeScatterFX->SetVector(m_pLightDir, &vSunDir);

D3DXVECTOR4 vSunColourIntensity = D3DXVECTOR4(1.0f, 1.0f, 1.0f, 100.0f);
vSunColourIntensity.w = 9.2f - 550.0f * fMieMult;
pSkydomeScatterFX->SetVector(m_pvSunColorIntensity, &vSunColourIntensity);

float fDensityAltBase[4];
fDensityAltBase[0] = vEyey /1000.0f;
fDensityAltBase[1] = powf(0.95f, fDensityAltBase[0]);
fDensityAltBase[2] = 20000.0f * expf(-vEyey / 10000.0f);
fDensityAltBase[3] = 8000.0f * expf(-vEyey / 20000.0f);
pSkydomeScatterFX->SetValue(m_pvDensityAlt, &fDensityAltBase, 16);

D3DXVECTOR4 vBetaR, vBetaDashR, vBetaM, vBetaDashM, vBetaRM, vOneOverBetaRM;

// MF
vBetaR = *m_pAtmosphere->GetBetaRayleigh();
vBetaR *= fRayMult;

pSkydomeScatterFX->SetVector(m_pvBetaRayleigh, &vBetaR);

vBetaDashR = *m_pAtmosphere->GetBetaDashRayleigh();
vBetaDashR *= fRayMult;
pSkydomeScatterFX->SetVector(m_pvBetaDashRayleigh, &vBetaDashR);

vBetaM = *m_pAtmosphere->GetBetaMie();
vBetaM *= fMieMult;
pSkydomeScatterFX->SetVector(m_pvBetaMie, &vBetaM);

vBetaDashM = *m_pAtmosphere->GetBetaDashMie();
vBetaDashM *= fMieMult;
pSkydomeScatterFX->SetVector(m_pvBetaDashMie, &vBetaDashM);

vBetaRM = vBetaR + vBetaM;
//pSkydomeScatterFX->SetVector(m_pvBetaRM, &vBetaRM);
vOneOverBetaRM.x = 1.0f / vBetaRM.x;
vOneOverBetaRM.y = 1.0f / vBetaRM.y;
vOneOverBetaRM.z = 1.0f / vBetaRM.z;
pSkydomeScatterFX->SetVector(m_pvOneOverBetaRM, &vOneOverBetaRM);

float fIns = 0.3f; // Inscattering Multiplier


float SunTheta = m_pAtmosphere->GetSunTheta(); // Sun Theta

// vHG = [1-g^2,1+g^2,-2g,insc]
float g = 0.444f + vEyey * (float)1.053e-5 -
(float)5.984e-2 * SunTheta - (float)3.521e-6 * SunTheta * vEyey;
float g2 = 0.100f + vEyey * (float)1.11e-5 - (float)3.18e-2 * SunTheta - (float)6.01e-6
* SunTheta * vEyey;
float c = -0.076923f + 153.846f * fMieMult;
g = g + c*(g2-g);
D3DXVECTOR4 vG = D3DXVECTOR4(1-g*g, 1+g*g, 2*g, fIns);
pSkydomeScatterFX->SetVector(m_pvHG, &vG);

D3DXVECTOR4 DensityAlt = D3DXVECTOR4(0.0f, 0.0f, 1.0f, 1.0f);
DensityAlt.x = vEyey / 1000.0f;
DensityAlt.y = powf(0.95f, DensityAlt.x);
DensityAlt.z = 20000.0f * expf(-vEyey / 10000.0f);
DensityAlt.w = 8000.0f * expf(-vEyey / 20000.0f);

D3DXVECTOR4 vEye = D3DXVECTOR4(objRenderer->GetCameraPos()->x,
vEyey,
objRenderer->GetCameraPos()->z,
1.0f);
pSkydomeScatterFX->SetVector(m_pvEyePos, &vEye);



and the Atmosphere object has the following:

// Calculate Rayleigh and Mie scattering terms
fBetaRayleighMultiplier = RayleighMultiplierDefault;
fBetaMieMultiplier = MieMultiplierDefault;
SetTime(0.0f);
vSunColour = D3DXVECTOR4(1.0f, 1.0f, 1.0f, 1.0f); // Set to white

float fLambda[3],fLambda2[3], fLambda4[3]; // Lambda Factors
fLambda[0] = RedWavelength; // red // note 650e-9 m = 650nm.
fLambda[1] = GreenWavelength; // green
fLambda[2] = BlueWavelength; // blue

for (int i=0; i < 3; i++)
{
fLambda2 = fLambda*fLambda;
fLambda4 = fLambda2*fLambda2;
}
vLambda2 = D3DXVECTOR4(fLambda2[0], fLambda2[1], fLambda2[2], 0.0f);
vLambda4 = D3DXVECTOR4(fLambda4[0], fLambda4[1], fLambda4[2], 0.0f);

vBetaRayleigh.w = 0.0f;
vBetaRayleigh.x = 8 * PI * PI * PI * (n*n-1)*(n*n-1);
vBetaRayleigh.y = vBetaRayleigh.x;
vBetaRayleigh.z = vBetaRayleigh.y;
vBetaRayleigh.x = vBetaRayleigh.x / (3 * N * vLambda4.x); // Red (650nm)
vBetaRayleigh.y = vBetaRayleigh.y / (3 * N * vLambda4.y); // Green (610nm)
vBetaRayleigh.z = vBetaRayleigh.z / (3 * N * vLambda4.z); // Blue (475nm)


/*vBetaRayleigh.x = 2.44e-5f;
vBetaRayleigh.y = 1.18e-5f;
vBetaRayleigh.z = 6.95e-6f;*/



float K = 0.67f;
float c = (0.6544f*T - 0.6510f) * (float)10e-16;
//float c = (0.6544f*2.0f - 0.6510f) * (float)10e-16;
vBetaMie.w = 0.0f;
vBetaMie.x = 0.434f * c * PI * 4 * PI * PI * K;
vBetaMie.y = vBetaMie.x;
vBetaMie.z = vBetaMie.x;
vBetaMie.x = vBetaMie.x / vLambda2.x; // Red (650nm)
vBetaMie.y = vBetaMie.y / vLambda2.y; // Red (610nm)
vBetaMie.z = vBetaMie.z / vLambda2.z; // Red (475nm)

/*vBetaMie.x = 2.4e-6f;
vBetaMie.y = 6e-7f;
vBetaMie.z = 4e-7f;*/


vBetaDashRayleigh = D3DXVECTOR4(vBetaRayleigh.x * 3 / (16 * PI),
vBetaRayleigh.y * 3 / (16 * PI),
vBetaRayleigh.z * 3 / (16 * PI),
0.0f);

vBetaDashMie = D3DXVECTOR4( vBetaMie.x / (4 * PI),
vBetaMie.y / (4 * PI),
vBetaMie.z / (4 * PI),
0.0f);



Finally the header for the atmosphere object has the following constants:

#define PlanetRadius 6.378e6
#define AtmosphereRadius 6.378e6
#define SunBrightness 40.0f
#define RedWavelength 650e-9f // -9
#define GreenWavelength 610e-9f
#define BlueWavelength 475e-9f
#define MieMultiplierDefault 0.01f
#define RayleighMultiplierDefault 0.2f

#define n 1.003f // Refractive index of air
#define N 2.545e25f // Molecular Density
#define T 555e-9f // Turbidity - see page 35



At this stage if I override the Rayleigh and Mie constants I get a pretty much dark blue background with no sun appearing and almost no change in shading intensity.

I'd appreciate it if anybody can see where it has gone wrong as I've spent several days trying to debug this. As far as I can see I've covered all bases so I'm suspecting a stupid error somewhere.

Thanks in advance,
Marco

Share this post


Link to post
Share on other sites
Quote:
Original post by Formski
vBetaR.x = vBetaR.x / (3 * N * pow(0.0000650f,4.0f)); // Red (650nm)
vBetaR.y = vBetaR.y / (3 * N * pow(0.0000610f,4.0f)); // Green (610nm)
vBetaR.z = vBetaR.z / (3 * N * pow(0.0000475f,4.0f)); // Blue (475nm)
Marco


Alot of the constants you are using are very small numbers, e.g. (3 * N * pow(0.0000650f,4.0f)) with N = 0.00001f produces a value of around 1.0e-67, which is WAY smaller than a float can represent. If you calculate that you get 0's in your floats. You need to change your units I think!

Share this post


Link to post
Share on other sites
Quote:
Original post by Ysaneya
Atmosphere radius is the same than planet radius !?

Y.


Sorry, this was a constant copied from another piece of code somewhere else that is not used anywhere.

Share this post


Link to post
Share on other sites
when you're setting your effect parameters, you set your sun color to white. this isn't correct. the sun color is white in space but once it reaches our atmosphere it is absorbed by atmospheric particles and out-scattered. try using this method to calculate your sun color.

       Vector3 ComputeAttenuation(float fTheta, int nTurbidity)
{
float fBeta = 0.04608365822050f * nTurbidity - 0.04586025928522f;
float fTauR, fTauA;
float[] fTau = new float[3];
float m = 1.0f / ((float)Math.Cos(fTheta) + 0.15f * (float)Math.Pow(93.885f - fTheta / MathHelper.Pi * 180.0f, -1.253f)); // Relative Optical Mass

int i;
float[] fLambda = new float[3];
fLambda[0] = 0.65f; // red (in um.)
fLambda[1] = 0.57f; // green (in um.)
fLambda[2] = 0.475f; // blue (in um.)

for (i = 0; i < 3; i++)
{
// Rayleigh Scattering
// Results agree with the graph (pg 115, MI) */
// lambda in um.
fTauR = (float)Math.Exp(-m * 0.008735f * (float)Math.Pow(fLambda, -4.08f));

// Aerosal (water + dust) attenuation
// beta - amount of aerosols present
// alpha - ratio of small to large particle sizes. (0:4,usually 1.3)
// Results agree with the graph (pg 121, MI)
const float fAlpha = 1.3f;
fTauA = (float)Math.Exp(-m * fBeta * (float)Math.Pow(fLambda, -fAlpha)); // lambda should be in um

fTau = fTauR * fTauA;
}

return new Vector3(fTau[0], fTau[1], fTau[2]);
}


see how things work out with this little update. also note that i tried setting the sun color to white in my implementation and it didn't give me your results, so something else somewhere must be also broken.

(i got this method from the Hoffman/Preetham scattering demo)

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.

Participate in the game development conversation and more when you create an account on GameDev.net!

Sign me up!