]> git.tdb.fi Git - libs/gl.git/blobdiff - builtin_data/_sky.glsl
Add an effect for rendering a procedurally generated sky
[libs/gl.git] / builtin_data / _sky.glsl
diff --git a/builtin_data/_sky.glsl b/builtin_data/_sky.glsl
new file mode 100644 (file)
index 0000000..45ca330
--- /dev/null
@@ -0,0 +1,138 @@
+struct AtmosphericEvents
+{
+       vec3 rayleigh_scatter;
+       vec3 mie_scatter;
+       vec3 mie_absorb;
+       vec3 ozone_absorb;
+};
+
+uniform Atmosphere
+{
+       AtmosphericEvents events;
+       float rayleigh_density_decay;
+       float mie_density_decay;
+       float ozone_band_center;
+       float ozone_band_extent;
+       float planet_radius;
+       float atmosphere_thickness;
+       vec3 ground_albedo;
+       int n_steps;
+};
+
+uniform View
+{
+       float view_height;
+       vec4 light_color;
+       vec3 light_dir;
+};
+
+struct OpticalPathInfo
+{
+       vec3 optical_depth;
+       vec3 luminance;
+};
+
+const float pi = 3.1415926535;
+const float mie_asymmetry = 0.8;
+
+uniform sampler2D transmittance_lookup;
+
+vec3 rayleigh_density(vec3 base, float height)
+{
+       return base*exp(height/rayleigh_density_decay);
+}
+
+float rayleigh_phase(float cos_theta)
+{
+       return 3.0*(1.0+cos_theta*cos_theta)/(16.0*pi);
+}
+
+vec3 mie_density(vec3 base, float height)
+{
+       return base*exp(height/mie_density_decay);
+}
+
+float mie_phase(float cos_theta)
+{
+       float g = mie_asymmetry;
+       float num = (1.0-g*g)*(1.0+cos_theta*cos_theta);
+       float denom = (2.0+g*g)*pow(1.0+g*g-2.0*g*cos_theta, 1.5);
+       return 3.0/(8.0*pi)*num/denom;
+}
+
+vec3 ozone_density(vec3 base, float height)
+{
+       return base*max(1.0-abs(height-ozone_band_center)/ozone_band_extent, 0.0);
+}
+
+AtmosphericEvents calculate_events(float height)
+{
+       AtmosphericEvents ev;
+       ev.rayleigh_scatter = rayleigh_density(events.rayleigh_scatter, height);
+       ev.mie_scatter = mie_density(events.mie_scatter, height);
+       ev.mie_absorb = mie_density(events.mie_absorb, height);
+       ev.ozone_absorb = ozone_density(events.ozone_absorb, height);
+       return ev;
+}
+
+vec3 total_extinction(AtmosphericEvents ev)
+{
+       return ev.rayleigh_scatter+ev.mie_scatter+ev.mie_absorb+ev.ozone_absorb;
+}
+
+float ray_sphere_intersect(vec3 ray_start, vec3 ray_dir, vec3 sphere_center, float sphere_radius)
+{
+       float t = dot(sphere_center-ray_start, ray_dir);
+       vec3 nearest = ray_start+t*ray_dir-sphere_center;
+       float d_sq = dot(nearest, nearest);
+       float r_sq = sphere_radius*sphere_radius;
+       if(d_sq>r_sq)
+               return -1.0;
+
+       float offset = sqrt(r_sq-d_sq);
+       if(offset<t)
+               return t-offset;
+       else if(offset>-t)
+               return t+offset;
+       else
+               return -1.0;
+}
+
+#pragma MSP stage(fragment)
+OpticalPathInfo raymarch_path(float start_height, vec3 look_dir)
+{
+       float cos_theta = dot(look_dir, light_dir);
+       float p_rayleigh = rayleigh_phase(cos_theta);
+       float p_mie = mie_phase(cos_theta);
+
+       vec3 planet_center = vec3(0.0, 0.0, -planet_radius);
+       vec3 pos = vec3(0.0, 0.0, start_height);
+       vec3 path_luminance = vec3(0.0);
+       vec3 path_extinction = vec3(0.0);
+
+       float ground_t = ray_sphere_intersect(pos, look_dir, planet_center, planet_radius);
+       float space_t = ray_sphere_intersect(pos, look_dir, planet_center, planet_radius+atmosphere_thickness);
+       float ray_length = (ground_t>0.0 ? ground_t : space_t);
+       float step_size = ray_length/n_steps;
+
+       for(int i=0; i<=n_steps; ++i)
+       {
+               vec3 from_center = pos-planet_center;
+               float height = length(from_center);
+               float light_z = dot(from_center/height, light_dir);
+               height -= planet_radius;
+
+               AtmosphericEvents ev = calculate_events(height);
+               vec3 transmittance = exp(-path_extinction);
+               vec3 in_transmittance = texture(transmittance_lookup, vec2(sqrt(height/atmosphere_thickness), light_z)).rgb;
+               vec3 in_luminance = (ev.rayleigh_scatter*p_rayleigh+ev.mie_scatter*p_mie)*step_size;
+               if(i==n_steps && ground_t>0.0)
+                       in_luminance += ground_albedo*light_z/pi;
+               path_luminance += transmittance*in_transmittance*in_luminance;
+
+               path_extinction += total_extinction(ev)*step_size;
+               pos += look_dir*step_size;
+       }
+
+       return OpticalPathInfo(path_extinction, path_luminance);
+}