blender/intern/cycles/kernel/light/distant.h
Weizhen Huang 1284e98ab8 Cycles: use low-distortion mapping when sampling cone and hemisphere
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
2023-08-23 17:25:27 +02:00

149 lines
4.3 KiB
C

/* SPDX-FileCopyrightText: 2011-2022 Blender Foundation
*
* SPDX-License-Identifier: Apache-2.0 */
#pragma once
#include "kernel/geom/geom.h"
#include "kernel/light/common.h"
CCL_NAMESPACE_BEGIN
ccl_device_inline void distant_light_uv(const ccl_global KernelLight *klight,
const float3 D,
ccl_private float *u,
ccl_private float *v)
{
/* Map direction (x, y, z) to disk [-0.5, 0.5]^2:
* r^2 = (1 - z) / (1 - cos(klight->distant.angle))
* u_ = 0.5 * x * r / sin_angle(D, -klight->co)
* v_ = 0.5 * y * r / sin_angle(D, -klight->co) */
const float fac = klight->distant.half_inv_sin_half_angle / len(D - klight->co);
/* Get u axis and v axis. */
const Transform itfm = klight->itfm;
const float u_ = dot(D, float4_to_float3(itfm.x)) * fac;
const float v_ = dot(D, float4_to_float3(itfm.y)) * fac;
/* NOTE: Return barycentric coordinates in the same notation as Embree and OptiX. */
*u = v_ + 0.5f;
*v = -u_ - v_;
}
ccl_device_inline bool distant_light_sample(const ccl_global KernelLight *klight,
const float2 rand,
ccl_private LightSample *ls)
{
float unused;
ls->Ng = sample_uniform_cone(
klight->co, klight->distant.one_minus_cosangle, rand, &unused, &ls->pdf);
ls->P = ls->Ng;
ls->D = -ls->Ng;
ls->t = FLT_MAX;
ls->eval_fac = klight->distant.eval_fac;
distant_light_uv(klight, ls->D, &ls->u, &ls->v);
return true;
}
/* Special intersection check.
* Returns true if the distant_light_sample_from_intersection() for this light would return true.
*
* The intersection parameters t, u, v are optimized for the shadow ray towards a dedicated light:
* u = v = 0, t = FLT_MAX.
*/
ccl_device bool distant_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)
{
kernel_assert(klight->type == LIGHT_DISTANT);
if (klight->distant.angle == 0.0f) {
return false;
}
if (vector_angle(-klight->co, ray->D) > klight->distant.angle) {
return false;
}
*t = FLT_MAX;
*u = 0.0f;
*v = 0.0f;
return true;
}
ccl_device bool distant_light_sample_from_intersection(KernelGlobals kg,
const float3 ray_D,
const int lamp,
ccl_private LightSample *ccl_restrict ls)
{
ccl_global const KernelLight *klight = &kernel_data_fetch(lights, lamp);
const int shader = klight->shader_id;
const LightType type = (LightType)klight->type;
if (type != LIGHT_DISTANT) {
return false;
}
if (!(shader & SHADER_USE_MIS)) {
return false;
}
if (klight->distant.angle == 0.0f) {
return false;
}
/* Workaround to prevent a hang in the classroom scene with AMD HIP drivers 22.10,
* Remove when a compiler fix is available. */
#ifdef __HIP__
ls->shader = klight->shader_id;
#endif
if (vector_angle(-klight->co, ray_D) > klight->distant.angle) {
return false;
}
ls->type = type;
#ifndef __HIP__
ls->shader = klight->shader_id;
#endif
ls->object = PRIM_NONE;
ls->prim = PRIM_NONE;
ls->lamp = lamp;
ls->t = FLT_MAX;
ls->P = -ray_D;
ls->Ng = -ray_D;
ls->D = ray_D;
ls->group = lamp_lightgroup(kg, lamp);
ls->pdf = klight->distant.pdf;
ls->eval_fac = klight->distant.eval_fac;
distant_light_uv(klight, ray_D, &ls->u, &ls->v);
return true;
}
ccl_device_forceinline bool distant_light_tree_parameters(const float3 centroid,
const float theta_e,
ccl_private float &cos_theta_u,
ccl_private float2 &distance,
ccl_private float3 &point_to_centroid)
{
/* Treating it as a disk light 1 unit away */
cos_theta_u = fast_cosf(theta_e);
distance = make_float2(1.0f / cos_theta_u, 1.0f);
point_to_centroid = -centroid;
return true;
}
CCL_NAMESPACE_END