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