This fixes#69535 and #98930.
We use a equi-solid-angle sampling algorithm for rectangular area lights,
but it is not particularly robust for small area lights (either small
in general and/or small because it's being viewed from grazing angles).
The actual sampling part is fine since it just gets clamped into the
valid area anyways, and the difference isn't notable for small lights.
However, we also need to compute the solid angle to get the sampling PDF,
and that computation is quite sensitive to numerical issues for small
values.
Therefore, this commit adds a fallback path for small values, which instead
uses the classic equi-area sampling PDF term times the area-to-solid-angle
Jacobian term. This approximation assumes that all points on the light have
the same distance and angle to the sampling point, which is of course not
strictly the case, but it's close enough for small area lights and better
than failing altogether.
Pull Request: https://projects.blender.org/blender/blender/pulls/122323
Reformulates some terms in the equi-solid-angle rectangle sampling code to
handle small area lamps better, and allows for some rounding error in the
check whether the sampled position is inside the area light.
Pull Request: https://projects.blender.org/blender/blender/pulls/122323
In the original paper, the falloff inside `bcone.theta_e` is assumed to
be `pi/2`, which is too large for spot light and resulted in an
overestimation near the cone boundary.
To address this issue, attenuate the energy of a spot light using the
minimal possible angle formed by the light axis and the shading point
when traversing the light tree.
Ref: #122362
Pull Request: https://projects.blender.org/blender/blender/pulls/122667
The refactor in 97d9bbbc97 changed the way q is computed in the spherical triangle sampling code. While the new approach is more efficient and saves a few operations, it introduces numerical precision issues for skinny/small (spherical) triangles.
Therefore, this change moves the computation of q back to the method from the paper, while keeping the more efficient solid angle computation.
Pull Request: https://projects.blender.org/blender/blender/pulls/119224
it is difficult to keep in mind when MIS weight is needed, better to
handle this logic in the lower-level functions.
This reduces code duplication in many places.
it is not clear from which point the `cos_theta_u` should be computed in
volume segment, so the original implementation was mixing the closest point
and the point where the minimal angle is formed.
Use the closest point on segment as a conservative measure.
Pull Request: https://projects.blender.org/blender/blender/pulls/119965
in the test scene `all_light_types_in_volume.blend`, `theta - theta_o -
theta_u` is slightly above the threshold. Even if we do a strict check
with `acos` on the failing cases, it will go to the other branch and
deliver a result which is also 1.0f. Better to relax the threshold.
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
When using light linking with the light tree, the root index of a
mesh light subtree can be 0. The current code assumed this wasn't
possible, and as such it caused rendering issues, specifically the
incorrect computation of the PDF of certain mesh lights during
forward path tracing.
So we adjust the code to allow mesh light subtree root node
indices of 0.
This was worked on by Alaska, Sergey, and Weizhen
Pull Request: https://projects.blender.org/blender/blender/pulls/119770
By restricting the sample range along the ray to the valid segment.
Supports
**Mesh Light**
- [x] restrict the ray segment to the side with MIS
**Area Light**
- [x] when the spread is zero, find the intersection of the ray and the bounding box/cylinder of the rectangle/ellipse area light beam
- [x] when the spread is non-zero, find the intersection of the ray and the minimal enclosing cone of the area light beam
*note the result is also unbiased when we just consider the cone from the sampled point in volume segment. Far away from the light source it's less noisy than the current solution, but near the light source it's much noisier. We have to restrict the sample region on the area light to the part that lits the ray then, I haven't tried yet to see if it would be less noisy.*
**Point Light**
- [x] the complete ray segment should be valid.
**Spot Light**
- [x] intersect the ray with the spot light cone
- [x] support non-zero radius
Pull Request: https://projects.blender.org/blender/blender/pulls/119438
For spherical spot light, when the shading point is close to the light
source, we switch to sampling the light spread instead of the visible
cone from the shading point. This has the benefit of less noise when the
spread is small.
However, the light spread sampling was not considering non-uniform
object scaling, where the actual spread might be different.
This patch switches sampling method only when the smallest enclosing
spread cone is smaller than the visible cone from the shading point.
An alternative method would be to compute the actual solid angle of the
scaled cone, and sample from the scaled cone. However, that involves
ray transformation and modifying the sampling pdf and angle. Since
non-uniform scaling is rather a niche case, it's probably not worth the
computation effort.
Pull Request: https://projects.blender.org/blender/blender/pulls/119661
Ref: #118534
turns out `in_volume_segment` does need to be checked. If the ray origin
lies on the wrong side of the mesh light, part of the ray could still be
lit by the other side, so the sample should not be considered invalid.
Pull Request: https://projects.blender.org/blender/blender/pulls/119529
if emission sampling is not set, we do not use MIS weight when sampling
from the BSDF, but we were still drawing samples from the light,
resulting in double contribution.
Pull Request: https://projects.blender.org/blender/blender/pulls/118534
Add new "Soft Falloff" option on point and spot light that uses
the old light behavior from Blender versions before 4.0. Blend
files saved with those older versions will use the option.
This option is enabled by default on new lights.
Fix#114241
Co-authored-by: Weizhen Huang <weizhen@blender.org>
Co-authored-by: Clément Foucault <foucault.clem@gmail.com>
Pull Request: https://projects.blender.org/blender/blender/pulls/117832
Along with the 4.1 libraries upgrade, we are bumping the clang-format
version from 8-12 to 17. This affects quite a few files.
If not already the case, you may consider pointing your IDE to the
clang-format binary bundled with the Blender precompiled libraries.
The solid angle computation for the sampling/PDF code was running into
cancellation issues for small solid angles.
Reformulating the terms using asin() instead avoids this.
based on concentric disk mapping.
Concentric disk mapping was already present, but not used everywhere.
Now `sample_cos_hemisphere()`, `sample_uniform_hemisphere()`, and
`sample_uniform_cone()` use concentric disk mapping.
This changes the noise in many test images.
Pull Request: https://projects.blender.org/blender/blender/pulls/109774
This reverts commit 206ab6437b.
Seems that the illegal address error should be covered elsewhere, but it's not directly
clear where. Revert the commit for further investigation.
Discovered during an investigation into #111277
in rare situations (E.G. When normals are NaN), an emitter
won't be selected as part of `light_tree_cluster_select_emitter()`
and as a result of that, an `emitter_index` of `-1` is passed to
`kernel_data_fetch(light_tree_emitters, emitter_index)` resulting in
an "illegal address" error on some devices.
Pull Request: https://projects.blender.org/blender/blender/pulls/111292
This pull request covers up a subtle difference between the CPU and GPU
when rendering with a light tree. Specifically a case where the user
has a sun light with a small angle.
The difference was caused by the dot() function being different between
CPU and GPU backends, with the GPU showing more meaningful
floating-point precision losses when working with small suns.
Pull Request: https://projects.blender.org/blender/blender/pulls/110307
The issue was an out-of-bounds read access when checking whether
the world volume emission needs to be accumulated.
Solution is to check for this case. Done in the generic place, so
that the shade_volume kernel is more readable and no branching
added there, and there is no impact on scenes without the light
linking.
Assume that the world emissive volume belongs to the default light
linking group, as there is no way to link it explicitly to anything.
Pull Request: https://projects.blender.org/blender/blender/pulls/110733
Using area-preserving mapping from cone to disk. Has somewhat distortion
near 90°.
The texture rotates with the transformation of the light object, can
have negative and non-uniform scaling.
Pull Request: https://projects.blender.org/blender/blender/pulls/109842
This fixes the issue described in https://projects.blender.org/blender/blender/issues/108957.
Instead of modeling distant lights like a disk light at infinity, it models them as cones. This way, the radiance is constant across the entire range of directions that it covers.
For smaller angles, the difference is very subtle, but for very large angles it becomes obvious (here's the file from #108957, the angle is 179°):
| Old | New |
| - | - |
|  |  |
One notable detail is the sampling method: Using `sample_uniform_cone` can increase noise, since the sampling method no longer preserves the stratification of the samples. This is visible in the "light tree multi distant" test scene.
Turns out we can do better, and after a bit of testing I found a way to adapt the concentric Shirley mapping to uniform cone sampling. I hope the comment explains the logic behind it reasonably well.
Here's the result, note that even the noise distribution is the same when using the new sampling:
| Method | Old | New, basic sampling | New, concentric sampling |
| - | - |- | - |
| Image |  |  |  |
| Render time (at higher spp)| 9.03sec | 8.79sec | 8.96sec |
I'm not sure if I got the `light->normalized` handling right, since I don't really know what the expectation from Hydra is here.
Co-authored-by: Weizhen Huang <weizhen@blender.org>
Pull Request: https://projects.blender.org/blender/blender/pulls/108996
The spotlight is now treated as a sphere instead of a view-aligned disk.
The implementation remains almost identical to that of a point light,
except for the spotlight attenuation and spot blend. There is no
attenuation inside the sphere. Ref #108505
Other changes include:
## Sampling
Instead of sampling the disk area, the new implementation samples either
the cone of the visible portion on the sphere or the spread cone, based
on which cone has a smaller solid angle. This reduces noise when the
spotlight has a large radius and a small spread angle.
| Before | After |
| -- | -- |
||
## Texture
Spot light can now project texture using UV coordinates.
<video src="/attachments/6db989d2-7a3c-4b41-9340-f5690d48c4fb"
title="spot_light_texture.mp4" controls></video>
## Normalization
Previously, the normalization factor for the spotlight was \(\pi r^2\),
the area of a disk. This factor has been adjusted to \(4\pi r^2\) to
account for the surface area of a sphere. This change also affects point
light since they share the same kernel type.
## Versioning
Some pipeline uses the `Normal` socket of the Texture Coordinate node for
projection, because `ls->Ng` was set to the incoming direction at the
current shading point. Now that `ls->Ng` corresponds to the normal
direction of a point on the sphere (except when the radius is zero),
we replace these nodes with a combination of the Geometry shader node
and the Vector Transform node, which gives the same result as before.

Example file see https://archive.blender.org/developer/T93676
Pull Request: https://projects.blender.org/blender/blender/pulls/109329
for energy preservation and better compatibility with other renderes. Ref: #108505
Point light now behaves the same as a spherical mesh light with the same overall energy (scaling from emission strength to power is \(4\pi^2R^2\)).
# Cycles
## Comparison
| Mesh Light | This patch | Previous behavior |
| -------- | -------- | -------- |
|  |  |  |
The behavior stays the same when `radius = 0`.
| This patch | Previous behavior |
| -------- | -------- |
|  |  |
No obvious performance change observed.
## Sampling
When shading point lies outside the sphere, sample the spanned solid angle uniformly.
When shading point lies inside the sphere, sample spherical direction uniformly when inside volume or the surface is transmissive, otherwise sample cosine-weighted upper hemisphere.
## Light Tree
When shading point lies outside the sphere, treat as a disk light spanning the same solid angle.
When shading point lies inside the sphere, it behaves like a background light, with estimated outgoing radiance
\[L_o=\int f_aL_i\cos\theta_i\mathrm{d}\omega_i=\int f_a\frac{E}{\pi r^2}\cos\theta_i\mathrm{d}\omega_i\approx f_a \frac{E}{r^2}\],
with \(f_a\) being the BSDF and \(E\) `measure.energy` in `light_tree.cpp`.
The importance calculation for `LIGHT_POINT` is
\[L_o=f_a E\cos\theta_i\frac{\cos\theta}{d^2}\].
Consider `min_importance = 0` because maximal incidence angle is \(\pi\), we could substitute \(d^2\) with \(\frac{r^2}{2}\) so the averaged outgoing radiance is \(f_a \frac{E}{r^2}\).
This only holds for non-transmissive surface, but should be fine to use in volume.
# EEVEE
When shading point lies outside the sphere, the sphere light is equivalent to a disk light spanning the same solid angle. The sine of the new half-angle is the tangent of the previous half-angle.
When shading point lies inside the sphere, integrating over the cosine-weighted hemisphere gives 1.0.
## Comparison with Cycles
The plane is diffuse, the blue sphere has specular component.
| Before | |After ||
|---|--|--|--|
|Cycles|EEVEE|Cycles|EEVEE|
|||||
Pull Request: https://projects.blender.org/blender/blender/pulls/108506
This fixes an issue where the light tree sampling algorithm would
discard light samples from groups of distance lights with an angle
greater than 0 when it shouldn't.
Pull Request: https://projects.blender.org/blender/blender/pulls/108832