Blender  V2.93
eevee_shadows_cascade.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 2019, Blender Foundation.
17  */
18 
23 #include "BLI_rect.h"
24 #include "BLI_sys_types.h" /* bool */
25 
26 #include "BKE_object.h"
27 
28 #include "eevee_private.h"
29 
30 #include "BLI_rand.h" /* needs to be after for some reason. */
31 
33 {
34  if (linfo->cascade_len >= MAX_SHADOW_CASCADE) {
35  return;
36  }
37 
38  const Light *la = (Light *)ob->data;
39  EEVEE_Shadow *sh_data = linfo->shadow_data + linfo->shadow_len;
40  EEVEE_ShadowCascade *csm_data = linfo->shadow_cascade_data + linfo->cascade_len;
41  EEVEE_ShadowCascadeRender *csm_render = linfo->shadow_cascade_render + linfo->cascade_len;
42 
44 
45  linfo->shadow_cascade_light_indices[linfo->cascade_len] = linfo->num_light;
46  evli->shadow_id = linfo->shadow_len++;
47  sh_data->type_data_id = linfo->cascade_len++;
48  csm_data->tex_id = linfo->num_cascade_layer;
49  csm_render->cascade_fade = la->cascade_fade;
50  csm_render->cascade_count = la->cascade_count;
51  csm_render->cascade_exponent = la->cascade_exponent;
52  csm_render->cascade_max_dist = la->cascade_max_dist;
53  csm_render->original_bias = max_ff(la->bias, 0.0f);
54 
55  linfo->num_cascade_layer += la->cascade_count;
56 }
57 
58 static void shadow_cascade_random_matrix_set(float mat[4][4], float radius, int sample_ofs)
59 {
60  float jitter[3];
61 #ifndef DEBUG_SHADOW_DISTRIBUTION
62  EEVEE_sample_ellipse(sample_ofs, mat[0], mat[1], radius, radius, jitter);
63 #else
64  for (int i = 0; i <= sample_ofs; i++) {
65  EEVEE_sample_ellipse(i, mat[0], mat[1], radius, radius, jitter);
66  float p[3];
67  add_v3_v3v3(p, jitter, mat[2]);
68  DRW_debug_sphere(p, 0.01f, (float[4]){1.0f, (sample_ofs == i) ? 1.0f : 0.0f, 0.0f, 1.0f});
69  }
70 #endif
71  add_v3_v3(mat[2], jitter);
72  orthogonalize_m4(mat, 2);
73 }
74 
75 static double round_to_digits(double value, int digits)
76 {
77  double factor = pow(10.0, digits - ceil(log10(fabs(value))));
78  return round(value * factor) / factor;
79 }
80 
81 static void frustum_min_bounding_sphere(const float corners[8][3],
82  float r_center[3],
83  float *r_radius)
84 {
85 #if 0 /* Simple solution but waste too much space. */
86  float minvec[3], maxvec[3];
87 
88  /* compute the bounding box */
89  INIT_MINMAX(minvec, maxvec);
90  for (int i = 0; i < 8; i++) {
91  minmax_v3v3_v3(minvec, maxvec, corners[i]);
92  }
93 
94  /* compute the bounding sphere of this box */
95  r_radius = len_v3v3(minvec, maxvec) * 0.5f;
96  add_v3_v3v3(r_center, minvec, maxvec);
97  mul_v3_fl(r_center, 0.5f);
98 #else
99  /* Find averaged center. */
100  zero_v3(r_center);
101  for (int i = 0; i < 8; i++) {
102  add_v3_v3(r_center, corners[i]);
103  }
104  mul_v3_fl(r_center, 1.0f / 8.0f);
105 
106  /* Search the largest distance from the sphere center. */
107  *r_radius = 0.0f;
108  for (int i = 0; i < 8; i++) {
109  float rad = len_squared_v3v3(corners[i], r_center);
110  if (rad > *r_radius) {
111  *r_radius = rad;
112  }
113  }
114 
115  /* TODO try to reduce the radius further by moving the center.
116  * Remember we need a __stable__ solution! */
117 
118  /* Try to reduce float imprecision leading to shimmering. */
119  *r_radius = (float)round_to_digits(sqrtf(*r_radius), 3);
120 #endif
121 }
122 
124  EEVEE_Light *evli,
125  DRWView *view,
126  float view_near,
127  float view_far,
128  int sample_ofs)
129 {
130  EEVEE_Shadow *shdw_data = linfo->shadow_data + (int)evli->shadow_id;
131  EEVEE_ShadowCascade *csm_data = linfo->shadow_cascade_data + (int)shdw_data->type_data_id;
132  EEVEE_ShadowCascadeRender *csm_render = linfo->shadow_cascade_render +
133  (int)shdw_data->type_data_id;
134  int cascade_nbr = csm_render->cascade_count;
135  float cascade_fade = csm_render->cascade_fade;
136  float cascade_max_dist = csm_render->cascade_max_dist;
137  float cascade_exponent = csm_render->cascade_exponent;
138 
139  float jitter_ofs[2];
140  double ht_point[2];
141  double ht_offset[2] = {0.0, 0.0};
142  const uint ht_primes[2] = {2, 3};
143 
144  BLI_halton_2d(ht_primes, ht_offset, sample_ofs, ht_point);
145 
146  /* Not really sure why we need 4.0 factor here. */
147  jitter_ofs[0] = (ht_point[0] * 2.0 - 1.0) * 4.0 / linfo->shadow_cascade_size;
148  jitter_ofs[1] = (ht_point[1] * 2.0 - 1.0) * 4.0 / linfo->shadow_cascade_size;
149 
150  /* Camera Matrices */
151  float persinv[4][4], vp_projmat[4][4];
152  DRW_view_persmat_get(view, persinv, true);
153  DRW_view_winmat_get(view, vp_projmat, false);
154  bool is_persp = DRW_view_is_persp_get(view);
155 
156  /* obmat = Object Space > World Space */
157  /* viewmat = World Space > View Space */
158  float(*viewmat)[4] = csm_render->viewmat;
159  eevee_light_matrix_get(evli, viewmat);
160  /* At this point, viewmat == normalize_m4(obmat) */
161 
162  if (linfo->soft_shadows) {
163  shadow_cascade_random_matrix_set(viewmat, evli->radius, sample_ofs);
164  }
165 
166  copy_m4_m4(csm_render->viewinv, viewmat);
167  invert_m4(viewmat);
168 
169  copy_v3_v3(csm_data->shadow_vec, csm_render->viewinv[2]);
170 
171  /* Compute near and far value based on all shadow casters cumulated AABBs. */
172  float sh_near = -1.0e30f, sh_far = 1.0e30f;
173  BoundBox shcaster_bounds;
175  &shcaster_bounds, linfo->shcaster_aabb.min, linfo->shcaster_aabb.max);
176 #ifdef DEBUG_CSM
177  float dbg_col1[4] = {1.0f, 0.5f, 0.6f, 1.0f};
178  DRW_debug_bbox(&shcaster_bounds, dbg_col1);
179 #endif
180  for (int i = 0; i < 8; i++) {
181  mul_m4_v3(viewmat, shcaster_bounds.vec[i]);
182  sh_near = max_ff(sh_near, shcaster_bounds.vec[i][2]);
183  sh_far = min_ff(sh_far, shcaster_bounds.vec[i][2]);
184  }
185 #ifdef DEBUG_CSM
186  float dbg_col2[4] = {0.5f, 1.0f, 0.6f, 1.0f};
187  float pts[2][3] = {{0.0, 0.0, sh_near}, {0.0, 0.0, sh_far}};
188  mul_m4_v3(csm_render->viewinv, pts[0]);
189  mul_m4_v3(csm_render->viewinv, pts[1]);
190  DRW_debug_sphere(pts[0], 1.0f, dbg_col1);
191  DRW_debug_sphere(pts[1], 1.0f, dbg_col2);
192 #endif
193  /* The rest of the function is assuming inverted Z. */
194  /* Add a little bias to avoid invalid matrices. */
195  sh_far = -(sh_far - 1e-3);
196  sh_near = -sh_near;
197 
198  /* The technique consists into splitting
199  * the view frustum into several sub-frustum
200  * that are individually receiving one shadow map */
201 
202  float csm_start, csm_end;
203 
204  if (is_persp) {
205  csm_start = view_near;
206  csm_end = max_ff(view_far, -cascade_max_dist);
207  /* Avoid artifacts */
208  csm_end = min_ff(view_near, csm_end);
209  }
210  else {
211  csm_start = -view_far;
212  csm_end = view_far;
213  }
214 
215  /* init near/far */
216  for (int c = 0; c < MAX_CASCADE_NUM; c++) {
217  csm_data->split_start[c] = csm_end;
218  csm_data->split_end[c] = csm_end;
219  }
220 
221  /* Compute split planes */
222  float splits_start_ndc[MAX_CASCADE_NUM];
223  float splits_end_ndc[MAX_CASCADE_NUM];
224 
225  {
226  /* Nearest plane */
227  float p[4] = {1.0f, 1.0f, csm_start, 1.0f};
228  /* TODO: we don't need full m4 multiply here */
229  mul_m4_v4(vp_projmat, p);
230  splits_start_ndc[0] = p[2];
231  if (is_persp) {
232  splits_start_ndc[0] /= p[3];
233  }
234  }
235 
236  {
237  /* Farthest plane */
238  float p[4] = {1.0f, 1.0f, csm_end, 1.0f};
239  /* TODO: we don't need full m4 multiply here */
240  mul_m4_v4(vp_projmat, p);
241  splits_end_ndc[cascade_nbr - 1] = p[2];
242  if (is_persp) {
243  splits_end_ndc[cascade_nbr - 1] /= p[3];
244  }
245  }
246 
247  csm_data->split_start[0] = csm_start;
248  csm_data->split_end[cascade_nbr - 1] = csm_end;
249 
250  for (int c = 1; c < cascade_nbr; c++) {
251  /* View Space */
252  float linear_split = interpf(csm_end, csm_start, c / (float)cascade_nbr);
253  float exp_split = csm_start * powf(csm_end / csm_start, c / (float)cascade_nbr);
254 
255  if (is_persp) {
256  csm_data->split_start[c] = interpf(exp_split, linear_split, cascade_exponent);
257  }
258  else {
259  csm_data->split_start[c] = linear_split;
260  }
261  csm_data->split_end[c - 1] = csm_data->split_start[c];
262 
263  /* Add some overlap for smooth transition */
264  csm_data->split_start[c] = interpf((c > 1) ? csm_data->split_end[c - 2] :
265  csm_data->split_start[0],
266  csm_data->split_end[c - 1],
267  cascade_fade);
268 
269  /* NDC Space */
270  {
271  float p[4] = {1.0f, 1.0f, csm_data->split_start[c], 1.0f};
272  /* TODO: we don't need full m4 multiply here */
273  mul_m4_v4(vp_projmat, p);
274  splits_start_ndc[c] = p[2];
275 
276  if (is_persp) {
277  splits_start_ndc[c] /= p[3];
278  }
279  }
280 
281  {
282  float p[4] = {1.0f, 1.0f, csm_data->split_end[c - 1], 1.0f};
283  /* TODO: we don't need full m4 multiply here */
284  mul_m4_v4(vp_projmat, p);
285  splits_end_ndc[c - 1] = p[2];
286 
287  if (is_persp) {
288  splits_end_ndc[c - 1] /= p[3];
289  }
290  }
291  }
292 
293  /* Set last cascade split fade distance into the first split_start. */
294  float prev_split = (cascade_nbr > 1) ? csm_data->split_end[cascade_nbr - 2] :
295  csm_data->split_start[0];
296  csm_data->split_start[0] = interpf(
297  prev_split, csm_data->split_end[cascade_nbr - 1], cascade_fade);
298 
299  /* For each cascade */
300  for (int c = 0; c < cascade_nbr; c++) {
301  float(*projmat)[4] = csm_render->projmat[c];
302  /* Given 8 frustum corners */
303  float corners[8][3] = {
304  /* Near Cap */
305  {1.0f, -1.0f, splits_start_ndc[c]},
306  {-1.0f, -1.0f, splits_start_ndc[c]},
307  {-1.0f, 1.0f, splits_start_ndc[c]},
308  {1.0f, 1.0f, splits_start_ndc[c]},
309  /* Far Cap */
310  {1.0f, -1.0f, splits_end_ndc[c]},
311  {-1.0f, -1.0f, splits_end_ndc[c]},
312  {-1.0f, 1.0f, splits_end_ndc[c]},
313  {1.0f, 1.0f, splits_end_ndc[c]},
314  };
315 
316  /* Transform them into world space */
317  for (int i = 0; i < 8; i++) {
318  mul_project_m4_v3(persinv, corners[i]);
319  }
320 
321  float center[3];
322  frustum_min_bounding_sphere(corners, center, &(csm_render->radius[c]));
323 
324 #ifdef DEBUG_CSM
325  float dbg_col[4] = {0.0f, 0.0f, 0.0f, 1.0f};
326  if (c < 3) {
327  dbg_col[c] = 1.0f;
328  }
329  DRW_debug_bbox((BoundBox *)&corners, dbg_col);
330  DRW_debug_sphere(center, csm_render->radius[c], dbg_col);
331 #endif
332 
333  /* Project into light-space. */
334  mul_m4_v3(viewmat, center);
335 
336  /* Snap projection center to nearest texel to cancel shimmering. */
337  float shadow_origin[2], shadow_texco[2];
338  /* Light to texture space. */
339  mul_v2_v2fl(
340  shadow_origin, center, linfo->shadow_cascade_size / (2.0f * csm_render->radius[c]));
341 
342  /* Find the nearest texel. */
343  shadow_texco[0] = roundf(shadow_origin[0]);
344  shadow_texco[1] = roundf(shadow_origin[1]);
345 
346  /* Compute offset. */
347  sub_v2_v2(shadow_texco, shadow_origin);
348  /* Texture to light space. */
349  mul_v2_fl(shadow_texco, (2.0f * csm_render->radius[c]) / linfo->shadow_cascade_size);
350 
351  /* Apply offset. */
352  add_v2_v2(center, shadow_texco);
353 
354  /* Expand the projection to cover frustum range */
355  rctf rect_cascade;
356  BLI_rctf_init_pt_radius(&rect_cascade, center, csm_render->radius[c]);
357  orthographic_m4(projmat,
358  rect_cascade.xmin,
359  rect_cascade.xmax,
360  rect_cascade.ymin,
361  rect_cascade.ymax,
362  sh_near,
363  sh_far);
364 
365  /* Anti-Aliasing */
366  if (linfo->soft_shadows) {
367  add_v2_v2(projmat[3], jitter_ofs);
368  }
369 
370  float viewprojmat[4][4];
371  mul_m4_m4m4(viewprojmat, projmat, viewmat);
372  mul_m4_m4m4(csm_data->shadowmat[c], texcomat, viewprojmat);
373 
374 #ifdef DEBUG_CSM
375  DRW_debug_m4_as_bbox(viewprojmat, dbg_col, true);
376 #endif
377  }
378 
379  /* Bias is in clip-space, divide by range. */
380  shdw_data->bias = csm_render->original_bias * 0.05f / fabsf(sh_far - sh_near);
381  shdw_data->near = sh_near;
382  shdw_data->far = sh_far;
383 }
384 
387 {
388  for (int i = 0; i < csm_render->cascade_count; i++) {
389  if (view[i] == NULL) {
390  view[i] = DRW_view_create(csm_render->viewmat, csm_render->projmat[i], NULL, NULL, NULL);
391  }
392  else {
393  DRW_view_update(view[i], csm_render->viewmat, csm_render->projmat[i], NULL, NULL);
394  }
395  }
396 }
397 
399  EEVEE_Data *vedata,
400  DRWView *view,
401  int cascade_index)
402 {
403  EEVEE_PassList *psl = vedata->psl;
404  EEVEE_StorageList *stl = vedata->stl;
405  EEVEE_EffectsInfo *effects = stl->effects;
406  EEVEE_PrivateData *g_data = stl->g_data;
407  EEVEE_LightsInfo *linfo = sldata->lights;
408 
409  EEVEE_Light *evli = linfo->light_data + linfo->shadow_cascade_light_indices[cascade_index];
410  EEVEE_Shadow *shdw_data = linfo->shadow_data + (int)evli->shadow_id;
411  EEVEE_ShadowCascade *csm_data = linfo->shadow_cascade_data + (int)shdw_data->type_data_id;
412  EEVEE_ShadowCascadeRender *csm_render = linfo->shadow_cascade_render +
413  (int)shdw_data->type_data_id;
414 
415  float near = DRW_view_near_distance_get(view);
416  float far = DRW_view_far_distance_get(view);
417 
418  eevee_shadow_cascade_setup(linfo, evli, view, near, far, effects->taa_current_sample - 1);
419 
420  /* Meh, Reusing the cube views. */
422  eevee_ensure_cascade_views(csm_render, g_data->cube_views);
423 
424  /* Render shadow cascades */
425  /* Render cascade separately: seems to be faster for the general case.
426  * The only time it's more beneficial is when the CPU culling overhead
427  * outweigh the instancing overhead. which is rarely the case. */
428  for (int j = 0; j < csm_render->cascade_count; j++) {
429  DRW_view_set_active(g_data->cube_views[j]);
430  int layer = csm_data->tex_id + j;
432  sldata->shadow_fb, sldata->shadow_cascade_pool, 0, layer, 0);
434  GPU_framebuffer_clear_depth(sldata->shadow_fb, 1.0f);
436  }
437 }
typedef float(TangentPoint)[2]
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
#define BLI_assert(a)
Definition: BLI_assert.h:58
MINLINE float max_ff(float a, float b)
MINLINE float min_ff(float a, float b)
MINLINE float interpf(float a, float b, float t)
void orthographic_m4(float mat[4][4], const float left, const float right, const float bottom, const float top, const float nearClip, const float farClip)
Definition: math_geom.c:4802
void mul_project_m4_v3(const float M[4][4], float vec[3])
Definition: math_matrix.c:824
void orthogonalize_m4(float R[4][4], int axis)
Definition: math_matrix.c:1514
void mul_m4_m4m4(float R[4][4], const float A[4][4], const float B[4][4])
Definition: math_matrix.c:262
bool invert_m4(float R[4][4])
Definition: math_matrix.c:1187
void mul_m4_v4(const float M[4][4], float r[4])
Definition: math_matrix.c:866
void mul_m4_v3(const float M[4][4], float r[3])
Definition: math_matrix.c:732
void copy_m4_m4(float m1[4][4], const float m2[4][4])
Definition: math_matrix.c:95
MINLINE float len_v3v3(const float a[3], const float b[3]) ATTR_WARN_UNUSED_RESULT
void minmax_v3v3_v3(float min[3], float max[3], const float vec[3])
Definition: math_vector.c:1020
MINLINE void sub_v2_v2(float r[2], const float a[2])
MINLINE float len_squared_v3v3(const float a[3], const float b[3]) ATTR_WARN_UNUSED_RESULT
MINLINE void mul_v2_fl(float r[2], float f)
MINLINE void mul_v3_fl(float r[3], float f)
MINLINE void copy_v3_v3(float r[3], const float a[3])
MINLINE void add_v2_v2(float r[2], const float a[2])
MINLINE void add_v3_v3v3(float r[3], const float a[3], const float b[3])
MINLINE void zero_v3(float r[3])
MINLINE void mul_v2_v2fl(float r[2], const float a[2], float f)
MINLINE void add_v3_v3(float r[3], const float a[3])
Random number functions.
void BLI_halton_2d(const unsigned int prime[2], double offset[2], int n, double *r)
Definition: rand.cc:310
void BLI_rctf_init_pt_radius(struct rctf *rect, const float xy[2], float size)
Definition: rct.c:500
unsigned int uint
Definition: BLI_sys_types.h:83
#define INIT_MINMAX(min, max)
static AppView * view
NSNotificationCenter * center
void GPU_framebuffer_bind(GPUFrameBuffer *fb)
BASIC_Shaders sh_data[GPU_SHADER_CFG_LEN]
Definition: basic_engine.c:76
ATTR_WARN_UNUSED_RESULT const BMVert const BMEdge * e
void DRW_debug_m4_as_bbox(const float m[4][4], const float color[4], const bool invert)
Definition: draw_debug.c:107
void DRW_debug_sphere(const float center[3], const float radius, const float color[4])
Definition: draw_debug.c:126
void DRW_debug_bbox(const BoundBox *bbox, const float color[4])
Definition: draw_debug.c:89
float DRW_view_near_distance_get(const DRWView *view)
void DRW_view_persmat_get(const DRWView *view, float mat[4][4], bool inverse)
bool DRW_view_is_persp_get(const DRWView *view)
void DRW_view_winmat_get(const DRWView *view, float mat[4][4], bool inverse)
DRWView * DRW_view_create(const float viewmat[4][4], const float winmat[4][4], const float(*culling_viewmat)[4], const float(*culling_winmat)[4], DRWCallVisibilityFn *visibility_fn)
void DRW_view_update(DRWView *view, const float viewmat[4][4], const float winmat[4][4], const float(*culling_viewmat)[4], const float(*culling_winmat)[4])
float DRW_view_far_distance_get(const DRWView *view)
void DRW_view_set_active(DRWView *view)
void DRW_draw_pass(DRWPass *pass)
void eevee_light_matrix_get(const EEVEE_Light *evli, float r_mat[4][4])
Definition: eevee_lights.c:32
#define MAX_SHADOW_CASCADE
Definition: eevee_private.h:53
void EEVEE_sample_ellipse(int sample_ofs, const float x_axis[3], const float y_axis[3], float size_x, float size_y, float rsample[3])
static const float texcomat[4][4]
#define MAX_CASCADE_NUM
Definition: eevee_private.h:51
void eevee_contact_shadow_setup(const Light *la, EEVEE_Shadow *evsh)
Definition: eevee_shadows.c:34
static void eevee_ensure_cascade_views(EEVEE_ShadowCascadeRender *csm_render, DRWView *view[MAX_CASCADE_NUM])
static void eevee_shadow_cascade_setup(EEVEE_LightsInfo *linfo, EEVEE_Light *evli, DRWView *view, float view_near, float view_far, int sample_ofs)
static double round_to_digits(double value, int digits)
void EEVEE_shadows_draw_cascades(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata, DRWView *view, int cascade_index)
void EEVEE_shadows_cascade_add(EEVEE_LightsInfo *linfo, EEVEE_Light *evli, Object *ob)
static void frustum_min_bounding_sphere(const float corners[8][3], float r_center[3], float *r_radius)
static void shadow_cascade_random_matrix_set(float mat[4][4], float radius, int sample_ofs)
void GPU_framebuffer_texture_layer_attach(GPUFrameBuffer *fb, GPUTexture *tex, int slot, int layer, int mip)
#define powf(x, y)
#define fabsf(x)
#define sqrtf(x)
static unsigned c
Definition: RandGen.cpp:97
INLINE Rall1d< T, V, S > pow(const Rall1d< T, V, S > &arg, double m)
Definition: rall1d.h:359
float vec[8][3]
EEVEE_StorageList * stl
EEVEE_PassList * psl
struct EEVEE_Light light_data[MAX_LIGHT]
uchar shadow_cascade_light_indices[MAX_SHADOW_CASCADE]
struct EEVEE_Shadow shadow_data[MAX_SHADOW]
struct EEVEE_ShadowCascadeRender shadow_cascade_render[MAX_SHADOW_CASCADE]
struct EEVEE_ShadowCascade shadow_cascade_data[MAX_SHADOW_CASCADE]
struct DRWPass * shadow_pass
struct DRWView * cube_views[6]
float projmat[MAX_CASCADE_NUM][4][4]
float type_data_id
struct EEVEE_PrivateData * g_data
struct EEVEE_EffectsInfo * effects
struct GPUFrameBuffer * shadow_fb
struct GPUTexture * shadow_cascade_pool
struct EEVEE_LightsInfo * lights
float cascade_exponent
int cascade_count
float cascade_max_dist
float cascade_fade
float bias
void * data
float xmax
Definition: DNA_vec_types.h:85
float xmin
Definition: DNA_vec_types.h:85
float ymax
Definition: DNA_vec_types.h:86
float ymin
Definition: DNA_vec_types.h:86
ccl_device_inline float2 fabs(const float2 &a)
ccl_device_inline float3 ceil(const float3 &a)