Original Post
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: and the following is the setup code 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
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;
}
}
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);






