Skip to main content
GameDev.net gamedev.net
🔒 Locked 🎮 Unity

Atmospheric Scattering (Sean O'Neill - GPU Gems2)

Started by Formski Aug 27, 2007 at 1:31 AM 39 replies 37.9k views
Original Post
Formski
Formski
Hi, I'm currently trying to implement Sean O'Neill's atmospheric scattering algorithm from GPU Gems 2 after having tried the Preetham method and not liking the result much. My system is a terrain with a skybox about 150000 units in radius always centred on the camera. I am trying to modify his SkyFromAtmosphere shader to HLSL under DirectX 9.0. I am aiming for a large viewing distance. The following is the effect file code:

float4x4 WorldViewProj;

float3 v3LightDir;		// Light direction
float3 v3CameraPos;		// Camera's current position
float3 v3InvWavelength;	// 1 / pow(wavelength, 4) for RGB channels

float fCameraHeight;
float fCameraHeight2;
float fInnerRadius;
float fInnerRadius2;
float fOuterRadius;
float fOuterRadius2;

// Scattering parameters
float KrESun;			// Kr * ESun
float KmESun;			// Km * ESun
float Kr4PI;			// Kr * 4 * PI
float Km4PI;			// Km * 4 * PI

// Phase function
float g;
float g2;

float fScale;			// 1 / (outerRadius - innerRadius) = 4 here
float fScaleDepth;		// Where the average atmosphere density is found
float fScaleOverScaleDepth;	// scale / scaleDepth
float fSkydomeRadius;	// Skydome radius (allows us to normalize skydome distances etc)

int numSamples;
float samples;

// Application to vertex structure
struct a2v
{
	float4 Position : POSITION0;
};

// Vertex to pixel shader structure
struct v2p
{
	float4 Position			: POSITION0;
	float4 RayleighColor	: COLOR0;
	float4 MieColor			: COLOR1;
	float3 Direction		: TEXCOORD0;
};

float scale(float cos)
{
	float x = 1.0 - cos;
	return fScaleDepth * exp(-0.00287 + x*(0.459 + x*(3.83 + x*(-6.80 + x*5.25))));
}

void RenderSkyVS(in a2v IN, out v2p OUT)
{
	// Transform to clipspace
	OUT.Position = mul(IN.Position, WorldViewProj);
	
	// Get the ray from the camera to the vertex, and it's length (far point)
	float3 v3Pos = IN.Position / fSkydomeRadius + fInnerRadius; 
	float3 v3Ray = v3Pos - v3CameraPos;
	float fFar = length(v3Ray);
	v3Ray /= fFar;
	
	// Calculate the ray's starting position, then calculate its scattering offset
	float3 v3Start = v3CameraPos;
	float fHeight = length(v3Start);
	float fDepth = exp(fScaleOverScaleDepth * (fInnerRadius - fCameraHeight));
	float fStartAngle = dot(v3Ray, v3Start) / fHeight;
	float fStartOffset = fDepth * scale(fStartAngle);
			
	// Init loop variables
	float fSampleLength = fFar / samples;
	float fScaledLength = fSampleLength * fScale;
	float3 v3SampleRay = v3Ray * fSampleLength;
	float3 v3SamplePoint = v3Start + v3SampleRay * 0.5f;
	
	// Loop the ray
	float3 color;
	for (int i = 0; i < numSamples; i++)
	{
		float fHeight = length(v3SamplePoint);
		float fDepth = exp(fScaleOverScaleDepth * (fInnerRadius-fHeight));
		
		float fLightAngle = dot(v3LightDir, v3SamplePoint) / fHeight;
		float fCameraAngle = dot(v3Ray, v3SamplePoint) / fHeight;
		
		float fScatter = (fStartOffset + fDepth*(scale(fLightAngle) - scale(fCameraAngle)));
		float3 v3Attenuate = exp(-fScatter * (v3InvWavelength * Kr4PI + Km4PI));
		
		// Accumulate color
		v3Attenuate *= (fDepth * fScaledLength);
		color += v3Attenuate;
		
		// Next sample point
		v3SamplePoint += v3SampleRay;
	}
	
	// Finally, scale the Mie and Rayleigh colors
	OUT.RayleighColor.xyz = color * (v3InvWavelength * KrESun);
	OUT.RayleighColor.w = 1.0f;
	
	OUT.MieColor.xyz = color * KmESun;
	OUT.MieColor.w = 1.0f;

	OUT.Direction = v3CameraPos - v3Pos;
}

float4 RenderSkyPS(in v2p IN) : COLOR0
{
	float cos = dot(v3LightDir, IN.Direction) / length(IN.Direction);
	
	//float rayleighPhase = 0.75f * (1.0f + cos*cos);
	float miePhase = 1.5f * ((1.0f - g2) / (2.0f + g2)) *
					 (1.0f + cos*cos) / pow(1.0f + g2 - 2.0f * g * cos, 1.5f);
	
	//return rayleighPhase * IN.RayleighColor + miePhase * IN.MieColor;
	return IN.RayleighColor + miePhase * IN.MieColor;
}

technique RenderSky
{
	pass p0
	{	
		VertexShader = compile vs_2_0 RenderSkyVS();
		PixelShader = compile ps_2_0 RenderSkyPS();
		ZWriteEnable = 0;
	}	
}



and the following is the setup code

D3DXVECTOR4 vecCamera = *objRenderer->GetCameraPos();
	D3DXMATRIX matWVP;

	D3DXVECTOR3 vecPos;
	D3DXQUATERNION quatRotate;
	D3DXVECTOR3 vecScale;
	D3DXMatrixDecompose(&vecScale, &quatRotate, &vecPos, objRenderer->GetViewMatrix());
	D3DXMatrixRotationQuaternion(&matWVP, &quatRotate);
	D3DXMatrixMultiply(&matWVP, &matWVP, objRenderer->GetSkydomeProjectionMatrix());

	D3DXVECTOR4 vSunDir = m_pAtmosphere->GetDirection();
	vSunDir.w = 1.0;

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

	float g = m_pAtmosphere->GetHeyseyG();
	
	D3DXVECTOR4 vSunColourIntensity = m_pAtmosphere->GetColorAndIntensity();
	
	D3DXVECTOR4 vInvWavelength;
	float m_fWavelength[3];
	float m_fWavelength4[3];
	m_fWavelength[0] = 0.650f;//650e-9f;		// 650 nm for red
	m_fWavelength[1] = 0.570f;//570e-9f;		// 570 nm for green
	m_fWavelength[2] = 0.475f;//475e-9f;		// 475 nm for blue
	m_fWavelength4[0] = powf(m_fWavelength[0], 4.0f);
	m_fWavelength4[1] = powf(m_fWavelength[1], 4.0f);
	m_fWavelength4[2] = powf(m_fWavelength[2], 4.0f);
	vInvWavelength.x = 1.0f / m_fWavelength4[0];
	vInvWavelength.y = 1.0f / m_fWavelength4[1];
	vInvWavelength.z = 1.0f / m_fWavelength4[2];

	float fInnerRadius = 10.0f;
	float fOuterRadius = 10.25f;
	
	float fScale = 1 / (fOuterRadius - fInnerRadius);
	float fScaleDepth = 0.0125f;//0.25f;//(fOuterRadius - fInnerRadius) / 2.0f;
	float fScaleOverScaleDepth = 16.0f;//fScale / fScaleDepth;
	
	vecCamera /= fSkydomeRadius;
	// Gets sun when commented but loses everything else
	vecCamera.y += fInnerRadius;
	if (vecCamera.y <= fInnerRadius) 
		vecCamera.y = fInnerRadius + 1.0e-6f;
	vecCamera.x = 0;
	vecCamera.z = 0;

	float fCameraHeight = vecCamera.y;	

	float fkr4PI = fRayMult * 4.0f * PI;
	float fkm4PI = fMieMult * 4.0f * PI;
	float fKrESun = fRayMult * vSunColourIntensity.w;
	float fKmESun = fMieMult * vSunColourIntensity.w;

	pSkydomeScatterFX->SetMatrix(m_pWorldViewProj, &matWVP);
	pSkydomeScatterFX->SetVector(m_pv3LightDir, &vSunDir);
	pSkydomeScatterFX->SetVector(m_pv3CameraPos, &vecCamera);
	pSkydomeScatterFX->SetVector(m_pv3InvWavelength, &vInvWavelength);
	pSkydomeScatterFX->SetFloat(m_pfCameraHeight,fCameraHeight);
	pSkydomeScatterFX->SetFloat(m_pfCameraHeight2,fCameraHeight*fCameraHeight);
	pSkydomeScatterFX->SetFloat(m_pfInnerRadius, fInnerRadius);
	pSkydomeScatterFX->SetFloat(m_pfInnerRadius2, fInnerRadius * fInnerRadius);
	pSkydomeScatterFX->SetFloat(m_pfOuterRadius, fOuterRadius);
	pSkydomeScatterFX->SetFloat(m_pfOuterRadius2, fOuterRadius * fOuterRadius);

	pSkydomeScatterFX->SetFloat(m_pKrESun,fKrESun);
	pSkydomeScatterFX->SetFloat(m_pKmESun,fKmESun);
	pSkydomeScatterFX->SetFloat(m_pKr4PI, fkr4PI);
	pSkydomeScatterFX->SetFloat(m_pKm4PI, fkm4PI);
	pSkydomeScatterFX->SetFloat(m_pg, g);
	pSkydomeScatterFX->SetFloat(m_pg2, g*g);
	pSkydomeScatterFX->SetFloat(m_pfScale, fScale);
	pSkydomeScatterFX->SetFloat(m_pScaleDepth, fScaleDepth);
	pSkydomeScatterFX->SetFloat(m_pScaleOverScaleDepth, fScaleOverScaleDepth);
	pSkydomeScatterFX->SetFloat(m_pfSkydomeRadius, fSkydomeRadius);
	pSkydomeScatterFX->SetInt(m_pnumSamples, iNumSamples);
	pSkydomeScatterFX->SetInt(m_psamples, iNumSamples);


The problem with the above is that there is no sun visible. The skydome colour seems correct (i.e. blue when the sun light direction is above the horizon then as it drops below it goes orange/yellow/red then black) but there is no sun disc visible. Also dropping InnerRadius to 1.0 doesn't help things, and converting the skydome to a unit sized skydome doesn't help either. The interesting thing is that when I set InnerRadius and OuterRadius to 0 and pass that to the shader then there is a sun! However the skydome colouring is not correct as sun(theta) gets to about 160 degrees (i.e. 50 degrees) below the horizon before the sky kind of 'follows it' down (the blue shading kind of drops to the horizon) The other thing I have noticed is that the number of samples has a major effect on the result. As the number of samples increases to about 8 or 9 the sky is brightening. Once it goes above this figure though the sky brightness holds constant, although the FPS drops significantly Thanks in advance, Formski
Formski
Formski
Never mind - I got it!

The line in the vertex shader:
float3 v3Pos = IN.Position / fSkydomeRadius + fInnerRadius; 

should have been:
float3 v3Pos = IN.Position / fSkydomeRadius v3Pos.y += fInnerRadius; 

Oops!

Formski
FoxHunter2
FoxHunter2
What values do you use for:

- fSkydomeRadius (is innerradius and outerradius independent of this value)?
- g
- numSamples
- raymult/miemult
- vecCamera = Camera Position?

Is the sun direction and sun intensity code the same as in Preethams code?

Did you change anything else from the code you posted?

It sort of works for me (i don't get an entirely black/white dome), but the results are still way from being correct.

Screen 1 is the default status, screen 2 is up to where the dome changes, it then quickly drops back to screen 1 after this state.

thanks so far

[Edited by - FoxHunter2 on September 3, 2007 5:44:28 AM]
Formski
Formski
for fSkydomeRadius I use the radius of the skydome (obviously!) - what I have is a skydome bounded by (-1,-1,-1) -> (1,1,1) multiplied by fSkydomeRadius.

As for the others:

InnerRadius = 10
OuterRadius = 10 * 1.025 (i.e. 102.5% of the Inner Radius because the scale function in the shader requires this)
g = -0.991
numSamples = 2 for starters
raymult/miemult = 0.0015 & 0.0025 respectively
vecCamera = X=0,Z=0,Y=height / fSkydomeRadius + InnerRadius

It's not perfect because the upper part of the skydome is too dark and the sun shrinks and disappears before getting to the zenith (I think it's either a density problem with the Rayleigh side of things or the coordinates of the camera/vertex are not aligned properly)

Straight ahead:


Looking up abit:


Btw. I'm not currently doing any scattering on the terrain so ignore it.

Formski
AvengerDr
AvengerDr
Can this shader also be used to render a planet's atmosphere, as viewed from "outer space"? If not, what changes would have to be made?

Thanks in advance!
Formski
Formski
This shader is a HLSL version of a shader that came from the GPU Gems 2 book's CD. It was originally written by Sean O'Neill and is a modification of his method from the article here, removing the lookup tables entirely.

The CD had on it various other shaders, one of which covered Atmosphere from space.

Formski
Ashkan
Ashkan
Quote:
Can this shader also be used to render a planet's atmosphere, as viewed from "outer space"? If not, what changes would have to be made?


Yes, it can. Many atmospheric scattering models assume that the camera is always on or very close to the ground (such as Hoffman and Pretham's method). O'Neil's implementation doesn't.
AvengerDr
AvengerDr
And what modifications to that source code would have to be made in order for it to render the atmosphere from space? Also, to what exactly should it be applied to? Should the atmosphere be applied to a sphere slightly larger than the one having the diffuse texture (sto to speak) or should it be applied to the same one holding the diffuse texture?
Trurl
Trurl
Glancing at Formski's screenshots, it appears he's falling victim to a subtle flaw in O'Neil's implementation on NVIDIA. There are odd color artifacts near the sun. This is due to the fact that NVIDIA clamps the vertex output color and secondary color to 1.0, however it does not clamp explicitly declared varying variables. So, you need to transmit the attenuation and in-scattering coefficients via some varying other than the builting color varyings. This will solve the color artifact problem.
FoxHunter2
FoxHunter2
@Formski: thanks, this renders a very nice sun (albeit it suffers from the same artifacts Trurls mentioned).
But the sky still looks like in my first screenshot, which means it's completely black with the horizon being a white/orange gradient.

@Trurl:
can you explain what needs to be changed in the shader to fix the artifacts?
Formski
Formski
Thanks Trurl - even though I'm using an ATI chipset (Mobility Radeon 9700 with 64MB RAM) it probably does the same thing (which makes sense too) I'd noticed the colours at various attitude but hadn't looked into it yet.

I changed the Rayleigh and Mie COLOR variables to TEXCOORDs instead (TEXCOORD worked with float4s which was nice!)

Current view:




I changed my skydome generation too. Originally I had a half sphereskydome that was about 200000 units radius (I used half the clipspace)

What I have now is a skydome with a radius that is the Earth's radius in metres (6.378e6) + 200000 for the atmosphere thickness. I also have dropped the centre of the skydome 6378e6 in the Y direction so that the top of the skydome is 200000 units above the terrain.

This seems to have helped with the blueness above although I'm not 100% certain I have everything setup correctly.

Foxhunter2 (OT) where did you get your terrain's textures from? I'm using the NVidia texture pack textures and they need serious work on them, whereas your Geomipmap demo textures look nice.
s_p_oneil
s_p_oneil
Wow. I haven't visited GameDev in quite a while, and when I come back I just happen to see a thread about my scattering algorithm on the front page (talk about a coincidence).

AvengerDr:
Yes, you can use it to render the atmosphere from space. Go to http://sponeil.net to download the source for everything. If you can't get your hands on GPU Gems 2, send me an email and I'll see if I can find the article on it. (Gamedev has my article on how to do it on the CPU, which also explains the basic idea of the algorithm. You should read both for a better understanding.)

FoxHunter2:
A number of the parameters you pass to it need to be within fairly strict ranges. It looks like some parameters you have are not within the necessary ranges. (The GPU Gems 2 article explains more about why it's so strict.)

FoxHunter2 and Formski:
nVidia cards clip the primary and secondary colors to the 0-1 range between the vertex shader and fragment shader. This causes some nasty artifacts. To fix it, change the shaders to pass those values in some other variable.

Everyone:
This algorithm was really designed for HDR rendering, which uses an exponential exposure function to scale the colors down to the 0-1 range. The screenshots (like the ones Formski posted) look much worse without it. If you look at the screenshots on my home page, I think you'll agree they look much better, even with the nVidia artifacts.

One more thing:
My project hasn't been updated in a while, and the shaders were written back in the GeForce FX days. There are better ways to do a number of things now, from doing texture lookups in the vertex shader to get the lookup table back (much more accurate), to performing the exposure function in the scattering shader without having to render to a float p-buffer and using an extra pass.
FoxHunter2
FoxHunter2
Thanks, changing the COLOR to TEXCOORDs removed the artifacts of the sun.
I'm now using the same values as Formski, but this is still the result I get:


I tried various multipliers, they all change the size of the sun and height of the horizon, but the sky color remains the same like in the screenshot. I wonder what values are responsible for setting the correct sky color.

float fInnerRadius = 10.0f;float fOuterRadius = 10.25f;   float fScale = 1 / (fOuterRadius - fInnerRadius);float fScaleDepth = 0.0125f;            //0.25f;//(fOuterRadius - fInnerRadius) / 2.0f;float fScaleOverScaleDepth = 16.0f;     //fScale / fScaleDepth;//      float fScaleOverScaleDepth = fScale / fScaleDepth;float fRayMult = 0.002f;float fMieMult = 0.0015f;float fkr4PI = fRayMult * 4.0f * (float)Math.PI;float fkm4PI = fMieMult * 4.0f * (float)Math.PI;float fKrESun = fRayMult * sun.GetIntensity();float fKmESun = fMieMult * sun.GetIntensity();float g = -0.991f;//      float fSkydomeRadius = 1500;float fSkydomeRadius = (6.378e6f) + 200000;int iNumSamples = 4;Vector3 vecCamera = camera.Position;vecCamera /= fSkydomeRadius;vecCamera.Y += fInnerRadius;vecCamera.X = 0;vecCamera.Z = 0;


@Formski: I use the textures from the Riemers tutorials, you can find them here:
http://www.riemers.net/eng/Tutorials/XNA/Csharp/series4.php
But they're not perfect, I will exchange some of them, because I don't like them.

regards

[Edited by - FoxHunter2 on September 3, 2007 5:06:08 AM]
Ashkan
Ashkan
@s_p_oneil:
Big thumbs-up for Sean. Are you planning to improve on the current technique. Any new projects you're working on?

By the way, I get a connection time-out error while trying to reach your site. Anybody else experiencing the same problem?
s_p_oneil
s_p_oneil
Quote:
Original post by Ashkan
@s_p_oneil:
Big thumbs-up for Sean. Are you planning to improve on the current technique. Any new projects you're working on?

By the way, I get a connection time-out error while trying to reach your site. Anybody else experiencing the same problem?


Thanks, and try sponeil.net (not sponeil.org). My previous ISP screwed up and gave sponeil.org to some squatter.

I haven't done any 3D graphics work since I published the GPU Gems article and the 4th GamaSutra article. I've been too busy, too tired, and no one has been willing to pay me to work part-time on it. ;-)

My only current game/graphics project is that I'm writing a Ruby extension for the SFML library (sfml.sourceforge.net). I'm trying to get a nice and simple game library in a simple programming language I like so I can teach my son the basics of programming (he's 8).

SFML uses OpenGL, so I might eventually add some 3D classes to it and then play around with some 3D algorithms in Ruby (for faster prototyping with cleaner code). I'm hoping to find another part-time contract, though. I need money to keep the kids in Montessori school. ;-)
Formski
Formski
From what I understand the 'blueness' of the sky is caused by the Rayleigh scattering, which from what I see in your code should work fine. I would check your shader to ensure that it is receiving all the parameters correctly - is your v3InvWavelength parameter set correctly?

I've added in two little tweaks that look nice (IMHO) - I've run the colour through a 1-exp(Exposure * finalcolour) filter in the pixel shader as Sean suggested, and also reintroduced the Rayleigh phase function. I modified it slightly to ' 0.75 * (2.0 + 0.5 * cos*cos)' but will probably keep playing with it further.

Results below:







Also thanks for the link Foxhunter - now to play more with the terrain side of things.

With water - can you simply apply the scattering to the water in the same way as the terrain after doing the water calcs?
s_p_oneil
s_p_oneil
Looks good, Formski. The biggest problem left will be how sunrise/sunset looks from space. If you set the camera on the ground with the sun at the horizon, and then back the camera into space (keeping the sun at the horizon), the sunset should get redder as the sun is going through more atmosphere, but instead it turns bright blue again.

In the CPU demo, it gets redder like it's supposed to, but the lack of a pixel shader for the phase function makes it look bad. I believe this problem is due to the inaccuracies in the function that's replacing the lookup table. The higher the angle gets, the worse the accuracy is. However, I had to cut a few corners to get the shader code to fit in the GeForce FX and the Radeon 9600, so something there may be causing it as well. ;-)
FoxHunter2
FoxHunter2
Quote:
Original post by Formski
With water - can you simply apply the scattering to the water in the same way as the terrain after doing the water calcs?


I haven't tried it yet, but basically it should work fine with any surface, since you acutally only add the inscatter and extinction terms to the original color.
Hello Kitty
Hello Kitty
Is the skydome sphere geometry supposed to be centered around the camera, or centered around the planet?

Formski, can you post your entire source code?

can this method have scattering on the terrain?

thanks

-goodbye-
Formski
Formski
This is my shader:
float4x4 WorldViewProj;float3 v3LightDir;		// Light directionfloat3 v3CameraPos;		// Camera's current positionfloat3 v3InvWavelength;	// 1 / pow(wavelength, 4) for RGB channelsfloat fCameraHeight;float fCameraHeight2;float fInnerRadius;float fInnerRadius2;float fOuterRadius;float fOuterRadius2;// Scattering parametersfloat KrESun;			// Kr * ESunfloat KmESun;			// Km * ESunfloat Kr4PI;			// Kr * 4 * PIfloat Km4PI;			// Km * 4 * PI// Phase functionfloat g;float g2;float fScale;			// 1 / (outerRadius - innerRadius) = 4 herefloat fScaleDepth;		// Where the average atmosphere density is foundfloat fScaleOverScaleDepth;	// scale / scaleDepthfloat fSkydomeRadius;	// Skydome radius (allows us to normalize skydome distances etc)float fExposure;	// Exposure parameter for pixel shaderint numSamples;float samples;// Application to vertex structurestruct a2v{	float4 Position : POSITION0;};// Vertex to pixel shader structurestruct v2p{	float4 Position			: POSITION0;	float3 Direction		: TEXCOORD0;	float4 RayleighColor    : TEXCOORD1;	float4 MieColor			: TEXCOORD2;	//float4 RayleighColor	: COLOR;	//float4 MieColor		: COLOR;	};float scale(float cos){	float x = 1.0 - cos;	return fScaleDepth * exp(-0.00287 + x*(0.459 + x*(3.83 + x*(-6.80 + x*5.25))));}void RenderSkyVS(in a2v IN, out v2p OUT){	// Transform to clipspace	OUT.Position = mul(IN.Position, WorldViewProj);		// Get the ray from the camera to the vertex, and it's length (far point)	float3 v3Pos = IN.Position / fSkydomeRadius;	//v3Pos.y += 1;	v3Pos.y += fInnerRadius;	float3 v3Ray = v3Pos - v3CameraPos;	float fFar = length(v3Ray);	v3Ray /= fFar;		// Calculate the ray's starting position, then calculate its scattering offset	float3 v3Start = v3CameraPos;	float fHeight = length(v3Start);	float fDepth = exp(fScaleOverScaleDepth * (fInnerRadius - fCameraHeight));	float fStartAngle = dot(v3Ray, v3Start) / fHeight;	float fStartOffset = fDepth * scale(fStartAngle);				// Init loop variables	float fSampleLength = fFar / samples;	float fScaledLength = fSampleLength * fScale;	float3 v3SampleRay = v3Ray * fSampleLength;	float3 v3SamplePoint = v3Start + v3SampleRay * 0.5f;		// Loop the ray	float3 color;	for (int i = 0; i < numSamples; i++)	{		float fHeight = length(v3SamplePoint);		float fDepth = exp(fScaleOverScaleDepth * (fInnerRadius-fHeight));				float fLightAngle = dot(v3LightDir, v3SamplePoint) / fHeight;		float fCameraAngle = dot(v3Ray, v3SamplePoint) / fHeight;				float fScatter = (fStartOffset + fDepth*(scale(fLightAngle) - scale(fCameraAngle)));		float3 v3Attenuate = exp(-fScatter * (v3InvWavelength * Kr4PI + Km4PI));				// Accumulate color		v3Attenuate *= (fDepth * fScaledLength);		color += v3Attenuate;				// Next sample point		v3SamplePoint += v3SampleRay;	}		// Finally, scale the Mie and Rayleigh colors	OUT.RayleighColor.xyz = color * (v3InvWavelength * KrESun);	OUT.RayleighColor.w = 1.0f;	OUT.MieColor.xyz = color * KmESun;	OUT.MieColor.w = 1.0f;	OUT.Direction = v3CameraPos - v3Pos;}float4 RenderSkyPS(in v2p IN) : COLOR0{	float cos = dot(v3LightDir, IN.Direction) / length(IN.Direction);		//float rayleighPhase = 0.75 * (1.0 + cos*cos);	float rayleighPhase = 0.75 * (2.0 + 0.5 * cos*cos);		float miePhase = 1.5f * ((1.0f - g2) / (2.0f + g2)) *					 (1.0f + cos*cos) / pow(1.0f + g2 - 2.0f * g * cos, 1.5f);	// exposure => 1.0 - exp(-fExposure * color)	return 1 - exp(-fExposure * (rayleighPhase * IN.RayleighColor + miePhase * IN.MieColor));}technique RenderSky{	pass p0	{			VertexShader = compile vs_2_0 RenderSkyVS();		PixelShader = compile ps_2_0 RenderSkyPS();		ZWriteEnable = 0;	}	}

my parameters are set like this:
D3DXVECTOR4 vecCamera = *objRenderer->GetCameraPos();	D3DXMATRIX matWVP;	D3DXMatrixTranslation(&matWVP, -vecCamera.x, 0, -vecCamera.z);	D3DXMatrixMultiply(&matWVP, &matWVP, objRenderer->GetViewMatrix());	D3DXMatrixMultiply(&matWVP, &matWVP, objRenderer->GetProjectionMatrix());	// Scattering Sky Parameters	D3DXVECTOR4 vSunDir = m_pAtmosphere->GetDirection();	D3DXVECTOR4 vSunColourIntensity = m_pAtmosphere->GetColorAndIntensity();	float fInnerRadius = m_pAtmosphere->GetInnerRadius();	float fOuterRadius = m_pAtmosphere->GetOuterRadius();	float fScale = m_pAtmosphere->GetScale();	float fScaleDepth = m_pAtmosphere->GetScaleDepth();	float fScaleOverScaleDepth = m_pAtmosphere->GetScaleOverScaleDepth();	float g = m_pAtmosphere->GetHeyseyG();	float fkr4PI = m_pAtmosphere->GetKr4PI();	float fkm4PI = m_pAtmosphere->GetKm4PI();	float fKrESun = m_pAtmosphere->GetKrEsun();	float fKmESun = m_pAtmosphere->GetKmEsun();		vecCamera.x = 0;	vecCamera.z = 0;	vecCamera.y /= fSkydomeRadius;		// Set to unit scale	vecCamera.y *= fInnerRadius;		// Set to scale being used	vecCamera.y += fInnerRadius;		// Scale based on the actual Inner Radius of Earth	pSkydomeScatterFX->SetMatrix(m_pWorldViewProj, &matWVP);	pSkydomeScatterFX->SetVector(m_pv3LightDir, &vSunDir);	pSkydomeScatterFX->SetVector(m_pv3CameraPos, &vecCamera);	pSkydomeScatterFX->SetVector(m_pv3InvWavelength, m_pAtmosphere->GetInvWavelength());	pSkydomeScatterFX->SetFloat(m_pfCameraHeight,vecCamera.y);	pSkydomeScatterFX->SetFloat(m_pfCameraHeight2,vecCamera.y*vecCamera.y);	pSkydomeScatterFX->SetFloat(m_pfInnerRadius, fInnerRadius);	pSkydomeScatterFX->SetFloat(m_pfInnerRadius2, fInnerRadius * fInnerRadius);	pSkydomeScatterFX->SetFloat(m_pfOuterRadius, fOuterRadius);	pSkydomeScatterFX->SetFloat(m_pfOuterRadius2, fOuterRadius * fOuterRadius);	pSkydomeScatterFX->SetFloat(m_pKrESun,fKrESun);	pSkydomeScatterFX->SetFloat(m_pKmESun,fKmESun);	pSkydomeScatterFX->SetFloat(m_pKr4PI, fkr4PI);	pSkydomeScatterFX->SetFloat(m_pKm4PI, fkm4PI);	pSkydomeScatterFX->SetFloat(m_pg, g);	pSkydomeScatterFX->SetFloat(m_pg2, g*g);	pSkydomeScatterFX->SetFloat(m_pfScale, fScale);	pSkydomeScatterFX->SetFloat(m_pScaleDepth, fScaleDepth);	pSkydomeScatterFX->SetFloat(m_pScaleOverScaleDepth, fScaleOverScaleDepth);	pSkydomeScatterFX->SetFloat(m_pfSkydomeRadius, fSkydomeRadius);	pSkydomeScatterFX->SetInt(m_pnumSamples, iNumSamples);	pSkydomeScatterFX->SetInt(m_psamples, iNumSamples);	pSkydomeScatterFX->SetFloat(m_pfExposure, fExposure);

and finally the skydome generation code:
if (FAILED(objRenderer->GetD3Device()->CreateVertexDeclaration(skydomevertex_decl, &objSkydomeVertexDecl)))		return ;	void *ptrD3DBuffer;	// Marco - skydome generator that only creates what is necessary	// Radius = PlanetRadius + 200000	// Y offset is PlanetRadius	// Don't need anything below the horizon (i.e. Y < 0)	int nRings = 160;	int nSegments = 20;	// set vertex count and index count 	DWORD dwVertices = ( nRings + 1 ) * ( nSegments + 1 ) ;	DWORD dwIndices = 2 * nRings * ( nSegments + 1 ) ;	// Create the vertex buffer	if (FAILED(objRenderer->GetD3Device()->CreateVertexBuffer(iVertSize * dwVertices,		D3DUSAGE_WRITEONLY, 0, D3DPOOL_MANAGED , &l_pSkydomeVertices, NULL)))	{		return;	}	// Create the index buffer	if (FAILED(objRenderer->GetD3Device()->CreateIndexBuffer(dwIndices * sizeof(WORD),		0, D3DFMT_INDEX16, D3DPOOL_MANAGED, &l_pSkydomeIndices, NULL)))	{		return;	}	// Lock the vertex buffer	if (FAILED(l_pSkydomeVertices->Lock(0, 0, &ptrD3DBuffer, 0)))	{		return;	}	VertexSkydome* pVertex= (VertexSkydome*)ptrD3DBuffer;	// lock the index buffer 	if (FAILED(l_pSkydomeIndices->Lock(0, 0, &ptrD3DBuffer, 0)))		return;	WORD * pIndices = (WORD*)ptrD3DBuffer;	// Establish constants used in sphere generation		float fDeltaRingAngle = ( D3DX_PI / nRings );	float fDeltaSegAngle = ( 2.0f * D3DX_PI / nSegments );	WORD wVerticeIndex = 0 ; 	// Generate the group of rings for the sphere	for( int ring = 0; ring < nRings + 1 ; ring++ )	{		float r0 = sinf ( ring * fDeltaRingAngle );		float y0 = cosf ( ring * fDeltaRingAngle );		// Generate the group of segments for the current ring		for( int seg = 0; seg < nSegments + 1 ; seg++ )		{			float x0 = r0 * sinf( seg * fDeltaSegAngle );			float z0 = r0 * cosf( seg * fDeltaSegAngle );			// Add one vertices to the strip which makes up the sphere			pVertex->posX = x0 * fSkydomeRadius;			pVertex->posY = y0 * fSkydomeRadius - fSkydomeYAdjust;			pVertex->posZ = z0 * fSkydomeRadius;			pVertex ++;			// add two indices except for last ring 			if ( ring != nRings ) 			{				* pIndices = wVerticeIndex + ( WORD ) ( nSegments + 1 ) ; 				pIndices ++ ;				* pIndices = wVerticeIndex ; 				pIndices ++ ;				wVerticeIndex ++ ; 			} ; 		} ; // end for seg 	} // end for ring 	iNumSkydomeVerts = dwVertices;	l_pSkydomeVertices->Unlock();	iSkydomePrimitives = wVerticeIndex / 2;	l_pSkydomeIndices->Unlock();


It's pretty messy and could use a tidy up, but it works. The skydome needs to be planet radius which is why I do what I do.

The scattering can be used on terrain although I haven't yet done it. The ground shaders from the book should work for that.

Formski

Topic Locked

This topic has been locked by a moderator. New replies are not allowed.

Sign in to reply to this topic.