3 // Atmospheric scattering shader for flightgear
\r
4 // Written by Lauri Peltonen (Zan)
\r
5 // Implementation of O'Neil's algorithm
\r
6 // Ground haze layer added by Thorsten Renk
\r
8 varying vec3 rayleigh;
\r
11 varying vec3 hazeColor;
\r
14 varying float delta_z;
\r
16 varying float earthShade;
\r
18 uniform float overcast;
\r
19 uniform float saturation;
\r
20 uniform float visibility;
\r
21 uniform float avisibility;
\r
22 uniform float scattering;
\r
23 uniform float cloud_self_shading;
\r
24 uniform float horizon_roughness;
\r
26 const float EarthRadius = 5800000.0;
\r
28 float miePhase(in float cosTheta, in float g)
\r
31 float a = 1.5 * (1.0 - g2);
\r
32 float b = (2.0 + g2);
\r
33 float c = 1.0 + cosTheta*cosTheta;
\r
34 float d = pow(1.0 + g2 - 2.0 * g * cosTheta, 0.6667);
\r
36 return (a*c) / (b*d);
\r
39 float rayleighPhase(in float cosTheta)
\r
41 //return 1.5 * (1.0 + cosTheta*cosTheta);
\r
42 return 1.5 * (2.0 + 0.5*cosTheta*cosTheta);
\r
45 float rand2D(in vec2 co){
\r
46 return fract(sin(dot(co.xy ,vec2(12.9898,78.233))) * 43758.5453);
\r
49 float simple_interpolate(in float a, in float b, in float x)
\r
51 return a + smoothstep(0.0,1.0,x) * (b-a);
\r
54 float interpolatedNoise2D(in float x, in float y)
\r
56 float integer_x = x - fract(x);
\r
57 float fractional_x = x - integer_x;
\r
59 float integer_y = y - fract(y);
\r
60 float fractional_y = y - integer_y;
\r
62 float v1 = rand2D(vec2(integer_x, integer_y));
\r
63 float v2 = rand2D(vec2(integer_x+1.0, integer_y));
\r
64 float v3 = rand2D(vec2(integer_x, integer_y+1.0));
\r
65 float v4 = rand2D(vec2(integer_x+1.0, integer_y +1.0));
\r
67 float i1 = simple_interpolate(v1 , v2 , fractional_x);
\r
68 float i2 = simple_interpolate(v3 , v4 , fractional_x);
\r
70 return simple_interpolate(i1 , i2 , fractional_y);
\r
73 float Noise2D(in vec2 coord, in float wavelength)
\r
75 return interpolatedNoise2D(coord.x/wavelength, coord.y/wavelength);
\r
82 vec3 shadedFogColor = vec3(0.65, 0.67, 0.78);
\r
83 float cosTheta = dot(normalize(eye), gl_LightSource[0].position.xyz);
\r
85 // position of the horizon line
\r
87 float lAltitude = alt + delta_z;
\r
88 float radiusEye = EarthRadius + alt;
\r
89 float radiusLayer = EarthRadius + lAltitude;
\r
93 if (radiusEye > radiusLayer) cthorizon = -sqrt(radiusEye * radiusEye - radiusLayer * radiusLayer)/radiusEye;
\r
94 else cthorizon = sqrt(radiusLayer * radiusLayer - radiusEye * radiusEye)/radiusLayer;
\r
96 ctterrain = -sqrt(radiusEye * radiusEye - EarthRadius * EarthRadius)/radiusEye;
\r
98 vec3 color = rayleigh * rayleighPhase(cosTheta);
\r
99 color += mie * miePhase(cosTheta, -0.8);
\r
101 vec3 black = vec3(0.0,0.0,0.0);
\r
104 float ovc = overcast;
\r
108 float sat = 1.0 - ((1.0 - saturation) * 2.0);
\r
109 if (sat < 0.3) sat = 0.3;
\r
114 if (color.r > 0.58) color.r = 1.0 - exp(-1.5 * color.r);
\r
115 if (color.g > 0.58) color.g = 1.0 - exp(-1.5 * color.g);
\r
116 if (color.b > 0.58) color.b = 1.0 - exp(-1.5 * color.b);
\r
120 // fog computations for a ground haze layer, extending from zero to lAltitude
\r
124 float transmission;
\r
128 float costheta = ct;
\r
130 float vis = min(visibility, avisibility);
\r
133 if (delta_z > 0.0) // we're inside the layer
\r
135 if (costheta>0.0 + ctterrain) // looking up, view ray intersecting upper layer edge
\r
137 transmission = exp(-min((delta_z/max(costheta,0.1)),25000.0)/vis);
\r
138 //transmission = 1.0;
\r
139 vAltitude = min(vis * costheta, delta_z);
\r
140 delta_zv = delta_z - vAltitude;
\r
143 else // looking down, view range intersecting terrain (which may not be drawn)
\r
145 transmission = exp(alt/vis/costheta);
\r
146 vAltitude = min(-vis * costheta, alt);
\r
147 delta_zv = delta_z + vAltitude;
\r
150 else // we see the layer from above
\r
152 if (costheta < 0.0 + cthorizon)
\r
154 transmission = exp(-min(lAltitude/abs(costheta),25000.0)/vis);
\r
155 transmission = transmission * exp(-alt/avisibility/abs(costheta));
\r
156 transmission = 1.0 - (1.0 - transmission) * smoothstep(0+cthorizon, -0.02+cthorizon, costheta);
\r
157 vAltitude = min(lAltitude, -vis * costheta);
\r
158 delta_zv = vAltitude;
\r
162 transmission = 1.0;
\r
167 // combined intensity reduction by cloud shading and fog self-shading, corrected for Weber-Fechner perception law
\r
168 float eqColorFactor = 1.0 - 0.1 * delta_zv/vis - (1.0 - min(scattering,cloud_self_shading));
\r
171 // there's always residual intensity, we should never be driven to zero
\r
172 if (eqColorFactor < 0.2) eqColorFactor = 0.2;
\r
175 // postprocessing of haze color
\r
176 vec3 hColor = hazeColor;
\r
179 // high altitude desaturation
\r
180 float intensity = length(hColor);
\r
181 hColor = intensity * normalize (mix(hColor, intensity * vec3 (1.0,1.0,1.0), 0.7 * smoothstep(5000.0, 50000.0, alt)));
\r
183 hColor = clamp(hColor,0.0,1.0);
\r
186 hColor.x = 0.83 * hColor.x;
\r
187 hColor.y = 0.9 * hColor.y;
\r
191 // further blueshift when in shadow, either cloud shadow, or self-shadow or Earth shadow, dependent on indirect
\r
194 float fade_out = max(0.65 - 0.3 *overcast, 0.45);
\r
195 intensity = length(hColor);
\r
196 vec3 oColor = hColor;
\r
197 oColor = intensity * normalize(mix(oColor, shadedFogColor, (smoothstep(0.1,1.0,ovc))));
\r
198 oColor = clamp(oColor,0.0,1.0);
\r
199 color = ovc * mix(color, oColor * earthShade ,smoothstep(-0.1+ctterrain, 0.0+ctterrain, ct)) + (1.0-ovc) * color;
\r
202 hColor = intensity * normalize(mix(hColor, 1.5 * shadedFogColor, 1.0 -smoothstep(0.25, fade_out,earthShade) ));
\r
203 hColor = intensity * normalize(mix(hColor, shadedFogColor, (1.0 - smoothstep(0.5,0.9,eqColorFactor))));
\r
204 hColor = hColor * earthShade;
\r
206 // accounting for overcast and saturation
\r
210 color = sat * color + (1.0 - sat) * mix(color, black, smoothstep(0.4+cthorizon,0.2+cthorizon,ct));
\r
213 // the terrain below the horizon gets drawn in one optical thickness
\r
214 vec3 terrainHazeColor = eqColorFactor * hColor;
\r
216 // determine a visibility-dependent angle for how smoothly the haze blends over the skydome
\r
218 float hazeBlendAngle = max(0.01,1000.0/avisibility + 0.3 * (1.0 - smoothstep(5000.0, 30000.0, avisibility)));
\r
219 float altFactor = smoothstep(-300.0, 0.0, delta_z);
\r
220 float altFactor2 = 0.2 + 0.8 * smoothstep(-3000.0, 0.0, delta_z);
\r
221 hazeBlendAngle = hazeBlendAngle + 0.1 * altFactor;
\r
222 hazeBlendAngle = hazeBlendAngle + (1.0-horizon_roughness) * altFactor2 * 0.1 * Noise2D(vec2(0.0,cphi), 0.3);
\r
224 terrainHazeColor = clamp(terrainHazeColor,0.0,1.0);
\r
225 color = mix(color, terrainHazeColor ,smoothstep(hazeBlendAngle + ctterrain, 0.0+ctterrain, ct));
\r
228 // mix fog the skydome with the right amount of haze
\r
230 hColor = clamp(hColor,0.0,1.0);
\r
231 color = transmission * color + (1.0-transmission) * eqColorFactor * hColor;
\r
234 gl_FragColor = vec4(color, 1.0);
\r
235 gl_FragDepth = 0.1;
\r