2023-06-14 16:52:36 +10:00
|
|
|
/* SPDX-FileCopyrightText: 2011-2022 Blender Foundation
|
|
|
|
|
*
|
|
|
|
|
* SPDX-License-Identifier: Apache-2.0 */
|
2022-11-30 20:17:45 +01:00
|
|
|
|
|
|
|
|
#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,
|
2022-12-02 15:21:57 +01:00
|
|
|
const float3 axis_u,
|
|
|
|
|
const float len_u,
|
|
|
|
|
const float3 axis_v,
|
|
|
|
|
const float len_v,
|
2023-05-24 18:56:58 +02:00
|
|
|
const float2 rand,
|
2022-11-30 20:17:45 +01:00
|
|
|
bool sample_coord)
|
|
|
|
|
{
|
|
|
|
|
/* Compute local reference system R. */
|
2022-12-02 15:21:57 +01:00
|
|
|
float3 x = axis_u;
|
|
|
|
|
float3 y = axis_v;
|
2022-11-30 20:17:45 +01:00
|
|
|
float3 z = cross(x, y);
|
|
|
|
|
/* Compute rectangle coords in local reference system. */
|
2024-05-26 01:18:14 +02:00
|
|
|
float3 dir = *light_p - P;
|
2022-11-30 20:17:45 +01:00
|
|
|
float z0 = dot(dir, z);
|
|
|
|
|
/* Flip 'z' to make it point against Q. */
|
|
|
|
|
if (z0 > 0.0f) {
|
|
|
|
|
z *= -1.0f;
|
|
|
|
|
z0 *= -1.0f;
|
|
|
|
|
}
|
2024-05-26 01:18:14 +02:00
|
|
|
float xc = dot(dir, x), yc = dot(dir, y);
|
|
|
|
|
float x0 = xc - 0.5f * len_u, x1 = xc + 0.5f * len_u;
|
|
|
|
|
float y0 = yc - 0.5f * len_v, y1 = yc + 0.5f * len_v;
|
2023-10-05 22:53:05 +02:00
|
|
|
/* Compute predefined constants. */
|
2024-06-04 23:37:48 +02:00
|
|
|
float4 nz = make_float4(-y0, x1, y1, -x0);
|
|
|
|
|
nz /= sqrt(nz * nz + z0 * z0);
|
2023-10-27 12:13:48 +11:00
|
|
|
/* The original paper uses `acos()` to compute the internal angles here, and then computes the
|
2023-10-05 22:53:05 +02:00
|
|
|
* solid angle as their sum minus 2*pi. However, for very small rectangles, this results in
|
|
|
|
|
* excessive cancellation error since the sum will be almost 2*pi as well.
|
2023-10-10 09:44:46 +11:00
|
|
|
* This can be avoided by using that `asin(x) = pi/2 - acos(x)`. */
|
2023-10-05 22:53:05 +02:00
|
|
|
float g0 = safe_asinf(-nz.x * nz.y);
|
|
|
|
|
float g1 = safe_asinf(-nz.y * nz.z);
|
|
|
|
|
float g2 = safe_asinf(-nz.z * nz.w);
|
|
|
|
|
float g3 = safe_asinf(-nz.w * nz.x);
|
|
|
|
|
float S = -(g0 + g1 + g2 + g3);
|
2022-11-30 20:17:45 +01:00
|
|
|
|
|
|
|
|
if (sample_coord) {
|
2023-10-05 22:53:05 +02:00
|
|
|
/* Compute predefined constants. */
|
|
|
|
|
float b0 = nz.x;
|
|
|
|
|
float b1 = nz.z;
|
|
|
|
|
float b0sq = b0 * b0;
|
|
|
|
|
/* Compute cu.
|
|
|
|
|
* In the original paper, an additional constant k is involved here. However, just like above,
|
2023-10-27 12:13:48 +11:00
|
|
|
* it causes cancellation issues. The same `asin()` terms from above can be used instead, and
|
|
|
|
|
* the extra +pi that would remain in the expression for au can be removed by flipping the sign
|
2023-10-05 22:53:05 +02:00
|
|
|
* of cos(au) and sin(au), which also cancels if we flip the sign of b1 in the fu term. */
|
|
|
|
|
float au = rand.x * S + g2 + g3;
|
|
|
|
|
float fu = (cosf(au) * b0 + b1) / sinf(au);
|
|
|
|
|
float cu = copysignf(1.0f / sqrtf(fu * fu + b0sq), fu);
|
2022-11-30 20:17:45 +01:00
|
|
|
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. */
|
2024-05-26 01:18:14 +02:00
|
|
|
float d2 = sqr(xu) + sqr(z0);
|
|
|
|
|
float h0 = y0 / sqrtf(d2 + sqr(y0));
|
|
|
|
|
float h1 = y1 / sqrtf(d2 + sqr(y1));
|
2023-05-24 18:56:58 +02:00
|
|
|
float hv = h0 + rand.y * (h1 - h0), hv2 = hv * hv;
|
2024-05-26 01:18:14 +02:00
|
|
|
float yv = (hv2 < 1.0f - 1e-6f) ? hv * sqrtf(d2 / (1.0f - hv2)) : y1;
|
2022-11-30 20:17:45 +01:00
|
|
|
|
|
|
|
|
/* Transform (xu, yv, z0) to world coords. */
|
|
|
|
|
*light_p = P + xu * x + yv * y + z0 * z;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* return pdf */
|
2024-05-27 04:55:08 +02:00
|
|
|
if (S < 1e-5f || reduce_min(sqr(nz)) > 0.99999f) {
|
|
|
|
|
/* The solid angle is too small to be computed accurately in single precision.
|
|
|
|
|
* As a fallback, approximate it using the planar sampling PDF,
|
|
|
|
|
* for such tiny lights the difference is irrelevant.
|
|
|
|
|
*
|
|
|
|
|
* A threshold of 1e-5 was found to be the smallest option that avoids structured
|
|
|
|
|
* artifacts at all tested parameter combinations. The additional check of nz is
|
|
|
|
|
* needed for the case where the light is viewed from grazing angles, see e.g. #98930.
|
|
|
|
|
*/
|
|
|
|
|
const float t = len(dir);
|
|
|
|
|
return -t * t * t / (z0 * len_u * len_v);
|
2023-09-24 14:52:38 +10:00
|
|
|
}
|
|
|
|
|
else {
|
2024-05-27 04:55:08 +02:00
|
|
|
return 1.0f / S;
|
2023-09-24 14:52:38 +10:00
|
|
|
}
|
2022-11-30 20:17:45 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Light spread. */
|
|
|
|
|
|
|
|
|
|
ccl_device float area_light_spread_attenuation(const float3 D,
|
|
|
|
|
const float3 lightNg,
|
2022-12-07 18:51:26 +01:00
|
|
|
const float tan_half_spread,
|
2022-11-30 20:17:45 +01:00
|
|
|
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). */
|
2023-06-22 17:47:54 +02:00
|
|
|
const float tan_a = tan_angle(-D, lightNg);
|
|
|
|
|
|
2022-12-07 19:55:13 +01:00
|
|
|
if (tan_half_spread == 0.0f) {
|
|
|
|
|
/* The factor M_PI_F comes from integrating the radiance over the hemisphere */
|
2023-06-22 17:47:54 +02:00
|
|
|
return (tan_a > 1e-5f) ? 0.0f : M_PI_F;
|
2022-12-07 19:55:13 +01:00
|
|
|
}
|
2023-06-22 17:47:54 +02:00
|
|
|
|
2022-12-07 18:51:26 +01:00
|
|
|
return max((tan_half_spread - tan_a) * normalize_spread, 0.0f);
|
2022-11-30 20:17:45 +01:00
|
|
|
}
|
|
|
|
|
|
Cycles: improve sampling of ellipse area light with spread
**Problem**:
Area lights in Cycles have spread angle, in which case some part of the area light might be invisible to a shading point. The current implementation samples the whole area light, resulting some samples invisible and thus simply discarded. A technique is applied on rectangular light to sample a subset of the area light that is potentially visible (rB3f24cfb9582e1c826406301d37808df7ca6aa64c), however, ellipse (including disk) area lights remained untreated. The purpose of this patch is to apply a techniques to ellipse area light.
**Related Task**:
T87053
**Results**:
These are renderings before and after the patch:
|16spp|Disk light|Ellipse light|Square light (for reference, no changes)
|Before|{F13996789}|{F13996788}|{F13996822}
|After|{F13996759}|{F13996787}|{F13996852}
**Explanation**:
The visible region on an area light is found by drawing a cone from the shading point to the plane where the area light lies, with the aperture of the cone being the light spread.
{F13990078,height=200}
Ideally, we would like to draw samples only from the intersection of the area light and the projection of the cone onto the plane (forming a circle). However, the shape of the intersection is often irregular and thus hard to sample from directly.
{F13990104,height=200}
Instead, the current implementation draws samples from the bounding rectangle of the intersection. In this case, we still end up with some invalid samples outside of the circle, but already much less than sampling the original area light, and the bounding rectangle is easy to sample from.
{F13990125}
The above technique is only applied to rectangle area lights, ellipse area light still suffers from poor sampling. We could apply a similar technique to ellipse area lights, that is, find the
smallest regular shape (rectangle, circle, or ellipse) that covers the intersection (or maybe not the smallest but easy to compute).
For disk area light, we consider the relative position of both circles. Denoting `dist` as the distance between the centre of two circles, and `r1`, `r2` their radii. If `dist > r1 + r2`, the area light is completely invisible, we directly return `false`. If `dist < abs(r1 - r2)`, the smaller circle lies inside the larger one, and we sample whichever circle is smaller. Otherwise, the two circles intersect, we compute the bounding rectangle of the intersection, in which case `axis_u`, `len_u`, `axis_v`, `len_v` needs to be computed anew. Depending on the distance between the two circles, `len_v` is either the diameter of the smaller circle or the length of the common chord.
|{F13990211,height=195}|{F13990225,height=195}|{F13990274,height=195}|{F13990210,height=195}
|`dist > r1 + r2`|`dist < abs(r1 - r2)`|`dist^2 < abs(r1^2 - r2^2)`|`dist^2 > abs(r1^2 - r2^2)`
For ellipse area light, it's hard to find the smallest bounding shape of the intersection, therefore, we compute the bounding rectangle of the ellipse itself, then treat it as a rectangle light.
|{F13990386,height=195}|{F13990385,height=195}|{F13990387,height=195}
We also check the areas of the bounding rectangle of the intersection, the ellipse (disk) light, and the spread circle, then draw samples from the smallest shape of the three. For ellipse light, this also detects where one shape lies inside the other. I am not sure if we should add this measure to rectangle area light and sample from the spread circle when it has smaller area, as we seem to have a better sampling technique for rectangular (uniformly sample the solid angle). Maybe we could add [area-preserving parameterization for spherical
ellipse](https://arxiv.org/pdf/1805.09048.pdf) in the future.
**Limitation**:
At some point we switch from sampling the ellipse to sampling the rectangle, depending on the area of the both, and there seems to be a visible line (with |slope| =1) on the final rendering
which demonstrate at which point we switch between the two methods. We could see that the new sampling method clearly has lower variance near the boundaries, but close to that visible line,
the rectangle sampling method seems to have larger variance. I could not spot any bug in the implementation, and I am not sure if this happens because different sampling patterns for ellipse and rectangle are used.
|Before (256spp)|After (256spp)
|{F13996995}|{F13996998}
Differential Revision: https://developer.blender.org/D16694
2022-12-05 14:35:25 +01:00
|
|
|
/* Compute the minimal rectangle, circle or ellipse that covers the valid sample region, to reduce
|
|
|
|
|
* noise with low spread. */
|
|
|
|
|
ccl_device bool area_light_spread_clamp_light(const float3 P,
|
|
|
|
|
const float3 lightNg,
|
|
|
|
|
ccl_private float3 *lightP,
|
|
|
|
|
ccl_private float3 *axis_u,
|
|
|
|
|
ccl_private float *len_u,
|
|
|
|
|
ccl_private float3 *axis_v,
|
|
|
|
|
ccl_private float *len_v,
|
2022-12-07 18:51:26 +01:00
|
|
|
const float tan_half_spread,
|
Cycles: improve sampling of ellipse area light with spread
**Problem**:
Area lights in Cycles have spread angle, in which case some part of the area light might be invisible to a shading point. The current implementation samples the whole area light, resulting some samples invisible and thus simply discarded. A technique is applied on rectangular light to sample a subset of the area light that is potentially visible (rB3f24cfb9582e1c826406301d37808df7ca6aa64c), however, ellipse (including disk) area lights remained untreated. The purpose of this patch is to apply a techniques to ellipse area light.
**Related Task**:
T87053
**Results**:
These are renderings before and after the patch:
|16spp|Disk light|Ellipse light|Square light (for reference, no changes)
|Before|{F13996789}|{F13996788}|{F13996822}
|After|{F13996759}|{F13996787}|{F13996852}
**Explanation**:
The visible region on an area light is found by drawing a cone from the shading point to the plane where the area light lies, with the aperture of the cone being the light spread.
{F13990078,height=200}
Ideally, we would like to draw samples only from the intersection of the area light and the projection of the cone onto the plane (forming a circle). However, the shape of the intersection is often irregular and thus hard to sample from directly.
{F13990104,height=200}
Instead, the current implementation draws samples from the bounding rectangle of the intersection. In this case, we still end up with some invalid samples outside of the circle, but already much less than sampling the original area light, and the bounding rectangle is easy to sample from.
{F13990125}
The above technique is only applied to rectangle area lights, ellipse area light still suffers from poor sampling. We could apply a similar technique to ellipse area lights, that is, find the
smallest regular shape (rectangle, circle, or ellipse) that covers the intersection (or maybe not the smallest but easy to compute).
For disk area light, we consider the relative position of both circles. Denoting `dist` as the distance between the centre of two circles, and `r1`, `r2` their radii. If `dist > r1 + r2`, the area light is completely invisible, we directly return `false`. If `dist < abs(r1 - r2)`, the smaller circle lies inside the larger one, and we sample whichever circle is smaller. Otherwise, the two circles intersect, we compute the bounding rectangle of the intersection, in which case `axis_u`, `len_u`, `axis_v`, `len_v` needs to be computed anew. Depending on the distance between the two circles, `len_v` is either the diameter of the smaller circle or the length of the common chord.
|{F13990211,height=195}|{F13990225,height=195}|{F13990274,height=195}|{F13990210,height=195}
|`dist > r1 + r2`|`dist < abs(r1 - r2)`|`dist^2 < abs(r1^2 - r2^2)`|`dist^2 > abs(r1^2 - r2^2)`
For ellipse area light, it's hard to find the smallest bounding shape of the intersection, therefore, we compute the bounding rectangle of the ellipse itself, then treat it as a rectangle light.
|{F13990386,height=195}|{F13990385,height=195}|{F13990387,height=195}
We also check the areas of the bounding rectangle of the intersection, the ellipse (disk) light, and the spread circle, then draw samples from the smallest shape of the three. For ellipse light, this also detects where one shape lies inside the other. I am not sure if we should add this measure to rectangle area light and sample from the spread circle when it has smaller area, as we seem to have a better sampling technique for rectangular (uniformly sample the solid angle). Maybe we could add [area-preserving parameterization for spherical
ellipse](https://arxiv.org/pdf/1805.09048.pdf) in the future.
**Limitation**:
At some point we switch from sampling the ellipse to sampling the rectangle, depending on the area of the both, and there seems to be a visible line (with |slope| =1) on the final rendering
which demonstrate at which point we switch between the two methods. We could see that the new sampling method clearly has lower variance near the boundaries, but close to that visible line,
the rectangle sampling method seems to have larger variance. I could not spot any bug in the implementation, and I am not sure if this happens because different sampling patterns for ellipse and rectangle are used.
|Before (256spp)|After (256spp)
|{F13996995}|{F13996998}
Differential Revision: https://developer.blender.org/D16694
2022-12-05 14:35:25 +01:00
|
|
|
ccl_private bool *sample_rectangle)
|
2022-11-30 20:17:45 +01:00
|
|
|
{
|
2023-06-26 11:47:50 +02:00
|
|
|
/* Distance from shading point to area light plane and the closest point on that plane. */
|
|
|
|
|
const float t = dot(lightNg, P - *lightP);
|
|
|
|
|
const float3 closest_P = P - t * lightNg;
|
2022-11-30 20:17:45 +01:00
|
|
|
|
|
|
|
|
/* Radius of circle on area light that actually affects the shading point. */
|
2022-12-07 18:51:26 +01:00
|
|
|
const float r_spread = t * tan_half_spread;
|
2022-11-30 20:17:45 +01:00
|
|
|
|
|
|
|
|
/* Local uv coordinates of closest point. */
|
Cycles: improve sampling of ellipse area light with spread
**Problem**:
Area lights in Cycles have spread angle, in which case some part of the area light might be invisible to a shading point. The current implementation samples the whole area light, resulting some samples invisible and thus simply discarded. A technique is applied on rectangular light to sample a subset of the area light that is potentially visible (rB3f24cfb9582e1c826406301d37808df7ca6aa64c), however, ellipse (including disk) area lights remained untreated. The purpose of this patch is to apply a techniques to ellipse area light.
**Related Task**:
T87053
**Results**:
These are renderings before and after the patch:
|16spp|Disk light|Ellipse light|Square light (for reference, no changes)
|Before|{F13996789}|{F13996788}|{F13996822}
|After|{F13996759}|{F13996787}|{F13996852}
**Explanation**:
The visible region on an area light is found by drawing a cone from the shading point to the plane where the area light lies, with the aperture of the cone being the light spread.
{F13990078,height=200}
Ideally, we would like to draw samples only from the intersection of the area light and the projection of the cone onto the plane (forming a circle). However, the shape of the intersection is often irregular and thus hard to sample from directly.
{F13990104,height=200}
Instead, the current implementation draws samples from the bounding rectangle of the intersection. In this case, we still end up with some invalid samples outside of the circle, but already much less than sampling the original area light, and the bounding rectangle is easy to sample from.
{F13990125}
The above technique is only applied to rectangle area lights, ellipse area light still suffers from poor sampling. We could apply a similar technique to ellipse area lights, that is, find the
smallest regular shape (rectangle, circle, or ellipse) that covers the intersection (or maybe not the smallest but easy to compute).
For disk area light, we consider the relative position of both circles. Denoting `dist` as the distance between the centre of two circles, and `r1`, `r2` their radii. If `dist > r1 + r2`, the area light is completely invisible, we directly return `false`. If `dist < abs(r1 - r2)`, the smaller circle lies inside the larger one, and we sample whichever circle is smaller. Otherwise, the two circles intersect, we compute the bounding rectangle of the intersection, in which case `axis_u`, `len_u`, `axis_v`, `len_v` needs to be computed anew. Depending on the distance between the two circles, `len_v` is either the diameter of the smaller circle or the length of the common chord.
|{F13990211,height=195}|{F13990225,height=195}|{F13990274,height=195}|{F13990210,height=195}
|`dist > r1 + r2`|`dist < abs(r1 - r2)`|`dist^2 < abs(r1^2 - r2^2)`|`dist^2 > abs(r1^2 - r2^2)`
For ellipse area light, it's hard to find the smallest bounding shape of the intersection, therefore, we compute the bounding rectangle of the ellipse itself, then treat it as a rectangle light.
|{F13990386,height=195}|{F13990385,height=195}|{F13990387,height=195}
We also check the areas of the bounding rectangle of the intersection, the ellipse (disk) light, and the spread circle, then draw samples from the smallest shape of the three. For ellipse light, this also detects where one shape lies inside the other. I am not sure if we should add this measure to rectangle area light and sample from the spread circle when it has smaller area, as we seem to have a better sampling technique for rectangular (uniformly sample the solid angle). Maybe we could add [area-preserving parameterization for spherical
ellipse](https://arxiv.org/pdf/1805.09048.pdf) in the future.
**Limitation**:
At some point we switch from sampling the ellipse to sampling the rectangle, depending on the area of the both, and there seems to be a visible line (with |slope| =1) on the final rendering
which demonstrate at which point we switch between the two methods. We could see that the new sampling method clearly has lower variance near the boundaries, but close to that visible line,
the rectangle sampling method seems to have larger variance. I could not spot any bug in the implementation, and I am not sure if this happens because different sampling patterns for ellipse and rectangle are used.
|Before (256spp)|After (256spp)
|{F13996995}|{F13996998}
Differential Revision: https://developer.blender.org/D16694
2022-12-05 14:35:25 +01:00
|
|
|
const float spread_u = dot(*axis_u, closest_P - *lightP);
|
|
|
|
|
const float spread_v = dot(*axis_v, closest_P - *lightP);
|
|
|
|
|
|
|
|
|
|
const bool is_round = !(*sample_rectangle) && (*len_u == *len_v);
|
|
|
|
|
|
|
|
|
|
/* Whether we should sample the spread circle. */
|
2022-12-07 19:55:13 +01:00
|
|
|
bool sample_spread = (r_spread == 0.0f);
|
|
|
|
|
if (is_round && !sample_spread) {
|
Cycles: improve sampling of ellipse area light with spread
**Problem**:
Area lights in Cycles have spread angle, in which case some part of the area light might be invisible to a shading point. The current implementation samples the whole area light, resulting some samples invisible and thus simply discarded. A technique is applied on rectangular light to sample a subset of the area light that is potentially visible (rB3f24cfb9582e1c826406301d37808df7ca6aa64c), however, ellipse (including disk) area lights remained untreated. The purpose of this patch is to apply a techniques to ellipse area light.
**Related Task**:
T87053
**Results**:
These are renderings before and after the patch:
|16spp|Disk light|Ellipse light|Square light (for reference, no changes)
|Before|{F13996789}|{F13996788}|{F13996822}
|After|{F13996759}|{F13996787}|{F13996852}
**Explanation**:
The visible region on an area light is found by drawing a cone from the shading point to the plane where the area light lies, with the aperture of the cone being the light spread.
{F13990078,height=200}
Ideally, we would like to draw samples only from the intersection of the area light and the projection of the cone onto the plane (forming a circle). However, the shape of the intersection is often irregular and thus hard to sample from directly.
{F13990104,height=200}
Instead, the current implementation draws samples from the bounding rectangle of the intersection. In this case, we still end up with some invalid samples outside of the circle, but already much less than sampling the original area light, and the bounding rectangle is easy to sample from.
{F13990125}
The above technique is only applied to rectangle area lights, ellipse area light still suffers from poor sampling. We could apply a similar technique to ellipse area lights, that is, find the
smallest regular shape (rectangle, circle, or ellipse) that covers the intersection (or maybe not the smallest but easy to compute).
For disk area light, we consider the relative position of both circles. Denoting `dist` as the distance between the centre of two circles, and `r1`, `r2` their radii. If `dist > r1 + r2`, the area light is completely invisible, we directly return `false`. If `dist < abs(r1 - r2)`, the smaller circle lies inside the larger one, and we sample whichever circle is smaller. Otherwise, the two circles intersect, we compute the bounding rectangle of the intersection, in which case `axis_u`, `len_u`, `axis_v`, `len_v` needs to be computed anew. Depending on the distance between the two circles, `len_v` is either the diameter of the smaller circle or the length of the common chord.
|{F13990211,height=195}|{F13990225,height=195}|{F13990274,height=195}|{F13990210,height=195}
|`dist > r1 + r2`|`dist < abs(r1 - r2)`|`dist^2 < abs(r1^2 - r2^2)`|`dist^2 > abs(r1^2 - r2^2)`
For ellipse area light, it's hard to find the smallest bounding shape of the intersection, therefore, we compute the bounding rectangle of the ellipse itself, then treat it as a rectangle light.
|{F13990386,height=195}|{F13990385,height=195}|{F13990387,height=195}
We also check the areas of the bounding rectangle of the intersection, the ellipse (disk) light, and the spread circle, then draw samples from the smallest shape of the three. For ellipse light, this also detects where one shape lies inside the other. I am not sure if we should add this measure to rectangle area light and sample from the spread circle when it has smaller area, as we seem to have a better sampling technique for rectangular (uniformly sample the solid angle). Maybe we could add [area-preserving parameterization for spherical
ellipse](https://arxiv.org/pdf/1805.09048.pdf) in the future.
**Limitation**:
At some point we switch from sampling the ellipse to sampling the rectangle, depending on the area of the both, and there seems to be a visible line (with |slope| =1) on the final rendering
which demonstrate at which point we switch between the two methods. We could see that the new sampling method clearly has lower variance near the boundaries, but close to that visible line,
the rectangle sampling method seems to have larger variance. I could not spot any bug in the implementation, and I am not sure if this happens because different sampling patterns for ellipse and rectangle are used.
|Before (256spp)|After (256spp)
|{F13996995}|{F13996998}
Differential Revision: https://developer.blender.org/D16694
2022-12-05 14:35:25 +01:00
|
|
|
/* Distance between the centers of the disk light and the valid region circle. */
|
|
|
|
|
const float dist = len(make_float2(spread_u, spread_v));
|
|
|
|
|
|
|
|
|
|
/* Radius of the disk light. */
|
|
|
|
|
const float r = *len_u * 0.5f;
|
|
|
|
|
|
|
|
|
|
if (dist >= r + r_spread) {
|
|
|
|
|
/* Two circles are outside each other or touch externally. */
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
sample_spread = (dist <= fabsf(r - r_spread)) && (r_spread < r);
|
|
|
|
|
if (dist > fabsf(r - r_spread)) {
|
|
|
|
|
/* Two circles intersect. Find the smallest rectangle that covers the intersection */
|
|
|
|
|
const float len_u_ = r + r_spread - dist;
|
|
|
|
|
const float len_v_ = (fabsf(sqr(r) - sqr(r_spread)) >= sqr(dist)) ?
|
|
|
|
|
2.0f * fminf(r, r_spread) :
|
|
|
|
|
sqrtf(sqr(2.0f * r_spread) -
|
|
|
|
|
sqr(dist + (sqr(r_spread) - sqr(r)) / dist));
|
|
|
|
|
|
|
|
|
|
const float rect_area = len_u_ * len_v_;
|
|
|
|
|
const float circle_area = M_PI_F * sqr(r);
|
|
|
|
|
const float spread_area = M_PI_F * sqr(r_spread);
|
|
|
|
|
|
|
|
|
|
/* Sample the shape with minimal area. */
|
|
|
|
|
if (rect_area < fminf(circle_area, spread_area)) {
|
|
|
|
|
*sample_rectangle = true;
|
|
|
|
|
*axis_u = normalize(*lightP - closest_P);
|
|
|
|
|
*axis_v = rotate_around_axis(*axis_u, lightNg, M_PI_2_F);
|
|
|
|
|
*len_u = len_u_;
|
|
|
|
|
*len_v = len_v_;
|
|
|
|
|
*lightP = 0.5f * (*lightP + closest_P + *axis_u * (r_spread - r));
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
sample_spread = (spread_area < circle_area);
|
|
|
|
|
}
|
2022-11-30 20:17:45 +01:00
|
|
|
}
|
2022-12-07 19:55:13 +01:00
|
|
|
else if (!is_round && !sample_spread) {
|
Cycles: improve sampling of ellipse area light with spread
**Problem**:
Area lights in Cycles have spread angle, in which case some part of the area light might be invisible to a shading point. The current implementation samples the whole area light, resulting some samples invisible and thus simply discarded. A technique is applied on rectangular light to sample a subset of the area light that is potentially visible (rB3f24cfb9582e1c826406301d37808df7ca6aa64c), however, ellipse (including disk) area lights remained untreated. The purpose of this patch is to apply a techniques to ellipse area light.
**Related Task**:
T87053
**Results**:
These are renderings before and after the patch:
|16spp|Disk light|Ellipse light|Square light (for reference, no changes)
|Before|{F13996789}|{F13996788}|{F13996822}
|After|{F13996759}|{F13996787}|{F13996852}
**Explanation**:
The visible region on an area light is found by drawing a cone from the shading point to the plane where the area light lies, with the aperture of the cone being the light spread.
{F13990078,height=200}
Ideally, we would like to draw samples only from the intersection of the area light and the projection of the cone onto the plane (forming a circle). However, the shape of the intersection is often irregular and thus hard to sample from directly.
{F13990104,height=200}
Instead, the current implementation draws samples from the bounding rectangle of the intersection. In this case, we still end up with some invalid samples outside of the circle, but already much less than sampling the original area light, and the bounding rectangle is easy to sample from.
{F13990125}
The above technique is only applied to rectangle area lights, ellipse area light still suffers from poor sampling. We could apply a similar technique to ellipse area lights, that is, find the
smallest regular shape (rectangle, circle, or ellipse) that covers the intersection (or maybe not the smallest but easy to compute).
For disk area light, we consider the relative position of both circles. Denoting `dist` as the distance between the centre of two circles, and `r1`, `r2` their radii. If `dist > r1 + r2`, the area light is completely invisible, we directly return `false`. If `dist < abs(r1 - r2)`, the smaller circle lies inside the larger one, and we sample whichever circle is smaller. Otherwise, the two circles intersect, we compute the bounding rectangle of the intersection, in which case `axis_u`, `len_u`, `axis_v`, `len_v` needs to be computed anew. Depending on the distance between the two circles, `len_v` is either the diameter of the smaller circle or the length of the common chord.
|{F13990211,height=195}|{F13990225,height=195}|{F13990274,height=195}|{F13990210,height=195}
|`dist > r1 + r2`|`dist < abs(r1 - r2)`|`dist^2 < abs(r1^2 - r2^2)`|`dist^2 > abs(r1^2 - r2^2)`
For ellipse area light, it's hard to find the smallest bounding shape of the intersection, therefore, we compute the bounding rectangle of the ellipse itself, then treat it as a rectangle light.
|{F13990386,height=195}|{F13990385,height=195}|{F13990387,height=195}
We also check the areas of the bounding rectangle of the intersection, the ellipse (disk) light, and the spread circle, then draw samples from the smallest shape of the three. For ellipse light, this also detects where one shape lies inside the other. I am not sure if we should add this measure to rectangle area light and sample from the spread circle when it has smaller area, as we seem to have a better sampling technique for rectangular (uniformly sample the solid angle). Maybe we could add [area-preserving parameterization for spherical
ellipse](https://arxiv.org/pdf/1805.09048.pdf) in the future.
**Limitation**:
At some point we switch from sampling the ellipse to sampling the rectangle, depending on the area of the both, and there seems to be a visible line (with |slope| =1) on the final rendering
which demonstrate at which point we switch between the two methods. We could see that the new sampling method clearly has lower variance near the boundaries, but close to that visible line,
the rectangle sampling method seems to have larger variance. I could not spot any bug in the implementation, and I am not sure if this happens because different sampling patterns for ellipse and rectangle are used.
|Before (256spp)|After (256spp)
|{F13996995}|{F13996998}
Differential Revision: https://developer.blender.org/D16694
2022-12-05 14:35:25 +01:00
|
|
|
/* Compute rectangle encompassing the circle that affects the shading point,
|
|
|
|
|
* clamped to the bounds of the area light. */
|
|
|
|
|
const float min_u = max(spread_u - r_spread, -*len_u * 0.5f);
|
|
|
|
|
const float max_u = min(spread_u + r_spread, *len_u * 0.5f);
|
|
|
|
|
const float min_v = max(spread_v - r_spread, -*len_v * 0.5f);
|
|
|
|
|
const float max_v = min(spread_v + r_spread, *len_v * 0.5f);
|
|
|
|
|
|
|
|
|
|
/* Skip if rectangle is empty. */
|
|
|
|
|
if (min_u >= max_u || min_v >= max_v) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
2022-11-30 20:17:45 +01:00
|
|
|
|
Cycles: improve sampling of ellipse area light with spread
**Problem**:
Area lights in Cycles have spread angle, in which case some part of the area light might be invisible to a shading point. The current implementation samples the whole area light, resulting some samples invisible and thus simply discarded. A technique is applied on rectangular light to sample a subset of the area light that is potentially visible (rB3f24cfb9582e1c826406301d37808df7ca6aa64c), however, ellipse (including disk) area lights remained untreated. The purpose of this patch is to apply a techniques to ellipse area light.
**Related Task**:
T87053
**Results**:
These are renderings before and after the patch:
|16spp|Disk light|Ellipse light|Square light (for reference, no changes)
|Before|{F13996789}|{F13996788}|{F13996822}
|After|{F13996759}|{F13996787}|{F13996852}
**Explanation**:
The visible region on an area light is found by drawing a cone from the shading point to the plane where the area light lies, with the aperture of the cone being the light spread.
{F13990078,height=200}
Ideally, we would like to draw samples only from the intersection of the area light and the projection of the cone onto the plane (forming a circle). However, the shape of the intersection is often irregular and thus hard to sample from directly.
{F13990104,height=200}
Instead, the current implementation draws samples from the bounding rectangle of the intersection. In this case, we still end up with some invalid samples outside of the circle, but already much less than sampling the original area light, and the bounding rectangle is easy to sample from.
{F13990125}
The above technique is only applied to rectangle area lights, ellipse area light still suffers from poor sampling. We could apply a similar technique to ellipse area lights, that is, find the
smallest regular shape (rectangle, circle, or ellipse) that covers the intersection (or maybe not the smallest but easy to compute).
For disk area light, we consider the relative position of both circles. Denoting `dist` as the distance between the centre of two circles, and `r1`, `r2` their radii. If `dist > r1 + r2`, the area light is completely invisible, we directly return `false`. If `dist < abs(r1 - r2)`, the smaller circle lies inside the larger one, and we sample whichever circle is smaller. Otherwise, the two circles intersect, we compute the bounding rectangle of the intersection, in which case `axis_u`, `len_u`, `axis_v`, `len_v` needs to be computed anew. Depending on the distance between the two circles, `len_v` is either the diameter of the smaller circle or the length of the common chord.
|{F13990211,height=195}|{F13990225,height=195}|{F13990274,height=195}|{F13990210,height=195}
|`dist > r1 + r2`|`dist < abs(r1 - r2)`|`dist^2 < abs(r1^2 - r2^2)`|`dist^2 > abs(r1^2 - r2^2)`
For ellipse area light, it's hard to find the smallest bounding shape of the intersection, therefore, we compute the bounding rectangle of the ellipse itself, then treat it as a rectangle light.
|{F13990386,height=195}|{F13990385,height=195}|{F13990387,height=195}
We also check the areas of the bounding rectangle of the intersection, the ellipse (disk) light, and the spread circle, then draw samples from the smallest shape of the three. For ellipse light, this also detects where one shape lies inside the other. I am not sure if we should add this measure to rectangle area light and sample from the spread circle when it has smaller area, as we seem to have a better sampling technique for rectangular (uniformly sample the solid angle). Maybe we could add [area-preserving parameterization for spherical
ellipse](https://arxiv.org/pdf/1805.09048.pdf) in the future.
**Limitation**:
At some point we switch from sampling the ellipse to sampling the rectangle, depending on the area of the both, and there seems to be a visible line (with |slope| =1) on the final rendering
which demonstrate at which point we switch between the two methods. We could see that the new sampling method clearly has lower variance near the boundaries, but close to that visible line,
the rectangle sampling method seems to have larger variance. I could not spot any bug in the implementation, and I am not sure if this happens because different sampling patterns for ellipse and rectangle are used.
|Before (256spp)|After (256spp)
|{F13996995}|{F13996998}
Differential Revision: https://developer.blender.org/D16694
2022-12-05 14:35:25 +01:00
|
|
|
const float rect_len_u = max_u - min_u;
|
|
|
|
|
const float rect_len_v = max_v - min_v;
|
|
|
|
|
|
|
|
|
|
const float rect_area = rect_len_u * rect_len_v;
|
|
|
|
|
const float ellipse_area = (*sample_rectangle) ? FLT_MAX : M_PI_4_F * (*len_u) * (*len_v);
|
|
|
|
|
const float spread_area = M_PI_F * sqr(r_spread);
|
|
|
|
|
|
|
|
|
|
/* Sample the shape with minimal area. */
|
|
|
|
|
/* NOTE: we don't switch to spread circle sampling for rectangle light because rectangle light
|
|
|
|
|
* supports solid angle sampling, which has less variance than sampling the area. If ellipse
|
|
|
|
|
* area light also supports solid angle sampling, `*sample_rectangle ||` could be deleted. */
|
|
|
|
|
if (*sample_rectangle || rect_area < fminf(ellipse_area, spread_area)) {
|
|
|
|
|
*sample_rectangle = true;
|
|
|
|
|
|
|
|
|
|
/* 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);
|
|
|
|
|
|
|
|
|
|
*len_u = rect_len_u;
|
|
|
|
|
*len_v = rect_len_v;
|
|
|
|
|
*lightP = *lightP + *axis_u * new_center_u + *axis_v * new_center_v;
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
*sample_rectangle = false;
|
|
|
|
|
sample_spread = (spread_area < ellipse_area);
|
|
|
|
|
}
|
2022-11-30 20:17:45 +01:00
|
|
|
|
Cycles: improve sampling of ellipse area light with spread
**Problem**:
Area lights in Cycles have spread angle, in which case some part of the area light might be invisible to a shading point. The current implementation samples the whole area light, resulting some samples invisible and thus simply discarded. A technique is applied on rectangular light to sample a subset of the area light that is potentially visible (rB3f24cfb9582e1c826406301d37808df7ca6aa64c), however, ellipse (including disk) area lights remained untreated. The purpose of this patch is to apply a techniques to ellipse area light.
**Related Task**:
T87053
**Results**:
These are renderings before and after the patch:
|16spp|Disk light|Ellipse light|Square light (for reference, no changes)
|Before|{F13996789}|{F13996788}|{F13996822}
|After|{F13996759}|{F13996787}|{F13996852}
**Explanation**:
The visible region on an area light is found by drawing a cone from the shading point to the plane where the area light lies, with the aperture of the cone being the light spread.
{F13990078,height=200}
Ideally, we would like to draw samples only from the intersection of the area light and the projection of the cone onto the plane (forming a circle). However, the shape of the intersection is often irregular and thus hard to sample from directly.
{F13990104,height=200}
Instead, the current implementation draws samples from the bounding rectangle of the intersection. In this case, we still end up with some invalid samples outside of the circle, but already much less than sampling the original area light, and the bounding rectangle is easy to sample from.
{F13990125}
The above technique is only applied to rectangle area lights, ellipse area light still suffers from poor sampling. We could apply a similar technique to ellipse area lights, that is, find the
smallest regular shape (rectangle, circle, or ellipse) that covers the intersection (or maybe not the smallest but easy to compute).
For disk area light, we consider the relative position of both circles. Denoting `dist` as the distance between the centre of two circles, and `r1`, `r2` their radii. If `dist > r1 + r2`, the area light is completely invisible, we directly return `false`. If `dist < abs(r1 - r2)`, the smaller circle lies inside the larger one, and we sample whichever circle is smaller. Otherwise, the two circles intersect, we compute the bounding rectangle of the intersection, in which case `axis_u`, `len_u`, `axis_v`, `len_v` needs to be computed anew. Depending on the distance between the two circles, `len_v` is either the diameter of the smaller circle or the length of the common chord.
|{F13990211,height=195}|{F13990225,height=195}|{F13990274,height=195}|{F13990210,height=195}
|`dist > r1 + r2`|`dist < abs(r1 - r2)`|`dist^2 < abs(r1^2 - r2^2)`|`dist^2 > abs(r1^2 - r2^2)`
For ellipse area light, it's hard to find the smallest bounding shape of the intersection, therefore, we compute the bounding rectangle of the ellipse itself, then treat it as a rectangle light.
|{F13990386,height=195}|{F13990385,height=195}|{F13990387,height=195}
We also check the areas of the bounding rectangle of the intersection, the ellipse (disk) light, and the spread circle, then draw samples from the smallest shape of the three. For ellipse light, this also detects where one shape lies inside the other. I am not sure if we should add this measure to rectangle area light and sample from the spread circle when it has smaller area, as we seem to have a better sampling technique for rectangular (uniformly sample the solid angle). Maybe we could add [area-preserving parameterization for spherical
ellipse](https://arxiv.org/pdf/1805.09048.pdf) in the future.
**Limitation**:
At some point we switch from sampling the ellipse to sampling the rectangle, depending on the area of the both, and there seems to be a visible line (with |slope| =1) on the final rendering
which demonstrate at which point we switch between the two methods. We could see that the new sampling method clearly has lower variance near the boundaries, but close to that visible line,
the rectangle sampling method seems to have larger variance. I could not spot any bug in the implementation, and I am not sure if this happens because different sampling patterns for ellipse and rectangle are used.
|Before (256spp)|After (256spp)
|{F13996995}|{F13996998}
Differential Revision: https://developer.blender.org/D16694
2022-12-05 14:35:25 +01:00
|
|
|
if (sample_spread) {
|
2022-12-07 19:55:13 +01:00
|
|
|
*sample_rectangle = false;
|
Cycles: improve sampling of ellipse area light with spread
**Problem**:
Area lights in Cycles have spread angle, in which case some part of the area light might be invisible to a shading point. The current implementation samples the whole area light, resulting some samples invisible and thus simply discarded. A technique is applied on rectangular light to sample a subset of the area light that is potentially visible (rB3f24cfb9582e1c826406301d37808df7ca6aa64c), however, ellipse (including disk) area lights remained untreated. The purpose of this patch is to apply a techniques to ellipse area light.
**Related Task**:
T87053
**Results**:
These are renderings before and after the patch:
|16spp|Disk light|Ellipse light|Square light (for reference, no changes)
|Before|{F13996789}|{F13996788}|{F13996822}
|After|{F13996759}|{F13996787}|{F13996852}
**Explanation**:
The visible region on an area light is found by drawing a cone from the shading point to the plane where the area light lies, with the aperture of the cone being the light spread.
{F13990078,height=200}
Ideally, we would like to draw samples only from the intersection of the area light and the projection of the cone onto the plane (forming a circle). However, the shape of the intersection is often irregular and thus hard to sample from directly.
{F13990104,height=200}
Instead, the current implementation draws samples from the bounding rectangle of the intersection. In this case, we still end up with some invalid samples outside of the circle, but already much less than sampling the original area light, and the bounding rectangle is easy to sample from.
{F13990125}
The above technique is only applied to rectangle area lights, ellipse area light still suffers from poor sampling. We could apply a similar technique to ellipse area lights, that is, find the
smallest regular shape (rectangle, circle, or ellipse) that covers the intersection (or maybe not the smallest but easy to compute).
For disk area light, we consider the relative position of both circles. Denoting `dist` as the distance between the centre of two circles, and `r1`, `r2` their radii. If `dist > r1 + r2`, the area light is completely invisible, we directly return `false`. If `dist < abs(r1 - r2)`, the smaller circle lies inside the larger one, and we sample whichever circle is smaller. Otherwise, the two circles intersect, we compute the bounding rectangle of the intersection, in which case `axis_u`, `len_u`, `axis_v`, `len_v` needs to be computed anew. Depending on the distance between the two circles, `len_v` is either the diameter of the smaller circle or the length of the common chord.
|{F13990211,height=195}|{F13990225,height=195}|{F13990274,height=195}|{F13990210,height=195}
|`dist > r1 + r2`|`dist < abs(r1 - r2)`|`dist^2 < abs(r1^2 - r2^2)`|`dist^2 > abs(r1^2 - r2^2)`
For ellipse area light, it's hard to find the smallest bounding shape of the intersection, therefore, we compute the bounding rectangle of the ellipse itself, then treat it as a rectangle light.
|{F13990386,height=195}|{F13990385,height=195}|{F13990387,height=195}
We also check the areas of the bounding rectangle of the intersection, the ellipse (disk) light, and the spread circle, then draw samples from the smallest shape of the three. For ellipse light, this also detects where one shape lies inside the other. I am not sure if we should add this measure to rectangle area light and sample from the spread circle when it has smaller area, as we seem to have a better sampling technique for rectangular (uniformly sample the solid angle). Maybe we could add [area-preserving parameterization for spherical
ellipse](https://arxiv.org/pdf/1805.09048.pdf) in the future.
**Limitation**:
At some point we switch from sampling the ellipse to sampling the rectangle, depending on the area of the both, and there seems to be a visible line (with |slope| =1) on the final rendering
which demonstrate at which point we switch between the two methods. We could see that the new sampling method clearly has lower variance near the boundaries, but close to that visible line,
the rectangle sampling method seems to have larger variance. I could not spot any bug in the implementation, and I am not sure if this happens because different sampling patterns for ellipse and rectangle are used.
|Before (256spp)|After (256spp)
|{F13996995}|{F13996998}
Differential Revision: https://developer.blender.org/D16694
2022-12-05 14:35:25 +01:00
|
|
|
*lightP = *lightP + *axis_u * spread_u + *axis_v * spread_v;
|
|
|
|
|
*len_u = r_spread * 2.0f;
|
|
|
|
|
*len_v = r_spread * 2.0f;
|
|
|
|
|
return true;
|
|
|
|
|
}
|
2022-11-30 20:17:45 +01:00
|
|
|
|
Cycles: improve sampling of ellipse area light with spread
**Problem**:
Area lights in Cycles have spread angle, in which case some part of the area light might be invisible to a shading point. The current implementation samples the whole area light, resulting some samples invisible and thus simply discarded. A technique is applied on rectangular light to sample a subset of the area light that is potentially visible (rB3f24cfb9582e1c826406301d37808df7ca6aa64c), however, ellipse (including disk) area lights remained untreated. The purpose of this patch is to apply a techniques to ellipse area light.
**Related Task**:
T87053
**Results**:
These are renderings before and after the patch:
|16spp|Disk light|Ellipse light|Square light (for reference, no changes)
|Before|{F13996789}|{F13996788}|{F13996822}
|After|{F13996759}|{F13996787}|{F13996852}
**Explanation**:
The visible region on an area light is found by drawing a cone from the shading point to the plane where the area light lies, with the aperture of the cone being the light spread.
{F13990078,height=200}
Ideally, we would like to draw samples only from the intersection of the area light and the projection of the cone onto the plane (forming a circle). However, the shape of the intersection is often irregular and thus hard to sample from directly.
{F13990104,height=200}
Instead, the current implementation draws samples from the bounding rectangle of the intersection. In this case, we still end up with some invalid samples outside of the circle, but already much less than sampling the original area light, and the bounding rectangle is easy to sample from.
{F13990125}
The above technique is only applied to rectangle area lights, ellipse area light still suffers from poor sampling. We could apply a similar technique to ellipse area lights, that is, find the
smallest regular shape (rectangle, circle, or ellipse) that covers the intersection (or maybe not the smallest but easy to compute).
For disk area light, we consider the relative position of both circles. Denoting `dist` as the distance between the centre of two circles, and `r1`, `r2` their radii. If `dist > r1 + r2`, the area light is completely invisible, we directly return `false`. If `dist < abs(r1 - r2)`, the smaller circle lies inside the larger one, and we sample whichever circle is smaller. Otherwise, the two circles intersect, we compute the bounding rectangle of the intersection, in which case `axis_u`, `len_u`, `axis_v`, `len_v` needs to be computed anew. Depending on the distance between the two circles, `len_v` is either the diameter of the smaller circle or the length of the common chord.
|{F13990211,height=195}|{F13990225,height=195}|{F13990274,height=195}|{F13990210,height=195}
|`dist > r1 + r2`|`dist < abs(r1 - r2)`|`dist^2 < abs(r1^2 - r2^2)`|`dist^2 > abs(r1^2 - r2^2)`
For ellipse area light, it's hard to find the smallest bounding shape of the intersection, therefore, we compute the bounding rectangle of the ellipse itself, then treat it as a rectangle light.
|{F13990386,height=195}|{F13990385,height=195}|{F13990387,height=195}
We also check the areas of the bounding rectangle of the intersection, the ellipse (disk) light, and the spread circle, then draw samples from the smallest shape of the three. For ellipse light, this also detects where one shape lies inside the other. I am not sure if we should add this measure to rectangle area light and sample from the spread circle when it has smaller area, as we seem to have a better sampling technique for rectangular (uniformly sample the solid angle). Maybe we could add [area-preserving parameterization for spherical
ellipse](https://arxiv.org/pdf/1805.09048.pdf) in the future.
**Limitation**:
At some point we switch from sampling the ellipse to sampling the rectangle, depending on the area of the both, and there seems to be a visible line (with |slope| =1) on the final rendering
which demonstrate at which point we switch between the two methods. We could see that the new sampling method clearly has lower variance near the boundaries, but close to that visible line,
the rectangle sampling method seems to have larger variance. I could not spot any bug in the implementation, and I am not sure if this happens because different sampling patterns for ellipse and rectangle are used.
|Before (256spp)|After (256spp)
|{F13996995}|{F13996998}
Differential Revision: https://developer.blender.org/D16694
2022-12-05 14:35:25 +01:00
|
|
|
/* Don't clamp. */
|
2022-11-30 20:17:45 +01:00
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
2024-03-25 13:02:02 +01:00
|
|
|
ccl_device_forceinline bool area_light_is_ellipse(const ccl_global KernelAreaLight *light)
|
|
|
|
|
{
|
|
|
|
|
return light->invarea < 0.0f;
|
|
|
|
|
}
|
|
|
|
|
|
2022-11-30 20:17:45 +01:00
|
|
|
/* Common API. */
|
2023-05-12 23:07:28 +02:00
|
|
|
/* Compute `eval_fac` and `pdf`. Also sample a new position on the light if `sample_coord`. */
|
2022-11-30 20:17:45 +01:00
|
|
|
template<bool in_volume_segment>
|
2023-05-12 23:07:28 +02:00
|
|
|
ccl_device_inline bool area_light_eval(const ccl_global KernelLight *klight,
|
|
|
|
|
const float3 ray_P,
|
|
|
|
|
ccl_private float3 *light_P,
|
|
|
|
|
ccl_private LightSample *ccl_restrict ls,
|
2023-05-24 18:56:58 +02:00
|
|
|
const float2 rand,
|
2023-05-12 23:07:28 +02:00
|
|
|
bool sample_coord)
|
2022-11-30 20:17:45 +01:00
|
|
|
{
|
2023-05-12 23:07:28 +02:00
|
|
|
float3 axis_u = klight->area.axis_u;
|
|
|
|
|
float3 axis_v = klight->area.axis_v;
|
|
|
|
|
float len_u = klight->area.len_u;
|
|
|
|
|
float len_v = klight->area.len_v;
|
2022-11-30 20:17:45 +01:00
|
|
|
|
2023-05-12 23:07:28 +02:00
|
|
|
const float3 Ng = klight->area.dir;
|
|
|
|
|
const float invarea = fabsf(klight->area.invarea);
|
|
|
|
|
bool sample_rectangle = (klight->area.invarea > 0.0f);
|
2022-11-30 20:17:45 +01:00
|
|
|
|
2023-05-12 23:07:28 +02:00
|
|
|
float3 light_P_new = *light_P;
|
2022-11-30 20:17:45 +01:00
|
|
|
|
Cycles: improve sampling of ellipse area light with spread
**Problem**:
Area lights in Cycles have spread angle, in which case some part of the area light might be invisible to a shading point. The current implementation samples the whole area light, resulting some samples invisible and thus simply discarded. A technique is applied on rectangular light to sample a subset of the area light that is potentially visible (rB3f24cfb9582e1c826406301d37808df7ca6aa64c), however, ellipse (including disk) area lights remained untreated. The purpose of this patch is to apply a techniques to ellipse area light.
**Related Task**:
T87053
**Results**:
These are renderings before and after the patch:
|16spp|Disk light|Ellipse light|Square light (for reference, no changes)
|Before|{F13996789}|{F13996788}|{F13996822}
|After|{F13996759}|{F13996787}|{F13996852}
**Explanation**:
The visible region on an area light is found by drawing a cone from the shading point to the plane where the area light lies, with the aperture of the cone being the light spread.
{F13990078,height=200}
Ideally, we would like to draw samples only from the intersection of the area light and the projection of the cone onto the plane (forming a circle). However, the shape of the intersection is often irregular and thus hard to sample from directly.
{F13990104,height=200}
Instead, the current implementation draws samples from the bounding rectangle of the intersection. In this case, we still end up with some invalid samples outside of the circle, but already much less than sampling the original area light, and the bounding rectangle is easy to sample from.
{F13990125}
The above technique is only applied to rectangle area lights, ellipse area light still suffers from poor sampling. We could apply a similar technique to ellipse area lights, that is, find the
smallest regular shape (rectangle, circle, or ellipse) that covers the intersection (or maybe not the smallest but easy to compute).
For disk area light, we consider the relative position of both circles. Denoting `dist` as the distance between the centre of two circles, and `r1`, `r2` their radii. If `dist > r1 + r2`, the area light is completely invisible, we directly return `false`. If `dist < abs(r1 - r2)`, the smaller circle lies inside the larger one, and we sample whichever circle is smaller. Otherwise, the two circles intersect, we compute the bounding rectangle of the intersection, in which case `axis_u`, `len_u`, `axis_v`, `len_v` needs to be computed anew. Depending on the distance between the two circles, `len_v` is either the diameter of the smaller circle or the length of the common chord.
|{F13990211,height=195}|{F13990225,height=195}|{F13990274,height=195}|{F13990210,height=195}
|`dist > r1 + r2`|`dist < abs(r1 - r2)`|`dist^2 < abs(r1^2 - r2^2)`|`dist^2 > abs(r1^2 - r2^2)`
For ellipse area light, it's hard to find the smallest bounding shape of the intersection, therefore, we compute the bounding rectangle of the ellipse itself, then treat it as a rectangle light.
|{F13990386,height=195}|{F13990385,height=195}|{F13990387,height=195}
We also check the areas of the bounding rectangle of the intersection, the ellipse (disk) light, and the spread circle, then draw samples from the smallest shape of the three. For ellipse light, this also detects where one shape lies inside the other. I am not sure if we should add this measure to rectangle area light and sample from the spread circle when it has smaller area, as we seem to have a better sampling technique for rectangular (uniformly sample the solid angle). Maybe we could add [area-preserving parameterization for spherical
ellipse](https://arxiv.org/pdf/1805.09048.pdf) in the future.
**Limitation**:
At some point we switch from sampling the ellipse to sampling the rectangle, depending on the area of the both, and there seems to be a visible line (with |slope| =1) on the final rendering
which demonstrate at which point we switch between the two methods. We could see that the new sampling method clearly has lower variance near the boundaries, but close to that visible line,
the rectangle sampling method seems to have larger variance. I could not spot any bug in the implementation, and I am not sure if this happens because different sampling patterns for ellipse and rectangle are used.
|Before (256spp)|After (256spp)
|{F13996995}|{F13996998}
Differential Revision: https://developer.blender.org/D16694
2022-12-05 14:35:25 +01:00
|
|
|
if (in_volume_segment) {
|
2023-05-12 23:07:28 +02:00
|
|
|
light_P_new += sample_rectangle ?
|
2023-05-24 18:56:58 +02:00
|
|
|
rectangle_sample(axis_u * len_u * 0.5f, axis_v * len_v * 0.5f, rand) :
|
|
|
|
|
ellipse_sample(axis_u * len_u * 0.5f, axis_v * len_v * 0.5f, rand);
|
2022-11-30 20:17:45 +01:00
|
|
|
ls->pdf = invarea;
|
|
|
|
|
}
|
|
|
|
|
else {
|
2022-12-07 18:51:26 +01:00
|
|
|
if (klight->area.normalize_spread > 0) {
|
2023-05-12 23:07:28 +02:00
|
|
|
if (!area_light_spread_clamp_light(ray_P,
|
Cycles: improve sampling of ellipse area light with spread
**Problem**:
Area lights in Cycles have spread angle, in which case some part of the area light might be invisible to a shading point. The current implementation samples the whole area light, resulting some samples invisible and thus simply discarded. A technique is applied on rectangular light to sample a subset of the area light that is potentially visible (rB3f24cfb9582e1c826406301d37808df7ca6aa64c), however, ellipse (including disk) area lights remained untreated. The purpose of this patch is to apply a techniques to ellipse area light.
**Related Task**:
T87053
**Results**:
These are renderings before and after the patch:
|16spp|Disk light|Ellipse light|Square light (for reference, no changes)
|Before|{F13996789}|{F13996788}|{F13996822}
|After|{F13996759}|{F13996787}|{F13996852}
**Explanation**:
The visible region on an area light is found by drawing a cone from the shading point to the plane where the area light lies, with the aperture of the cone being the light spread.
{F13990078,height=200}
Ideally, we would like to draw samples only from the intersection of the area light and the projection of the cone onto the plane (forming a circle). However, the shape of the intersection is often irregular and thus hard to sample from directly.
{F13990104,height=200}
Instead, the current implementation draws samples from the bounding rectangle of the intersection. In this case, we still end up with some invalid samples outside of the circle, but already much less than sampling the original area light, and the bounding rectangle is easy to sample from.
{F13990125}
The above technique is only applied to rectangle area lights, ellipse area light still suffers from poor sampling. We could apply a similar technique to ellipse area lights, that is, find the
smallest regular shape (rectangle, circle, or ellipse) that covers the intersection (or maybe not the smallest but easy to compute).
For disk area light, we consider the relative position of both circles. Denoting `dist` as the distance between the centre of two circles, and `r1`, `r2` their radii. If `dist > r1 + r2`, the area light is completely invisible, we directly return `false`. If `dist < abs(r1 - r2)`, the smaller circle lies inside the larger one, and we sample whichever circle is smaller. Otherwise, the two circles intersect, we compute the bounding rectangle of the intersection, in which case `axis_u`, `len_u`, `axis_v`, `len_v` needs to be computed anew. Depending on the distance between the two circles, `len_v` is either the diameter of the smaller circle or the length of the common chord.
|{F13990211,height=195}|{F13990225,height=195}|{F13990274,height=195}|{F13990210,height=195}
|`dist > r1 + r2`|`dist < abs(r1 - r2)`|`dist^2 < abs(r1^2 - r2^2)`|`dist^2 > abs(r1^2 - r2^2)`
For ellipse area light, it's hard to find the smallest bounding shape of the intersection, therefore, we compute the bounding rectangle of the ellipse itself, then treat it as a rectangle light.
|{F13990386,height=195}|{F13990385,height=195}|{F13990387,height=195}
We also check the areas of the bounding rectangle of the intersection, the ellipse (disk) light, and the spread circle, then draw samples from the smallest shape of the three. For ellipse light, this also detects where one shape lies inside the other. I am not sure if we should add this measure to rectangle area light and sample from the spread circle when it has smaller area, as we seem to have a better sampling technique for rectangular (uniformly sample the solid angle). Maybe we could add [area-preserving parameterization for spherical
ellipse](https://arxiv.org/pdf/1805.09048.pdf) in the future.
**Limitation**:
At some point we switch from sampling the ellipse to sampling the rectangle, depending on the area of the both, and there seems to be a visible line (with |slope| =1) on the final rendering
which demonstrate at which point we switch between the two methods. We could see that the new sampling method clearly has lower variance near the boundaries, but close to that visible line,
the rectangle sampling method seems to have larger variance. I could not spot any bug in the implementation, and I am not sure if this happens because different sampling patterns for ellipse and rectangle are used.
|Before (256spp)|After (256spp)
|{F13996995}|{F13996998}
Differential Revision: https://developer.blender.org/D16694
2022-12-05 14:35:25 +01:00
|
|
|
Ng,
|
2023-05-12 23:07:28 +02:00
|
|
|
&light_P_new,
|
|
|
|
|
&axis_u,
|
|
|
|
|
&len_u,
|
|
|
|
|
&axis_v,
|
|
|
|
|
&len_v,
|
2022-12-07 18:51:26 +01:00
|
|
|
klight->area.tan_half_spread,
|
Cycles: improve sampling of ellipse area light with spread
**Problem**:
Area lights in Cycles have spread angle, in which case some part of the area light might be invisible to a shading point. The current implementation samples the whole area light, resulting some samples invisible and thus simply discarded. A technique is applied on rectangular light to sample a subset of the area light that is potentially visible (rB3f24cfb9582e1c826406301d37808df7ca6aa64c), however, ellipse (including disk) area lights remained untreated. The purpose of this patch is to apply a techniques to ellipse area light.
**Related Task**:
T87053
**Results**:
These are renderings before and after the patch:
|16spp|Disk light|Ellipse light|Square light (for reference, no changes)
|Before|{F13996789}|{F13996788}|{F13996822}
|After|{F13996759}|{F13996787}|{F13996852}
**Explanation**:
The visible region on an area light is found by drawing a cone from the shading point to the plane where the area light lies, with the aperture of the cone being the light spread.
{F13990078,height=200}
Ideally, we would like to draw samples only from the intersection of the area light and the projection of the cone onto the plane (forming a circle). However, the shape of the intersection is often irregular and thus hard to sample from directly.
{F13990104,height=200}
Instead, the current implementation draws samples from the bounding rectangle of the intersection. In this case, we still end up with some invalid samples outside of the circle, but already much less than sampling the original area light, and the bounding rectangle is easy to sample from.
{F13990125}
The above technique is only applied to rectangle area lights, ellipse area light still suffers from poor sampling. We could apply a similar technique to ellipse area lights, that is, find the
smallest regular shape (rectangle, circle, or ellipse) that covers the intersection (or maybe not the smallest but easy to compute).
For disk area light, we consider the relative position of both circles. Denoting `dist` as the distance between the centre of two circles, and `r1`, `r2` their radii. If `dist > r1 + r2`, the area light is completely invisible, we directly return `false`. If `dist < abs(r1 - r2)`, the smaller circle lies inside the larger one, and we sample whichever circle is smaller. Otherwise, the two circles intersect, we compute the bounding rectangle of the intersection, in which case `axis_u`, `len_u`, `axis_v`, `len_v` needs to be computed anew. Depending on the distance between the two circles, `len_v` is either the diameter of the smaller circle or the length of the common chord.
|{F13990211,height=195}|{F13990225,height=195}|{F13990274,height=195}|{F13990210,height=195}
|`dist > r1 + r2`|`dist < abs(r1 - r2)`|`dist^2 < abs(r1^2 - r2^2)`|`dist^2 > abs(r1^2 - r2^2)`
For ellipse area light, it's hard to find the smallest bounding shape of the intersection, therefore, we compute the bounding rectangle of the ellipse itself, then treat it as a rectangle light.
|{F13990386,height=195}|{F13990385,height=195}|{F13990387,height=195}
We also check the areas of the bounding rectangle of the intersection, the ellipse (disk) light, and the spread circle, then draw samples from the smallest shape of the three. For ellipse light, this also detects where one shape lies inside the other. I am not sure if we should add this measure to rectangle area light and sample from the spread circle when it has smaller area, as we seem to have a better sampling technique for rectangular (uniformly sample the solid angle). Maybe we could add [area-preserving parameterization for spherical
ellipse](https://arxiv.org/pdf/1805.09048.pdf) in the future.
**Limitation**:
At some point we switch from sampling the ellipse to sampling the rectangle, depending on the area of the both, and there seems to be a visible line (with |slope| =1) on the final rendering
which demonstrate at which point we switch between the two methods. We could see that the new sampling method clearly has lower variance near the boundaries, but close to that visible line,
the rectangle sampling method seems to have larger variance. I could not spot any bug in the implementation, and I am not sure if this happens because different sampling patterns for ellipse and rectangle are used.
|Before (256spp)|After (256spp)
|{F13996995}|{F13996998}
Differential Revision: https://developer.blender.org/D16694
2022-12-05 14:35:25 +01:00
|
|
|
&sample_rectangle))
|
|
|
|
|
{
|
2022-11-30 20:17:45 +01:00
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
Cycles: improve sampling of ellipse area light with spread
**Problem**:
Area lights in Cycles have spread angle, in which case some part of the area light might be invisible to a shading point. The current implementation samples the whole area light, resulting some samples invisible and thus simply discarded. A technique is applied on rectangular light to sample a subset of the area light that is potentially visible (rB3f24cfb9582e1c826406301d37808df7ca6aa64c), however, ellipse (including disk) area lights remained untreated. The purpose of this patch is to apply a techniques to ellipse area light.
**Related Task**:
T87053
**Results**:
These are renderings before and after the patch:
|16spp|Disk light|Ellipse light|Square light (for reference, no changes)
|Before|{F13996789}|{F13996788}|{F13996822}
|After|{F13996759}|{F13996787}|{F13996852}
**Explanation**:
The visible region on an area light is found by drawing a cone from the shading point to the plane where the area light lies, with the aperture of the cone being the light spread.
{F13990078,height=200}
Ideally, we would like to draw samples only from the intersection of the area light and the projection of the cone onto the plane (forming a circle). However, the shape of the intersection is often irregular and thus hard to sample from directly.
{F13990104,height=200}
Instead, the current implementation draws samples from the bounding rectangle of the intersection. In this case, we still end up with some invalid samples outside of the circle, but already much less than sampling the original area light, and the bounding rectangle is easy to sample from.
{F13990125}
The above technique is only applied to rectangle area lights, ellipse area light still suffers from poor sampling. We could apply a similar technique to ellipse area lights, that is, find the
smallest regular shape (rectangle, circle, or ellipse) that covers the intersection (or maybe not the smallest but easy to compute).
For disk area light, we consider the relative position of both circles. Denoting `dist` as the distance between the centre of two circles, and `r1`, `r2` their radii. If `dist > r1 + r2`, the area light is completely invisible, we directly return `false`. If `dist < abs(r1 - r2)`, the smaller circle lies inside the larger one, and we sample whichever circle is smaller. Otherwise, the two circles intersect, we compute the bounding rectangle of the intersection, in which case `axis_u`, `len_u`, `axis_v`, `len_v` needs to be computed anew. Depending on the distance between the two circles, `len_v` is either the diameter of the smaller circle or the length of the common chord.
|{F13990211,height=195}|{F13990225,height=195}|{F13990274,height=195}|{F13990210,height=195}
|`dist > r1 + r2`|`dist < abs(r1 - r2)`|`dist^2 < abs(r1^2 - r2^2)`|`dist^2 > abs(r1^2 - r2^2)`
For ellipse area light, it's hard to find the smallest bounding shape of the intersection, therefore, we compute the bounding rectangle of the ellipse itself, then treat it as a rectangle light.
|{F13990386,height=195}|{F13990385,height=195}|{F13990387,height=195}
We also check the areas of the bounding rectangle of the intersection, the ellipse (disk) light, and the spread circle, then draw samples from the smallest shape of the three. For ellipse light, this also detects where one shape lies inside the other. I am not sure if we should add this measure to rectangle area light and sample from the spread circle when it has smaller area, as we seem to have a better sampling technique for rectangular (uniformly sample the solid angle). Maybe we could add [area-preserving parameterization for spherical
ellipse](https://arxiv.org/pdf/1805.09048.pdf) in the future.
**Limitation**:
At some point we switch from sampling the ellipse to sampling the rectangle, depending on the area of the both, and there seems to be a visible line (with |slope| =1) on the final rendering
which demonstrate at which point we switch between the two methods. We could see that the new sampling method clearly has lower variance near the boundaries, but close to that visible line,
the rectangle sampling method seems to have larger variance. I could not spot any bug in the implementation, and I am not sure if this happens because different sampling patterns for ellipse and rectangle are used.
|Before (256spp)|After (256spp)
|{F13996995}|{F13996998}
Differential Revision: https://developer.blender.org/D16694
2022-12-05 14:35:25 +01:00
|
|
|
if (sample_rectangle) {
|
|
|
|
|
ls->pdf = area_light_rect_sample(
|
2023-05-24 18:56:58 +02:00
|
|
|
ray_P, &light_P_new, axis_u, len_u, axis_v, len_v, rand, sample_coord);
|
Cycles: improve sampling of ellipse area light with spread
**Problem**:
Area lights in Cycles have spread angle, in which case some part of the area light might be invisible to a shading point. The current implementation samples the whole area light, resulting some samples invisible and thus simply discarded. A technique is applied on rectangular light to sample a subset of the area light that is potentially visible (rB3f24cfb9582e1c826406301d37808df7ca6aa64c), however, ellipse (including disk) area lights remained untreated. The purpose of this patch is to apply a techniques to ellipse area light.
**Related Task**:
T87053
**Results**:
These are renderings before and after the patch:
|16spp|Disk light|Ellipse light|Square light (for reference, no changes)
|Before|{F13996789}|{F13996788}|{F13996822}
|After|{F13996759}|{F13996787}|{F13996852}
**Explanation**:
The visible region on an area light is found by drawing a cone from the shading point to the plane where the area light lies, with the aperture of the cone being the light spread.
{F13990078,height=200}
Ideally, we would like to draw samples only from the intersection of the area light and the projection of the cone onto the plane (forming a circle). However, the shape of the intersection is often irregular and thus hard to sample from directly.
{F13990104,height=200}
Instead, the current implementation draws samples from the bounding rectangle of the intersection. In this case, we still end up with some invalid samples outside of the circle, but already much less than sampling the original area light, and the bounding rectangle is easy to sample from.
{F13990125}
The above technique is only applied to rectangle area lights, ellipse area light still suffers from poor sampling. We could apply a similar technique to ellipse area lights, that is, find the
smallest regular shape (rectangle, circle, or ellipse) that covers the intersection (or maybe not the smallest but easy to compute).
For disk area light, we consider the relative position of both circles. Denoting `dist` as the distance between the centre of two circles, and `r1`, `r2` their radii. If `dist > r1 + r2`, the area light is completely invisible, we directly return `false`. If `dist < abs(r1 - r2)`, the smaller circle lies inside the larger one, and we sample whichever circle is smaller. Otherwise, the two circles intersect, we compute the bounding rectangle of the intersection, in which case `axis_u`, `len_u`, `axis_v`, `len_v` needs to be computed anew. Depending on the distance between the two circles, `len_v` is either the diameter of the smaller circle or the length of the common chord.
|{F13990211,height=195}|{F13990225,height=195}|{F13990274,height=195}|{F13990210,height=195}
|`dist > r1 + r2`|`dist < abs(r1 - r2)`|`dist^2 < abs(r1^2 - r2^2)`|`dist^2 > abs(r1^2 - r2^2)`
For ellipse area light, it's hard to find the smallest bounding shape of the intersection, therefore, we compute the bounding rectangle of the ellipse itself, then treat it as a rectangle light.
|{F13990386,height=195}|{F13990385,height=195}|{F13990387,height=195}
We also check the areas of the bounding rectangle of the intersection, the ellipse (disk) light, and the spread circle, then draw samples from the smallest shape of the three. For ellipse light, this also detects where one shape lies inside the other. I am not sure if we should add this measure to rectangle area light and sample from the spread circle when it has smaller area, as we seem to have a better sampling technique for rectangular (uniformly sample the solid angle). Maybe we could add [area-preserving parameterization for spherical
ellipse](https://arxiv.org/pdf/1805.09048.pdf) in the future.
**Limitation**:
At some point we switch from sampling the ellipse to sampling the rectangle, depending on the area of the both, and there seems to be a visible line (with |slope| =1) on the final rendering
which demonstrate at which point we switch between the two methods. We could see that the new sampling method clearly has lower variance near the boundaries, but close to that visible line,
the rectangle sampling method seems to have larger variance. I could not spot any bug in the implementation, and I am not sure if this happens because different sampling patterns for ellipse and rectangle are used.
|Before (256spp)|After (256spp)
|{F13996995}|{F13996998}
Differential Revision: https://developer.blender.org/D16694
2022-12-05 14:35:25 +01:00
|
|
|
}
|
|
|
|
|
else {
|
2022-12-07 19:55:13 +01:00
|
|
|
if (klight->area.tan_half_spread == 0.0f) {
|
|
|
|
|
ls->pdf = 1.0f;
|
|
|
|
|
}
|
|
|
|
|
else {
|
2023-05-12 23:07:28 +02:00
|
|
|
if (sample_coord) {
|
2023-05-24 18:56:58 +02:00
|
|
|
light_P_new += ellipse_sample(axis_u * len_u * 0.5f, axis_v * len_v * 0.5f, rand);
|
2023-05-12 23:07:28 +02:00
|
|
|
}
|
|
|
|
|
ls->pdf = 4.0f * M_1_PI_F / (len_u * len_v);
|
2022-12-07 19:55:13 +01:00
|
|
|
}
|
Cycles: improve sampling of ellipse area light with spread
**Problem**:
Area lights in Cycles have spread angle, in which case some part of the area light might be invisible to a shading point. The current implementation samples the whole area light, resulting some samples invisible and thus simply discarded. A technique is applied on rectangular light to sample a subset of the area light that is potentially visible (rB3f24cfb9582e1c826406301d37808df7ca6aa64c), however, ellipse (including disk) area lights remained untreated. The purpose of this patch is to apply a techniques to ellipse area light.
**Related Task**:
T87053
**Results**:
These are renderings before and after the patch:
|16spp|Disk light|Ellipse light|Square light (for reference, no changes)
|Before|{F13996789}|{F13996788}|{F13996822}
|After|{F13996759}|{F13996787}|{F13996852}
**Explanation**:
The visible region on an area light is found by drawing a cone from the shading point to the plane where the area light lies, with the aperture of the cone being the light spread.
{F13990078,height=200}
Ideally, we would like to draw samples only from the intersection of the area light and the projection of the cone onto the plane (forming a circle). However, the shape of the intersection is often irregular and thus hard to sample from directly.
{F13990104,height=200}
Instead, the current implementation draws samples from the bounding rectangle of the intersection. In this case, we still end up with some invalid samples outside of the circle, but already much less than sampling the original area light, and the bounding rectangle is easy to sample from.
{F13990125}
The above technique is only applied to rectangle area lights, ellipse area light still suffers from poor sampling. We could apply a similar technique to ellipse area lights, that is, find the
smallest regular shape (rectangle, circle, or ellipse) that covers the intersection (or maybe not the smallest but easy to compute).
For disk area light, we consider the relative position of both circles. Denoting `dist` as the distance between the centre of two circles, and `r1`, `r2` their radii. If `dist > r1 + r2`, the area light is completely invisible, we directly return `false`. If `dist < abs(r1 - r2)`, the smaller circle lies inside the larger one, and we sample whichever circle is smaller. Otherwise, the two circles intersect, we compute the bounding rectangle of the intersection, in which case `axis_u`, `len_u`, `axis_v`, `len_v` needs to be computed anew. Depending on the distance between the two circles, `len_v` is either the diameter of the smaller circle or the length of the common chord.
|{F13990211,height=195}|{F13990225,height=195}|{F13990274,height=195}|{F13990210,height=195}
|`dist > r1 + r2`|`dist < abs(r1 - r2)`|`dist^2 < abs(r1^2 - r2^2)`|`dist^2 > abs(r1^2 - r2^2)`
For ellipse area light, it's hard to find the smallest bounding shape of the intersection, therefore, we compute the bounding rectangle of the ellipse itself, then treat it as a rectangle light.
|{F13990386,height=195}|{F13990385,height=195}|{F13990387,height=195}
We also check the areas of the bounding rectangle of the intersection, the ellipse (disk) light, and the spread circle, then draw samples from the smallest shape of the three. For ellipse light, this also detects where one shape lies inside the other. I am not sure if we should add this measure to rectangle area light and sample from the spread circle when it has smaller area, as we seem to have a better sampling technique for rectangular (uniformly sample the solid angle). Maybe we could add [area-preserving parameterization for spherical
ellipse](https://arxiv.org/pdf/1805.09048.pdf) in the future.
**Limitation**:
At some point we switch from sampling the ellipse to sampling the rectangle, depending on the area of the both, and there seems to be a visible line (with |slope| =1) on the final rendering
which demonstrate at which point we switch between the two methods. We could see that the new sampling method clearly has lower variance near the boundaries, but close to that visible line,
the rectangle sampling method seems to have larger variance. I could not spot any bug in the implementation, and I am not sure if this happens because different sampling patterns for ellipse and rectangle are used.
|Before (256spp)|After (256spp)
|{F13996995}|{F13996998}
Differential Revision: https://developer.blender.org/D16694
2022-12-05 14:35:25 +01:00
|
|
|
}
|
2022-11-30 20:17:45 +01:00
|
|
|
}
|
|
|
|
|
|
2023-05-12 23:07:28 +02:00
|
|
|
if (sample_coord) {
|
|
|
|
|
*light_P = light_P_new;
|
|
|
|
|
ls->D = normalize_len(*light_P - ray_P, &ls->t);
|
Cycles: improve sampling of ellipse area light with spread
**Problem**:
Area lights in Cycles have spread angle, in which case some part of the area light might be invisible to a shading point. The current implementation samples the whole area light, resulting some samples invisible and thus simply discarded. A technique is applied on rectangular light to sample a subset of the area light that is potentially visible (rB3f24cfb9582e1c826406301d37808df7ca6aa64c), however, ellipse (including disk) area lights remained untreated. The purpose of this patch is to apply a techniques to ellipse area light.
**Related Task**:
T87053
**Results**:
These are renderings before and after the patch:
|16spp|Disk light|Ellipse light|Square light (for reference, no changes)
|Before|{F13996789}|{F13996788}|{F13996822}
|After|{F13996759}|{F13996787}|{F13996852}
**Explanation**:
The visible region on an area light is found by drawing a cone from the shading point to the plane where the area light lies, with the aperture of the cone being the light spread.
{F13990078,height=200}
Ideally, we would like to draw samples only from the intersection of the area light and the projection of the cone onto the plane (forming a circle). However, the shape of the intersection is often irregular and thus hard to sample from directly.
{F13990104,height=200}
Instead, the current implementation draws samples from the bounding rectangle of the intersection. In this case, we still end up with some invalid samples outside of the circle, but already much less than sampling the original area light, and the bounding rectangle is easy to sample from.
{F13990125}
The above technique is only applied to rectangle area lights, ellipse area light still suffers from poor sampling. We could apply a similar technique to ellipse area lights, that is, find the
smallest regular shape (rectangle, circle, or ellipse) that covers the intersection (or maybe not the smallest but easy to compute).
For disk area light, we consider the relative position of both circles. Denoting `dist` as the distance between the centre of two circles, and `r1`, `r2` their radii. If `dist > r1 + r2`, the area light is completely invisible, we directly return `false`. If `dist < abs(r1 - r2)`, the smaller circle lies inside the larger one, and we sample whichever circle is smaller. Otherwise, the two circles intersect, we compute the bounding rectangle of the intersection, in which case `axis_u`, `len_u`, `axis_v`, `len_v` needs to be computed anew. Depending on the distance between the two circles, `len_v` is either the diameter of the smaller circle or the length of the common chord.
|{F13990211,height=195}|{F13990225,height=195}|{F13990274,height=195}|{F13990210,height=195}
|`dist > r1 + r2`|`dist < abs(r1 - r2)`|`dist^2 < abs(r1^2 - r2^2)`|`dist^2 > abs(r1^2 - r2^2)`
For ellipse area light, it's hard to find the smallest bounding shape of the intersection, therefore, we compute the bounding rectangle of the ellipse itself, then treat it as a rectangle light.
|{F13990386,height=195}|{F13990385,height=195}|{F13990387,height=195}
We also check the areas of the bounding rectangle of the intersection, the ellipse (disk) light, and the spread circle, then draw samples from the smallest shape of the three. For ellipse light, this also detects where one shape lies inside the other. I am not sure if we should add this measure to rectangle area light and sample from the spread circle when it has smaller area, as we seem to have a better sampling technique for rectangular (uniformly sample the solid angle). Maybe we could add [area-preserving parameterization for spherical
ellipse](https://arxiv.org/pdf/1805.09048.pdf) in the future.
**Limitation**:
At some point we switch from sampling the ellipse to sampling the rectangle, depending on the area of the both, and there seems to be a visible line (with |slope| =1) on the final rendering
which demonstrate at which point we switch between the two methods. We could see that the new sampling method clearly has lower variance near the boundaries, but close to that visible line,
the rectangle sampling method seems to have larger variance. I could not spot any bug in the implementation, and I am not sure if this happens because different sampling patterns for ellipse and rectangle are used.
|Before (256spp)|After (256spp)
|{F13996995}|{F13996998}
Differential Revision: https://developer.blender.org/D16694
2022-12-05 14:35:25 +01:00
|
|
|
}
|
|
|
|
|
|
2023-07-07 17:03:02 +02:00
|
|
|
/* Convert radiant flux to radiance. */
|
|
|
|
|
ls->eval_fac = M_1_PI_F * invarea;
|
2022-11-30 20:17:45 +01:00
|
|
|
|
2022-12-07 18:51:26 +01:00
|
|
|
if (klight->area.normalize_spread > 0) {
|
2022-11-30 20:17:45 +01:00
|
|
|
/* Area Light spread angle attenuation */
|
|
|
|
|
ls->eval_fac *= area_light_spread_attenuation(
|
2023-05-12 23:07:28 +02:00
|
|
|
ls->D, Ng, klight->area.tan_half_spread, klight->area.normalize_spread);
|
2022-11-30 20:17:45 +01:00
|
|
|
}
|
|
|
|
|
|
2023-05-12 22:40:02 +02:00
|
|
|
if (in_volume_segment || (!sample_rectangle && klight->area.tan_half_spread > 0)) {
|
2024-02-07 19:07:11 +01:00
|
|
|
ls->pdf *= light_pdf_area_to_solid_angle(Ng, -ls->D, ls->t);
|
2022-11-30 20:17:45 +01:00
|
|
|
}
|
|
|
|
|
|
2024-03-15 13:00:23 +01:00
|
|
|
return in_volume_segment || ls->eval_fac > 0;
|
2023-05-12 23:07:28 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
template<bool in_volume_segment>
|
|
|
|
|
ccl_device_inline bool area_light_sample(const ccl_global KernelLight *klight,
|
2023-05-24 18:56:58 +02:00
|
|
|
const float2 rand,
|
2023-05-12 23:07:28 +02:00
|
|
|
const float3 P,
|
|
|
|
|
ccl_private LightSample *ls)
|
|
|
|
|
{
|
|
|
|
|
ls->P = klight->co;
|
|
|
|
|
ls->Ng = klight->area.dir;
|
|
|
|
|
|
|
|
|
|
if (!in_volume_segment) {
|
|
|
|
|
if (dot(ls->P - P, ls->Ng) > 0.0f) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2023-05-24 18:56:58 +02:00
|
|
|
if (!area_light_eval<in_volume_segment>(klight, P, &ls->P, ls, rand, true)) {
|
2023-05-12 23:07:28 +02:00
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const float3 inplane = ls->P - klight->co;
|
2024-05-26 01:18:14 +02:00
|
|
|
float light_u = dot(inplane, klight->area.axis_u);
|
|
|
|
|
float light_v = dot(inplane, klight->area.axis_v);
|
2023-05-12 23:07:28 +02:00
|
|
|
|
2024-05-26 01:18:14 +02:00
|
|
|
if (!in_volume_segment && klight->area.normalize_spread > 0) {
|
2024-03-25 13:02:02 +01:00
|
|
|
const bool is_ellipse = area_light_is_ellipse(&klight->area);
|
2023-05-12 23:07:28 +02:00
|
|
|
|
2024-05-26 01:18:14 +02:00
|
|
|
/* Check whether the sampled point lies outside of the area light.
|
|
|
|
|
* For very small area lights, numerical issues can cause this to be
|
|
|
|
|
* slightly off since the sampling logic clamps the result right at the border,
|
|
|
|
|
* so allow for a small margin of error. */
|
|
|
|
|
const float len_u_epsilon = ((0.5f + 1e-7f) * klight->area.len_u + 1e-6f);
|
|
|
|
|
const float len_v_epsilon = ((0.5f + 1e-7f) * klight->area.len_v + 1e-6f);
|
|
|
|
|
if (is_ellipse && (sqr(light_u / len_u_epsilon) + sqr(light_v / len_v_epsilon) > 1.0f)) {
|
2023-05-12 23:07:28 +02:00
|
|
|
return false;
|
|
|
|
|
}
|
2024-05-26 01:18:14 +02:00
|
|
|
if (!is_ellipse && (fabsf(light_u) > len_u_epsilon || fabsf(light_v) > len_v_epsilon)) {
|
2023-05-12 23:07:28 +02:00
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2024-05-26 01:18:14 +02:00
|
|
|
light_u /= klight->area.len_u;
|
|
|
|
|
light_v /= klight->area.len_v;
|
|
|
|
|
|
2023-05-12 23:07:28 +02:00
|
|
|
/* NOTE: Return barycentric coordinates in the same notation as Embree and OptiX. */
|
|
|
|
|
ls->u = light_v + 0.5f;
|
|
|
|
|
ls->v = -light_u - light_v;
|
|
|
|
|
|
2022-11-30 20:17:45 +01:00
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
2023-05-25 12:22:25 +02:00
|
|
|
ccl_device_forceinline void area_light_mnee_sample_update(const ccl_global KernelLight *klight,
|
|
|
|
|
ccl_private LightSample *ls,
|
|
|
|
|
const float3 P)
|
2022-11-30 20:17:45 +01:00
|
|
|
{
|
2023-05-11 16:32:05 +02:00
|
|
|
if (klight->area.tan_half_spread == 0) {
|
|
|
|
|
/* Update position on the light to keep the direction fixed. */
|
2023-05-24 18:56:58 +02:00
|
|
|
area_light_eval<false>(klight, P, &ls->P, ls, zero_float2(), true);
|
2023-05-11 16:32:05 +02:00
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
ls->D = normalize_len(ls->P - P, &ls->t);
|
2023-05-24 18:56:58 +02:00
|
|
|
area_light_eval<false>(klight, P, &ls->P, ls, zero_float2(), false);
|
2023-05-12 23:07:28 +02:00
|
|
|
/* Convert pdf to be in area measure. */
|
2024-02-07 19:07:11 +01:00
|
|
|
ls->pdf /= light_pdf_area_to_solid_angle(ls->Ng, -ls->D, ls->t);
|
2022-11-30 20:17:45 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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);
|
2024-03-25 13:02:02 +01:00
|
|
|
const bool is_ellipse = area_light_is_ellipse(&klight->area);
|
2022-11-30 20:17:45 +01:00
|
|
|
if (invarea == 0.0f) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
2022-12-02 15:21:57 +01:00
|
|
|
const float3 inv_extent_u = klight->area.axis_u / klight->area.len_u;
|
|
|
|
|
const float3 inv_extent_v = klight->area.axis_v / klight->area.len_v;
|
2022-11-30 20:17:45 +01:00
|
|
|
const float3 Ng = klight->area.dir;
|
|
|
|
|
|
|
|
|
|
/* One sided. */
|
|
|
|
|
if (dot(ray->D, Ng) >= 0.0f) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const float3 light_P = klight->co;
|
|
|
|
|
|
|
|
|
|
float3 P;
|
2022-12-02 15:21:57 +01:00
|
|
|
return ray_quad_intersect(ray->P,
|
|
|
|
|
ray->D,
|
|
|
|
|
ray->tmin,
|
|
|
|
|
ray->tmax,
|
|
|
|
|
light_P,
|
|
|
|
|
inv_extent_u,
|
|
|
|
|
inv_extent_v,
|
|
|
|
|
Ng,
|
|
|
|
|
&P,
|
|
|
|
|
t,
|
|
|
|
|
u,
|
|
|
|
|
v,
|
Cycles: improve sampling of ellipse area light with spread
**Problem**:
Area lights in Cycles have spread angle, in which case some part of the area light might be invisible to a shading point. The current implementation samples the whole area light, resulting some samples invisible and thus simply discarded. A technique is applied on rectangular light to sample a subset of the area light that is potentially visible (rB3f24cfb9582e1c826406301d37808df7ca6aa64c), however, ellipse (including disk) area lights remained untreated. The purpose of this patch is to apply a techniques to ellipse area light.
**Related Task**:
T87053
**Results**:
These are renderings before and after the patch:
|16spp|Disk light|Ellipse light|Square light (for reference, no changes)
|Before|{F13996789}|{F13996788}|{F13996822}
|After|{F13996759}|{F13996787}|{F13996852}
**Explanation**:
The visible region on an area light is found by drawing a cone from the shading point to the plane where the area light lies, with the aperture of the cone being the light spread.
{F13990078,height=200}
Ideally, we would like to draw samples only from the intersection of the area light and the projection of the cone onto the plane (forming a circle). However, the shape of the intersection is often irregular and thus hard to sample from directly.
{F13990104,height=200}
Instead, the current implementation draws samples from the bounding rectangle of the intersection. In this case, we still end up with some invalid samples outside of the circle, but already much less than sampling the original area light, and the bounding rectangle is easy to sample from.
{F13990125}
The above technique is only applied to rectangle area lights, ellipse area light still suffers from poor sampling. We could apply a similar technique to ellipse area lights, that is, find the
smallest regular shape (rectangle, circle, or ellipse) that covers the intersection (or maybe not the smallest but easy to compute).
For disk area light, we consider the relative position of both circles. Denoting `dist` as the distance between the centre of two circles, and `r1`, `r2` their radii. If `dist > r1 + r2`, the area light is completely invisible, we directly return `false`. If `dist < abs(r1 - r2)`, the smaller circle lies inside the larger one, and we sample whichever circle is smaller. Otherwise, the two circles intersect, we compute the bounding rectangle of the intersection, in which case `axis_u`, `len_u`, `axis_v`, `len_v` needs to be computed anew. Depending on the distance between the two circles, `len_v` is either the diameter of the smaller circle or the length of the common chord.
|{F13990211,height=195}|{F13990225,height=195}|{F13990274,height=195}|{F13990210,height=195}
|`dist > r1 + r2`|`dist < abs(r1 - r2)`|`dist^2 < abs(r1^2 - r2^2)`|`dist^2 > abs(r1^2 - r2^2)`
For ellipse area light, it's hard to find the smallest bounding shape of the intersection, therefore, we compute the bounding rectangle of the ellipse itself, then treat it as a rectangle light.
|{F13990386,height=195}|{F13990385,height=195}|{F13990387,height=195}
We also check the areas of the bounding rectangle of the intersection, the ellipse (disk) light, and the spread circle, then draw samples from the smallest shape of the three. For ellipse light, this also detects where one shape lies inside the other. I am not sure if we should add this measure to rectangle area light and sample from the spread circle when it has smaller area, as we seem to have a better sampling technique for rectangular (uniformly sample the solid angle). Maybe we could add [area-preserving parameterization for spherical
ellipse](https://arxiv.org/pdf/1805.09048.pdf) in the future.
**Limitation**:
At some point we switch from sampling the ellipse to sampling the rectangle, depending on the area of the both, and there seems to be a visible line (with |slope| =1) on the final rendering
which demonstrate at which point we switch between the two methods. We could see that the new sampling method clearly has lower variance near the boundaries, but close to that visible line,
the rectangle sampling method seems to have larger variance. I could not spot any bug in the implementation, and I am not sure if this happens because different sampling patterns for ellipse and rectangle are used.
|Before (256spp)|After (256spp)
|{F13996995}|{F13996998}
Differential Revision: https://developer.blender.org/D16694
2022-12-05 14:35:25 +01:00
|
|
|
is_ellipse);
|
2022-11-30 20:17:45 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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)
|
|
|
|
|
{
|
|
|
|
|
ls->u = isect->u;
|
|
|
|
|
ls->v = isect->v;
|
|
|
|
|
ls->D = ray_D;
|
2023-05-12 23:07:28 +02:00
|
|
|
ls->Ng = klight->area.dir;
|
2022-11-30 20:17:45 +01:00
|
|
|
|
2023-05-12 23:07:28 +02:00
|
|
|
float3 light_P = klight->co;
|
2023-05-24 18:56:58 +02:00
|
|
|
return area_light_eval<false>(klight, ray_P, &light_P, ls, zero_float2(), false);
|
2022-11-30 20:17:45 +01:00
|
|
|
}
|
2022-12-02 19:04:00 +01:00
|
|
|
|
2024-03-25 13:02:02 +01:00
|
|
|
/* Returns the maximal distance between the light center and the boundary. */
|
|
|
|
|
ccl_device_forceinline float area_light_max_extent(const ccl_global KernelAreaLight *light)
|
|
|
|
|
{
|
|
|
|
|
return 0.5f * (area_light_is_ellipse(light) ? fmaxf(light->len_u, light->len_v) :
|
|
|
|
|
len(make_float2(light->len_u, light->len_v)));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Find the ray segment lit by the area light. */
|
|
|
|
|
ccl_device_inline bool area_light_valid_ray_segment(const ccl_global KernelAreaLight *light,
|
|
|
|
|
float3 P,
|
|
|
|
|
float3 D,
|
|
|
|
|
ccl_private float2 *t_range)
|
|
|
|
|
{
|
|
|
|
|
bool valid;
|
|
|
|
|
const float tan_half_spread = light->tan_half_spread;
|
|
|
|
|
float3 axis = light->dir;
|
|
|
|
|
|
|
|
|
|
const bool angle_almost_zero = (tan_half_spread < 1e-5f);
|
|
|
|
|
if (angle_almost_zero) {
|
|
|
|
|
/* Map to local coordinate of the light. Do not use `itfm` in `KernelLight` as there might be
|
|
|
|
|
* additional scaling in the light size. */
|
|
|
|
|
const Transform tfm = make_transform(light->axis_u, light->axis_v, axis);
|
|
|
|
|
P = transform_point(&tfm, P);
|
|
|
|
|
D = transform_direction(&tfm, D);
|
|
|
|
|
axis = make_float3(0.0f, 0.0f, 1.0f);
|
|
|
|
|
|
|
|
|
|
const float half_len_u = 0.5f * light->len_u;
|
|
|
|
|
const float half_len_v = 0.5f * light->len_v;
|
|
|
|
|
if (area_light_is_ellipse(light)) {
|
|
|
|
|
valid = ray_infinite_cylinder_intersect(P, D, half_len_u, half_len_v, t_range);
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
const float3 bbox_min = make_float3(-half_len_u, -half_len_v, 0.0f);
|
|
|
|
|
const float3 bbox_max = make_float3(half_len_u, half_len_v, FLT_MAX);
|
|
|
|
|
valid = ray_aabb_intersect(bbox_min, bbox_max, P, D, t_range);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
/* Conservative estimation with the smallest possible cone covering the whole spread. */
|
|
|
|
|
const float3 apex_to_point = P + area_light_max_extent(light) / tan_half_spread * axis;
|
|
|
|
|
const float cos_angle_sq = 1.0f / (1.0f + sqr(tan_half_spread));
|
|
|
|
|
|
|
|
|
|
valid = ray_cone_intersect(axis, apex_to_point, D, cos_angle_sq, t_range);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Limit the range to the positive side of the area light. */
|
|
|
|
|
return valid && ray_plane_intersect(axis, P, D, t_range);
|
|
|
|
|
}
|
|
|
|
|
|
2022-12-02 19:04:00 +01:00
|
|
|
template<bool in_volume_segment>
|
|
|
|
|
ccl_device_forceinline bool area_light_tree_parameters(const ccl_global KernelLight *klight,
|
|
|
|
|
const float3 centroid,
|
|
|
|
|
const float3 P,
|
|
|
|
|
const float3 N,
|
|
|
|
|
const float3 bcone_axis,
|
|
|
|
|
ccl_private float &cos_theta_u,
|
|
|
|
|
ccl_private float2 &distance,
|
|
|
|
|
ccl_private float3 &point_to_centroid)
|
|
|
|
|
{
|
2024-03-27 15:06:05 +01:00
|
|
|
/* TODO: a cheap substitute for minimal distance between point and primitive. Does it worth the
|
|
|
|
|
* overhead to compute the accurate minimal distance? */
|
|
|
|
|
float min_distance;
|
|
|
|
|
point_to_centroid = safe_normalize_len(centroid - P, &min_distance);
|
|
|
|
|
distance = make_float2(min_distance, min_distance);
|
2022-12-02 19:04:00 +01:00
|
|
|
|
2022-12-05 20:16:10 +01:00
|
|
|
cos_theta_u = FLT_MAX;
|
|
|
|
|
|
2022-12-02 19:04:00 +01:00
|
|
|
const float3 extentu = klight->area.axis_u * klight->area.len_u;
|
|
|
|
|
const float3 extentv = klight->area.axis_v * klight->area.len_v;
|
|
|
|
|
for (int i = 0; i < 4; i++) {
|
|
|
|
|
const float3 corner = ((i & 1) - 0.5f) * extentu + 0.5f * ((i & 2) - 1) * extentv + centroid;
|
|
|
|
|
float distance_point_to_corner;
|
|
|
|
|
const float3 point_to_corner = safe_normalize_len(corner - P, &distance_point_to_corner);
|
|
|
|
|
cos_theta_u = fminf(cos_theta_u, dot(point_to_centroid, point_to_corner));
|
|
|
|
|
if (!in_volume_segment) {
|
|
|
|
|
distance.x = fmaxf(distance.x, distance_point_to_corner);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const bool front_facing = dot(bcone_axis, point_to_centroid) < 0;
|
|
|
|
|
const bool shape_above_surface = dot(N, centroid - P) + fabsf(dot(N, extentu)) +
|
|
|
|
|
fabsf(dot(N, extentv)) >
|
|
|
|
|
0;
|
|
|
|
|
|
Fix #114634: correlated samples in volume when using equiangular sampling and light tree
The same random number was used when sampling from the volume segment
and from the direct scattering position, causing correlation issues with
light tree.
To solve this problem, we ensure the same light is picked for
volume segment/direct scattering, equiangular/distance sampling by
sampling the light tree only once in volume segment. From the direct
scattering position in volume, we sample a position on the picked light
as usual. If sampling from the light tree fails, we continue with
indirect scattering.
For unbiased MIS weight for forward sampling, we retrieve the `P`, `D`
and `t` used in volume segment for traversing the light tree.
The main changes are:
1. `light_tree_sample()` and `light_distribution_sample()` now only pick
lights. Sampling a position on light is done separately via
`light_sample()`.
2. `light_tree_sample()` is now only called only once from volume
segment. For direct lighting we call `light_sample()`.
3. `light_tree_pdf()` now has a template `<in_volume_segment>`.
4. A new field `emitter_id` is added to struct `LightSample`, which just
stores the picked emitter index.
5. Additional field `previous_dt = ray->tmax - ray->tmin` is added to
`state->ray`, because we need this quantity for computing the pdf.
6. Distant/Background lights are also picked by light tree in volume
segment now, because we have no way to pick them afterwards. The direct
sample event for these lights will be handled by
`VOLUME_SAMPLE_DISTANCE`.
7. Original paper suggests to use the maximal importance, this results
in very poor sampling probability for distant and point lights therefore
excessive noise. We have a minimal importance for surface to balance, we
could do the same for volume but I do not want to spend much time on
this now. Just doing `min_importance = 0.0f` seems to do the job
okayish. This way we still won't sample the light with zero
`max_importance`.
The current solution might perform worse with distance sampling, because
the light tree measure is biased towards equiangular sampling. However,
it is difficult to perform MIS between equiangular and distance sampling
if different lights are picked for each method. This is something we can
look into in the future if proved to be a serious regression.
Pull Request: https://projects.blender.org/blender/blender/pulls/119389
2024-03-25 18:50:52 +01:00
|
|
|
return front_facing && shape_above_surface;
|
2022-12-02 19:04:00 +01:00
|
|
|
}
|
|
|
|
|
|
2022-11-30 20:17:45 +01:00
|
|
|
CCL_NAMESPACE_END
|