Blender  V2.93
workbench_shadow.c
Go to the documentation of this file.
1 /*
2  * This program is free software; you can redistribute it and/or
3  * modify it under the terms of the GNU General Public License
4  * as published by the Free Software Foundation; either version 2
5  * of the License, or (at your option) any later version.
6  *
7  * This program is distributed in the hope that it will be useful,
8  * but WITHOUT ANY WARRANTY; without even the implied warranty of
9  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10  * GNU General Public License for more details.
11  *
12  * You should have received a copy of the GNU General Public License
13  * along with this program; if not, write to the Free Software Foundation,
14  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
15  *
16  * Copyright 2020, Blender Foundation.
17  */
18 
32 #include "DRW_render.h"
33 
34 #include "BKE_object.h"
35 
36 #include "BLI_math.h"
37 
38 #include "workbench_engine.h"
39 #include "workbench_private.h"
40 
41 static void compute_parallel_lines_nor_and_dist(const float v1[2],
42  const float v2[2],
43  const float v3[2],
44  float r_line[4])
45 {
46  sub_v2_v2v2(r_line, v2, v1);
47  /* Find orthogonal vector. */
48  SWAP(float, r_line[0], r_line[1]);
49  r_line[0] = -r_line[0];
50  /* Edge distances. */
51  r_line[2] = dot_v2v2(r_line, v1);
52  r_line[3] = dot_v2v2(r_line, v3);
53  /* Make sure r_line[2] is the minimum. */
54  if (r_line[2] > r_line[3]) {
55  SWAP(float, r_line[2], r_line[3]);
56  }
57 }
58 
60 {
63 
64  if (wpd->shadow_changed) {
65  const float up[3] = {0.0f, 0.0f, 1.0f};
66  unit_m4(wpd->shadow_mat);
67 
68  /* TODO fix singularity. */
70  cross_v3_v3v3(wpd->shadow_mat[0], wpd->shadow_mat[2], up);
71  normalize_v3(wpd->shadow_mat[0]);
72  cross_v3_v3v3(wpd->shadow_mat[1], wpd->shadow_mat[2], wpd->shadow_mat[0]);
73 
75 
77  }
78 
79  float planes[6][4];
81  /* we only need the far plane. */
82  copy_v4_v4(wpd->shadow_far_plane, planes[2]);
83 
84  BoundBox frustum_corners;
85  DRW_culling_frustum_corners_get(NULL, &frustum_corners);
86 
87  float shadow_near_corners[4][3];
88  mul_v3_mat3_m4v3(shadow_near_corners[0], wpd->shadow_inv, frustum_corners.vec[0]);
89  mul_v3_mat3_m4v3(shadow_near_corners[1], wpd->shadow_inv, frustum_corners.vec[3]);
90  mul_v3_mat3_m4v3(shadow_near_corners[2], wpd->shadow_inv, frustum_corners.vec[7]);
91  mul_v3_mat3_m4v3(shadow_near_corners[3], wpd->shadow_inv, frustum_corners.vec[4]);
92 
94  for (int i = 0; i < 4; i++) {
95  minmax_v3v3_v3(wpd->shadow_near_min, wpd->shadow_near_max, shadow_near_corners[i]);
96  }
97 
98  compute_parallel_lines_nor_and_dist(shadow_near_corners[0],
99  shadow_near_corners[1],
100  shadow_near_corners[2],
101  wpd->shadow_near_sides[0]);
102  compute_parallel_lines_nor_and_dist(shadow_near_corners[1],
103  shadow_near_corners[2],
104  shadow_near_corners[0],
105  wpd->shadow_near_sides[1]);
106 }
107 
109 {
110  const DRWContextState *draw_ctx = DRW_context_state_get();
111  const Scene *scene = draw_ctx->scene;
112 
113  float view_matrix[4][4];
114  DRW_view_viewmat_get(NULL, view_matrix, false);
115 
116  /* Turn the light in a way where it's more user friendly to control. */
118  SWAP(float, wpd->shadow_direction_ws[2], wpd->shadow_direction_ws[1]);
119  wpd->shadow_direction_ws[2] = -wpd->shadow_direction_ws[2];
120  wpd->shadow_direction_ws[0] = -wpd->shadow_direction_ws[0];
121 
122  /* Shadow direction. */
124 
125  /* Clamp to avoid overshadowing and shading errors. */
126  float focus = clamp_f(scene->display.shadow_focus, 0.0001f, 0.99999f);
128  wd->shadow_focus = 1.0f - focus * (1.0f - wd->shadow_shift);
129 
130  if (SHADOW_ENABLED(wpd)) {
132  wd->shadow_add = 1.0f - wd->shadow_mul;
133  }
134  else {
135  wd->shadow_mul = 0.0f;
136  wd->shadow_add = 1.0f;
137  }
138 }
139 
141 {
142  WORKBENCH_PassList *psl = data->psl;
143  WORKBENCH_PrivateData *wpd = data->stl->wpd;
144  struct GPUShader *sh;
145  DRWShadingGroup *grp;
146 
147  if (SHADOW_ENABLED(wpd)) {
149 
150 #if DEBUG_SHADOW_VOLUME
151  DRWState depth_pass_state = DRW_STATE_DEPTH_LESS;
152  DRWState depth_fail_state = DRW_STATE_DEPTH_GREATER_EQUAL;
154 #else
158 #endif
159 
160  /* TODO(fclem): Merge into one pass with sub-passes. */
161  DRW_PASS_CREATE(psl->shadow_ps[0], state | depth_pass_state);
162  DRW_PASS_CREATE(psl->shadow_ps[1], state | depth_fail_state);
163 
164  /* Stencil Shadow passes. */
165  for (int manifold = 0; manifold < 2; manifold++) {
166  sh = workbench_shader_shadow_pass_get(manifold);
167  wpd->shadow_pass_grp[manifold] = grp = DRW_shgroup_create(sh, psl->shadow_ps[0]);
168  DRW_shgroup_stencil_mask(grp, 0xFF); /* Needed once to set the stencil state for the pass. */
169 
170  sh = workbench_shader_shadow_fail_get(manifold, false);
171  wpd->shadow_fail_grp[manifold] = grp = DRW_shgroup_create(sh, psl->shadow_ps[1]);
172  DRW_shgroup_stencil_mask(grp, 0xFF); /* Needed once to set the stencil state for the pass. */
173 
174  sh = workbench_shader_shadow_fail_get(manifold, true);
175  wpd->shadow_fail_caps_grp[manifold] = grp = DRW_shgroup_create(sh, psl->shadow_ps[1]);
176  }
177  }
178  else {
179  psl->shadow_ps[0] = NULL;
180  psl->shadow_ps[1] = NULL;
181  }
182 }
183 
185  Object *ob,
187 {
188  if (oed->shadow_bbox_dirty || wpd->shadow_changed) {
189  float tmp_mat[4][4];
190  mul_m4_m4m4(tmp_mat, wpd->shadow_inv, ob->obmat);
191 
192  /* Get AABB in shadow space. */
193  INIT_MINMAX(oed->shadow_min, oed->shadow_max);
194 
195  /* From object space to shadow space */
196  BoundBox *bbox = BKE_object_boundbox_get(ob);
197  for (int i = 0; i < 8; i++) {
198  float corner[3];
199  mul_v3_m4v3(corner, tmp_mat, bbox->vec[i]);
201  }
202  oed->shadow_depth = oed->shadow_max[2] - oed->shadow_min[2];
203  /* Extend towards infinity. */
204  oed->shadow_max[2] += 1e4f;
205 
206  /* Get extended AABB in world space. */
208  for (int i = 0; i < 8; i++) {
209  mul_m4_v3(wpd->shadow_mat, oed->shadow_bbox.vec[i]);
210  }
211  oed->shadow_bbox_dirty = false;
212  }
213 
214  return &oed->shadow_bbox;
215 }
216 
218  Object *ob,
220 {
221  BoundBox *shadow_bbox = workbench_shadow_object_shadow_bbox_get(wpd, ob, oed);
222  const DRWView *default_view = DRW_view_default_get();
223  return DRW_culling_box_test(default_view, shadow_bbox);
224 }
225 
227  Object *ob,
229 {
230  BoundBox *shadow_bbox = workbench_shadow_object_shadow_bbox_get(wpd, ob, oed);
231 
232  const int corners[4] = {0, 3, 4, 7};
233  float dist = 1e4f, dist_isect;
234  for (int i = 0; i < 4; i++) {
235  if (isect_ray_plane_v3(shadow_bbox->vec[corners[i]],
237  wpd->shadow_far_plane,
238  &dist_isect,
239  true)) {
240  if (dist_isect < dist) {
241  dist = dist_isect;
242  }
243  }
244  else {
245  /* All rays are parallels. If one fails, the other will too. */
246  break;
247  }
248  }
249  return max_ii(dist - oed->shadow_depth, 0);
250 }
251 
253  Object *ob,
255 {
256  /* Just to be sure the min, max are updated. */
258  /* Test if near plane is in front of the shadow. */
259  if (oed->shadow_min[2] > wpd->shadow_near_max[2]) {
260  return false;
261  }
262 
263  /* Separation Axis Theorem test */
264 
265  /* Test bbox sides first (faster) */
266  if ((oed->shadow_min[0] > wpd->shadow_near_max[0]) ||
267  (oed->shadow_max[0] < wpd->shadow_near_min[0]) ||
268  (oed->shadow_min[1] > wpd->shadow_near_max[1]) ||
269  (oed->shadow_max[1] < wpd->shadow_near_min[1])) {
270  return false;
271  }
272  /* Test projected near rectangle sides */
273  const float pts[4][2] = {
274  {oed->shadow_min[0], oed->shadow_min[1]},
275  {oed->shadow_min[0], oed->shadow_max[1]},
276  {oed->shadow_max[0], oed->shadow_min[1]},
277  {oed->shadow_max[0], oed->shadow_max[1]},
278  };
279 
280  for (int i = 0; i < 2; i++) {
281  float min_dst = FLT_MAX, max_dst = -FLT_MAX;
282  for (int j = 0; j < 4; j++) {
283  float dst = dot_v2v2(wpd->shadow_near_sides[i], pts[j]);
284  /* Do min max */
285  if (min_dst > dst) {
286  min_dst = dst;
287  }
288  if (max_dst < dst) {
289  max_dst = dst;
290  }
291  }
292 
293  if ((wpd->shadow_near_sides[i][2] > max_dst) || (wpd->shadow_near_sides[i][3] < min_dst)) {
294  return false;
295  }
296  }
297  /* No separation axis found. Both shape intersect. */
298  return true;
299 }
300 
302 {
304  data->shadow_bbox_dirty = true;
305 }
306 
307 void workbench_shadow_cache_populate(WORKBENCH_Data *data, Object *ob, const bool has_transp_mat)
308 {
309  WORKBENCH_PrivateData *wpd = data->stl->wpd;
310 
311  bool is_manifold;
312  struct GPUBatch *geom_shadow = DRW_cache_object_edge_detection_get(ob, &is_manifold);
313  if (geom_shadow == NULL) {
314  return;
315  }
316 
318  &ob->id,
320  sizeof(WORKBENCH_ObjectData),
322  NULL);
323 
324  if (workbench_shadow_object_cast_visible_shadow(wpd, ob, engine_object_data)) {
325  mul_v3_mat3_m4v3(engine_object_data->shadow_dir, ob->imat, wpd->shadow_direction_ws);
326 
327  DRWShadingGroup *grp;
328  bool use_shadow_pass_technique = !workbench_shadow_camera_in_object_shadow(
329  wpd, ob, engine_object_data);
330 
331  /* Shadow pass technique needs object to be have all its surface opaque. */
332  if (has_transp_mat) {
333  use_shadow_pass_technique = false;
334  }
335 
336  /* We cannot use Shadow Pass technique on non-manifold object (see T76168). */
337  if (use_shadow_pass_technique && !is_manifold && (wpd->cull_state != 0)) {
338  use_shadow_pass_technique = false;
339  }
340 
341  if (use_shadow_pass_technique) {
342  grp = DRW_shgroup_create_sub(wpd->shadow_pass_grp[is_manifold]);
343  DRW_shgroup_uniform_vec3(grp, "lightDirection", engine_object_data->shadow_dir, 1);
344  DRW_shgroup_uniform_float_copy(grp, "lightDistance", 1e5f);
345  DRW_shgroup_call_no_cull(grp, geom_shadow, ob);
346 #if DEBUG_SHADOW_VOLUME
347  DRW_debug_bbox(&engine_object_data->shadow_bbox, (float[4]){1.0f, 0.0f, 0.0f, 1.0f});
348 #endif
349  }
350  else {
351  float extrude_distance = workbench_shadow_object_shadow_distance(
352  wpd, ob, engine_object_data);
353 
354  /* TODO(fclem): only use caps if they are in the view frustum. */
355  const bool need_caps = true;
356  if (need_caps) {
357  grp = DRW_shgroup_create_sub(wpd->shadow_fail_caps_grp[is_manifold]);
358  DRW_shgroup_uniform_vec3(grp, "lightDirection", engine_object_data->shadow_dir, 1);
359  DRW_shgroup_uniform_float_copy(grp, "lightDistance", extrude_distance);
361  }
362 
363  grp = DRW_shgroup_create_sub(wpd->shadow_fail_grp[is_manifold]);
364  DRW_shgroup_uniform_vec3(grp, "lightDirection", engine_object_data->shadow_dir, 1);
365  DRW_shgroup_uniform_float_copy(grp, "lightDistance", extrude_distance);
366  DRW_shgroup_call_no_cull(grp, geom_shadow, ob);
367 #if DEBUG_SHADOW_VOLUME
368  DRW_debug_bbox(&engine_object_data->shadow_bbox, (float[4]){0.0f, 1.0f, 0.0f, 1.0f});
369 #endif
370  }
371  }
372 }
General operations, lookup, etc. for blender objects.
void BKE_boundbox_init_from_minmax(struct BoundBox *bb, const float min[3], const float max[3])
Definition: object.c:3778
struct BoundBox * BKE_object_boundbox_get(struct Object *ob)
Definition: object.c:3817
MINLINE float clamp_f(float value, float min, float max)
MINLINE int max_ii(int a, int b)
bool isect_ray_plane_v3(const float ray_origin[3], const float ray_direction[3], const float plane[4], float *r_lambda, const bool clip)
Definition: math_geom.c:1808
void mul_m4_m4m4(float R[4][4], const float A[4][4], const float B[4][4])
Definition: math_matrix.c:262
void unit_m4(float m[4][4])
Definition: rct.c:1140
bool invert_m4_m4(float R[4][4], const float A[4][4])
Definition: math_matrix.c:1278
void mul_m4_v3(const float M[4][4], float r[3])
Definition: math_matrix.c:732
void mul_v3_m4v3(float r[3], const float M[4][4], const float v[3])
Definition: math_matrix.c:742
void mul_v3_mat3_m4v3(float r[3], const float M[4][4], const float v[3])
Definition: math_matrix.c:804
MINLINE void copy_v4_v4(float r[4], const float a[4])
void minmax_v3v3_v3(float min[3], float max[3], const float vec[3])
Definition: math_vector.c:1020
MINLINE bool compare_v3v3(const float a[3], const float b[3], const float limit) ATTR_WARN_UNUSED_RESULT
MINLINE float normalize_v3(float r[3])
MINLINE void copy_v3_v3(float r[3], const float a[3])
MINLINE void cross_v3_v3v3(float r[3], const float a[3], const float b[3])
MINLINE void sub_v2_v2v2(float r[2], const float a[2], const float b[2])
MINLINE float dot_v2v2(const float a[2], const float b[2]) ATTR_WARN_UNUSED_RESULT
#define INIT_MINMAX(min, max)
#define SWAP(type, a, b)
#define DRW_shgroup_call_no_cull(shgroup, geom, ob)
Definition: DRW_render.h:433
DRWState
Definition: DRW_render.h:312
@ DRW_STATE_STENCIL_ALWAYS
Definition: DRW_render.h:331
@ DRW_STATE_DEPTH_LESS
Definition: DRW_render.h:322
@ DRW_STATE_DEPTH_GREATER_EQUAL
Definition: DRW_render.h:326
@ DRW_STATE_WRITE_STENCIL_SHADOW_FAIL
Definition: DRW_render.h:319
@ DRW_STATE_BLEND_ADD_FULL
Definition: DRW_render.h:338
@ DRW_STATE_WRITE_COLOR
Definition: DRW_render.h:315
@ DRW_STATE_WRITE_STENCIL_SHADOW_PASS
Definition: DRW_render.h:318
#define DRW_PASS_CREATE(pass, state)
Definition: DRW_render.h:593
GPUBatch
Definition: GPU_batch.h:93
_GL_VOID GLfloat value _GL_VOID_RET _GL_VOID const GLuint GLboolean *residences _GL_BOOL_RET _GL_VOID GLsizei GLfloat GLfloat GLfloat GLfloat const GLubyte *bitmap _GL_VOID_RET _GL_VOID GLenum const void *lists _GL_VOID_RET _GL_VOID const GLdouble *equation _GL_VOID_RET _GL_VOID GLdouble GLdouble blue _GL_VOID_RET _GL_VOID GLfloat GLfloat blue _GL_VOID_RET _GL_VOID GLint GLint blue _GL_VOID_RET _GL_VOID GLshort GLshort blue _GL_VOID_RET _GL_VOID GLubyte GLubyte blue _GL_VOID_RET _GL_VOID GLuint GLuint blue _GL_VOID_RET _GL_VOID GLushort GLushort blue _GL_VOID_RET _GL_VOID GLbyte GLbyte GLbyte alpha _GL_VOID_RET _GL_VOID GLdouble GLdouble GLdouble alpha _GL_VOID_RET _GL_VOID GLfloat GLfloat GLfloat alpha _GL_VOID_RET _GL_VOID GLint GLint GLint alpha _GL_VOID_RET _GL_VOID GLshort GLshort GLshort alpha _GL_VOID_RET _GL_VOID GLubyte GLubyte GLubyte alpha _GL_VOID_RET _GL_VOID GLuint GLuint GLuint alpha _GL_VOID_RET _GL_VOID GLushort GLushort GLushort alpha _GL_VOID_RET _GL_VOID GLenum mode _GL_VOID_RET _GL_VOID GLint GLsizei GLsizei GLenum type _GL_VOID_RET _GL_VOID GLsizei GLenum GLenum const void *pixels _GL_VOID_RET _GL_VOID const void *pointer _GL_VOID_RET _GL_VOID GLdouble v _GL_VOID_RET _GL_VOID GLfloat v _GL_VOID_RET _GL_VOID GLint GLint i2 _GL_VOID_RET _GL_VOID GLint j _GL_VOID_RET _GL_VOID GLfloat param _GL_VOID_RET _GL_VOID GLint param _GL_VOID_RET _GL_VOID GLdouble GLdouble GLdouble GLdouble GLdouble zFar _GL_VOID_RET _GL_UINT GLdouble *equation _GL_VOID_RET _GL_VOID GLenum GLint *params _GL_VOID_RET _GL_VOID GLenum GLfloat *v _GL_VOID_RET _GL_VOID GLenum GLfloat *params _GL_VOID_RET _GL_VOID GLfloat *values _GL_VOID_RET _GL_VOID GLushort *values _GL_VOID_RET _GL_VOID GLenum GLfloat *params _GL_VOID_RET _GL_VOID GLenum GLdouble *params _GL_VOID_RET _GL_VOID GLenum GLint *params _GL_VOID_RET _GL_VOID GLsizei const void *pointer _GL_VOID_RET _GL_VOID GLsizei const void *pointer _GL_VOID_RET _GL_BOOL GLfloat param _GL_VOID_RET _GL_VOID GLint param _GL_VOID_RET _GL_VOID GLenum GLfloat param _GL_VOID_RET _GL_VOID GLenum GLint param _GL_VOID_RET _GL_VOID GLushort pattern _GL_VOID_RET _GL_VOID GLdouble GLdouble GLint GLint const GLdouble *points _GL_VOID_RET _GL_VOID GLdouble GLdouble GLint GLint GLdouble v1
struct GPUShader GPUShader
Definition: GPU_shader.h:33
ATTR_WARN_UNUSED_RESULT const BMVert * v2
Scene scene
GPUBatch * DRW_cache_object_edge_detection_get(Object *ob, bool *r_is_manifold)
Definition: draw_cache.c:811
GPUBatch * DRW_cache_object_surface_get(Object *ob)
Definition: draw_cache.c:886
void DRW_debug_bbox(const BoundBox *bbox, const float color[4])
Definition: draw_debug.c:89
DrawData * DRW_drawdata_ensure(ID *id, DrawEngineType *engine_type, size_t size, DrawDataInitCb init_cb, DrawDataFreeCb free_cb)
Definition: draw_manager.c:899
const DRWContextState * DRW_context_state_get(void)
void DRW_shgroup_uniform_float_copy(DRWShadingGroup *shgroup, const char *name, const float value)
const DRWView * DRW_view_default_get(void)
void DRW_shgroup_uniform_vec3(DRWShadingGroup *shgroup, const char *name, const float *value, int arraysize)
DRWShadingGroup * DRW_shgroup_create(struct GPUShader *shader, DRWPass *pass)
DRWShadingGroup * DRW_shgroup_create_sub(DRWShadingGroup *shgroup)
void DRW_shgroup_stencil_mask(DRWShadingGroup *shgroup, uint mask)
void DRW_view_viewmat_get(const DRWView *view, float mat[4][4], bool inverse)
void DRW_culling_frustum_planes_get(const DRWView *view, float planes[6][4])
void DRW_culling_frustum_corners_get(const DRWView *view, BoundBox *corners)
bool DRW_culling_box_test(const DRWView *view, const BoundBox *bbox)
static ulong state[N]
float vec[8][3]
struct Scene * scene
Definition: DRW_render.h:745
float imat[4][4]
float obmat[4][4]
float light_direction[3]
struct SceneDisplay display
struct DRWPass * shadow_ps[2]
struct DRWShadingGroup * shadow_fail_caps_grp[2]
struct DRWShadingGroup * shadow_pass_grp[2]
struct DRWShadingGroup * shadow_fail_grp[2]
DrawEngineType draw_engine_workbench
GPUShader * workbench_shader_shadow_fail_get(bool manifold, bool cap)
GPUShader * workbench_shader_shadow_pass_get(bool manifold)
#define SHADOW_ENABLED(wpd)
static BoundBox * workbench_shadow_object_shadow_bbox_get(WORKBENCH_PrivateData *wpd, Object *ob, WORKBENCH_ObjectData *oed)
void workbench_shadow_data_update(WORKBENCH_PrivateData *wpd, WORKBENCH_UBO_World *wd)
static bool workbench_shadow_object_cast_visible_shadow(WORKBENCH_PrivateData *wpd, Object *ob, WORKBENCH_ObjectData *oed)
static void compute_parallel_lines_nor_and_dist(const float v1[2], const float v2[2], const float v3[2], float r_line[4])
void workbench_shadow_cache_populate(WORKBENCH_Data *data, Object *ob, const bool has_transp_mat)
static bool workbench_shadow_camera_in_object_shadow(WORKBENCH_PrivateData *wpd, Object *ob, WORKBENCH_ObjectData *oed)
static void workbench_init_object_data(DrawData *dd)
static float workbench_shadow_object_shadow_distance(WORKBENCH_PrivateData *wpd, Object *ob, WORKBENCH_ObjectData *oed)
void workbench_shadow_cache_init(WORKBENCH_Data *data)
static void workbench_shadow_update(WORKBENCH_PrivateData *wpd)