Uses a light tree to more effectively sample scenes with many lights. This can significantly reduce noise, at the cost of a somewhat longer render time per sample. Light tree sampling is enabled by default. It can be disabled in the Sampling > Lights panel. Scenes using light clamping or ray visibility tricks may render different as these are biased techniques that depend on the sampling strategy. The implementation is currently disabled on AMD HIP. This is planned to be fixed before the release. Implementation by Jeffrey Liu, Weizhen Huang, Alaska and Brecht Van Lommel. Ref T77889
180 lines
5.2 KiB
C++
180 lines
5.2 KiB
C++
/* SPDX-License-Identifier: Apache-2.0
|
|
* Copyright 2011-2022 Blender Foundation */
|
|
|
|
#pragma once
|
|
|
|
#include "kernel/light/common.h"
|
|
|
|
CCL_NAMESPACE_BEGIN
|
|
|
|
ccl_device float spot_light_attenuation(float3 dir,
|
|
float cos_half_spot_angle,
|
|
float spot_smooth,
|
|
float3 N)
|
|
{
|
|
float attenuation = dot(dir, N);
|
|
|
|
if (attenuation <= cos_half_spot_angle) {
|
|
attenuation = 0.0f;
|
|
}
|
|
else {
|
|
float t = attenuation - cos_half_spot_angle;
|
|
|
|
if (t < spot_smooth && spot_smooth != 0.0f)
|
|
attenuation *= smoothstepf(t / spot_smooth);
|
|
}
|
|
|
|
return attenuation;
|
|
}
|
|
|
|
template<bool in_volume_segment>
|
|
ccl_device_inline bool spot_light_sample(const ccl_global KernelLight *klight,
|
|
const float randu,
|
|
const float randv,
|
|
const float3 P,
|
|
ccl_private LightSample *ls)
|
|
{
|
|
ls->P = klight->co;
|
|
|
|
const float3 center = klight->co;
|
|
const float radius = klight->spot.radius;
|
|
/* disk oriented normal */
|
|
const float3 lightN = normalize(P - center);
|
|
ls->P = center;
|
|
|
|
if (radius > 0.0f) {
|
|
/* disk light */
|
|
ls->P += disk_light_sample(lightN, randu, randv) * radius;
|
|
}
|
|
|
|
const float invarea = klight->spot.invarea;
|
|
ls->pdf = invarea;
|
|
|
|
ls->D = normalize_len(ls->P - P, &ls->t);
|
|
/* we set the light normal to the outgoing direction to support texturing */
|
|
ls->Ng = -ls->D;
|
|
|
|
ls->eval_fac = (0.25f * M_1_PI_F) * invarea;
|
|
|
|
/* spot light attenuation */
|
|
ls->eval_fac *= spot_light_attenuation(
|
|
klight->spot.dir, klight->spot.cos_half_spot_angle, klight->spot.spot_smooth, -ls->D);
|
|
if (!in_volume_segment && ls->eval_fac == 0.0f) {
|
|
return false;
|
|
}
|
|
|
|
float2 uv = map_to_sphere(ls->Ng);
|
|
ls->u = uv.x;
|
|
ls->v = uv.y;
|
|
|
|
ls->pdf *= lamp_light_pdf(lightN, -ls->D, ls->t);
|
|
return true;
|
|
}
|
|
|
|
ccl_device_forceinline void spot_light_update_position(const ccl_global KernelLight *klight,
|
|
ccl_private LightSample *ls,
|
|
const float3 P)
|
|
{
|
|
ls->D = normalize_len(ls->P - P, &ls->t);
|
|
ls->Ng = -ls->D;
|
|
|
|
float2 uv = map_to_sphere(ls->Ng);
|
|
ls->u = uv.x;
|
|
ls->v = uv.y;
|
|
|
|
float invarea = klight->spot.invarea;
|
|
ls->eval_fac = (0.25f * M_1_PI_F) * invarea;
|
|
ls->pdf = invarea;
|
|
|
|
/* spot light attenuation */
|
|
ls->eval_fac *= spot_light_attenuation(
|
|
klight->spot.dir, klight->spot.cos_half_spot_angle, klight->spot.spot_smooth, ls->Ng);
|
|
}
|
|
|
|
ccl_device_inline bool spot_light_intersect(const ccl_global KernelLight *klight,
|
|
const ccl_private Ray *ccl_restrict ray,
|
|
ccl_private float *t)
|
|
{
|
|
/* Spot/Disk light. */
|
|
const float3 lightP = klight->co;
|
|
const float radius = klight->spot.radius;
|
|
if (radius == 0.0f) {
|
|
return false;
|
|
}
|
|
/* disk oriented normal */
|
|
const float3 lightN = normalize(ray->P - lightP);
|
|
/* One sided. */
|
|
if (dot(ray->D, lightN) >= 0.0f) {
|
|
return false;
|
|
}
|
|
|
|
float3 P;
|
|
return ray_disk_intersect(ray->P, ray->D, ray->tmin, ray->tmax, lightP, lightN, radius, &P, t);
|
|
}
|
|
|
|
ccl_device_inline bool spot_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)
|
|
{
|
|
/* the normal of the oriented disk */
|
|
const float3 lightN = normalize(ray_P - klight->co);
|
|
/* We set the light normal to the outgoing direction to support texturing. */
|
|
ls->Ng = -ls->D;
|
|
|
|
float invarea = klight->spot.invarea;
|
|
ls->eval_fac = (0.25f * M_1_PI_F) * invarea;
|
|
ls->pdf = invarea;
|
|
|
|
/* spot light attenuation */
|
|
ls->eval_fac *= spot_light_attenuation(
|
|
klight->spot.dir, klight->spot.cos_half_spot_angle, klight->spot.spot_smooth, -ls->D);
|
|
|
|
if (ls->eval_fac == 0.0f) {
|
|
return false;
|
|
}
|
|
|
|
float2 uv = map_to_sphere(ls->Ng);
|
|
ls->u = uv.x;
|
|
ls->v = uv.y;
|
|
|
|
/* compute pdf */
|
|
if (ls->t != FLT_MAX) {
|
|
ls->pdf *= lamp_light_pdf(lightN, -ls->D, ls->t);
|
|
}
|
|
else {
|
|
ls->pdf = 0.f;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
template<bool in_volume_segment>
|
|
ccl_device_forceinline bool spot_light_tree_parameters(const ccl_global KernelLight *klight,
|
|
const float3 centroid,
|
|
const float3 P,
|
|
ccl_private float &cos_theta_u,
|
|
ccl_private float2 &distance,
|
|
ccl_private float3 &point_to_centroid)
|
|
{
|
|
float min_distance;
|
|
const float3 point_to_centroid_ = safe_normalize_len(centroid - P, &min_distance);
|
|
|
|
const float radius = klight->spot.radius;
|
|
const float hypotenus = sqrtf(sqr(radius) + sqr(min_distance));
|
|
cos_theta_u = min_distance / hypotenus;
|
|
|
|
if (in_volume_segment) {
|
|
return true;
|
|
}
|
|
|
|
distance = make_float2(hypotenus, min_distance);
|
|
point_to_centroid = point_to_centroid_;
|
|
|
|
return true;
|
|
}
|
|
|
|
CCL_NAMESPACE_END
|