Hello,
I've been trying to make an atmospheric scatter shader with DirectX11 by following an old post on this site:
http://www.gamedev.net/topic/621187-sean-oneils-atmospheric-scattering/
I've had some luck with the sky from space, and I feel like I'm getting closer to getting things right. Though, I'm at a loss as to what I'm doing wrong at the moment.
The rasterizer is setup to draw the surface using back face culling, and the atmosphere using front face culling:
Back Face:
// Setup the rasterizer description
// The rasterizer desc. will determin how and what polygons are drawn
rasterBackCullDesc.AntialiasedLineEnable = false;
rasterBackCullDesc.CullMode = D3D11_CULL_BACK;
rasterBackCullDesc.DepthBias = 0;
rasterBackCullDesc.DepthBiasClamp = 0.0f;
rasterBackCullDesc.DepthClipEnable = true;
rasterBackCullDesc.FillMode = D3D11_FILL_SOLID;
rasterBackCullDesc.FrontCounterClockwise = false;
rasterBackCullDesc.MultisampleEnable = false;
rasterBackCullDesc.ScissorEnable = false;
rasterBackCullDesc.SlopeScaledDepthBias = 0.0f;
// Create the rasterizer state from the desription
result = m_device->CreateRasterizerState(&rasterBackCullDesc, &m_rasterBackCullState);
if(FAILED(result))
{
return false;
}
Front Face:
// Setup the rasterizer description for Front Face Culling
rasterFrontCullDesc.AntialiasedLineEnable = false;
rasterFrontCullDesc.CullMode = D3D11_CULL_FRONT;
rasterFrontCullDesc.DepthBias = 0;
rasterFrontCullDesc.DepthBiasClamp = 0.0f;
rasterFrontCullDesc.DepthClipEnable = true;
rasterFrontCullDesc.FillMode = D3D11_FILL_SOLID;
rasterFrontCullDesc.FrontCounterClockwise = false;
rasterFrontCullDesc.MultisampleEnable = false;
rasterFrontCullDesc.ScissorEnable = false;
rasterFrontCullDesc.SlopeScaledDepthBias = 0.0f;
// Create the rasterizer state from the desription
result = m_device->CreateRasterizerState(&rasterFrontCullDesc, &m_rasterFrontCullState);
if (FAILED(result))
{
return false;
}
My camera's initial position is:
// Initialize the Camera object
m_Camera->SetPosition(0.0f, 0.0f, -40.0f);
My light source's position is (0,0,0).
The direction of the light is setup as:
// Initialize the Light object
m_Light->SetDirection(0.5f, 0.5f, -0.5f);
On each draw frame I compute the data needed for the shader:
// Atmosphere Data
float km = 0.0025f;
float kr = 0.0015f;
float eSun = 15.0f;
D3DXVECTOR3 invWaveLength = D3DXVECTOR3(
(1.0f / pow(0.650f, 4.0f)),
(1.0f / pow(0.570f, 4.0f)),
(1.0f / pow(0.475f, 4.0f)));
float outerRadius = 10.25f;
float outerRadius2 = outerRadius * outerRadius;
float innerRadius = 10.0f;
float innerRadius2 = innerRadius * innerRadius;
float krESun = kr * eSun;
float kmESun = km * eSun;
float kr4Pi = kr * 4.0f * M_PI;
float km4Pi = km * 4.0f * M_PI;
float scaleDepth = 0.25f;
float invScaleDepth = 1.0f / scaleDepth;
float scale = 1.0f / (outerRadius - innerRadius);
float scaleOverScaleDepth = scale / scaleDepth;
D3DXVECTOR3 lightPosition;
D3DXVec3Normalize(&lightPosition, &lightDirection);
D3DXVECTOR3 cameraPosNorm;
D3DXVec3Normalize(&cameraPosNorm, &cameraPosition);
float cameraHeight = D3DXVec3Length(&cameraPosition);
float cameraHeight2 = cameraHeight * cameraHeight;
// Get a pointer to the data in the constant buffer
lightBuffer = (LightBufferType*)mappedResource.pData;
// Copy the lighting variables into the constant buffer
lightBuffer->ambientColor = ambientColor;
lightBuffer->diffuseColor = diffuseColor;
lightBuffer->lightDirection = lightDirection;
lightBuffer->padding = 0.0f;
lightBuffer->padding2 = 0.0f;
lightBuffer->cameraPosition = cameraPosition;
lightBuffer->lightPosition = lightPosition;
lightBuffer->cameraHeight = cameraHeight;
lightBuffer->cameraHeight2 = cameraHeight2;
lightBuffer->invWaveLength = invWaveLength;
lightBuffer->outerRadius = outerRadius;
lightBuffer->outerRadius2 = outerRadius2;
lightBuffer->innerRadius = innerRadius;
lightBuffer->innerRadius2 = innerRadius2;
lightBuffer->krESun = krESun;
lightBuffer->kmESun = kmESun;
lightBuffer->kr4Pi = kr4Pi;
lightBuffer->km4Pi = km4Pi;
lightBuffer->scaleDepth = scaleDepth;
lightBuffer->invScaleDepth = invScaleDepth;
lightBuffer->scale = scale;
lightBuffer->scaleOverScaleDepth = scaleOverScaleDepth;
My shader code for the sky from space shader:
////////////////////////////////////////////////////////////////////////////////
// Filename: atmosphereic.ps
////////////////////////////////////////////////////////////////////////////////
/////////////
// GLOBALS //
/////////////
cbuffer LightBuffer
{
float4 ambientColor;
float4 diffuseColor;
float3 lightDirection;
float padding;
float padding2;
float3 cameraPosition; // The camera's current position
float3 lightPosition; // The direction vector to the light source
float3 invWaveLength; // 1 / pow(wavelength, 4) for the red, green, and blue channels
float cameraHeight; // The camera's current height
float cameraHeight2; // fCameraHeight^2
float outerRadius; // The outer (atmosphere) radius
float outerRadius2; // fOuterRadius^2
float innerRadius; // The inner (planetary) radius
float innerRadius2; // fInnerRadius^2
float krESun; // Kr * ESun
float kmESun; // Km * ESun
float kr4Pi; // Kr * 4 * PI
float km4Pi; // Km * 4 * PI
float scaleDepth; // The scale depth (the altitude at which the average atmospheric density is found)
float invScaleDepth; // 1 / fScaleDepth
float scale; // 1 / (fOuterRadius - fInnerRadius)
float scaleOverScaleDepth; // fScale / fScaleDepth
};
//////////////
// TYPEDEFS //
//////////////
struct PixelInputType
{
float4 position : SV_POSITION;
float2 tex : TEXCOORD0;
float3 positionWS : TEXCOORD1;
float3 normal : NORMAL;
};
//////////////////
// SAMPLE TOTAL //
//////////////////
static const int nSamples = 4;
static const float fSamples = 4.0;
///////////////////////////////
// MIE PHASE ASYMETRY FACTOR //
///////////////////////////////
static const float g = -0.98;
//static const float g2 = g*g;
////////////////////
// LOCALS METHODS //
////////////////////
float getScaleFromCos(float cos)
{
float x = 1.0 - cos;
return scaleDepth * exp(-0.00287 + x * (0.459 + x * (3.83 + x * (-6.80 + x * 5.25))));
}
float getMiePhase(float cos, float cos2, float g, float g2)
{
return 1.5 * ((1.0 - g2) / (2.0 + g2)) * (1.0 + cos2) / pow(abs(1.0 + g2 - 2.0 * g * cos), 1.5);
}
float getRayleighPhase(float cos2)
{
return 0.75 + 0.75 * cos2;
}
float getNearIntersection(float3 position, float3 ray, float distance2, float radius2)
{
float B = 2.0 * dot(position, ray);
float C = distance2 - radius2;
float det = max(0.0, B * B - 4.0 * C);
return 0.5 * (-B - sqrt(det));
}
////////////////////////////////////////////////////////////////////////////////
// Pixel Shader
////////////////////////////////////////////////////////////////////////////////
float4 AtmosphericPixelShader(PixelInputType input) : SV_TARGET
{
// Get the ray from the camera to the vertex and its length
// This is the far point of the ray passing through the atmosphere
float3 position = input.position - input.positionWS;
float3 ray = position - cameraPosition;
position = normalize(position);
float far = length(ray);
ray /= far;
// Calculate the closest intersection of the ray with the outeratmosphere
// Which is the near point of the ray passing through the atmosphere
float near = getNearIntersection(cameraPosition, ray, cameraHeight2, outerRadius2);
// Calculate the ray's starting position, then calculate its scattering offset
float3 start = cameraPosition + ray * near;
far -= near;
float startAngle = dot(ray, start) / outerRadius;
float startDepth = exp(-invScaleDepth);
float startOffset = startDepth * getScaleFromCos(startAngle);
// Initialize the scattering loop variables
float sampleLength = far / fSamples;
float scaledLength = sampleLength * scale;
float3 sampleRay = ray * sampleLength;
float3 samplePoint = start + sampleRay * 0.5;
// Now loop through the sample rays
float3 frontColor = float3(0.0, 0.0, 0.0);
float3 attenuate;
for(int i = 0; i < nSamples; i++)
{
float height = length(samplePoint);
float depth = exp(scaleOverScaleDepth * (innerRadius - height));
float lightAngle = dot(lightPosition, samplePoint) / height;
float cameraAngle = dot(ray, samplePoint) / height;
float scatter = (startOffset + depth * (getScaleFromCos(lightAngle) - getScaleFromCos(cameraAngle)));
attenuate = exp(-scatter * (invWaveLength * kr4Pi + km4Pi));
frontColor += attenuate * (depth * scaledLength);
samplePoint += sampleRay;
}
float3 c0 = frontColor * (invWaveLength * krESun);
float3 c1 = frontColor * kmESun;
float3 direction = cameraPosition - position;
float cos = dot(lightPosition, direction) / length(direction);
float cos2 = cos * cos;
float3 color = getRayleighPhase(cos2) * c0 + getMiePhase(cos, cos2, g, g*g) * c1;
return float4(color, color.b);
}
Here is a photo from the default camera position:
[attachment=18963:atmoshpereFromSpace_01.png]
Here is another one as I just more closer to the planet.
[attachment=18964:atmoshpereFromSpace_02.png]
Another photo from a closer angle and a little above the default position:
[attachment=18965:atmoshpereFromSpace_03.png]
I'm also trying to a ground shader with little luck, but that is next.
It feels like I have my camera or light position/direction wrong. I can see a little of the effect in the atmosphere, but the data I'm giving it might be wrong. If anyone can see where I've gone wrong or has some advice, it would be greatly appreciated.
Thanks.