]> git.tdb.fi Git - libs/gl.git/blob - source/effects/sky.cpp
Check the flat qualifier from the correct member
[libs/gl.git] / source / effects / sky.cpp
1 #include <msp/geometry/hypersphere.h>
2 #include <msp/geometry/ray.h>
3 #include "directionallight.h"
4 #include "error.h"
5 #include "mesh.h"
6 #include "renderer.h"
7 #include "resources.h"
8 #include "texture2d.h"
9 #include "sky.h"
10
11 using namespace std;
12
13 namespace Msp {
14 namespace GL {
15
16 Sky::Sky(Renderable &c, DirectionalLight &s):
17         Effect(c),
18         sun(s),
19         transmittance_lookup(128, 64, (COLOR_ATTACHMENT,RGBA16F)),
20         transmittance_shprog(Resources::get_global().get<Program>("_sky_transmittance.glsl.shader")),
21         transmittance_lookup_dirty(true),
22         distant(256, 128, (COLOR_ATTACHMENT,RGBA16F)),
23         distant_shprog(Resources::get_global().get<Program>("_sky_distant.glsl.shader")),
24         fullscreen_mesh(Resources::get_global().get<Mesh>("_fullscreen_quad.mesh")),
25         backdrop_shprog(Resources::get_global().get<Program>("_sky_backdrop.glsl.shader")),
26         sampler(Resources::get_global().get<Sampler>("_linear_clamp.samp")),
27         wrap_sampler(Resources::get_global().get<Sampler>("_linear_clamp_v.samp")),
28         dummy_texture(Resources::get_global().get<Texture>("_placeholder.png")),
29         rendered(false)
30 {
31         shdata.uniform("n_steps", 50);
32
33         set_planet(Planet::earth());
34         set_view_height(5.0f);
35 }
36
37 void Sky::set_planet(const Planet &p)
38 {
39         planet = p;
40         shdata.uniform("events.rayleigh_scatter", planet.rayleigh_scatter.r, planet.rayleigh_scatter.g, planet.rayleigh_scatter.b);
41         shdata.uniform("events.mie_scatter", planet.mie_scatter.r, planet.mie_scatter.g, planet.mie_scatter.b);
42         shdata.uniform("events.mie_absorb", planet.mie_absorb.r, planet.mie_absorb.g, planet.mie_absorb.b);
43         shdata.uniform("events.ozone_absorb", planet.ozone_absorb.r, planet.ozone_absorb.g, planet.ozone_absorb.b);
44         shdata.uniform("rayleigh_density_decay", planet.rayleigh_density_decay);
45         shdata.uniform("mie_density_decay", planet.mie_density_decay);
46         shdata.uniform("ozone_band_center", planet.ozone_band_center);
47         shdata.uniform("ozone_band_extent", planet.ozone_band_extent);
48         shdata.uniform("atmosphere_thickness", planet.atmosphere_thickness);
49         shdata.uniform("planet_radius", planet.planet_radius);
50         shdata.uniform("ground_albedo", planet.ground_albedo.r, planet.ground_albedo.g, planet.ground_albedo.b);
51 }
52
53 void Sky::set_view_height(float h)
54 {
55         view_height = h;
56         shdata.uniform("view_height", h);
57 }
58
59 Color Sky::get_transmittance(const Vector3 &look_dir)
60 {
61         Vector3 pos(0.0f, 0.0f, view_height);
62         Vector3 planet_center(0.0f, 0.0f, -planet.planet_radius);
63
64         Geometry::Ray<float, 3> ray(pos-planet_center, look_dir);
65         Geometry::HyperSphere<float, 3> surface(planet.planet_radius);
66         Geometry::SurfacePoint<float, 3> intersection;
67         if(surface.get_intersections(ray, &intersection, 1))
68                 return Color(0.0f);
69
70         Geometry::HyperSphere<float, 3> space(planet.planet_radius+planet.atmosphere_thickness);
71         if(!space.get_intersections(ray, &intersection, 1))
72                 return Color(1.0f);
73
74         float step_size = intersection.distance/50;
75
76         Color path_extinction(0.0f);
77         for(unsigned i=0; i<50; ++i)
78         {
79                 Vector3 from_center = pos-planet_center;
80                 float height = from_center.norm()-planet.planet_radius;
81
82                 Color rayleigh_scatter = planet.rayleigh_scatter*exp(height/planet.rayleigh_density_decay);
83                 Color mie_scatter = planet.mie_scatter*exp(height/planet.mie_density_decay);
84                 Color mie_absorb = planet.mie_absorb*exp(height/planet.mie_density_decay);
85                 Color ozone_absorb = planet.ozone_absorb*max(1.0f-abs(height-planet.ozone_band_center)/planet.ozone_band_extent, 0.0f);
86
87                 path_extinction = path_extinction+(rayleigh_scatter+mie_scatter+mie_absorb+ozone_absorb)*step_size;
88                 pos += look_dir*step_size;
89         }
90
91         return Color(exp(-path_extinction.r), exp(-path_extinction.g), exp(-path_extinction.b));
92 }
93
94 void Sky::setup_frame(Renderer &renderer)
95 {
96         if(rendered)
97                 return;
98
99         rendered = true;
100
101         shdata.uniform("light_color", sun.get_color());
102         shdata.uniform("light_dir", -sun.get_direction());
103         sun.set_transmittance(get_transmittance(-sun.get_direction()));
104
105         {
106                 Renderer::Push push(renderer);
107
108                 renderer.add_shader_data(shdata);
109
110                 if(transmittance_lookup_dirty)
111                 {
112                         transmittance_lookup_dirty = false;
113                         renderer.set_pipeline_key(this);
114                         renderer.set_framebuffer(&transmittance_lookup.get_framebuffer());
115                         renderer.set_shader_program(&transmittance_shprog);
116                         renderer.set_texture("transmittance_lookup", &dummy_texture, &sampler);
117                         fullscreen_mesh.draw(renderer);
118                 }
119
120                 renderer.set_pipeline_key(this, 1);
121                 renderer.set_framebuffer(&distant.get_framebuffer());
122                 renderer.set_shader_program(&distant_shprog);
123                 renderer.set_texture("transmittance_lookup", &transmittance_lookup.get_target_texture(0), &sampler);
124                 fullscreen_mesh.draw(renderer);
125         }
126
127         content.setup_frame(renderer);
128 }
129
130 void Sky::finish_frame()
131 {
132         if(rendered)
133         {
134                 rendered = false;
135                 content.finish_frame();
136         }
137 }
138
139 void Sky::render(Renderer &renderer, Tag tag) const
140 {
141         content.render(renderer, tag);
142
143         if(!is_enabled_for_method(tag))
144                 return;
145
146         Renderer::Push push(renderer);
147
148         renderer.set_pipeline_key(this, 2);
149         renderer.set_shader_program(&backdrop_shprog, &shdata);
150         renderer.set_texture("distant", &distant.get_target_texture(0), &wrap_sampler);
151         fullscreen_mesh.draw(renderer);
152 }
153
154 void Sky::set_debug_name(const string &name)
155 {
156 #ifdef DEBUG
157         transmittance_lookup.set_debug_name(name+" [RT:transmittance]");
158         distant.set_debug_name(name+" [RT:distant]");
159         shdata.set_debug_name(name+" [UBO]");
160 #else
161         (void)name;
162 #endif
163 }
164
165
166 Sky::Planet::Planet():
167         rayleigh_scatter(0.0f),
168         mie_scatter(0.0f),
169         mie_absorb(0.0f),
170         ozone_absorb(0.0f),
171         rayleigh_density_decay(1e3f),
172         mie_density_decay(1e3f),
173         ozone_band_center(1e4f),
174         ozone_band_extent(1e2f),
175         atmosphere_thickness(2e4f),
176         planet_radius(1e6f),
177         ground_albedo(0.2f)
178 { }
179
180 Sky::Planet Sky::Planet::earth()
181 {
182         Planet planet;
183         planet.rayleigh_scatter = Color(5.802e-6f, 13.558e-6f, 33.1e-6f);
184         planet.mie_scatter = Color(3.996e-6f, 3.996e-6f, 3.996e-6f);
185         planet.mie_absorb = Color(4.4e-6f, 4.4e-6f, 4.4e-6f);
186         planet.ozone_absorb = Color(0.65e-6f, 1.881e-6f, 0.085e-6f);
187         planet.rayleigh_density_decay = -8e3f;
188         planet.mie_density_decay = -1.2e3f;
189         planet.ozone_band_center = 25e3f;
190         planet.ozone_band_extent = 15e3f;
191         planet.atmosphere_thickness = 1e5f;
192         planet.planet_radius = 6.36e6f;
193         return planet;
194 }
195
196
197 Sky *Sky::Template::create(const map<string, Renderable *> &renderables) const
198 {
199         Renderable *content = get_item(renderables, content_name);
200         if(!content || !sun)
201                 throw invalid_operation("Sky::Template::create");
202
203         RefPtr<Sky> sky = new Sky(*content, *sun);
204         create_base(*sky);
205
206         return sky.release();
207 }
208
209
210 DataFile::Loader::ActionMap Sky::Template::Loader::shared_actions;
211
212 Sky::Template::Loader::Loader(Template &t, Collection &c):
213         DerivedObjectLoader<Template, Effect::Template::Loader>(t, c)
214 {
215         set_actions(shared_actions);
216 }
217
218 void Sky::Template::Loader::init_actions()
219 {
220         Effect::Template::Loader::init_actions();
221         add("sun", &Template::sun);
222 }
223
224 } // namespace GL
225 } // namespace Msp