blender/intern/cycles/kernel/integrator/intersect_mnee.h
Weizhen Huang cc93b7f5a4 Refactor: Cycles: Separate shader flags and runtime flags
We are running out of bits. Split the original `ShaderDataFlag` into
`ShaderRuntimeFlag`, which is determined by closures in the shader and
set up during rendering, and `ShaderDataFlag`, which is the same for the
whole shader graph and hence determined during shader compilation

The size of `ShaderData` did not change due to padding

Pull Request: https://projects.blender.org/blender/blender/pulls/161856
2026-07-27 19:31:18 +02:00

119 lines
4 KiB
C

/* SPDX-FileCopyrightText: 2011-2026 Blender Foundation
*
* SPDX-License-Identifier: Apache-2.0 */
#pragma once
#include "kernel/integrator/mnee.h"
#include "kernel/integrator/shade_surface.h"
CCL_NAMESPACE_BEGIN
#ifdef __MNEE__
/* Sample a light and run the MNEE manifold walk for a caustic receiver. On a successful walk
* the result is written to a shadow slot for integrator_shade_surface, otherwise nothing is
* written and direct light is sampled there. */
ccl_device_forceinline ShaderEvalResult
integrate_surface_mnee(KernelGlobals kg,
IntegratorState state,
ccl_private ShaderData *sd,
const ccl_private RNGState *rng_state)
{
/* Kernel must only be scheduled for caustic receivers. */
kernel_assert(sd->object_flag & SD_OBJECT_CAUSTICS_RECEIVER);
if (!kernel_data.integrator.use_direct_light) {
return SHADER_EVAL_OK;
}
/* Sample position on a light. */
LightSample ls ccl_optional_struct_init;
{
const uint32_t path_flag = INTEGRATOR_STATE(state, path, flag);
const uint bounce = INTEGRATOR_STATE(state, path, bounce);
const float3 rand_light = path_state_rng_3D(kg, rng_state, PRNG_LIGHT);
if (!light_sample_from_position(kg,
rand_light,
sd->time,
sd->P,
sd->N,
light_link_receiver_nee(kg, sd),
sd->runtime_flag,
bounce,
path_flag,
&ls))
{
return SHADER_EVAL_OK;
}
}
kernel_assert(ls.pdf != 0.0f);
/* The manifold walk connects a caustic light to the receiver across reflection; transmission
* caustics and triangle lights are not handled. */
if (ls.type == LIGHT_TRIANGLE || dot(ls.D, sd->N) < 0.0f) {
return SHADER_EVAL_OK;
}
if (!kernel_data_fetch(lights, ls.prim).use_caustics) {
return SHADER_EVAL_OK;
}
ShaderDataCausticsStorage emission_sd_storage;
ccl_private ShaderData *emission_sd = AS_SHADER_DATA(&emission_sd_storage);
Spectrum mnee_throughput = zero_spectrum();
float3 mnee_wo = zero_float3();
int mnee_vertex_count = 0;
const ShaderEvalResult result = kernel_path_mnee_sample(
kg, state, sd, emission_sd, rng_state, &ls, &mnee_throughput, &mnee_wo, mnee_vertex_count);
if (result == SHADER_EVAL_CACHE_MISS) {
return SHADER_EVAL_CACHE_MISS;
}
/* Store MNEE state in a shadow state, to avoid increasing path state size.
* This is then turned into an actual shadow ray state in shade_surface, or discarded. */
if (mnee_vertex_count > 0) {
Ray ray ccl_optional_struct_init;
light_sample_to_surface_shadow_ray(kg, emission_sd, &ls, &ray);
IntegratorShadowState shadow_state = integrator_shadow_path_init(
kg, state, DEVICE_KERNEL_INTEGRATOR_SHADOW_PATH_MNEE_PENDING, false);
integrator_state_write_mnee(
state, shadow_state, &ls, &ray, mnee_vertex_count, mnee_throughput, mnee_wo);
}
return SHADER_EVAL_OK;
}
#endif /* __MNEE__ */
ccl_device void integrator_intersect_mnee(KernelGlobals kg, IntegratorState state)
{
PROFILING_INIT(kg, PROFILING_SHADE_SURFACE_DIRECT_LIGHT);
ShaderData sd;
integrate_surface_shader_setup(kg, state, &sd);
const int shader = sd.shader & SHADER_MASK;
#ifdef __MNEE__
RNGState rng_state;
path_state_rng_load(state, &rng_state);
const ShaderEvalResult result = integrate_surface_mnee(kg, state, &sd, &rng_state);
if (result == SHADER_EVAL_CACHE_MISS) {
integrator_path_cache_miss(state, DEVICE_KERNEL_INTEGRATOR_INTERSECT_MNEE);
return;
}
#endif
integrator_path_next_sorted(kg,
state,
DEVICE_KERNEL_INTEGRATOR_INTERSECT_MNEE,
DEVICE_KERNEL_INTEGRATOR_SHADE_SURFACE,
shader);
}
CCL_NAMESPACE_END