Nielsen Atmospheric Scattering

Started by
15 comments, last by Formski 16 years, 9 months ago
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
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)
Err..um..yeah. Thanks for that!
Unfortunately it doesn't change the end result.

Marco
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?
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 airfloat N = 0.00001f;		// Molecular DensityvBetaR.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
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 skydomematrix 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}; // Constantsstruct 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
Atmosphere radius is the same than planet radius !?

Y.
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!
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.
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)

This topic is closed to new replies.

Advertisement