/* Sylvain Lefebvre - 2006-05-31 Sky model based on the work of Naty Hoffman and Arcot J. Preetham "Rendering Outdoor Light Scattering in Real Time". http://www.ati.com/developer/dx9/ATI-LightScattering.pdf Updated from the article of the same authors, in the book "Graphics Programming Methods", by Charles River Media Class LibSL::Nature::Sky helps using this shader See also the tutorials in LibSL/src/tutorials/Nature From the caller point of view, coordinates are ground coordinates (z=0 means on ground) Units are meters LIMITATIONS: This shader is NOT suitable for high altitudes. It is assumed that the viewer remains close to the ground. CODE STATUS: Experimental, unoptimized, reference code TODO: - Optimize !! - VP version (currently fp only) -- In order to draw the sky: m_cgSkyShaderCameraPos.set(cx,cy,cz); m_cgSkyShaderSunDir.set(sx,sy,sz); glPushAttrib(GL_ENABLE_BIT); glDisable(GL_DEPTH_TEST); glDepthMask(0); cgGLSetStateMatrixParameter(m_cgSkyModelviewInverse.handle(), CG_GL_MODELVIEW_MATRIX, CG_GL_MATRIX_INVERSE); cgGLSetStateMatrixParameter(m_cgSkyProjectionInverse.handle(), CG_GL_PROJECTION_MATRIX, CG_GL_MATRIX_INVERSE); m_cgSkyShader.begin(); // Technique "t_sky" must be activated glBegin(GL_QUADS); glVertex4f(-1,-1, 1, 1); glVertex4f( 1,-1, 1, 1); glVertex4f( 1, 1, 1, 1); glVertex4f(-1, 1, 1, 1); glEnd(); m_cgSkyShader.end(); glDepthMask(0xFF); glPopAttrib(); In order to draw an object on the ground, the fragment shader of the object must call groundFromAtmosphere(pos_on_ground,CameraPos,SunDir,color_on_ground) */ #ifndef _CG_LibSL_Nature_Atmosphere__ #define _CG_LibSL_Nature_Atmosphere__ #ifndef _CG_LibSL_Math_Intersections__ #include "LibSL_Math_Intersections.cg" #endif // Atmosphere parameters - set to reasonnable default values uniform float3 Sky_BetaR = float3(4.1e-06,6.93327e-06,1.43768e-05); // Raleigh coefficient uniform float3 Sky_BetaM = float3(2.3e-06,2.3e-06,2.3e-06); // Mie coefficient uniform float Sky_G = -0.994; // Henyey-Greenstein eccentricty parameter // Raleigh coefficient //uniform float3 Sky_BetaR = float3(6.95e-06,1.18e-05,2.44e-05); // Mie coefficient and Henyey-Greenstein 'elongation' parameter // Light haze //uniform float3 Sky_BetaM = float3(2e-05,3e-05,4e-05); //uniform float Sky_G = 0.07; // Heavy haze //uniform float3 Sky_BetaM = float3(8e-05,1e-04,1.2e-04); //uniform float Sky_G = 0.2; // Light fog //uniform float3 Sky_BetaM = float3(9e-04,1e-03,1.1e-03); //uniform float Sky_G = 0.4; // Heavy fog //uniform float3 Sky_BetaM = float3(1e-02,1e-02,1e-02); //uniform float Sky_G = 0.5; // Sun color and intensity uniform float3 Sky_ESun = float3(20.0,20.0,20.0); // Exposure uniform float Sky_Exposure=1.0; // Bias to artificially increase optical length uniform float Sky_AtmoLengthBias = 1.0; #define PI 3.14159265358979323846 // Earth and atmosphere shell radius #define Sky_Air_Zenith 8400.0 #define Sky_S_haze_over_S_air (1.25/8.4) const float Sky_OuterRadius = (6378000.0+Sky_Air_Zenith); const float Sky_InnerRadius = 6378000.0; // Vertex data definition struct SKY_VS_INPUT { float4 Pos : POSITION; }; struct SKY_VS_OUTPUT { float4 Pos : POSITION; float3 VDir : TEXCOORD0; }; // Aerial perspective // // Approximate extinction at pos as viewed from camera // Also approximates sun extinction // Returns: // Fex extinction // Lin in-scattered light from sun // Sun sun color void aerialPerspective(float3 pos,float3 camera,float3 sundir, out float3 Fex,out float3 Lin,out float3 Sun) { // Force viewpoint close to the ground to avoid color shifts camera.z = min(camera.z,Sky_InnerRadius+(Sky_OuterRadius-Sky_InnerRadius)*0.25); // Optical length float s_air = length(pos-camera); float s_haze = s_air * Sky_S_haze_over_S_air; // Extinction factor float3 Fex_air_haze = exp( - s_haze * (Sky_BetaM + Sky_BetaR) ); float3 Fex_air = exp( - (s_air-s_haze) * Sky_BetaR ); // Cosine of incoming sunlight angle theta float3 ViewDir = normalize(pos-camera); float cosTh = dot(sundir, ViewDir); // Rayleigh scattering from angle theta float3 BetaRTh = (1.0 + cosTh*cosTh)*Sky_BetaR*3.0/(16.0*PI); // Mie scattering from angle theta float3 BetaMTh = 1.0/(4.0*PI) *Sky_BetaM *(1.0-(Sky_G*Sky_G)) *pow(1.0+(Sky_G*Sky_G)-(2.0*Sky_G*cosTh), -3.0/2.0 ); // Compute sun attenuation due to atmosphere // FIXME: Give all the following as shader params ! // It is way too expensive to be computed here ... float cosTh_sun = -sundir.z; float Th_sun = (180.0/PI)*acos(cosTh_sun); float t_air = Sky_Air_Zenith / (cosTh_sun + 0.15*pow(93.885 - Th_sun, -1.253) ); float t_haze = t_air * Sky_S_haze_over_S_air; Sun = exp( - (Sky_BetaR*t_air + Sky_BetaM*t_haze) ); // In-scattered color from sun Lin = Sun * Sky_ESun * ( ((BetaRTh + BetaMTh) / (Sky_BetaR + Sky_BetaM)) * (1.0 - Fex_air_haze) + (BetaRTh / Sky_BetaR) * (1 - Fex_air) * Fex_air_haze ); Fex = Fex_air * Fex_air_haze; } // Simple HDR mapping float3 HDR(float3 light) { //return light; return (1.0 - exp(- light * Sky_Exposure)); } // Computes ground color viewed through atmosphere // groundcolor is the unlighted ground color float3 groundFromAtmosphere(float3 pos,float3 camera,float3 sundir, float3 groundcolor) { float3 Fex,Lin,Sun; aerialPerspective(pos +float3(0,0,Sky_InnerRadius), camera+float3(0,0,Sky_InnerRadius), sundir, Fex,Lin,Sun); return HDR( Lin + Fex*Sun*(-sundir.z)*groundcolor ); } // Computes sky color float3 skyFromAtmosphere(float3 pos,float3 camera,float3 sundir) { float3 Fex,Lin,Sun; aerialPerspective(pos,camera,sundir, Fex,Lin,Sun); return HDR(Lin); } // -------------------------- // Shader to draw a sky dome // -------------------------- uniform float3 Sky_CameraPos; uniform float3 Sky_SunDir; uniform float4x4 Sky_ModelviewInverse_GL_MVP; uniform float4x4 Sky_ProjectionInverse_GL_MVP; // Vertex program SKY_VS_OUTPUT vp_sky(SKY_VS_INPUT In) { SKY_VS_OUTPUT OUT; // back projects a screen space unit quad onto far plane // The quad is meant to be: // glBegin(GL_QUADS); // glVertex4f(-1,-1, 1, 1); // glVertex4f( 1,-1, 1, 1); // glVertex4f( 1, 1, 1, 1); // glVertex4f(-1, 1, 1, 1); // glEnd(); OUT.VDir = mul(Sky_ModelviewInverse_GL_MVP, float4(mul(Sky_ProjectionInverse_GL_MVP,In.Pos).xyz,0)); OUT.Pos = In.Pos; return OUT; } // Fragment program float4 fp_sky(SKY_VS_OUTPUT In) : COLOR0 { // compute view dir float3 vdir = normalize(In.VDir); // compute intersection with sphere float3 ray_dir = normalize(In.VDir); float3 ray_start = float3(0,0,Sky_CameraPos.z)+float3(0,0,Sky_InnerRadius); float sinPh = vdir.z; float l = - Sky_InnerRadius*sinPh + sqrt( (Sky_InnerRadius*Sky_InnerRadius)*((sinPh*sinPh)-1) + Sky_OuterRadius*Sky_OuterRadius ); float3 p = float3(0,0,Sky_InnerRadius) + l*vdir; return float4( skyFromAtmosphere(p,float3(0,0,Sky_InnerRadius),Sky_SunDir) ,1); } // Technique technique t_sky { pass P0 { VertexProgram = compile vp40 vp_sky(); FragmentProgram = compile fp40 fp_sky(); } } #endif