]> git.tdb.fi Git - libs/gl.git/blob - source/effects/sky.cpp
a210efc94f578e35c7593fab4277d3150ac4575e
[libs/gl.git] / source / effects / sky.cpp
1 #include <msp/geometry/hypersphere.h>
2 #include <msp/geometry/ray.h>
3 #include "light.h"
4 #include "mesh.h"
5 #include "renderer.h"
6 #include "resources.h"
7 #include "sky.h"
8
9 using namespace std;
10
11 namespace Msp {
12 namespace GL {
13
14 Sky::Sky(Renderable &r, Light &s):
15         Effect(r),
16         sun(s),
17         transmittance_lookup(128, 64, (RENDER_COLOR, RGB16F)),
18         transmittance_shprog(Resources::get_global().get<Program>("_sky_transmittance.glsl.shader")),
19         transmittance_lookup_dirty(true),
20         distant(256, 128, (RENDER_COLOR, RGB16F)),
21         distant_shprog(Resources::get_global().get<Program>("_sky_distant.glsl.shader")),
22         fullscreen_mesh(Resources::get_global().get<Mesh>("_fullscreen_quad.mesh")),
23         backdrop_shprog(Resources::get_global().get<Program>("_sky_backdrop.glsl.shader")),
24         sampler(Resources::get_global().get<Sampler>("_linear_clamp.samp")),
25         wrap_sampler(Resources::get_global().get<Sampler>("_linear_clamp_v.samp")),
26         rendered(false)
27 {
28         shdata.uniform("n_steps", 50);
29
30         set_planet(Planet::earth());
31         set_view_height(5.0f);
32 }
33
34 void Sky::set_planet(const Planet &p)
35 {
36         planet = p;
37         shdata.uniform("events.rayleigh_scatter", planet.rayleigh_scatter.r, planet.rayleigh_scatter.g, planet.rayleigh_scatter.b);
38         shdata.uniform("events.mie_scatter", planet.mie_scatter.r, planet.mie_scatter.g, planet.mie_scatter.b);
39         shdata.uniform("events.mie_absorb", planet.mie_absorb.r, planet.mie_absorb.g, planet.mie_absorb.b);
40         shdata.uniform("events.ozone_absorb", planet.ozone_absorb.r, planet.ozone_absorb.g, planet.ozone_absorb.b);
41         shdata.uniform("rayleigh_density_decay", planet.rayleigh_density_decay);
42         shdata.uniform("mie_density_decay", planet.mie_density_decay);
43         shdata.uniform("ozone_band_center", planet.ozone_band_center);
44         shdata.uniform("ozone_band_extent", planet.ozone_band_extent);
45         shdata.uniform("atmosphere_thickness", planet.atmosphere_thickness);
46         shdata.uniform("planet_radius", planet.planet_radius);
47         shdata.uniform("ground_albedo", planet.ground_albedo.r, planet.ground_albedo.g, planet.ground_albedo.b);
48 }
49
50 void Sky::set_view_height(float h)
51 {
52         view_height = h;
53         shdata.uniform("view_height", h);
54 }
55
56 Color Sky::get_transmittance(const Vector3 &look_dir)
57 {
58         Vector3 pos(0.0f, 0.0f, view_height);
59         Vector3 planet_center(0.0f, 0.0f, -planet.planet_radius);
60
61         Geometry::Ray<float, 3> ray(pos-planet_center, look_dir);
62         Geometry::HyperSphere<float, 3> surface(planet.planet_radius);
63         Geometry::SurfacePoint<float, 3> intersection;
64         if(surface.get_intersections(ray, &intersection, 1))
65                 return Color(0.0f);
66
67         Geometry::HyperSphere<float, 3> space(planet.planet_radius+planet.atmosphere_thickness);
68         if(!space.get_intersections(ray, &intersection, 1))
69                 return Color(1.0f);
70
71         float step_size = intersection.distance/50;
72
73         Color path_extinction(0.0f);
74         for(unsigned i=0; i<50; ++i)
75         {
76                 Vector3 from_center = pos-planet_center;
77                 float height = from_center.norm()-planet.planet_radius;
78
79                 Color rayleigh_scatter = planet.rayleigh_scatter*exp(height/planet.rayleigh_density_decay);
80                 Color mie_scatter = planet.mie_scatter*exp(height/planet.mie_density_decay);
81                 Color mie_absorb = planet.mie_absorb*exp(height/planet.mie_density_decay);
82                 Color ozone_absorb = planet.ozone_absorb*max(1.0f-abs(height-planet.ozone_band_center)/planet.ozone_band_extent, 0.0f);
83
84                 path_extinction = path_extinction+(rayleigh_scatter+mie_scatter+mie_absorb+ozone_absorb)*step_size;
85                 pos += look_dir*step_size;
86         }
87
88         return Color(exp(-path_extinction.r), exp(-path_extinction.g), exp(-path_extinction.b));
89 }
90
91 void Sky::setup_frame(Renderer &renderer)
92 {
93         if(rendered)
94                 return;
95
96         rendered = true;
97
98         shdata.uniform("light_color", sun.get_color());
99         shdata.uniform("light_dir", sun.get_position().slice<3>(0));
100         sun.set_transmittance(get_transmittance(normalize(sun.get_position().slice<3>(0))));
101
102         Renderer::Push push(renderer);
103
104         if(transmittance_lookup_dirty)
105         {
106                 transmittance_lookup_dirty = false;
107                 renderer.set_framebuffer(&transmittance_lookup.get_framebuffer());
108                 renderer.set_shader_program(&transmittance_shprog, &shdata);
109                 fullscreen_mesh.draw(renderer);
110         }
111
112         renderer.set_framebuffer(&distant.get_framebuffer());
113         renderer.set_shader_program(&distant_shprog, &shdata);
114         renderer.set_texture("transmittance_lookup", &transmittance_lookup.get_target_texture(0), &sampler);
115         fullscreen_mesh.draw(renderer);
116
117         renderable.setup_frame(renderer);
118 }
119
120 void Sky::finish_frame()
121 {
122         if(rendered)
123         {
124                 rendered = false;
125                 renderable.finish_frame();
126         }
127 }
128
129 void Sky::render(Renderer &renderer, Tag tag) const
130 {
131         renderable.render(renderer, tag);
132
133         Renderer::Push push(renderer);
134
135         renderer.set_shader_program(&backdrop_shprog, &shdata);
136         renderer.set_texture("distant", &distant.get_target_texture(0), &wrap_sampler);
137         fullscreen_mesh.draw(renderer);
138 }
139
140 void Sky::set_debug_name(const string &name)
141 {
142 #ifdef DEBUG
143         transmittance_lookup.set_debug_name(name+" [RT:transmittance]");
144         distant.set_debug_name(name+" [RT:distant]");
145         shdata.set_debug_name(name+" [UBO]");
146 #else
147         (void)name;
148 #endif
149 }
150
151
152 Sky::Planet::Planet():
153         rayleigh_scatter(0.0f),
154         mie_scatter(0.0f),
155         mie_absorb(0.0f),
156         ozone_absorb(0.0f),
157         rayleigh_density_decay(1e3f),
158         mie_density_decay(1e3f),
159         ozone_band_center(1e4f),
160         ozone_band_extent(1e2f),
161         atmosphere_thickness(2e4f),
162         planet_radius(1e6f)
163 { }
164
165 Sky::Planet Sky::Planet::earth()
166 {
167         Planet planet;
168         planet.rayleigh_scatter = Color(5.802e-6f, 13.558e-6f, 33.1e-6f);
169         planet.mie_scatter = Color(3.996e-6f, 3.996e-6f, 3.996e-6f);
170         planet.mie_absorb = Color(4.4e-6f, 4.4e-6f, 4.4e-6f);
171         planet.ozone_absorb = Color(0.65e-6f, 1.881e-6f, 0.085e-6f);
172         planet.rayleigh_density_decay = -8e3f;
173         planet.mie_density_decay = -1.2e3f;
174         planet.ozone_band_center = 25e3f;
175         planet.ozone_band_extent = 15e3f;
176         planet.atmosphere_thickness = 1e5f;
177         planet.planet_radius = 6.36e6f;
178         return planet;
179 }
180
181 } // namespace GL
182 } // namespace Msp