]> git.tdb.fi Git - libs/gl.git/blob - source/effects/shadowmap.cpp
eaf6e1373cb126a387d535e9e2b1352199eed2e7
[libs/gl.git] / source / effects / shadowmap.cpp
1 #include <cmath>
2 #include <cstdlib>
3 #include "camera.h"
4 #include "light.h"
5 #include "renderer.h"
6 #include "scene.h"
7 #include "shadowmap.h"
8 #include "tests.h"
9
10 using namespace std;
11
12 namespace Msp {
13 namespace GL {
14
15 WeakPtr<Sampler> ShadowMap::shadow_sampler;
16
17 ShadowMap::ShadowMap(unsigned s, Renderable &r, const Light &l):
18         Effect(r),
19         size(s),
20         light(l),
21         radius(1),
22         depth_bias(4),
23         rendered(false)
24 {
25         sampler = shadow_sampler;
26         if(!sampler)
27         {
28                 sampler = new Sampler;
29                 sampler->set_filter(LINEAR);
30                 sampler->set_compare(LEQUAL);
31                 sampler->set_wrap(CLAMP_TO_EDGE);
32                 shadow_sampler = sampler;
33         }
34         depth_buf.storage(DEPTH_COMPONENT32F, size, size, 1);
35         fbo.attach(DEPTH_ATTACHMENT, depth_buf, 0);
36         fbo.require_complete();
37
38         set_darkness(0.7);
39 }
40
41 void ShadowMap::set_target(const Vector3 &t, float r)
42 {
43         target = t;
44         radius = r;
45 }
46
47 void ShadowMap::set_darkness(float d)
48 {
49         if(d<0.0f || d>1.0f)
50                 throw invalid_argument("ShadowMap::set_darkness");
51
52         shdata.uniform("shadow_darkness", d);
53 }
54
55 void ShadowMap::set_depth_bias(float b)
56 {
57         if(b<0.0f)
58                 throw invalid_argument("ShadowMap::set_depth_bias");
59
60         depth_bias = b;
61 }
62
63 void ShadowMap::setup_frame(Renderer &renderer)
64 {
65         if(rendered)
66                 return;
67
68         rendered = true;
69         renderable.setup_frame(renderer);
70
71         Camera camera;
72         camera.set_object_matrix(*light.get_matrix());
73         camera.set_position(target);
74         // TODO support point and spot lights with a frustum projection.
75         // Omnidirectional lights also need a cube shadow map.
76         camera.set_orthographic(radius*2, radius*2);
77         camera.set_depth_clip(-radius, radius);
78
79         shadow_matrix = camera.get_object_matrix();
80         shadow_matrix.scale(radius*2, radius*2, -radius*2);
81         shadow_matrix.translate(-0.5, -0.5, depth_bias/size-0.5);
82         shadow_matrix.invert();
83
84         BindRestore bind_fbo(fbo);
85         Bind bind_depth(DepthTest::lequal());
86         fbo.clear(DEPTH_BUFFER_BIT);
87
88         Renderer::Push push(renderer);
89         renderer.set_camera(camera);
90
91         renderer.render(renderable, "shadow");
92 }
93
94 void ShadowMap::finish_frame()
95 {
96         renderable.finish_frame();
97         rendered = false;
98 }
99
100 void ShadowMap::render(Renderer &renderer, const Tag &tag) const
101 {
102         if(!enabled_passes.count(tag))
103                 return renderer.render(renderable, tag);
104
105         Renderer::Push _push_rend(renderer);
106
107         unsigned unit = renderer.allocate_effect_texunit();
108         int iunit = unit;
109         shdata.uniform("shadow_map", iunit);
110
111         Bind _bind_sampler(*sampler, unit);
112         Bind _bind_depth(depth_buf, unit);
113
114         if(const Camera *camera = renderer.get_camera())
115                 /* Multiply by camera's object matrix to form a matrix that transforms
116                 from eye space to shadow space. */
117                 shdata.uniform("shd_eye_matrix", shadow_matrix*camera->get_object_matrix());
118         else
119                 shdata.uniform("shd_eye_matrix", shadow_matrix);
120
121         renderer.add_shader_data(shdata);
122         renderer.render(renderable, tag);
123 }
124
125 } // namespace GL
126 } // namespace Msp