]> git.tdb.fi Git - libs/gl.git/blob - source/shadowmap.cpp
Use an orthographic camera for rendering the shadow map
[libs/gl.git] / source / shadowmap.cpp
1 #include <cmath>
2 #include <cstdlib>
3 #include "camera.h"
4 #include "light.h"
5 #include "matrix.h"
6 #include "misc.h"
7 #include "renderer.h"
8 #include "scene.h"
9 #include "shadowmap.h"
10 #include "tests.h"
11 #include "texunit.h"
12
13 using namespace std;
14
15 namespace Msp {
16 namespace GL {
17
18 ShadowMap::ShadowMap(unsigned s, const Renderable &r, const Light &l):
19         Effect(r),
20         size(s),
21         light(l),
22         radius(1),
23         depth_bias(4),
24         rendered(false)
25 {
26         depth_buf.set_min_filter(LINEAR);
27         depth_buf.set_compare_enabled(true);
28         depth_buf.set_compare_func(LEQUAL);
29         depth_buf.set_wrap(CLAMP_TO_EDGE);
30         depth_buf.storage(DEPTH_COMPONENT, size, size);
31         fbo.attach(DEPTH_ATTACHMENT, depth_buf, 0);
32         fbo.require_complete();
33
34         set_darkness(0.7);
35 }
36
37 void ShadowMap::set_target(const Vector3 &t, float r)
38 {
39         target = t;
40         radius = r;
41 }
42
43 void ShadowMap::set_darkness(float d)
44 {
45         if(d<0.0f || d>1.0f)
46                 throw invalid_argument("ShadowMap::set_darkness");
47
48         shdata.uniform("shadow_darkness", d);
49 }
50
51 void ShadowMap::set_depth_bias(float b)
52 {
53         if(b<0.0f)
54                 throw invalid_argument("ShadowMap::set_depth_bias");
55
56         depth_bias = b;
57 }
58
59 void ShadowMap::setup_frame() const
60 {
61         if(rendered)
62                 return;
63
64         rendered = true;
65         renderable.setup_frame();
66
67         Camera camera;
68         const Vector4 &lpos = light.get_position();
69         /* XXX Not really proper way to support positional lights, but good
70         enough when the light source is far away */
71         camera.set_look_direction(lpos.w*target-lpos.slice<3>(0));
72
73         camera.set_up_direction((abs(camera.get_look_direction().z)<0.99) ? Vector3(0, 0, 1) : Vector3(0, 1, 0));
74         camera.set_position(target);
75         camera.set_orthographic(radius*2, radius*2);
76         camera.set_depth_clip(-radius, radius);
77
78         shadow_matrix = camera.get_object_matrix();
79         shadow_matrix.scale(radius*2, radius*2, -radius*2);
80         shadow_matrix.translate(-0.5, -0.5, depth_bias/size-0.5);
81         shadow_matrix.invert();
82
83         BindRestore bind_fbo(fbo);
84         Bind bind_depth(DepthTest::lequal());
85         fbo.clear(DEPTH_BUFFER_BIT);
86
87         Renderer renderer(&camera);
88         renderable.render(renderer, "shadow");
89 }
90
91 void ShadowMap::finish_frame() const
92 {
93         renderable.finish_frame();
94         rendered = false;
95 }
96
97 void ShadowMap::render(Renderer &renderer, const Tag &tag) const
98 {
99         if(!enabled_passes.count(tag))
100                 return renderer.render(renderable, tag);
101
102         Renderer::Push _push_rend(renderer);
103
104         unsigned unit = renderer.allocate_effect_texunit();
105         int iunit = unit;
106         shdata.uniform("shadow", iunit);
107
108         Bind _bind_depth(depth_buf, unit);
109
110         if(const Camera *camera = renderer.get_camera())
111                 /* Multiply by camera's object matrix to form a matrix that transforms
112                 from eye space to shadow space. */
113                 shdata.uniform("shd_eye_matrix", shadow_matrix*camera->get_object_matrix());
114         else
115                 shdata.uniform("shd_eye_matrix", shadow_matrix);
116
117         renderer.add_shader_data(shdata);
118         renderer.render(renderable, tag);
119 }
120
121 } // namespace GL
122 } // namespace Msp