Here is a technique I created along with the help and inspiration of the Lux Aeternia team and the TV community.
It is a way of casting light rays across your hemisphere and other places to produce real time light rays on a sphere that can be stretched and deformed to produce some really cool effects. I call it Sphere Mapped Light Rays! (It was that or THE SIX ASSED MONKEY. The monkey lost out).
The original was nothing more than a Cloud Shader written by C Humphrey that I was going to use as clouds. After playing with it and deciding not to use it, I made a nasty error on one last attempt to make clouds look good, and I got this result. So i ripped apart that cloud shader to customize it for god rays instead (it was a Y stretching error that gave me the idea).
Im not quite sure if anyone else has made this effect, so it could be brand new!
Special thanks to:
C.Humphrey,
Pappa Zak,
Blindsided,
Waterman,
Mithian.
Here is a video of the action:
VIDEOhttp://www.youtube.com/watch?v=bVTcxmuhJREHere is the Shader Code:
//////////////////////////////////////////////////////////////
// // Adaptation for Sphere mapped Godrays: //
// Pat Shearon 03/30/2008 //
// Original: //
// Writen by C.Humphrey //
// 20/10/2007 //
// //
//////////////////////////////////////////////////////////////
float4x4 World : WORLD;
float4x4 View : VIEW;
float4x4 Projection : PROJECTION;
float timeOfDay;
float4 SkyColor = (0.5294118,0.8078431,0.9803922,1);
float4 NightColor = (0,0,0.5019608,1);
float4 MorningTint = (1,0.8431373,0,1);
float4 EveningTint =(1,0,0,1);
float cloudTimer;
float cloudIntensity = 1;
float3 EyePosition : CAMERAPOSITION;
texture cloud;
sampler cloudTexture = sampler_state
{
texture = <cloud> ;
magfilter = LINEAR;
minfilter = LINEAR;
mipfilter = LINEAR;
AddressU = Clamp;
AddressV = Clamp;
};
struct VS_INPUT
{
float2 TexCoord : TEXCOORD0;
float4 Position : POSITION0;
float3 Normal : NORMAL;
};
struct VS_OUTPUT
{
float2 TexCoord : TEXCOORD0;
float4 Position : POSITION0;
float3 ViewDirection : TEXCOORD2;
float3 Normal : TEXCOORD1;
};
VS_OUTPUT Transform(VS_INPUT Input)
{
float4x4 WorldViewProjection = mul(mul(World, View), Projection);
float3 ObjectPosition = mul(Input.Position, World);
VS_OUTPUT Output;
Output.Position = mul(Input.Position, WorldViewProjection);
Output.ViewDirection = EyePosition - ObjectPosition;
Output.TexCoord = Input.TexCoord;
Output.Normal = Input.Normal;
return Output;
}
struct PS_INPUT
{
float2 TexCoord : TEXCOORD0;
float3 ViewDirection : TEXCOORD2;
float3 Normal : TEXCOORD1;
};
float2 SwirlHoriz(float2 coord,bool clockwise)
{
if(clockwise)
{
coord.x += cloudTimer;
if(coord.x > 1)
coord.x = coord.x-1;
}
else
{
coord.x -= cloudTimer;
if(coord.x < 0)
coord.x = coord.x+1;
}
return coord;
}
float2 SwirlVert(float2 coord,bool up)
{
if(up)
{
coord.y -= cloudTimer;
if(coord.y < 0)
coord.y = coord.y+1;
}
else
{
coord.y += cloudTimer;
if(coord.y > 1)
coord.y = coord.y-1;
}
return coord;
}
float4 col;
float4 BasicShader(PS_INPUT Input) : COLOR0
{
float3 ViewDirection = normalize(Input.ViewDirection);
col = 0;
float4 ncol = 0;
col = (SkyColor + (1,1,1,1)) * Input.TexCoord.y;
ncol = NightColor * (4 - Input.TexCoord.y) * .125f;
if(timeOfDay <= 12)
{
col *= timeOfDay / 12;
ncol *= timeOfDay / 12;
}
else
{
col *= (timeOfDay - 24) / -12;
ncol *= (timeOfDay - 24) / -12;
}
col += (MorningTint * .05) * ((24 - timeOfDay)/24);
col += (EveningTint * .05) * (timeOfDay / 24);
col += ncol;
float4 cloudCol0 = 0;
float4 cloudCol1 = 0;
float4 cloudCol2 = 0;
float2 cloudTex0 = Input.TexCoord;
float2 cloudTex1 = Input.TexCoord;
float2 cloudTex2 = Input.TexCoord;
// Swirl the clouds
cloudTex0 = SwirlHoriz(cloudTex0,true);
cloudTex1 = SwirlHoriz(cloudTex1,true);
cloudTex1 = SwirlVert(cloudTex1,true);
cloudTex2 = SwirlHoriz(cloudTex2,false);
cloudTex2 = SwirlVert(cloudTex2,false);
cloudCol0 = tex2D(cloudTexture, cloudTex0);
cloudCol1 = tex2D(cloudTexture, cloudTex1);
cloudCol2 = tex2D(cloudTexture, cloudTex2);
float4 cloudCol = (cloudCol0 - (cloudCol1 + cloudCol2));
cloudCol *= Input.TexCoord.y;
col += cloudCol * cloudIntensity;
return float4(col.rgb, 0.2f);
}
technique BasicShader
{
pass P0
{
VertexShader = compile vs_2_0 Transform();
PixelShader = compile ps_2_0 BasicShader();
}
}
And the mesh to create to use it on:
RayDome = Truevision.Scene.CreateMeshBuilder("RayDome");
RayDome.CreateSphere(32, 32, 32);
RayDome.SetCollisionEnable(false);
RayDome.SetScale(1, 1000, 1);
RayDome.SetShader(RayShader);
RayDome.SetShadowCast(false, false);
RayDome.SetLightingMode(CONST_TV_LIGHTINGMODE.TV_LIGHTING_MANAGED);
RayDome.SetBlendingMode(CONST_TV_BLENDINGMODE.TV_BLEND_ADDALPHA);
RayDome.SetAlphaTest(false, 0, false);
RayDome.SetCullMode(CONST_TV_CULLING.TV_FRONT_CULL);
RayDome.SetMaterial(Truevision.Globals.GetMat("Rays"));
ps: any material will work
And here is the update piece to make things move:
//Update God Rays
cloudAnim += cloudAnimSpeed;
if (cloudAnim > 1)
cloudAnim = 0;
RayShader.SetEffectParamFloat("cloudTimer", cloudAnim);
RayShader.SetEffectParamFloat("timeOfDay", Zone.MapEnvironmentSettings._GameTime.Hour);
ps: cloudAnimSpeed = 0.0001 you can change this to change the ray speed
There are many ways to use this, as in following your sun, or implemented with a post process caustic shader on a full-screen quad for example!
Happy coding! and have fun!
Pat Shearon AKA
Hawthorne