Table of contents
Description
This shader is used to create atmospheric rings around a planet as depicted in the picture below:
Usage
You will need to assign this material to a sphere that is a little bigger then your planet. Also you will need to give this shader the radius of your planet and the radius of the sphere you use as the atmosphere. You can do that with this line of code
Copy to clipboard
//Create our entity AtmosphereEntity = sm->createEntity(PlanetName + "_atm_Ent",PlanetName + "_atm"); //assign it our material AtmosphereEntity->setMaterialName ("Atmosphere"); //also this will make the atmosphere to be rendered behind the planet AtmosphereEntity->setRenderQueueGroup (Ogre::RENDER_QUEUE_BACKGROUND); //here you will replace PlanetRadius and AtmosphereRadius with your values AtmosphereEntity->getSubEntity (0)->setCustomParameter (0, Ogre::Vector4(PlanetRadius,AtmosphereRadius,0,0));
And here is the gradient you need as a texture (just right click / save image as and save it into your media folder)
WARNING : It is extremely important that you do not rotate the atmosphere holding sphere, as this will screw up the shader!!!
Atmosphere.material
Copy to clipboard
vertex_program AtmosphereVertexProgram hlsl { source Atmosphere.hlsl entry_point main2VS target vs_2_0 default_params { param_named_auto lightPos light_position_object_space 0 param_named_auto WorldXf world_matrix param_named_auto ViewIXf camera_position_object_space param_named_auto WorldViewProj worldviewproj_matrix } } fragment_program AtmosphereFragmentProgram hlsl { source Atmosphere.hlsl entry_point mainBPS target ps_2_0 } material Atmosphere { technique { pass { depth_check off depth_write off scene_blend one one_minus_src_alpha vertex_program_ref AtmosphereVertexProgram { param_named_auto SurfaceAtmosphereRadius4 custom 0 } fragment_program_ref AtmosphereFragmentProgram { } texture_unit { texture AtmosphereGradient.jpg tex_address_mode clamp clamp filtering linear linear linear } } } }
Atmosphere.hlsl
Copy to clipboard
void main2VS( float3 pos : POSITION, uniform float4 lightPos, uniform float4x4 WorldXf, uniform float4 ViewIXf, uniform float4x4 WorldViewProj, uniform float4 SurfaceAtmosphereRadius4, out float4 oPosition: POSITION, out float2 oUV: TEXCOORD0, out float oAlpha: TEXCOORD1, out float3 oCamToPos: TEXCOORD2, out float3 oLightDir :TEXCOORD3 ) { float SurfaceRadius = SurfaceAtmosphereRadius4.x; float AtmosphereRadius = SurfaceAtmosphereRadius4.y; float StretchAmt = 0.5f; float4 Po = float4(pos.xyz,1); float4 Pw = mul(WorldXf, pos); float3 position = Pw.xyz; float4 camPos = ViewIXf; oPosition = mul(WorldViewProj, Po); float radius = length(position); float radius2 = radius * radius; float camHeight = length(camPos.xyz); float3 camToPos = position - camPos.xyz; float farDist = length(camToPos); float3 lightDir = normalize(lightPos.xyz); float3 normal = normalize(position); float3 rayDir = camToPos / farDist; float camHeight2 = camHeight * camHeight; // Calculate the closest intersection of the ray with the outer atmosphere float B = 2.0 * dot(camPos.xyz, rayDir); float C = camHeight2 - radius2; float det = max(0.0, B*B - 4.0 * C); float nearDist = 0.5 * (-B - sqrt(det)); float3 nearPos = camPos.xyz + (rayDir * nearDist); float3 nearNormal = normalize(nearPos); // get dot products we need float lc = dot(lightDir, camPos / camHeight); float ln = dot(lightDir, normal); float lnn = dot(lightDir, nearNormal); // get distance to surface horizon float altitude = camHeight - SurfaceRadius; float horizonDist = sqrt((altitude*altitude) + (2.0 * SurfaceRadius * altitude)); float maxDot = horizonDist / camHeight; // get distance to atmosphere horizon - use max(0,...) because we can go into the atmosphere altitude = max(0,camHeight - AtmosphereRadius); horizonDist = sqrt((altitude*altitude) + (2.0 * AtmosphereRadius * altitude)); // without this, the shift between inside and outside atmosphere is jarring float tweakAmount = 0.1; float minDot = max(tweakAmount,horizonDist / camHeight); // scale minDot from 0 to -1 as we enter the atmosphere float minDot2 = ((camHeight - SurfaceRadius) * (1.0 / (AtmosphereRadius - SurfaceRadius))) - (1.0 - tweakAmount); minDot = min(minDot, minDot2); // get dot product of the vertex we're looking out float posDot = dot(camToPos / farDist,-camPos.xyz / camHeight) - minDot; // calculate the height from surface in range 0..1 float height = posDot * (1.0 / (maxDot - minDot)); // push the horizon back based on artistic taste ln = max(0,ln + StretchAmt); lnn = max(0,lnn + StretchAmt); // the front color is the sum of the near and far normals float brightness = saturate(ln + (lnn * lc)); // use "saturate(lc + 1.0 + StretchAmt)" to make more of the sunset side color be used when behind the planet oUV.x = brightness * saturate(lc + 1.0 + StretchAmt); oUV.y = height; // as the camera gets lower in the atmosphere artificially increase the height // so that the alpha value gets raised and multiply the increase amount // by the dot product of the light and the vertex normal so that // vertices closer to the sun are less transparent than vertices far from the sun. height -= min(0.0,minDot2 + (ln * minDot2)); oAlpha = height * brightness; // normalised camera to position ray oCamToPos = -rayDir; oLightDir = normalize(lightPos.xyz - position.xyz); } float4 mainBPS( float2 uv : TEXCOORD0, float alpha : TEXCOORD1, float3 camToPos : TEXCOORD2, float3 lightDir :TEXCOORD3, uniform sampler2D TexSampler ) : COLOR { float Atmosphere_G = -0.95f; const float fExposure = 1.5; float g = Atmosphere_G; float g2 = g * g; // atmosphere color float4 diffuse = tex2D(TexSampler,uv); // sun outer color - might could use atmosphere color float4 diffuse2 = tex2D(TexSampler,float2(min(0.5,uv.x),1)); // this is equivilant but faster than fCos = dot(normalize(lightDir.xyz),normalize(camToPos)); float fCos = dot(lightDir.xyz,camToPos) * rsqrt( dot(lightDir.xyz,lightDir.xyz) * dot(camToPos,camToPos)); float fCos2 = fCos * fCos; // apply alpha to atmosphere float4 diffuseColor = diffuse * alpha; // sun glow color float fMiePhase = 1.5 * ((1.0 - g2) / (2.0 + g2)) * (1.0 + fCos2) /(1.0 + g2 - 2.0*g*fCos); float4 mieColor = diffuse2 * fMiePhase * alpha; // use exponential falloff because mie color is in high dynamic range // boost diffuse color near horizon because it gets desaturated by falloff return 1.0 - exp((diffuseColor * (1.0 + uv.y) + mieColor) * -fExposure); }
Credits
Full credits go to Alex Peterson over at his blog. I only converted the .fx he posted over there to ogre material.