Blender  V3.3
workbench_shadow.c
Go to the documentation of this file.
1 /* SPDX-License-Identifier: GPL-2.0-or-later
2  * Copyright 2020 Blender Foundation. */
3 
17 #include "DRW_render.h"
18 
19 #include "BKE_object.h"
20 
21 #include "BLI_math.h"
22 
23 #include "workbench_engine.h"
24 #include "workbench_private.h"
25 
26 static void compute_parallel_lines_nor_and_dist(const float v1[2],
27  const float v2[2],
28  const float v3[2],
29  float r_line[4])
30 {
31  sub_v2_v2v2(r_line, v2, v1);
32  /* Find orthogonal vector. */
33  SWAP(float, r_line[0], r_line[1]);
34  r_line[0] = -r_line[0];
35  /* Edge distances. */
36  r_line[2] = dot_v2v2(r_line, v1);
37  r_line[3] = dot_v2v2(r_line, v3);
38  /* Make sure r_line[2] is the minimum. */
39  if (r_line[2] > r_line[3]) {
40  SWAP(float, r_line[2], r_line[3]);
41  }
42 }
43 
45 {
48 
49  if (wpd->shadow_changed) {
50  const float up[3] = {0.0f, 0.0f, 1.0f};
51  unit_m4(wpd->shadow_mat);
52 
53  /* TODO: fix singularity. */
55  cross_v3_v3v3(wpd->shadow_mat[0], wpd->shadow_mat[2], up);
56  normalize_v3(wpd->shadow_mat[0]);
57  cross_v3_v3v3(wpd->shadow_mat[1], wpd->shadow_mat[2], wpd->shadow_mat[0]);
58 
60 
62  }
63 
64  float planes[6][4];
66  /* we only need the far plane. */
67  copy_v4_v4(wpd->shadow_far_plane, planes[2]);
68 
69  BoundBox frustum_corners;
70  DRW_culling_frustum_corners_get(NULL, &frustum_corners);
71 
72  float shadow_near_corners[4][3];
73  mul_v3_mat3_m4v3(shadow_near_corners[0], wpd->shadow_inv, frustum_corners.vec[0]);
74  mul_v3_mat3_m4v3(shadow_near_corners[1], wpd->shadow_inv, frustum_corners.vec[3]);
75  mul_v3_mat3_m4v3(shadow_near_corners[2], wpd->shadow_inv, frustum_corners.vec[7]);
76  mul_v3_mat3_m4v3(shadow_near_corners[3], wpd->shadow_inv, frustum_corners.vec[4]);
77 
79  for (int i = 0; i < 4; i++) {
80  minmax_v3v3_v3(wpd->shadow_near_min, wpd->shadow_near_max, shadow_near_corners[i]);
81  }
82 
83  compute_parallel_lines_nor_and_dist(shadow_near_corners[0],
84  shadow_near_corners[1],
85  shadow_near_corners[2],
86  wpd->shadow_near_sides[0]);
87  compute_parallel_lines_nor_and_dist(shadow_near_corners[1],
88  shadow_near_corners[2],
89  shadow_near_corners[0],
90  wpd->shadow_near_sides[1]);
91 }
92 
94 {
95  const DRWContextState *draw_ctx = DRW_context_state_get();
96  const Scene *scene = draw_ctx->scene;
97 
98  float view_matrix[4][4];
99  DRW_view_viewmat_get(NULL, view_matrix, false);
100 
101  /* Turn the light in a way where it's more user friendly to control. */
103  SWAP(float, wpd->shadow_direction_ws[2], wpd->shadow_direction_ws[1]);
104  wpd->shadow_direction_ws[2] = -wpd->shadow_direction_ws[2];
105  wpd->shadow_direction_ws[0] = -wpd->shadow_direction_ws[0];
106 
107  /* Shadow direction. */
109 
110  /* Clamp to avoid overshadowing and shading errors. */
111  float focus = clamp_f(scene->display.shadow_focus, 0.0001f, 0.99999f);
113  wd->shadow_focus = 1.0f - focus * (1.0f - wd->shadow_shift);
114 
115  if (SHADOW_ENABLED(wpd)) {
117  wd->shadow_add = 1.0f - wd->shadow_mul;
118  }
119  else {
120  wd->shadow_mul = 0.0f;
121  wd->shadow_add = 1.0f;
122  }
123 }
124 
126 {
127  WORKBENCH_PassList *psl = data->psl;
128  WORKBENCH_PrivateData *wpd = data->stl->wpd;
129  struct GPUShader *sh;
130  DRWShadingGroup *grp;
131 
132  if (SHADOW_ENABLED(wpd)) {
134 
135 #if DEBUG_SHADOW_VOLUME
136  DRWState depth_pass_state = DRW_STATE_DEPTH_LESS;
137  DRWState depth_fail_state = DRW_STATE_DEPTH_GREATER_EQUAL;
139 #else
143 #endif
144 
145  /* TODO(fclem): Merge into one pass with sub-passes. */
146  DRW_PASS_CREATE(psl->shadow_ps[0], state | depth_pass_state);
147  DRW_PASS_CREATE(psl->shadow_ps[1], state | depth_fail_state);
148 
149  /* Stencil Shadow passes. */
150  for (int manifold = 0; manifold < 2; manifold++) {
152  wpd->shadow_pass_grp[manifold] = grp = DRW_shgroup_create(sh, psl->shadow_ps[0]);
153  DRW_shgroup_stencil_mask(grp, 0xFF); /* Needed once to set the stencil state for the pass. */
154 
155  sh = workbench_shader_shadow_fail_get(manifold, false);
156  wpd->shadow_fail_grp[manifold] = grp = DRW_shgroup_create(sh, psl->shadow_ps[1]);
157  DRW_shgroup_stencil_mask(grp, 0xFF); /* Needed once to set the stencil state for the pass. */
158 
159  sh = workbench_shader_shadow_fail_get(manifold, true);
160  wpd->shadow_fail_caps_grp[manifold] = grp = DRW_shgroup_create(sh, psl->shadow_ps[1]);
161  }
162  }
163  else {
164  psl->shadow_ps[0] = NULL;
165  psl->shadow_ps[1] = NULL;
166  }
167 }
168 
170  Object *ob,
172 {
173  if (oed->shadow_bbox_dirty || wpd->shadow_changed) {
174  float tmp_mat[4][4];
175  mul_m4_m4m4(tmp_mat, wpd->shadow_inv, ob->obmat);
176 
177  /* Get AABB in shadow space. */
178  INIT_MINMAX(oed->shadow_min, oed->shadow_max);
179 
180  /* From object space to shadow space */
181  const BoundBox *bbox = BKE_object_boundbox_get(ob);
182  for (int i = 0; i < 8; i++) {
183  float corner[3];
184  mul_v3_m4v3(corner, tmp_mat, bbox->vec[i]);
186  }
187  oed->shadow_depth = oed->shadow_max[2] - oed->shadow_min[2];
188  /* Extend towards infinity. */
189  oed->shadow_max[2] += 1e4f;
190 
191  /* Get extended AABB in world space. */
193  for (int i = 0; i < 8; i++) {
194  mul_m4_v3(wpd->shadow_mat, oed->shadow_bbox.vec[i]);
195  }
196  oed->shadow_bbox_dirty = false;
197  }
198 
199  return &oed->shadow_bbox;
200 }
201 
203  Object *ob,
205 {
206  const BoundBox *shadow_bbox = workbench_shadow_object_shadow_bbox_get(wpd, ob, oed);
207  const DRWView *default_view = DRW_view_default_get();
208  return DRW_culling_box_test(default_view, shadow_bbox);
209 }
210 
212  Object *ob,
214 {
215  const BoundBox *shadow_bbox = workbench_shadow_object_shadow_bbox_get(wpd, ob, oed);
216 
217  const int corners[4] = {0, 3, 4, 7};
218  float dist = 1e4f, dist_isect;
219  for (int i = 0; i < 4; i++) {
220  if (isect_ray_plane_v3(shadow_bbox->vec[corners[i]],
222  wpd->shadow_far_plane,
223  &dist_isect,
224  true)) {
225  if (dist_isect < dist) {
226  dist = dist_isect;
227  }
228  }
229  else {
230  /* All rays are parallels. If one fails, the other will too. */
231  break;
232  }
233  }
234  return max_ii(dist - oed->shadow_depth, 0);
235 }
236 
238  Object *ob,
240 {
241  /* Just to be sure the min, max are updated. */
243  /* Test if near plane is in front of the shadow. */
244  if (oed->shadow_min[2] > wpd->shadow_near_max[2]) {
245  return false;
246  }
247 
248  /* Separation Axis Theorem test */
249 
250  /* Test bbox sides first (faster) */
251  if ((oed->shadow_min[0] > wpd->shadow_near_max[0]) ||
252  (oed->shadow_max[0] < wpd->shadow_near_min[0]) ||
253  (oed->shadow_min[1] > wpd->shadow_near_max[1]) ||
254  (oed->shadow_max[1] < wpd->shadow_near_min[1])) {
255  return false;
256  }
257  /* Test projected near rectangle sides */
258  const float pts[4][2] = {
259  {oed->shadow_min[0], oed->shadow_min[1]},
260  {oed->shadow_min[0], oed->shadow_max[1]},
261  {oed->shadow_max[0], oed->shadow_min[1]},
262  {oed->shadow_max[0], oed->shadow_max[1]},
263  };
264 
265  for (int i = 0; i < 2; i++) {
266  float min_dst = FLT_MAX, max_dst = -FLT_MAX;
267  for (int j = 0; j < 4; j++) {
268  float dst = dot_v2v2(wpd->shadow_near_sides[i], pts[j]);
269  /* Do min max */
270  if (min_dst > dst) {
271  min_dst = dst;
272  }
273  if (max_dst < dst) {
274  max_dst = dst;
275  }
276  }
277 
278  if ((wpd->shadow_near_sides[i][2] > max_dst) || (wpd->shadow_near_sides[i][3] < min_dst)) {
279  return false;
280  }
281  }
282  /* No separation axis found. Both shape intersect. */
283  return true;
284 }
285 
287 {
289  data->shadow_bbox_dirty = true;
290 }
291 
292 void workbench_shadow_cache_populate(WORKBENCH_Data *data, Object *ob, const bool has_transp_mat)
293 {
294  WORKBENCH_PrivateData *wpd = data->stl->wpd;
295 
296  bool is_manifold;
297  struct GPUBatch *geom_shadow = DRW_cache_object_edge_detection_get(ob, &is_manifold);
298  if (geom_shadow == NULL) {
299  return;
300  }
301 
303  &ob->id,
305  sizeof(WORKBENCH_ObjectData),
307  NULL);
308 
309  if (workbench_shadow_object_cast_visible_shadow(wpd, ob, engine_object_data)) {
310  mul_v3_mat3_m4v3(engine_object_data->shadow_dir, ob->imat, wpd->shadow_direction_ws);
311 
312  DRWShadingGroup *grp;
313  bool use_shadow_pass_technique = !workbench_shadow_camera_in_object_shadow(
314  wpd, ob, engine_object_data);
315 
316  /* Shadow pass technique needs object to be have all its surface opaque. */
317  if (has_transp_mat) {
318  use_shadow_pass_technique = false;
319  }
320 
321  /* We cannot use Shadow Pass technique on non-manifold object (see T76168). */
322  if (use_shadow_pass_technique && !is_manifold && (wpd->cull_state != 0)) {
323  use_shadow_pass_technique = false;
324  }
325 
326  if (use_shadow_pass_technique) {
327  grp = DRW_shgroup_create_sub(wpd->shadow_pass_grp[is_manifold]);
328  DRW_shgroup_uniform_vec3(grp, "lightDirection", engine_object_data->shadow_dir, 1);
329  DRW_shgroup_uniform_float_copy(grp, "lightDistance", 1e5f);
330  DRW_shgroup_call_no_cull(grp, geom_shadow, ob);
331 #if DEBUG_SHADOW_VOLUME
332  DRW_debug_bbox(&engine_object_data->shadow_bbox, (float[4]){1.0f, 0.0f, 0.0f, 1.0f});
333 #endif
334  }
335  else {
336  float extrude_distance = workbench_shadow_object_shadow_distance(
337  wpd, ob, engine_object_data);
338 
339  /* TODO(fclem): only use caps if they are in the view frustum. */
340  const bool need_caps = true;
341  if (need_caps) {
342  grp = DRW_shgroup_create_sub(wpd->shadow_fail_caps_grp[is_manifold]);
343  DRW_shgroup_uniform_vec3(grp, "lightDirection", engine_object_data->shadow_dir, 1);
344  DRW_shgroup_uniform_float_copy(grp, "lightDistance", extrude_distance);
346  }
347 
348  grp = DRW_shgroup_create_sub(wpd->shadow_fail_grp[is_manifold]);
349  DRW_shgroup_uniform_vec3(grp, "lightDirection", engine_object_data->shadow_dir, 1);
350  DRW_shgroup_uniform_float_copy(grp, "lightDistance", extrude_distance);
351  DRW_shgroup_call_no_cull(grp, geom_shadow, ob);
352 #if DEBUG_SHADOW_VOLUME
353  DRW_debug_bbox(&engine_object_data->shadow_bbox, (float[4]){0.0f, 1.0f, 0.0f, 1.0f});
354 #endif
355  }
356  }
357 }
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.cc:3645
const struct BoundBox * BKE_object_boundbox_get(struct Object *ob)
Definition: object.cc:3684
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, bool clip)
Definition: math_geom.c:1713
void mul_m4_m4m4(float R[4][4], const float A[4][4], const float B[4][4])
Definition: math_matrix.c:259
void unit_m4(float m[4][4])
Definition: rct.c:1090
bool invert_m4_m4(float R[4][4], const float A[4][4])
Definition: math_matrix.c:1287
void mul_m4_v3(const float M[4][4], float r[3])
Definition: math_matrix.c:729
void mul_v3_m4v3(float r[3], const float M[4][4], const float v[3])
Definition: math_matrix.c:739
void mul_v3_mat3_m4v3(float r[3], const float M[4][4], const float v[3])
Definition: math_matrix.c:800
MINLINE void copy_v4_v4(float r[4], const float a[4])
MINLINE bool compare_v3v3(const float a[3], const float b[3], float limit) ATTR_WARN_UNUSED_RESULT
void minmax_v3v3_v3(float min[3], float max[3], const float vec[3])
Definition: math_vector.c:867
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:431
DRWState
Definition: DRW_render.h:298
@ DRW_STATE_STENCIL_ALWAYS
Definition: DRW_render.h:319
@ DRW_STATE_DEPTH_LESS
Definition: DRW_render.h:310
@ DRW_STATE_DEPTH_GREATER_EQUAL
Definition: DRW_render.h:314
@ DRW_STATE_WRITE_STENCIL_SHADOW_FAIL
Definition: DRW_render.h:307
@ DRW_STATE_BLEND_ADD_FULL
Definition: DRW_render.h:326
@ DRW_STATE_WRITE_COLOR
Definition: DRW_render.h:303
@ DRW_STATE_WRITE_STENCIL_SHADOW_PASS
Definition: DRW_render.h:306
#define DRW_PASS_CREATE(pass, state)
Definition: DRW_render.h:690
GPUBatch
Definition: GPU_batch.h:78
_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:20
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:812
GPUBatch * DRW_cache_object_surface_get(Object *ob)
Definition: draw_cache.c:887
void DRW_debug_bbox(const BoundBox *bbox, const float color[4])
Definition: draw_debug.c:74
DrawData * DRW_drawdata_ensure(ID *id, DrawEngineType *engine_type, size_t size, DrawDataInitCb init_cb, DrawDataFreeCb free_cb)
Definition: draw_manager.c:866
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)
const int state
ccl_gpu_kernel_postfix ccl_global float int int int int sh
float vec[8][3]
struct Scene * scene
Definition: DRW_render.h:979
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)
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 const BoundBox * workbench_shadow_object_shadow_bbox_get(WORKBENCH_PrivateData *wpd, Object *ob, WORKBENCH_ObjectData *oed)
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)