Add Chromium-only Blender WebEngine parity work
This commit is contained in:
493
blender-5.2.0/intern/cycles/kernel/light/light.h
Normal file
493
blender-5.2.0/intern/cycles/kernel/light/light.h
Normal file
@@ -0,0 +1,493 @@
|
||||
/* SPDX-FileCopyrightText: 2010-2022 Blender Foundation
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0 */
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "kernel/geom/object.h"
|
||||
#include "kernel/globals.h"
|
||||
|
||||
#include "kernel/integrator/state.h"
|
||||
|
||||
#include "kernel/light/area.h"
|
||||
#include "kernel/light/background.h"
|
||||
#include "kernel/light/point.h"
|
||||
#include "kernel/light/spot.h"
|
||||
#include "kernel/light/sun.h"
|
||||
#include "kernel/light/triangle.h"
|
||||
#include "kernel/sample/lcg.h"
|
||||
#include "kernel/types.h"
|
||||
|
||||
CCL_NAMESPACE_BEGIN
|
||||
|
||||
/* Light info. */
|
||||
|
||||
ccl_device_inline bool light_select_reached_max_bounces(KernelGlobals kg,
|
||||
const int index,
|
||||
const int bounce)
|
||||
{
|
||||
return (bounce > kernel_data_fetch(lights, index).max_bounces);
|
||||
}
|
||||
|
||||
/* Light linking. */
|
||||
|
||||
ccl_device_inline int light_link_receiver_nee(KernelGlobals kg, const ccl_private ShaderData *sd)
|
||||
{
|
||||
#ifdef __LIGHT_LINKING__
|
||||
if (!(kernel_data.kernel_features & KERNEL_FEATURE_LIGHT_LINKING)) {
|
||||
return OBJECT_NONE;
|
||||
}
|
||||
|
||||
return sd->object;
|
||||
#else
|
||||
return OBJECT_NONE;
|
||||
#endif
|
||||
}
|
||||
|
||||
ccl_device_inline int light_link_receiver_forward(KernelGlobals kg, IntegratorState state)
|
||||
{
|
||||
#ifdef __LIGHT_LINKING__
|
||||
if (!(kernel_data.kernel_features & KERNEL_FEATURE_LIGHT_LINKING)) {
|
||||
return OBJECT_NONE;
|
||||
}
|
||||
|
||||
return INTEGRATOR_STATE(state, path, mis_ray_object);
|
||||
#else
|
||||
return OBJECT_NONE;
|
||||
#endif
|
||||
}
|
||||
|
||||
ccl_device_inline bool light_link_object_match(KernelGlobals kg,
|
||||
const int receiver,
|
||||
const int emitter)
|
||||
{
|
||||
#ifdef __LIGHT_LINKING__
|
||||
if (!(kernel_data.kernel_features & KERNEL_FEATURE_LIGHT_LINKING)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
kernel_assert(emitter != OBJECT_NONE);
|
||||
kernel_assert(receiver != OBJECT_NONE);
|
||||
|
||||
const uint64_t set_membership = kernel_data_fetch(objects, emitter).light_set_membership;
|
||||
const uint receiver_set = kernel_data_fetch(objects, receiver).receiver_light_set;
|
||||
return ((uint64_t(1) << uint64_t(receiver_set)) & set_membership) != 0;
|
||||
#else
|
||||
return true;
|
||||
#endif
|
||||
}
|
||||
|
||||
/* Sample point on an individual light. */
|
||||
|
||||
template<bool in_volume_segment>
|
||||
ccl_device_inline bool light_sample(KernelGlobals kg,
|
||||
const int lamp,
|
||||
const float2 rand,
|
||||
const float3 P,
|
||||
const float3 N,
|
||||
const int shader_flags,
|
||||
const uint32_t path_flag,
|
||||
ccl_private LightSample *ls)
|
||||
{
|
||||
const ccl_global KernelLight *klight = &kernel_data_fetch(lights, lamp);
|
||||
if (path_flag & PATH_RAY_SHADOW_CATCHER_PASS) {
|
||||
if (klight->shader_id & SHADER_EXCLUDE_SHADOW_CATCHER) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
const LightType type = (LightType)klight->type;
|
||||
ls->type = type;
|
||||
ls->shader = klight->shader_id;
|
||||
ls->object = klight->object_id;
|
||||
ls->prim = lamp;
|
||||
ls->group = object_lightgroup(kg, ls->object);
|
||||
|
||||
if (in_volume_segment && (type == LIGHT_SUN || type == LIGHT_BACKGROUND)) {
|
||||
/* Distant lights in a volume get a dummy sample, position will not actually
|
||||
* be used in that case. Only when sampling from a specific scatter position
|
||||
* do we actually need to evaluate these. */
|
||||
ls->P = zero_float3();
|
||||
ls->Ng = zero_float3();
|
||||
ls->D = zero_float3();
|
||||
ls->pdf = 1.0f;
|
||||
ls->eval_fac = 0.0f;
|
||||
ls->t = FLT_MAX;
|
||||
return true;
|
||||
}
|
||||
|
||||
if (type == LIGHT_SUN) {
|
||||
if (!sun_light_sample(klight, rand, ls)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else if (type == LIGHT_BACKGROUND) {
|
||||
/* infinite area light (e.g. light dome or env light) */
|
||||
const float3 D = -background_light_sample(kg, P, rand, &ls->pdf);
|
||||
|
||||
ls->P = D;
|
||||
ls->Ng = D;
|
||||
ls->D = -D;
|
||||
ls->t = FLT_MAX;
|
||||
ls->eval_fac = 1.0f;
|
||||
}
|
||||
else if (type == LIGHT_SPOT) {
|
||||
if (!spot_light_sample<in_volume_segment>(kg, klight, rand, P, N, shader_flags, ls)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else if (type == LIGHT_POINT) {
|
||||
if (!point_light_sample(klight, rand, P, N, shader_flags, ls)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else {
|
||||
/* area light */
|
||||
if (!area_light_sample<in_volume_segment>(klight, rand, P, ls)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return in_volume_segment || (ls->pdf > 0.0f);
|
||||
}
|
||||
|
||||
/* Sample a point on the chosen emitter. */
|
||||
|
||||
template<bool in_volume_segment>
|
||||
ccl_device bool light_sample(KernelGlobals kg,
|
||||
const float3 rand_light,
|
||||
const float time,
|
||||
const float3 P,
|
||||
const float3 N,
|
||||
const int object_receiver,
|
||||
const int shader_flags,
|
||||
const int bounce,
|
||||
const uint32_t path_flag,
|
||||
ccl_private LightSample *ls)
|
||||
{
|
||||
/* The first two dimensions of the Sobol sequence have better stratification, use them to sample
|
||||
* position on the light. */
|
||||
const float2 rand = make_float2(rand_light);
|
||||
|
||||
int prim;
|
||||
int visibility_flag;
|
||||
int object_id;
|
||||
#ifdef __LIGHT_TREE__
|
||||
if (kernel_data.integrator.use_light_tree) {
|
||||
const ccl_global KernelLightTreeEmitter *kemitter = &kernel_data_fetch(light_tree_emitters,
|
||||
ls->emitter_id);
|
||||
prim = kemitter->light.id;
|
||||
visibility_flag = kemitter->visibility_flag;
|
||||
object_id = (prim >= 0) ? ls->object : kemitter->object_id;
|
||||
}
|
||||
else
|
||||
#endif
|
||||
{
|
||||
const ccl_global KernelLightDistribution *kdistribution = &kernel_data_fetch(
|
||||
light_distribution, ls->emitter_id);
|
||||
prim = kdistribution->prim;
|
||||
object_id = kdistribution->object_id;
|
||||
visibility_flag = kdistribution->visibility_flag;
|
||||
}
|
||||
|
||||
if (!light_link_object_match(kg, object_receiver, object_id)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (prim >= 0) {
|
||||
/* Mesh light. */
|
||||
|
||||
/* Exclude synthetic meshes from shadow catcher pass. */
|
||||
if ((path_flag & PATH_RAY_SHADOW_CATCHER_PASS) &&
|
||||
!(kernel_data_fetch(object_flag, object_id) & SD_OBJECT_SHADOW_CATCHER))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!triangle_light_sample<in_volume_segment>(kg, prim, object_id, rand, time, ls, P)) {
|
||||
return false;
|
||||
}
|
||||
ls->shader |= visibility_flag;
|
||||
}
|
||||
else {
|
||||
const int light = ~prim;
|
||||
|
||||
if (UNLIKELY(light_select_reached_max_bounces(kg, light, bounce))) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!light_sample<in_volume_segment>(kg, light, rand, P, N, shader_flags, path_flag, ls)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
ls->pdf *= ls->pdf_selection;
|
||||
return in_volume_segment || (ls->pdf > 0.0f);
|
||||
}
|
||||
|
||||
/* Intersect ray with individual light. */
|
||||
|
||||
/* Returns the total number of hits (the input num_hits plus the number of the new intersections).
|
||||
*/
|
||||
template<bool is_main_path>
|
||||
ccl_device_forceinline int lights_intersect_impl(KernelGlobals kg,
|
||||
const ccl_private Ray *ccl_restrict ray,
|
||||
ccl_private Intersection *ccl_restrict isect,
|
||||
const int last_prim,
|
||||
const int last_object,
|
||||
const int last_type,
|
||||
const PathRayVisibility path_visibility,
|
||||
const uint32_t path_flag,
|
||||
const uint8_t path_mnee,
|
||||
const int receiver_forward,
|
||||
ccl_private uint *lcg_state,
|
||||
int num_hits)
|
||||
{
|
||||
#ifdef __SHADOW_LINKING__
|
||||
const bool is_indirect_ray = !(path_visibility & PATH_RAY_VISIBILITY_CAMERA);
|
||||
#endif
|
||||
|
||||
for (int lamp = 0; lamp < kernel_data.integrator.num_lights; lamp++) {
|
||||
const ccl_global KernelLight *klight = &kernel_data_fetch(lights, lamp);
|
||||
const int object = klight->object_id;
|
||||
|
||||
if (path_visibility & PATH_RAY_VISIBILITY_CAMERA) {
|
||||
if (klight->shader_id & SHADER_EXCLUDE_CAMERA) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (!(klight->shader_id & SHADER_USE_MIS)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
#ifdef __MNEE__
|
||||
/* This path should have been resolved with mnee, it will
|
||||
* generate a firefly for small lights since it is improbable. */
|
||||
if ((path_mnee & PATH_MNEE_CULL_LIGHT_CONNECTION) && klight->use_caustics) {
|
||||
continue;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
if (path_flag & PATH_RAY_SHADOW_CATCHER_PASS) {
|
||||
if (klight->shader_id & SHADER_EXCLUDE_SHADOW_CATCHER) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef __SHADOW_LINKING__
|
||||
/* For the main path exclude shadow-linked lights if intersecting with an indirect light ray.
|
||||
* Those lights are handled via dedicated light intersect and shade kernels.
|
||||
* For the shadow path used for the dedicated light shading ignore all non-shadow-linked
|
||||
* lights. */
|
||||
if (kernel_data.kernel_features & KERNEL_FEATURE_SHADOW_LINKING) {
|
||||
if (is_main_path) {
|
||||
if (is_indirect_ray &&
|
||||
kernel_data_fetch(objects, object).shadow_set_membership != LIGHT_LINK_MASK_ALL)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
}
|
||||
else if (kernel_data_fetch(objects, object).shadow_set_membership == LIGHT_LINK_MASK_ALL) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef __LIGHT_LINKING__
|
||||
/* Light linking. */
|
||||
if (!(path_visibility & PATH_RAY_VISIBILITY_CAMERA) &&
|
||||
!light_link_object_match(kg, receiver_forward, object))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
#endif
|
||||
|
||||
const LightType type = (LightType)klight->type;
|
||||
float t = 0.0f;
|
||||
|
||||
if (type == LIGHT_SPOT) {
|
||||
if (!spot_light_intersect(klight, ray, &t)) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
else if (type == LIGHT_POINT) {
|
||||
if (!point_light_intersect(klight, ray, &t)) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
else if (type == LIGHT_AREA) {
|
||||
if (!area_light_intersect(klight, ray, &t)) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
else if (type == LIGHT_SUN) {
|
||||
if (is_main_path || ray->tmax != FLT_MAX) {
|
||||
continue;
|
||||
}
|
||||
if (!sun_light_intersect(klight, ray, &t)) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
else {
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Avoid self-intersections. */
|
||||
if (last_prim == lamp && last_object == object && last_type == PRIMITIVE_LAMP) {
|
||||
continue;
|
||||
}
|
||||
|
||||
++num_hits;
|
||||
|
||||
#ifdef __SHADOW_LINKING__
|
||||
if (!is_main_path) {
|
||||
/* The non-main rays are only raced by the dedicated light kernel, after the shadow linking
|
||||
* feature check. */
|
||||
kernel_assert(kernel_data.kernel_features & KERNEL_FEATURE_SHADOW_LINKING);
|
||||
|
||||
if ((isect->prim != PRIM_NONE) && (lcg_step_float(lcg_state) > 1.0f / num_hits)) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
else
|
||||
#endif
|
||||
if (t >= isect->t)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
isect->t = t;
|
||||
isect->u = 0.0f;
|
||||
isect->v = 0.0f;
|
||||
isect->type = PRIMITIVE_LAMP;
|
||||
isect->prim = lamp;
|
||||
isect->object = object;
|
||||
}
|
||||
|
||||
return num_hits;
|
||||
}
|
||||
|
||||
/* Lights intersection for the main path.
|
||||
* Intersects spot, point, and area lights. */
|
||||
ccl_device bool lights_intersect(KernelGlobals kg,
|
||||
IntegratorState state,
|
||||
const ccl_private Ray *ccl_restrict ray,
|
||||
ccl_private Intersection *ccl_restrict isect,
|
||||
const int last_prim,
|
||||
const int last_object,
|
||||
const int last_type,
|
||||
const PathRayVisibility path_visibility,
|
||||
const uint32_t path_flag)
|
||||
{
|
||||
const uint8_t path_mnee = INTEGRATOR_STATE(state, path, mnee);
|
||||
const int receiver_forward = light_link_receiver_forward(kg, state);
|
||||
|
||||
lights_intersect_impl<true>(kg,
|
||||
ray,
|
||||
isect,
|
||||
last_prim,
|
||||
last_object,
|
||||
last_type,
|
||||
path_visibility,
|
||||
path_flag,
|
||||
path_mnee,
|
||||
receiver_forward,
|
||||
nullptr,
|
||||
0);
|
||||
|
||||
return isect->prim != PRIM_NONE;
|
||||
}
|
||||
|
||||
/* Lights intersection for the shadow linking.
|
||||
* Intersects spot, point, area, and sun lights.
|
||||
*
|
||||
* Returns the total number of hits (the input num_hits plus the number of the new intersections).
|
||||
*/
|
||||
ccl_device int lights_intersect_shadow_linked(KernelGlobals kg,
|
||||
const ccl_private Ray *ccl_restrict ray,
|
||||
ccl_private Intersection *ccl_restrict isect,
|
||||
const int last_prim,
|
||||
const int last_object,
|
||||
const int last_type,
|
||||
const PathRayVisibility path_visibility,
|
||||
const uint32_t path_flag,
|
||||
const int receiver_forward,
|
||||
ccl_private uint *lcg_state,
|
||||
const int num_hits)
|
||||
{
|
||||
return lights_intersect_impl<false>(kg,
|
||||
ray,
|
||||
isect,
|
||||
last_prim,
|
||||
last_object,
|
||||
last_type,
|
||||
path_visibility,
|
||||
path_flag,
|
||||
PATH_MNEE_NONE,
|
||||
receiver_forward,
|
||||
lcg_state,
|
||||
num_hits);
|
||||
}
|
||||
|
||||
/* Setup light sample from intersection. */
|
||||
|
||||
ccl_device LightEval
|
||||
light_eval_from_intersection(KernelGlobals kg,
|
||||
const ccl_private Intersection *ccl_restrict isect,
|
||||
const float3 ray_P,
|
||||
const float3 ray_D,
|
||||
const float3 N,
|
||||
const uint32_t path_flag)
|
||||
{
|
||||
const ccl_global KernelLight *klight = &kernel_data_fetch(lights, isect->prim);
|
||||
const LightType type = (LightType)klight->type;
|
||||
|
||||
if (type == LIGHT_SPOT) {
|
||||
return spot_light_eval_from_intersection(kg, klight, ray_P, ray_D, isect->t, N, path_flag);
|
||||
}
|
||||
if (type == LIGHT_POINT) {
|
||||
return point_light_eval_from_intersection(klight, ray_P, ray_D, isect->t, N, path_flag);
|
||||
}
|
||||
if (type == LIGHT_AREA) {
|
||||
return area_light_eval_from_intersection(klight, ray_P, ray_D, isect->t);
|
||||
}
|
||||
|
||||
kernel_assert(!"Invalid lamp type in light_eval_from_intersection");
|
||||
return LightEval{};
|
||||
}
|
||||
|
||||
/* Get light coordinates from position on light. */
|
||||
ccl_device void light_normal_uv_from_position(KernelGlobals kg,
|
||||
const ccl_global KernelLight *klight,
|
||||
const float3 P,
|
||||
const float3 D,
|
||||
ccl_private float3 &Ng,
|
||||
ccl_private float2 &uv)
|
||||
{
|
||||
const LightType type = (LightType)klight->type;
|
||||
|
||||
if (type == LIGHT_SPOT) {
|
||||
Ng = (klight->spot.is_sphere) ? normalize(P - klight->co) : -D;
|
||||
const float3 local_ray = spot_light_to_local(kg, klight, -D);
|
||||
uv = spot_light_uv(local_ray, klight->spot.half_cot_half_spot_angle);
|
||||
}
|
||||
else if (type == LIGHT_POINT) {
|
||||
Ng = (klight->spot.is_sphere) ? normalize(P - klight->co) : -D;
|
||||
uv = point_light_uv(kg, klight, Ng);
|
||||
}
|
||||
else if (type == LIGHT_AREA) {
|
||||
Ng = klight->area.dir;
|
||||
uv = area_light_uv(klight, P);
|
||||
}
|
||||
else if (type == LIGHT_SUN) {
|
||||
Ng = -D;
|
||||
uv = sun_light_uv(kg, klight, D);
|
||||
}
|
||||
else {
|
||||
kernel_assert(0);
|
||||
}
|
||||
}
|
||||
|
||||
CCL_NAMESPACE_END
|
||||
Reference in New Issue
Block a user