GCC Code Coverage Report


./
Coverage:
low: ≥ 0%
medium: ≥ 75.0%
high: ≥ 90.0%
Lines:
0 of 61, 0 excluded
0.0%
Functions:
0 of 6, 0 excluded
0.0%
Branches:
0 of 86, 0 excluded
0.0%

libs/render/src/eu/render/shadow.cc
Line Branch Exec Source
1 #include "eu/render/shadow.h"
2
3 #include "eu/render/camera.h"
4 #include "eu/render/render_settings.h"
5 #include "eu/render/world.h"
6
7 // #include "glm/glm.hpp"
8 // #include "glm/gtc/matrix_transform.hpp"
9
10 #include <algorithm>
11 #include <array>
12 #include <cmath>
13
14 namespace eu::render
15 {
16
17 OrthoCamera shadow_cam_from_light(const DirectionalLight& light, const World& world, const Camera& camera)
18 {
19 auto cam = OrthoCamera{};
20 const auto light_local_space = create_vectors(light);
21 const auto shadow_offset = light_local_space.front * world.lights.shadow_offset;
22 cam.position = camera.position - shadow_offset;
23 cam.rotation = light.rotation;
24 cam.near = world.lights.shadow_near;
25 cam.far = world.lights.shadow_far;
26 cam.size = world.lights.shadow_size;
27 return cam;
28 }
29
30
31 using FrustumCorners = std::array<v3, 8>;
32 static FrustumCorners calculate_frustum_corners_in_world_space(const m4& world_from_clip)
33 {
34 const auto calc_world_from_clip = [world_from_clip](float x, float y, float z)
35 {
36 const auto pt = world_from_clip * v4{x, y, z, 1.0f};
37 return pt.to_vec3_persp_div();
38 };
39
40 constexpr float neg = -1.0f;
41 constexpr float pos = 1.0f;
42
43 return {
44 calc_world_from_clip(neg, neg, neg),
45 calc_world_from_clip(neg, neg, pos),
46 calc_world_from_clip(neg, pos, neg),
47 calc_world_from_clip(neg, pos, pos),
48 calc_world_from_clip(pos, neg, neg),
49 calc_world_from_clip(pos, neg, pos),
50 calc_world_from_clip(pos, pos, neg),
51 calc_world_from_clip(pos, pos, pos)
52 };
53 }
54
55 static v3 calculate_frustum_center(const FrustumCorners& corners)
56 {
57 v3 sum(0.0f);
58 for (const auto& c: corners)
59 {
60 sum += c;
61 }
62 return sum / static_cast<float>(corners.size());
63 }
64
65 CompiledCamera calculate_tight_fitting_camera_around_perspective(
66 const CompiledCamera& perspective, const n3& light_direction
67 )
68 {
69 // algorithm inspired by learn open gl: https://learnopengl.com/Guest-Articles/2021/CSM
70
71 const auto dir = light_direction;
72
73 const auto world_pos_frustum_corners = calculate_frustum_corners_in_world_space(
74 (perspective.clip_from_view * perspective.view_from_world).get_inverted()
75 );
76
77 // calculate frustum center
78 const auto world_pos_center = calculate_frustum_center(world_pos_frustum_corners);
79
80 // calculate view matrix
81 const auto world_pos_eye = world_pos_center - dir * 100.0f;
82 constexpr auto y_up = kk::up;
83 constexpr auto x_up = kk::right;
84 const auto up = std::abs(y_up.dot(dir)) > 0.99f ? x_up : y_up;
85 const auto light_view_from_world = m4::from(Q::look_in_direction(v3::from_to(world_pos_eye, world_pos_center).get_normalized().value_or(kk::in), up)).value_or(m4_identity);
86
87 // calculate aabb
88 auto view_pos_min = v3{std::numeric_limits<float>::max()};
89 auto view_pos_max = v3{std::numeric_limits<float>::lowest()}; // lowest() is the lowest negative, min() is the lowest positive
90 for (const auto& world_pos: world_pos_frustum_corners)
91 {
92 const auto view_pos = (light_view_from_world * v4(world_pos, 1.0f)).to_vec3_persp_div();
93 view_pos_min = min(view_pos_min, view_pos);
94 view_pos_max = max(view_pos_max, view_pos);
95 }
96
97 // expand aabb (does this work???)
98 constexpr float z_mult = 1.0f; // Tune this parameter according to the scene
99 const auto temp_min_z = -view_pos_max.z;
100 const auto temp_max_z = -view_pos_min.z;
101 const auto min_z = temp_min_z < 0 ? temp_min_z * z_mult : temp_min_z / z_mult;
102 const auto max_z = temp_max_z < 0 ? temp_max_z / z_mult : temp_max_z * z_mult;
103
104 // calculate ortho projection
105 const auto light_clip_from_view
106 = m4::create_ortho_lrud(view_pos_min.x, view_pos_max.x, view_pos_max.y, view_pos_min.y, min_z, max_z);
107
108 return {
109 .clip_from_view = light_clip_from_view,
110 .view_from_world = light_view_from_world,
111 .position = world_pos_eye,
112 .in = dir
113 };
114 }
115
116
117 #if TEMP
118 struct Plane
119 {
120 float a;
121 float b;
122 float c;
123 float d;
124 };
125
126 Plane plane_from_point_normal(const v3& point, const v3& normal)
127 {
128 const auto normalized_normal = glm::normalize(normal);
129 return {
130 .a = normalized_normal.x,
131 .b = normalized_normal.y,
132 .c = normalized_normal.z,
133 .d = -glm::dot(point, normalized_normal)
134 };
135 }
136
137 float signed_distance(const Plane& plane, const v3& Pt)
138 {
139 return plane.a * Pt.x + plane.b * Pt.y + plane.c * Pt.z + plane.d;
140 }
141
142 CompiledCamera calculate_tight_fitting_camera_around_perspective(
143 const CompiledCamera& perspective, const CameraVectors& light
144 )
145 {
146 const auto world_pos_frustum_corners = calculate_frustum_corners_in_world_space(
147 glm::inverse(perspective.clip_from_view * perspective.view_from_world)
148 );
149
150 const auto center = calculate_frustum_center(world_pos_frustum_corners);
151
152 const auto near_and_far = [](const FrustumCorners& corners, const v3& p, const v3& dir)
153 {
154 const auto plane = plane_from_point_normal(p, dir);
155 return std::ranges::minmax_element(
156 corners,
157 [plane](const auto& lhs, const auto& rhs)
158 { return signed_distance(plane, lhs) < signed_distance(plane, rhs); }
159 );
160 };
161
162 // calculate nearest and fartest point based on distance from plane, using [0] and In
163 const auto [near_point, far_point] = near_and_far(world_pos_frustum_corners, center, light.front);
164
165 // calculate min/max aabb in "camera space" using distance and in/up
166 const auto [left_point, right_point] = near_and_far(world_pos_frustum_corners, center, light.right);
167 const auto [down_point, up_point] = near_and_far(world_pos_frustum_corners, center, light.up);
168
169
170 // calculate ortho from planes
171 }
172 #endif
173
174 CompiledCamera compile_the_shadow_camera(
175 const Camera& camera,
176 const Size& window_size,
177 const DirectionalLight& light,
178 const RenderSettings& settings,
179 const World& world
180 )
181 {
182 if (settings.use_tight_fit_shadows)
183 {
184 return calculate_tight_fitting_camera_around_perspective(
185 compile(camera, window_size), create_vectors(light).front
186 );
187 }
188 else
189 {
190 const auto cam = shadow_cam_from_light(light, world, camera);
191 return compile(cam, settings.shadow_map_resolution);
192 }
193 }
194
195
196
197
198 }
199