Welcome to mirror list, hosted at ThFree Co, Russian Federation.

area.h « light « kernel « cycles « intern - git.blender.org/blender.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
blob: 7d6f2a5bef9c32e1c7ed8dea1b3d9e47f4bdf256 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
/* SPDX-License-Identifier: Apache-2.0
 * Copyright 2011-2022 Blender Foundation */

#pragma once

#include "kernel/light/common.h"

CCL_NAMESPACE_BEGIN

/* Importance sampling.
 *
 * An Area-Preserving Parametrization for Spherical Rectangles.
 * Carlos Urena et al.
 *
 * NOTE: light_p is modified when sample_coord is true. */
ccl_device_inline float area_light_rect_sample(float3 P,
                                               ccl_private float3 *light_p,
                                               float3 extentu,
                                               float3 extentv,
                                               float randu,
                                               float randv,
                                               bool sample_coord)
{
  /* In our name system we're using P for the center, which is o in the paper. */
  float3 corner = *light_p - extentu * 0.5f - extentv * 0.5f;
  float extentu_len, extentv_len;
  /* Compute local reference system R. */
  float3 x = normalize_len(extentu, &extentu_len);
  float3 y = normalize_len(extentv, &extentv_len);
  float3 z = cross(x, y);
  /* Compute rectangle coords in local reference system. */
  float3 dir = corner - P;
  float z0 = dot(dir, z);
  /* Flip 'z' to make it point against Q. */
  if (z0 > 0.0f) {
    z *= -1.0f;
    z0 *= -1.0f;
  }
  float x0 = dot(dir, x);
  float y0 = dot(dir, y);
  float x1 = x0 + extentu_len;
  float y1 = y0 + extentv_len;
  /* Compute internal angles (gamma_i). */
  float4 diff = make_float4(x0, y1, x1, y0) - make_float4(x1, y0, x0, y1);
  float4 nz = make_float4(y0, x1, y1, x0) * diff;
  nz = nz / sqrt(z0 * z0 * diff * diff + nz * nz);
  float g0 = safe_acosf(-nz.x * nz.y);
  float g1 = safe_acosf(-nz.y * nz.z);
  float g2 = safe_acosf(-nz.z * nz.w);
  float g3 = safe_acosf(-nz.w * nz.x);
  /* Compute predefined constants. */
  float b0 = nz.x;
  float b1 = nz.z;
  float b0sq = b0 * b0;
  float k = M_2PI_F - g2 - g3;
  /* Compute solid angle from internal angles. */
  float S = g0 + g1 - k;

  if (sample_coord) {
    /* Compute cu. */
    float au = randu * S + k;
    float fu = (cosf(au) * b0 - b1) / sinf(au);
    float cu = 1.0f / sqrtf(fu * fu + b0sq) * (fu > 0.0f ? 1.0f : -1.0f);
    cu = clamp(cu, -1.0f, 1.0f);
    /* Compute xu. */
    float xu = -(cu * z0) / max(sqrtf(1.0f - cu * cu), 1e-7f);
    xu = clamp(xu, x0, x1);
    /* Compute yv. */
    float z0sq = z0 * z0;
    float y0sq = y0 * y0;
    float y1sq = y1 * y1;
    float d = sqrtf(xu * xu + z0sq);
    float h0 = y0 / sqrtf(d * d + y0sq);
    float h1 = y1 / sqrtf(d * d + y1sq);
    float hv = h0 + randv * (h1 - h0), hv2 = hv * hv;
    float yv = (hv2 < 1.0f - 1e-6f) ? (hv * d) / sqrtf(1.0f - hv2) : y1;

    /* Transform (xu, yv, z0) to world coords. */
    *light_p = P + xu * x + yv * y + z0 * z;
  }

  /* return pdf */
  if (S != 0.0f)
    return 1.0f / S;
  else
    return 0.0f;
}

/* Light spread. */

ccl_device float area_light_spread_attenuation(const float3 D,
                                               const float3 lightNg,
                                               const float tan_spread,
                                               const float normalize_spread)
{
  /* Model a soft-box grid, computing the ratio of light not hidden by the
   * slats of the grid at a given angle. (see D10594). */
  const float cos_a = -dot(D, lightNg);
  const float sin_a = safe_sqrtf(1.0f - sqr(cos_a));
  const float tan_a = sin_a / cos_a;
  return max((1.0f - (tan_spread * tan_a)) * normalize_spread, 0.0f);
}

/* Compute subset of area light that actually has an influence on the shading point, to
 * reduce noise with low spread. */
ccl_device bool area_light_spread_clamp_area_light(const float3 P,
                                                   const float3 lightNg,
                                                   ccl_private float3 *lightP,
                                                   ccl_private float3 *extentu,
                                                   ccl_private float3 *extentv,
                                                   const float tan_spread)
{
  /* Closest point in area light plane and distance to that plane. */
  const float3 closest_P = P - dot(lightNg, P - *lightP) * lightNg;
  const float t = len(closest_P - P);

  /* Radius of circle on area light that actually affects the shading point. */
  const float radius = t / tan_spread;

  /* TODO: would be faster to store as normalized vector + length, also in area_light_rect_sample.
   */
  float len_u, len_v;
  const float3 u = normalize_len(*extentu, &len_u);
  const float3 v = normalize_len(*extentv, &len_v);

  /* Local uv coordinates of closest point. */
  const float closest_u = dot(u, closest_P - *lightP);
  const float closest_v = dot(v, closest_P - *lightP);

  /* Compute rectangle encompassing the circle that affects the shading point,
   * clamped to the bounds of the area light. */
  const float min_u = max(closest_u - radius, -len_u * 0.5f);
  const float max_u = min(closest_u + radius, len_u * 0.5f);
  const float min_v = max(closest_v - radius, -len_v * 0.5f);
  const float max_v = min(closest_v + radius, len_v * 0.5f);

  /* Skip if rectangle is empty. */
  if (min_u >= max_u || min_v >= max_v) {
    return false;
  }

  /* Compute new area light center position and axes from rectangle in local
   * uv coordinates. */
  const float new_center_u = 0.5f * (min_u + max_u);
  const float new_center_v = 0.5f * (min_v + max_v);
  const float new_len_u = max_u - min_u;
  const float new_len_v = max_v - min_v;

  *lightP = *lightP + new_center_u * u + new_center_v * v;
  *extentu = u * new_len_u;
  *extentv = v * new_len_v;

  return true;
}

/* Common API. */

template<bool in_volume_segment>
ccl_device_inline bool area_light_sample(const ccl_global KernelLight *klight,
                                         const float randu,
                                         const float randv,
                                         const float3 P,
                                         ccl_private LightSample *ls)
{
  ls->P = make_float3(klight->co[0], klight->co[1], klight->co[2]);

  float3 extentu = make_float3(
      klight->area.extentu[0], klight->area.extentu[1], klight->area.extentu[2]);
  float3 extentv = make_float3(
      klight->area.extentv[0], klight->area.extentv[1], klight->area.extentv[2]);
  float3 Ng = make_float3(klight->area.dir[0], klight->area.dir[1], klight->area.dir[2]);
  float invarea = fabsf(klight->area.invarea);
  bool is_round = (klight->area.invarea < 0.0f);

  if (!in_volume_segment) {
    if (dot(ls->P - P, Ng) > 0.0f) {
      return false;
    }
  }

  float3 inplane;

  if (is_round || in_volume_segment) {
    inplane = ellipse_sample(extentu * 0.5f, extentv * 0.5f, randu, randv);
    ls->P += inplane;
    ls->pdf = invarea;
  }
  else {
    inplane = ls->P;

    float3 sample_extentu = extentu;
    float3 sample_extentv = extentv;

    if (!in_volume_segment && klight->area.tan_spread > 0.0f) {
      if (!area_light_spread_clamp_area_light(
              P, Ng, &ls->P, &sample_extentu, &sample_extentv, klight->area.tan_spread)) {
        return false;
      }
    }

    ls->pdf = area_light_rect_sample(
        P, &ls->P, sample_extentu, sample_extentv, randu, randv, true);
    inplane = ls->P - inplane;
  }

  const float light_u = dot(inplane, extentu) * (1.0f / dot(extentu, extentu));
  const float light_v = dot(inplane, extentv) * (1.0f / dot(extentv, extentv));

  /* NOTE: Return barycentric coordinates in the same notation as Embree and OptiX. */
  ls->u = light_v + 0.5f;
  ls->v = -light_u - light_v;

  ls->Ng = Ng;
  ls->D = normalize_len(ls->P - P, &ls->t);

  ls->eval_fac = 0.25f * invarea;

  if (klight->area.tan_spread > 0.0f) {
    /* Area Light spread angle attenuation */
    ls->eval_fac *= area_light_spread_attenuation(
        ls->D, ls->Ng, klight->area.tan_spread, klight->area.normalize_spread);
  }

  if (is_round) {
    ls->pdf *= lamp_light_pdf(Ng, -ls->D, ls->t);
  }

  return true;
}

ccl_device_forceinline void area_light_update_position(const ccl_global KernelLight *klight,
                                                       ccl_private LightSample *ls,
                                                       const float3 P)
{
  const float invarea = fabsf(klight->area.invarea);
  ls->D = normalize_len(ls->P - P, &ls->t);
  ls->pdf = invarea;

  if (klight->area.tan_spread > 0.f) {
    ls->eval_fac = 0.25f * invarea;
    ls->eval_fac *= area_light_spread_attenuation(
        ls->D, ls->Ng, klight->area.tan_spread, klight->area.normalize_spread);
  }
}

ccl_device_inline bool area_light_intersect(const ccl_global KernelLight *klight,
                                            const ccl_private Ray *ccl_restrict ray,
                                            ccl_private float *t,
                                            ccl_private float *u,
                                            ccl_private float *v)
{
  /* Area light. */
  const float invarea = fabsf(klight->area.invarea);
  const bool is_round = (klight->area.invarea < 0.0f);
  if (invarea == 0.0f) {
    return false;
  }

  const float3 extentu = make_float3(
      klight->area.extentu[0], klight->area.extentu[1], klight->area.extentu[2]);
  const float3 extentv = make_float3(
      klight->area.extentv[0], klight->area.extentv[1], klight->area.extentv[2]);
  const float3 Ng = make_float3(klight->area.dir[0], klight->area.dir[1], klight->area.dir[2]);

  /* One sided. */
  if (dot(ray->D, Ng) >= 0.0f) {
    return false;
  }

  const float3 light_P = make_float3(klight->co[0], klight->co[1], klight->co[2]);

  float3 P;
  return ray_quad_intersect(
      ray->P, ray->D, ray->tmin, ray->tmax, light_P, extentu, extentv, Ng, &P, t, u, v, is_round);
}

ccl_device_inline bool area_light_sample_from_intersection(
    const ccl_global KernelLight *klight,
    ccl_private const Intersection *ccl_restrict isect,
    const float3 ray_P,
    const float3 ray_D,
    ccl_private LightSample *ccl_restrict ls)
{

  /* area light */
  float invarea = fabsf(klight->area.invarea);

  float3 extentu = make_float3(
      klight->area.extentu[0], klight->area.extentu[1], klight->area.extentu[2]);
  float3 extentv = make_float3(
      klight->area.extentv[0], klight->area.extentv[1], klight->area.extentv[2]);
  float3 Ng = make_float3(klight->area.dir[0], klight->area.dir[1], klight->area.dir[2]);
  float3 light_P = make_float3(klight->co[0], klight->co[1], klight->co[2]);

  ls->u = isect->u;
  ls->v = isect->v;
  ls->D = ray_D;
  ls->Ng = Ng;

  const bool is_round = (klight->area.invarea < 0.0f);
  if (is_round) {
    ls->pdf = invarea * lamp_light_pdf(Ng, -ray_D, ls->t);
  }
  else {
    float3 sample_extentu = extentu;
    float3 sample_extentv = extentv;

    if (klight->area.tan_spread > 0.0f) {
      if (!area_light_spread_clamp_area_light(
              ray_P, Ng, &light_P, &sample_extentu, &sample_extentv, klight->area.tan_spread)) {
        return false;
      }
    }

    ls->pdf = area_light_rect_sample(ray_P, &light_P, sample_extentu, sample_extentv, 0, 0, false);
  }
  ls->eval_fac = 0.25f * invarea;

  if (klight->area.tan_spread > 0.0f) {
    /* Area Light spread angle attenuation */
    ls->eval_fac *= area_light_spread_attenuation(
        ls->D, ls->Ng, klight->area.tan_spread, klight->area.normalize_spread);
    if (ls->eval_fac == 0.0f) {
      return false;
    }
  }

  return true;
}

ccl_device_inline float area_light_tree_weight(const ccl_global KernelLight *klight,
                                               const float3 P,
                                               const float3 N)
{
  float3 light_P = make_float3(klight->co[0], klight->co[1], klight->co[2]);

  float3 extentu = make_float3(
      klight->area.extentu[0], klight->area.extentu[1], klight->area.extentu[2]);
  float3 extentv = make_float3(
      klight->area.extentv[0], klight->area.extentv[1], klight->area.extentv[2]);
  float3 Ng = make_float3(klight->area.dir[0], klight->area.dir[1], klight->area.dir[2]);
  bool is_round = (klight->area.invarea < 0.0f);

  if (dot(light_P - P, Ng) > 0.0f) {
    return 0.0f;
  }

  if (!is_round) {
    if (klight->area.tan_spread > 0.0f) {
      if (!area_light_spread_clamp_area_light(
              P, Ng, &light_P, &extentu, &extentv, klight->area.tan_spread)) {
        return 0.0f;
      }
    }
  }

  return 1.0f;
}

CCL_NAMESPACE_END