This code/shader simulates atmospheric scattering and renders the sun into the sky.

Because it's a bit tricky to set up everything right, I made a test level where you can directly see the results in realtime (and play with sun angles)

Download testlevel and source files:
http://www.stonehill-games.de/downloads/atmospheric_scattering_A8.rar

If you want to get real sun positions, use this script:
http://www.opserver.de/ubb7/ubbthreads.php?ubb=showflat&Number=434181

Shader code is taken from Sean O'Neil's GPU Gems 2:
http://http.developer.nvidia.com/GPUGems2/gpugems2_chapter16.html




EDIT: for those who want to set up the code by themselfs. Here you go:

1. create s centered sphere "sky_sphere.mdl" in MED with radius = 1 quant with inverted normals.

2. create the "atmospheric_scattering.fx" file:
Code:
#define PI 3.14159f

#define m_hdrExposure 1.25f

// Difference between inner and ounter radius. Must be 2.5%
#define m_outerScaleFactor 1.025f	

// Wave length of sun light
#define m_waveLength float3(0.65f, 0.57f, 0.475f) 

// Sun brightness constant
#define m_ESun 20.0f 			

// Rayleigh scattering constant
#define m_kr 0.0035f 	

// Mie scattering constant		
#define m_km 0.0010f 			

// The Mie phase asymmetry factor, must be between 0.999 to -0.999
#define m_g -0.99f			

// The scale depth (i.e. the altitude at which the atmosphere's average density is found)
#define m_scaleDepth 0.25f 	

#define radius vecSkill1.x


float4x4 matWorldViewProj;
float4x4 matWorld;
float4 vecViewPos;
float4 vecSunDir;
float4 vecSkill1;

float3 v3InvWavelength;
float fOuterRadius;				// The outer (planetary) radius
float fOuterRadius2;				// fOuterRadius^2
float fInnerRadius;				// The inner (planetary) radius
float fInnerRadius2;				// fInnerRadius^2
float fKrESun;						// Kr * ESun
float fKmESun;						// Km * ESun
float fKr4PI;						// Kr * 4 * PI
float fKm4PI;						// Km * 4 * PI
float fScale;						// 1 / (fOuterRadius - fInnerRadius)
float fScaleOverScaleDepth;	// fScale / fScaleDepth
float fHdrExposure;				// HDR exposure

struct v2f 
{
	float4 pos : POSITION;
	float2 uv : TEXCOORD0;
	float3 t0 : TEXCOORD1;
	float3 c0 : COLOR0;
	float3 c1 : COLOR1;
};

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

v2f VS (
float4 inPos : POSITION,
float2 inTexCoord : TEXCOORD0
)
{
	v2f Out;
	
	Out.pos = mul(inPos,matWorldViewProj);
	
	v3InvWavelength = float3(1.0f / pow(m_waveLength.x, 4.0f), 1.0f / pow(m_waveLength.y, 4.0f), 1.0f / pow(m_waveLength.z, 4.0f));
	fOuterRadius = radius * m_outerScaleFactor;
	fOuterRadius2 = fOuterRadius*fOuterRadius;		
	fInnerRadius = radius;			
	fInnerRadius2 = fInnerRadius*fInnerRadius;	
	fKrESun = m_kr*m_ESun;				
	fKmESun = m_km*m_ESun;				
	fKr4PI = m_kr*4.0f*PI;			
	fKm4PI = m_km*4.0f*PI;	
	fScale = (1.f / (fOuterRadius - fInnerRadius));					
	fScaleOverScaleDepth = fScale/m_scaleDepth;		

	float3 v3CameraPos = float3(0.f,radius,0.f); 								// The camera's current position
	float fCameraHeight = radius;													// The camera's current height
	//float fCameraHeight2 = fCameraHeight*fCameraHeight;		// fCameraHeight^2
	
	// Get the ray from the camera to the vertex and its length (which is the far point of the ray passing through the atmosphere)
	float3 v3Pos = mul(matWorld, inPos).xyz;
	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);
	
	float fSamples = 2.0;
	
	// Initialize the scattering loop variables
	float fSampleLength = fFar / fSamples;
	float fScaledLength = fSampleLength * fScale;
	float3 v3SampleRay = v3Ray * fSampleLength;
	float3 v3SamplePoint = v3Start + v3SampleRay * 0.5f;
	
	// Now loop through the sample rays
	float3 v3FrontColor = float3(0.0, 0.0, 0.0);
	for(int i=0; i<int(fSamples); i++)
	{
		float fHeight = length(v3SamplePoint);
		float fDepth = exp(fScaleOverScaleDepth * (fInnerRadius - fHeight));
		float fLightAngle = dot(vecSunDir.xyz*(-1.f), v3SamplePoint) / fHeight;
		float fCameraAngle = dot(v3Ray, v3SamplePoint) / fHeight;
		float fScatter = (fStartOffset + fDepth*(scale(fLightAngle) - scale(fCameraAngle)));
		float3 v3Attenuate = exp(-fScatter * (v3InvWavelength * fKr4PI + fKm4PI));
		v3FrontColor += v3Attenuate * fDepth * fScaledLength;
		v3SamplePoint += v3SampleRay;
	}
	
	Out.uv = inTexCoord.xy;
	
	// Finally, scale the Mie and Rayleigh colors and set up the varying variables for the pixel shader
	Out.c0 = v3FrontColor * v3InvWavelength * fKrESun;
	Out.c1 = v3FrontColor * fKmESun;
	Out.t0 = v3CameraPos - v3Pos;

	return Out;
}

// Calculates the Mie phase function
float getMiePhase(float fCos, float fCos2, float g, float g2)
{
	return 1.5 * ((1.0 - g2) / (2.0 + g2)) * (1.0 + fCos2) / pow(1.0 + g2 - 2.0*g*fCos, 1.5);
}

// Calculates the Rayleigh phase function
float getRayleighPhase(float fCos2)
{
	return 0.75 + 0.75*fCos2;
}

float4 PS(v2f IN) : COLOR
{

	float fCos = dot(vecSunDir.xyz*(-1.f), IN.t0) / length(IN.t0);
	float fCos2 = pow(fCos,2);
	float3 col = getRayleighPhase(fCos2) * IN.c0 + getMiePhase(fCos, fCos2, m_g, m_g*m_g) * IN.c1;

	//Adjust color from HDR
	fHdrExposure = m_hdrExposure;	
	col = 1.0 - exp(col * -fHdrExposure);

	return float4(col,1.0f);
}

technique tMain
{
	pass 
	{
		VertexShader = compile vs_3_0 VS(); 
		PixelShader = compile ps_3_0 PS();
	}
}



3. create the "atmospheric_scattering.c" script with following code:
Code:
#include <acknex.h>
#include <default.c>

var mtl_atmospheric_scattaring_radius=50000;

//atmospheric scattering material
MATERIAL* mtl_atmospheric_scattering =
{
	effect="atmospheric_scattering.fx";	
}

void init_atmospheric_scattering(ENTITY* sky_)
{
	//scale sky sphere to defined radius multiplied by factor 1.025 (difference of inner and outer atmosphere radius
	vec_scale(sky_.scale_x,mtl_atmospheric_scattaring_radius);
	vec_scale(sky_.scale_x,1.025);
	
	//pass sky sphere radius to material
	mtl_atmospheric_scattering.skill1=floatv(mtl_atmospheric_scattaring_radius);
}

void atmospheric_scattering_startup()
{
	//wait until level is loaded
	while(!level_ent){wait(1);}
	
	//create sky sphere
	ENTITY* sky_=ent_create("sky_sphere.mdl",nullvector,NULL);
	
	//set flags
	set(sky_,PASSABLE|UNTOUCHABLE|NOFOG);
	
	//assign material
	sky_.material=mtl_atmospheric_scattering;
	
	//update scale
	init_atmospheric_scattering(sky_);
	
	while(1)
	{
		//sky sphere needs to have always same position as camera minus sky sphere radius
		vec_set(sky_.x,camera.x);
		sky_.z-=mtl_atmospheric_scattaring_radius;

		wait(1);
	}
}




void main()
{
	video_screen=2;
	video_mode=8;

	//load level
	level_load(NULL);
}


Last edited by oliver2s; 12/16/13 15:36.