class DirectionalLight;
class Light;
+class PointLight;
/**
-Creates shadows on a renderable through a shadow map texture. In the setup
-phase, the scene is rendered to a depth texture from the point of view of the
-lightsource. This texture is then used in the rendering phase together with
-texture coordinate generation to determine whether each fragment is lit.
+Creates a depth texture which can be used to add shadows to a renderable.
+
+In the setup phase, the scene is rendered from the point of view of the light
+source and the depth values are recorded. This texture can be used by shaders
+to determine if the light can reach a particular position.
+
+A shadow map can be created as an atlas, containing multiple lights in the same
+depth texture. Each light is automatically allocated a region of the texture
+and region information is added to the uniform values.
+
+The shader fragment shadow.glsl provides interfaces to access the shadow map.
*/
class ShadowMap: public Effect
{
+public:
+ struct Template: Effect::Template
+ {
+ class Loader: public DataFile::DerivedObjectLoader<Template, Effect::Template::Loader>
+ {
+ private:
+ static ActionMap shared_actions;
+
+ public:
+ Loader(Template &, Collection &);
+ private:
+ virtual void init_actions();
+
+ void light(const std::string &);
+ void size_square(unsigned);
+ void target(float, float, float);
+ };
+
+ struct ShadowedLight
+ {
+ class Loader: public DataFile::ObjectLoader<ShadowedLight>
+ {
+ private:
+ static ActionMap shared_actions;
+
+ public:
+ Loader(ShadowedLight &);
+ private:
+ virtual void init_actions();
+ };
+
+ const Light *light = 0;
+ unsigned size;
+ std::string shadow_caster_name;
+ };
+
+ unsigned width = 2048;
+ unsigned height = 2048;
+ const Lighting *lighting = 0;
+ std::vector<ShadowedLight> lights;
+ Vector3 target;
+ float radius = 1.0f;
+ float depth_bias = 4.0f;
+ float darkness = 1.0f;
+
+ virtual ShadowMap *create(const std::map<std::string, Renderable *> &) const;
+ };
+
private:
+ enum ShadowType
+ {
+ NONE,
+ DIRECTIONAL,
+ TETRAHEDRON
+ };
+
struct ShadowedLight
{
const Light *light;
unsigned index;
Rect region;
- Camera shadow_camera;
+ ShadowType type;
+ unsigned view_index;
Renderable *shadow_caster;
};
+ struct ShadowView
+ {
+ unsigned light_index;
+ unsigned face;
+ Camera camera;
+ Matrix face_matrix;
+ };
+
unsigned width;
unsigned height;
const Lighting *lighting;
std::vector<ShadowedLight> lights;
+ std::vector<ShadowView> views;
Framebuffer fbo;
Texture2D depth_buf;
const Sampler &sampler;
std::string debug_name;
ShadowMap(unsigned, unsigned, Renderable &, const Lighting *);
+
public:
- ShadowMap(unsigned, Renderable &, const DirectionalLight &, Renderable &);
- ShadowMap(unsigned, unsigned, Renderable &, const Lighting &);
+ /** Creates a shadow map for a single light. */
+ ShadowMap(unsigned size, Renderable &content, const DirectionalLight &, Renderable &caster);
- void add_light(const DirectionalLight &, unsigned, Renderable &);
+ /** Creates a shadow map atlas, to which multiple lights can be added. */
+ ShadowMap(unsigned width, unsigned height, Renderable &, const Lighting &);
- /** Sets the ShadowMap target point and radius. The transformation matrix is
- computed so that a sphere with the specified parameters will be completely
- covered by the ShadowMap. */
+ /** Adds a directional light. The shadow map is rendered using an
+ orthogonal projection. */
+ void add_light(const DirectionalLight &, unsigned size, Renderable &caster);
+
+ /** Adds a point light. The shadow map is rendered using a perspective
+ projection, with four views in a tetrahedral arrangement. The shader must
+ clip vertices appropriately. Occluder_thsm.glsl can be used for this. */
+ void add_light(const PointLight &, unsigned size, Renderable &caster);
+private:
+ void add_light(const Light &, unsigned, ShadowType, Renderable &);
+
+public:
+ /** Sets the shadow volume parameters. For directional lights the shadow
+ camera will be positioned so that the spherical shadow volume fits entirely
+ in the view volume. For point lights the shadow volume's radius will be
+ used as the view distance. */
void set_target(const Vector3 &, float);
/** Sets the darkness of shadows. Must be in the range between 0.0 and 1.0,
- inclusive. Only usable with shaders, and provided through the
- shadow_darkness uniform. */
+ inclusive. Values other than 1.0 are not physically correct. */
void set_darkness(float);
- /** Sets a distance beyond objects from which the shadow starts. Expressed
- in pixel-sized units. Must be positive; values less than 1.0 are not
- recommended. Larger values produce less depth artifacts, but may prevent
- thin objects from casting shadows on nearby sufraces. */
+ /** Sets an offset for depth values to avoid surfaces incorrectly shadowing
+ themselves. Must be positive; values less than 1.0 are not recommended.
+ Larger values produce less artifacts, but may cause shadows to become
+ disconnected from the objects casting them. */
void set_depth_bias(float);
const Texture2D &get_depth_texture() const { return depth_buf; }