Cycles implements the "Taming the Shadow Terminator" paper by Matt Jen-Yuan Chiang to solve shadow terminator issues when a bump map is applied, as well as similar approach for the glossy reflection to ensure ray does not get reflected to inside of the object. This correction term is applied unconditionally, which makes it harder to have full control over shading via normals for stylistic reasons. This change exposes this corrective term as an option called "Bump Map Correction" which is available in the shader settings next to the "Transparent Shadows". The reason to make it per-shader rather than per-object is to allow flexibility of a control: it is possible that an object has multiple shaders attached to it, and only some of them used for bump mapping. Another, and possibly stronger reason to have it per-shader is ease of assets control: shader brings settings which are needed for its proper behavior. So if material at some point decides to take over normals, artists would not need to update settings on every asset which uses that material. The option is enabled by default, so there is no changes for existing setups. Pull Request: https://projects.blender.org/blender/blender/pulls/113480
819 lines
30 KiB
C++
819 lines
30 KiB
C++
/* SPDX-FileCopyrightText: 2011-2022 Blender Foundation
|
|
*
|
|
* SPDX-License-Identifier: Apache-2.0 */
|
|
|
|
#pragma once
|
|
|
|
#include "kernel/integrator/path_state.h"
|
|
#include "kernel/integrator/surface_shader.h"
|
|
|
|
#include "kernel/film/data_passes.h"
|
|
#include "kernel/film/denoising_passes.h"
|
|
#include "kernel/film/light_passes.h"
|
|
|
|
#include "kernel/light/sample.h"
|
|
|
|
#include "kernel/integrator/mnee.h"
|
|
|
|
#include "kernel/integrator/guiding.h"
|
|
#include "kernel/integrator/shadow_linking.h"
|
|
#include "kernel/integrator/subsurface.h"
|
|
#include "kernel/integrator/volume_stack.h"
|
|
|
|
CCL_NAMESPACE_BEGIN
|
|
|
|
ccl_device_forceinline void integrate_surface_shader_setup(KernelGlobals kg,
|
|
ConstIntegratorState state,
|
|
ccl_private ShaderData *sd)
|
|
{
|
|
Intersection isect ccl_optional_struct_init;
|
|
integrator_state_read_isect(state, &isect);
|
|
|
|
Ray ray ccl_optional_struct_init;
|
|
integrator_state_read_ray(state, &ray);
|
|
|
|
shader_setup_from_ray(kg, sd, &ray, &isect);
|
|
}
|
|
|
|
ccl_device_forceinline float3 integrate_surface_ray_offset(KernelGlobals kg,
|
|
const ccl_private ShaderData *sd,
|
|
const float3 ray_P,
|
|
const float3 ray_D)
|
|
{
|
|
/* No ray offset needed for other primitive types. */
|
|
if (!(sd->type & PRIMITIVE_TRIANGLE)) {
|
|
return ray_P;
|
|
}
|
|
|
|
/* Self intersection tests already account for the case where a ray hits the
|
|
* same primitive. However precision issues can still cause neighboring
|
|
* triangles to be hit. Here we test if the ray-triangle intersection with
|
|
* the same primitive would miss, implying that a neighboring triangle would
|
|
* be hit instead.
|
|
*
|
|
* This relies on triangle intersection to be watertight, and the object inverse
|
|
* object transform to match the one used by ray intersection exactly.
|
|
*
|
|
* Potential improvements:
|
|
* - It appears this happens when either barycentric coordinates are small,
|
|
* or dot(sd->Ng, ray_D) is small. Detect such cases and skip test?
|
|
* - Instead of ray offset, can we tweak P to lie within the triangle?
|
|
*/
|
|
float3 verts[3];
|
|
if (sd->type == PRIMITIVE_TRIANGLE) {
|
|
triangle_vertices(kg, sd->prim, verts);
|
|
}
|
|
else {
|
|
kernel_assert(sd->type == PRIMITIVE_MOTION_TRIANGLE);
|
|
motion_triangle_vertices(kg, sd->object, sd->prim, sd->time, verts);
|
|
}
|
|
|
|
float3 local_ray_P = ray_P;
|
|
float3 local_ray_D = ray_D;
|
|
|
|
if (!(sd->object_flag & SD_OBJECT_TRANSFORM_APPLIED)) {
|
|
const Transform itfm = object_get_inverse_transform(kg, sd);
|
|
local_ray_P = transform_point(&itfm, local_ray_P);
|
|
local_ray_D = transform_direction(&itfm, local_ray_D);
|
|
}
|
|
|
|
if (ray_triangle_intersect_self(local_ray_P, local_ray_D, verts)) {
|
|
return ray_P;
|
|
}
|
|
else {
|
|
return ray_offset(ray_P, sd->Ng);
|
|
}
|
|
}
|
|
|
|
ccl_device_forceinline bool integrate_surface_holdout(KernelGlobals kg,
|
|
ConstIntegratorState state,
|
|
ccl_private ShaderData *sd,
|
|
ccl_global float *ccl_restrict render_buffer)
|
|
{
|
|
/* Write holdout transparency to render buffer and stop if fully holdout. */
|
|
const uint32_t path_flag = INTEGRATOR_STATE(state, path, flag);
|
|
|
|
if (((sd->flag & SD_HOLDOUT) || (sd->object_flag & SD_OBJECT_HOLDOUT_MASK)) &&
|
|
(path_flag & PATH_RAY_TRANSPARENT_BACKGROUND))
|
|
{
|
|
const Spectrum holdout_weight = surface_shader_apply_holdout(kg, sd);
|
|
const Spectrum throughput = INTEGRATOR_STATE(state, path, throughput);
|
|
const float transparent = average(holdout_weight * throughput);
|
|
film_write_holdout(kg, state, path_flag, transparent, render_buffer);
|
|
if (isequal(holdout_weight, one_spectrum())) {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
ccl_device_forceinline void integrate_surface_emission(KernelGlobals kg,
|
|
IntegratorState state,
|
|
ccl_private const ShaderData *sd,
|
|
ccl_global float *ccl_restrict
|
|
render_buffer)
|
|
{
|
|
const uint32_t path_flag = INTEGRATOR_STATE(state, path, flag);
|
|
|
|
#ifdef __LIGHT_LINKING__
|
|
if (!light_link_object_match(kg, light_link_receiver_forward(kg, state), sd->object) &&
|
|
!(path_flag & PATH_RAY_CAMERA))
|
|
{
|
|
return;
|
|
}
|
|
#endif
|
|
|
|
#ifdef __SHADOW_LINKING__
|
|
/* Indirect emission of shadow-linked emissive surfaces is done via shadow rays to dedicated
|
|
* light sources. */
|
|
if (kernel_data.kernel_features & KERNEL_FEATURE_SHADOW_LINKING) {
|
|
if (!(path_flag & PATH_RAY_CAMERA) &&
|
|
kernel_data_fetch(objects, sd->object).shadow_set_membership != LIGHT_LINK_MASK_ALL)
|
|
{
|
|
return;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
/* Evaluate emissive closure. */
|
|
Spectrum L = surface_shader_emission(sd);
|
|
float mis_weight = 1.0f;
|
|
|
|
const bool has_mis = !(path_flag & PATH_RAY_MIS_SKIP) &&
|
|
(sd->flag & ((sd->flag & SD_BACKFACING) ? SD_MIS_BACK : SD_MIS_FRONT));
|
|
|
|
#ifdef __HAIR__
|
|
if (has_mis && (sd->type & PRIMITIVE_TRIANGLE))
|
|
#else
|
|
if (has_mis)
|
|
#endif
|
|
{
|
|
mis_weight = light_sample_mis_weight_forward_surface(kg, state, path_flag, sd);
|
|
}
|
|
|
|
guiding_record_surface_emission(kg, state, L, mis_weight);
|
|
film_write_surface_emission(
|
|
kg, state, L, mis_weight, render_buffer, object_lightgroup(kg, sd->object));
|
|
}
|
|
|
|
/* Branch off a shadow path and initialize common part of it.
|
|
* THe common is between the surface shading and configuration of a special shadow ray for the
|
|
* shadow linking. */
|
|
ccl_device_inline IntegratorShadowState
|
|
integrate_direct_light_shadow_init_common(KernelGlobals kg,
|
|
IntegratorState state,
|
|
ccl_private const Ray *ccl_restrict ray,
|
|
const Spectrum bsdf_spectrum,
|
|
const int light_group,
|
|
const int mnee_vertex_count)
|
|
{
|
|
|
|
/* Branch off shadow kernel. */
|
|
IntegratorShadowState shadow_state = integrator_shadow_path_init(
|
|
kg, state, DEVICE_KERNEL_INTEGRATOR_INTERSECT_SHADOW, false);
|
|
|
|
/* Copy volume stack and enter/exit volume. */
|
|
integrator_state_copy_volume_stack_to_shadow(kg, shadow_state, state);
|
|
|
|
/* Write shadow ray and associated state to global memory. */
|
|
integrator_state_write_shadow_ray(shadow_state, ray);
|
|
integrator_state_write_shadow_ray_self(kg, shadow_state, ray);
|
|
|
|
/* Copy state from main path to shadow path. */
|
|
const Spectrum unlit_throughput = INTEGRATOR_STATE(state, path, throughput);
|
|
const Spectrum throughput = unlit_throughput * bsdf_spectrum;
|
|
|
|
INTEGRATOR_STATE_WRITE(shadow_state, shadow_path, render_pixel_index) = INTEGRATOR_STATE(
|
|
state, path, render_pixel_index);
|
|
INTEGRATOR_STATE_WRITE(shadow_state, shadow_path, rng_offset) = INTEGRATOR_STATE(
|
|
state, path, rng_offset);
|
|
INTEGRATOR_STATE_WRITE(shadow_state, shadow_path, rng_hash) = INTEGRATOR_STATE(
|
|
state, path, rng_hash);
|
|
INTEGRATOR_STATE_WRITE(shadow_state, shadow_path, sample) = INTEGRATOR_STATE(
|
|
state, path, sample);
|
|
|
|
INTEGRATOR_STATE_WRITE(shadow_state, shadow_path, transparent_bounce) = INTEGRATOR_STATE(
|
|
state, path, transparent_bounce);
|
|
INTEGRATOR_STATE_WRITE(shadow_state, shadow_path, glossy_bounce) = INTEGRATOR_STATE(
|
|
state, path, glossy_bounce);
|
|
|
|
INTEGRATOR_STATE_WRITE(shadow_state, shadow_path, throughput) = throughput;
|
|
|
|
#ifdef __MNEE__
|
|
if (mnee_vertex_count > 0) {
|
|
INTEGRATOR_STATE_WRITE(shadow_state, shadow_path, transmission_bounce) =
|
|
INTEGRATOR_STATE(state, path, transmission_bounce) + mnee_vertex_count - 1;
|
|
INTEGRATOR_STATE_WRITE(shadow_state,
|
|
shadow_path,
|
|
diffuse_bounce) = INTEGRATOR_STATE(state, path, diffuse_bounce) + 1;
|
|
INTEGRATOR_STATE_WRITE(shadow_state,
|
|
shadow_path,
|
|
bounce) = INTEGRATOR_STATE(state, path, bounce) + mnee_vertex_count;
|
|
}
|
|
else
|
|
#endif
|
|
{
|
|
INTEGRATOR_STATE_WRITE(shadow_state, shadow_path, transmission_bounce) = INTEGRATOR_STATE(
|
|
state, path, transmission_bounce);
|
|
INTEGRATOR_STATE_WRITE(shadow_state, shadow_path, diffuse_bounce) = INTEGRATOR_STATE(
|
|
state, path, diffuse_bounce);
|
|
INTEGRATOR_STATE_WRITE(shadow_state, shadow_path, bounce) = INTEGRATOR_STATE(
|
|
state, path, bounce);
|
|
}
|
|
|
|
/* Write Light-group, +1 as light-group is int but we need to encode into a uint8_t. */
|
|
INTEGRATOR_STATE_WRITE(shadow_state, shadow_path, lightgroup) = light_group + 1;
|
|
|
|
#ifdef __PATH_GUIDING__
|
|
INTEGRATOR_STATE_WRITE(shadow_state, shadow_path, unlit_throughput) = unlit_throughput;
|
|
INTEGRATOR_STATE_WRITE(shadow_state, shadow_path, path_segment) = INTEGRATOR_STATE(
|
|
state, guiding, path_segment);
|
|
INTEGRATOR_STATE(shadow_state, shadow_path, guiding_mis_weight) = 0.0f;
|
|
#endif
|
|
|
|
return shadow_state;
|
|
}
|
|
|
|
/* Path tracing: sample point on light and evaluate light shader, then
|
|
* queue shadow ray to be traced. */
|
|
template<uint node_feature_mask>
|
|
ccl_device_forceinline void integrate_surface_direct_light(KernelGlobals kg,
|
|
IntegratorState state,
|
|
ccl_private ShaderData *sd,
|
|
ccl_private const RNGState *rng_state)
|
|
{
|
|
/* Test if there is a light or BSDF that needs direct light. */
|
|
if (!(kernel_data.integrator.use_direct_light && (sd->flag & SD_BSDF_HAS_EVAL))) {
|
|
return;
|
|
}
|
|
|
|
/* 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,
|
|
rng_state,
|
|
rand_light,
|
|
sd->time,
|
|
sd->P,
|
|
sd->N,
|
|
light_link_receiver_nee(kg, sd),
|
|
sd->flag,
|
|
bounce,
|
|
path_flag,
|
|
&ls))
|
|
{
|
|
return;
|
|
}
|
|
}
|
|
|
|
kernel_assert(ls.pdf != 0.0f);
|
|
|
|
/* Evaluate light shader.
|
|
*
|
|
* TODO: can we reuse sd memory? In theory we can move this after
|
|
* integrate_surface_bounce, evaluate the BSDF, and only then evaluate
|
|
* the light shader. This could also move to its own kernel, for
|
|
* non-constant light sources. */
|
|
ShaderDataCausticsStorage emission_sd_storage;
|
|
ccl_private ShaderData *emission_sd = AS_SHADER_DATA(&emission_sd_storage);
|
|
|
|
Ray ray ccl_optional_struct_init;
|
|
BsdfEval bsdf_eval ccl_optional_struct_init;
|
|
|
|
const bool is_transmission = dot(ls.D, sd->N) < 0.0f;
|
|
|
|
int mnee_vertex_count = 0;
|
|
#ifdef __MNEE__
|
|
IF_KERNEL_FEATURE(MNEE)
|
|
{
|
|
if (ls.lamp != LAMP_NONE) {
|
|
/* Is this a caustic light? */
|
|
const bool use_caustics = kernel_data_fetch(lights, ls.lamp).use_caustics;
|
|
if (use_caustics) {
|
|
/* Are we on a caustic caster? */
|
|
if (is_transmission && (sd->object_flag & SD_OBJECT_CAUSTICS_CASTER)) {
|
|
return;
|
|
}
|
|
|
|
/* Are we on a caustic receiver? */
|
|
if (!is_transmission && (sd->object_flag & SD_OBJECT_CAUSTICS_RECEIVER)) {
|
|
mnee_vertex_count = kernel_path_mnee_sample(
|
|
kg, state, sd, emission_sd, rng_state, &ls, &bsdf_eval);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
if (mnee_vertex_count > 0) {
|
|
/* Create shadow ray after successful manifold walk:
|
|
* emission_sd contains the last interface intersection and
|
|
* the light sample ls has been updated */
|
|
light_sample_to_surface_shadow_ray(kg, emission_sd, &ls, &ray);
|
|
}
|
|
else
|
|
#endif /* __MNEE__ */
|
|
{
|
|
const Spectrum light_eval = light_sample_shader_eval(kg, state, emission_sd, &ls, sd->time);
|
|
if (is_zero(light_eval)) {
|
|
return;
|
|
}
|
|
|
|
/* Evaluate BSDF. */
|
|
const float bsdf_pdf = surface_shader_bsdf_eval(kg, state, sd, ls.D, &bsdf_eval, ls.shader);
|
|
bsdf_eval_mul(&bsdf_eval, light_eval / ls.pdf);
|
|
|
|
if (ls.shader & SHADER_USE_MIS) {
|
|
const float mis_weight = light_sample_mis_weight_nee(kg, ls.pdf, bsdf_pdf);
|
|
bsdf_eval_mul(&bsdf_eval, mis_weight);
|
|
}
|
|
|
|
/* Path termination. */
|
|
const float terminate = path_state_rng_light_termination(kg, rng_state);
|
|
if (light_sample_terminate(kg, &ls, &bsdf_eval, terminate)) {
|
|
return;
|
|
}
|
|
|
|
/* Create shadow ray. */
|
|
light_sample_to_surface_shadow_ray(kg, sd, &ls, &ray);
|
|
}
|
|
|
|
if (ray.self.object != OBJECT_NONE) {
|
|
ray.P = integrate_surface_ray_offset(kg, sd, ray.P, ray.D);
|
|
}
|
|
|
|
/* Branch off shadow kernel. */
|
|
|
|
// TODO(: De-duplicate with the shade_Dedicated_light.
|
|
// Possibly by ensuring ls->group is always assigned properly.
|
|
const int light_group = ls.type != LIGHT_BACKGROUND ? ls.group :
|
|
kernel_data.background.lightgroup;
|
|
IntegratorShadowState shadow_state = integrate_direct_light_shadow_init_common(
|
|
kg, state, &ray, bsdf_eval_sum(&bsdf_eval), light_group, mnee_vertex_count);
|
|
|
|
if (is_transmission) {
|
|
#ifdef __VOLUME__
|
|
shadow_volume_stack_enter_exit(kg, shadow_state, sd);
|
|
#endif
|
|
}
|
|
|
|
uint32_t shadow_flag = INTEGRATOR_STATE(state, path, flag);
|
|
|
|
if (kernel_data.kernel_features & KERNEL_FEATURE_LIGHT_PASSES) {
|
|
PackedSpectrum pass_diffuse_weight;
|
|
PackedSpectrum pass_glossy_weight;
|
|
|
|
if (shadow_flag & PATH_RAY_ANY_PASS) {
|
|
/* Indirect bounce, use weights from earlier surface or volume bounce. */
|
|
pass_diffuse_weight = INTEGRATOR_STATE(state, path, pass_diffuse_weight);
|
|
pass_glossy_weight = INTEGRATOR_STATE(state, path, pass_glossy_weight);
|
|
}
|
|
else {
|
|
/* Direct light, use BSDFs at this bounce. */
|
|
shadow_flag |= PATH_RAY_SURFACE_PASS;
|
|
pass_diffuse_weight = PackedSpectrum(bsdf_eval_pass_diffuse_weight(&bsdf_eval));
|
|
pass_glossy_weight = PackedSpectrum(bsdf_eval_pass_glossy_weight(&bsdf_eval));
|
|
}
|
|
|
|
INTEGRATOR_STATE_WRITE(shadow_state, shadow_path, pass_diffuse_weight) = pass_diffuse_weight;
|
|
INTEGRATOR_STATE_WRITE(shadow_state, shadow_path, pass_glossy_weight) = pass_glossy_weight;
|
|
}
|
|
|
|
INTEGRATOR_STATE_WRITE(shadow_state, shadow_path, flag) = shadow_flag;
|
|
}
|
|
|
|
/* Path tracing: bounce off or through surface with new direction. */
|
|
ccl_device_forceinline int integrate_surface_bsdf_bssrdf_bounce(
|
|
KernelGlobals kg,
|
|
IntegratorState state,
|
|
ccl_private ShaderData *sd,
|
|
ccl_private const RNGState *rng_state)
|
|
{
|
|
/* Sample BSDF or BSSRDF. */
|
|
if (!(sd->flag & (SD_BSDF | SD_BSSRDF))) {
|
|
return LABEL_NONE;
|
|
}
|
|
|
|
float3 rand_bsdf = path_state_rng_3D(kg, rng_state, PRNG_SURFACE_BSDF);
|
|
ccl_private const ShaderClosure *sc = surface_shader_bsdf_bssrdf_pick(sd, &rand_bsdf);
|
|
|
|
#ifdef __SUBSURFACE__
|
|
/* BSSRDF closure, we schedule subsurface intersection kernel. */
|
|
if (CLOSURE_IS_BSSRDF(sc->type)) {
|
|
return subsurface_bounce(kg, state, sd, sc);
|
|
}
|
|
#endif
|
|
|
|
/* BSDF closure, sample direction. */
|
|
float bsdf_pdf = 0.0f, unguided_bsdf_pdf = 0.0f;
|
|
BsdfEval bsdf_eval ccl_optional_struct_init;
|
|
float3 bsdf_wo ccl_optional_struct_init;
|
|
int label;
|
|
|
|
float2 bsdf_sampled_roughness = make_float2(1.0f, 1.0f);
|
|
float bsdf_eta = 1.0f;
|
|
float mis_pdf = 1.0f;
|
|
|
|
#if defined(__PATH_GUIDING__) && PATH_GUIDING_LEVEL >= 4
|
|
if (kernel_data.integrator.use_surface_guiding) {
|
|
label = surface_shader_bsdf_guided_sample_closure(kg,
|
|
state,
|
|
sd,
|
|
sc,
|
|
rand_bsdf,
|
|
&bsdf_eval,
|
|
&bsdf_wo,
|
|
&bsdf_pdf,
|
|
&mis_pdf,
|
|
&unguided_bsdf_pdf,
|
|
&bsdf_sampled_roughness,
|
|
&bsdf_eta,
|
|
rng_state);
|
|
|
|
if (bsdf_pdf == 0.0f || bsdf_eval_is_zero(&bsdf_eval)) {
|
|
return LABEL_NONE;
|
|
}
|
|
|
|
INTEGRATOR_STATE_WRITE(state, path, unguided_throughput) *= bsdf_pdf / unguided_bsdf_pdf;
|
|
}
|
|
else
|
|
#endif
|
|
{
|
|
label = surface_shader_bsdf_sample_closure(kg,
|
|
sd,
|
|
sc,
|
|
INTEGRATOR_STATE(state, path, flag),
|
|
rand_bsdf,
|
|
&bsdf_eval,
|
|
&bsdf_wo,
|
|
&bsdf_pdf,
|
|
&bsdf_sampled_roughness,
|
|
&bsdf_eta);
|
|
|
|
if (bsdf_pdf == 0.0f || bsdf_eval_is_zero(&bsdf_eval)) {
|
|
return LABEL_NONE;
|
|
}
|
|
mis_pdf = bsdf_pdf;
|
|
unguided_bsdf_pdf = bsdf_pdf;
|
|
}
|
|
|
|
if (label & LABEL_TRANSPARENT) {
|
|
/* Only need to modify start distance for transparent. */
|
|
INTEGRATOR_STATE_WRITE(state, ray, tmin) = intersection_t_offset(sd->ray_length);
|
|
}
|
|
else {
|
|
/* Setup ray with changed origin and direction. */
|
|
const float3 D = normalize(bsdf_wo);
|
|
INTEGRATOR_STATE_WRITE(state, ray, P) = integrate_surface_ray_offset(kg, sd, sd->P, D);
|
|
INTEGRATOR_STATE_WRITE(state, ray, D) = D;
|
|
INTEGRATOR_STATE_WRITE(state, ray, tmin) = 0.0f;
|
|
INTEGRATOR_STATE_WRITE(state, ray, tmax) = FLT_MAX;
|
|
#ifdef __RAY_DIFFERENTIALS__
|
|
INTEGRATOR_STATE_WRITE(state, ray, dP) = differential_make_compact(sd->dP);
|
|
#endif
|
|
}
|
|
|
|
/* Update throughput. */
|
|
const Spectrum bsdf_weight = bsdf_eval_sum(&bsdf_eval) / bsdf_pdf;
|
|
INTEGRATOR_STATE_WRITE(state, path, throughput) *= bsdf_weight;
|
|
|
|
if (kernel_data.kernel_features & KERNEL_FEATURE_LIGHT_PASSES) {
|
|
if (INTEGRATOR_STATE(state, path, bounce) == 0) {
|
|
INTEGRATOR_STATE_WRITE(state, path, pass_diffuse_weight) = bsdf_eval_pass_diffuse_weight(
|
|
&bsdf_eval);
|
|
INTEGRATOR_STATE_WRITE(state, path, pass_glossy_weight) = bsdf_eval_pass_glossy_weight(
|
|
&bsdf_eval);
|
|
}
|
|
}
|
|
|
|
/* Update path state */
|
|
if (!(label & LABEL_TRANSPARENT)) {
|
|
INTEGRATOR_STATE_WRITE(state, path, mis_ray_pdf) = mis_pdf;
|
|
INTEGRATOR_STATE_WRITE(state, path, mis_origin_n) = sc->N;
|
|
INTEGRATOR_STATE_WRITE(state, path, min_ray_pdf) = fminf(
|
|
unguided_bsdf_pdf, INTEGRATOR_STATE(state, path, min_ray_pdf));
|
|
}
|
|
#ifdef __LIGHT_LINKING__
|
|
if (kernel_data.kernel_features & KERNEL_FEATURE_LIGHT_LINKING) {
|
|
INTEGRATOR_STATE_WRITE(state, path, mis_ray_object) = sd->object;
|
|
}
|
|
#endif
|
|
|
|
path_state_next(kg, state, label, sd->flag);
|
|
|
|
guiding_record_surface_bounce(kg,
|
|
state,
|
|
sd,
|
|
bsdf_weight,
|
|
bsdf_pdf,
|
|
sd->N,
|
|
normalize(bsdf_wo),
|
|
bsdf_sampled_roughness,
|
|
bsdf_eta);
|
|
|
|
return label;
|
|
}
|
|
|
|
#ifdef __VOLUME__
|
|
ccl_device_forceinline int integrate_surface_volume_only_bounce(IntegratorState state,
|
|
ccl_private ShaderData *sd)
|
|
{
|
|
if (!path_state_volume_next(state)) {
|
|
return LABEL_NONE;
|
|
}
|
|
|
|
/* Only modify start distance. */
|
|
INTEGRATOR_STATE_WRITE(state, ray, tmin) = intersection_t_offset(sd->ray_length);
|
|
|
|
return LABEL_TRANSMIT | LABEL_TRANSPARENT;
|
|
}
|
|
#endif
|
|
|
|
ccl_device_forceinline bool integrate_surface_terminate(IntegratorState state,
|
|
const uint32_t path_flag)
|
|
{
|
|
const float continuation_probability = (path_flag & PATH_RAY_TERMINATE_ON_NEXT_SURFACE) ?
|
|
0.0f :
|
|
INTEGRATOR_STATE(
|
|
state, path, continuation_probability);
|
|
if (continuation_probability == 0.0f) {
|
|
return true;
|
|
}
|
|
else if (continuation_probability != 1.0f) {
|
|
INTEGRATOR_STATE_WRITE(state, path, throughput) /= continuation_probability;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
#if defined(__AO__)
|
|
ccl_device_forceinline void integrate_surface_ao(KernelGlobals kg,
|
|
IntegratorState state,
|
|
ccl_private const ShaderData *ccl_restrict sd,
|
|
ccl_private const RNGState *ccl_restrict
|
|
rng_state,
|
|
ccl_global float *ccl_restrict render_buffer)
|
|
{
|
|
const uint32_t path_flag = INTEGRATOR_STATE(state, path, flag);
|
|
|
|
if (!(kernel_data.kernel_features & KERNEL_FEATURE_AO_ADDITIVE) &&
|
|
!(path_flag & PATH_RAY_CAMERA)) {
|
|
return;
|
|
}
|
|
|
|
/* Skip AO for paths that were split off for shadow catchers to avoid double-counting. */
|
|
if (path_flag & PATH_RAY_SHADOW_CATCHER_PASS) {
|
|
return;
|
|
}
|
|
|
|
const float2 rand_bsdf = path_state_rng_2D(kg, rng_state, PRNG_SURFACE_BSDF);
|
|
|
|
float3 ao_N;
|
|
const Spectrum ao_weight = surface_shader_ao(
|
|
kg, sd, kernel_data.integrator.ao_additive_factor, &ao_N);
|
|
|
|
float3 ao_D;
|
|
float ao_pdf;
|
|
sample_cos_hemisphere(ao_N, rand_bsdf, &ao_D, &ao_pdf);
|
|
|
|
bool skip_self = true;
|
|
|
|
Ray ray ccl_optional_struct_init;
|
|
ray.P = shadow_ray_offset(kg, sd, ao_D, &skip_self);
|
|
ray.D = ao_D;
|
|
if (skip_self) {
|
|
ray.P = integrate_surface_ray_offset(kg, sd, ray.P, ray.D);
|
|
}
|
|
ray.tmin = 0.0f;
|
|
ray.tmax = kernel_data.integrator.ao_bounces_distance;
|
|
ray.time = sd->time;
|
|
ray.self.object = (skip_self) ? sd->object : OBJECT_NONE;
|
|
ray.self.prim = (skip_self) ? sd->prim : PRIM_NONE;
|
|
ray.self.light_object = OBJECT_NONE;
|
|
ray.self.light_prim = PRIM_NONE;
|
|
ray.self.light = LAMP_NONE;
|
|
ray.dP = differential_zero_compact();
|
|
ray.dD = differential_zero_compact();
|
|
|
|
/* Branch off shadow kernel. */
|
|
IntegratorShadowState shadow_state = integrator_shadow_path_init(
|
|
kg, state, DEVICE_KERNEL_INTEGRATOR_INTERSECT_SHADOW, true);
|
|
|
|
/* Copy volume stack and enter/exit volume. */
|
|
integrator_state_copy_volume_stack_to_shadow(kg, shadow_state, state);
|
|
|
|
/* Write shadow ray and associated state to global memory. */
|
|
integrator_state_write_shadow_ray(shadow_state, &ray);
|
|
integrator_state_write_shadow_ray_self(kg, shadow_state, &ray);
|
|
|
|
/* Copy state from main path to shadow path. */
|
|
const uint16_t bounce = INTEGRATOR_STATE(state, path, bounce);
|
|
const uint16_t transparent_bounce = INTEGRATOR_STATE(state, path, transparent_bounce);
|
|
uint32_t shadow_flag = INTEGRATOR_STATE(state, path, flag) | PATH_RAY_SHADOW_FOR_AO;
|
|
const Spectrum throughput = INTEGRATOR_STATE(state, path, throughput) *
|
|
surface_shader_alpha(kg, sd);
|
|
|
|
INTEGRATOR_STATE_WRITE(shadow_state, shadow_path, render_pixel_index) = INTEGRATOR_STATE(
|
|
state, path, render_pixel_index);
|
|
INTEGRATOR_STATE_WRITE(shadow_state, shadow_path, rng_offset) = INTEGRATOR_STATE(
|
|
state, path, rng_offset);
|
|
INTEGRATOR_STATE_WRITE(shadow_state, shadow_path, rng_hash) = INTEGRATOR_STATE(
|
|
state, path, rng_hash);
|
|
INTEGRATOR_STATE_WRITE(shadow_state, shadow_path, sample) = INTEGRATOR_STATE(
|
|
state, path, sample);
|
|
INTEGRATOR_STATE_WRITE(shadow_state, shadow_path, flag) = shadow_flag;
|
|
INTEGRATOR_STATE_WRITE(shadow_state, shadow_path, bounce) = bounce;
|
|
INTEGRATOR_STATE_WRITE(shadow_state, shadow_path, transparent_bounce) = transparent_bounce;
|
|
INTEGRATOR_STATE_WRITE(shadow_state, shadow_path, throughput) = throughput;
|
|
|
|
if (kernel_data.kernel_features & KERNEL_FEATURE_AO_ADDITIVE) {
|
|
INTEGRATOR_STATE_WRITE(shadow_state, shadow_path, unshadowed_throughput) = ao_weight;
|
|
}
|
|
}
|
|
#endif /* defined(__AO__) */
|
|
|
|
template<uint node_feature_mask>
|
|
ccl_device int integrate_surface(KernelGlobals kg,
|
|
IntegratorState state,
|
|
ccl_global float *ccl_restrict render_buffer)
|
|
|
|
{
|
|
PROFILING_INIT_FOR_SHADER(kg, PROFILING_SHADE_SURFACE_SETUP);
|
|
|
|
/* Setup shader data. */
|
|
ShaderData sd;
|
|
integrate_surface_shader_setup(kg, state, &sd);
|
|
PROFILING_SHADER(sd.object, sd.shader);
|
|
|
|
int continue_path_label = 0;
|
|
|
|
const uint32_t path_flag = INTEGRATOR_STATE(state, path, flag);
|
|
|
|
/* Skip most work for volume bounding surface. */
|
|
#ifdef __VOLUME__
|
|
if (!(sd.flag & SD_HAS_ONLY_VOLUME)) {
|
|
#endif
|
|
guiding_record_surface_segment(kg, state, &sd);
|
|
|
|
#ifdef __SUBSURFACE__
|
|
/* Can skip shader evaluation for BSSRDF exit point without bump mapping. */
|
|
if (!(path_flag & PATH_RAY_SUBSURFACE) || ((sd.flag & SD_HAS_BSSRDF_BUMP)))
|
|
#endif
|
|
{
|
|
/* Evaluate shader. */
|
|
PROFILING_EVENT(PROFILING_SHADE_SURFACE_EVAL);
|
|
surface_shader_eval<node_feature_mask>(kg, state, &sd, render_buffer, path_flag);
|
|
|
|
/* Initialize additional RNG for BSDFs. */
|
|
if (sd.flag & SD_BSDF_NEEDS_LCG) {
|
|
sd.lcg_state = lcg_state_init(INTEGRATOR_STATE(state, path, rng_hash),
|
|
INTEGRATOR_STATE(state, path, rng_offset),
|
|
INTEGRATOR_STATE(state, path, sample),
|
|
0xb4bc3953);
|
|
}
|
|
}
|
|
|
|
#ifdef __SUBSURFACE__
|
|
if (path_flag & PATH_RAY_SUBSURFACE) {
|
|
/* When coming from inside subsurface scattering, setup a diffuse
|
|
* closure to perform lighting at the exit point. */
|
|
subsurface_shader_data_setup(kg, state, &sd, path_flag);
|
|
INTEGRATOR_STATE_WRITE(state, path, flag) &= ~PATH_RAY_SUBSURFACE;
|
|
}
|
|
else
|
|
#endif
|
|
{
|
|
/* Filter closures. */
|
|
surface_shader_prepare_closures(kg, state, &sd, path_flag);
|
|
|
|
/* Evaluate holdout. */
|
|
if (!integrate_surface_holdout(kg, state, &sd, render_buffer)) {
|
|
return LABEL_NONE;
|
|
}
|
|
|
|
/* Write emission. */
|
|
if (sd.flag & SD_EMISSION) {
|
|
integrate_surface_emission(kg, state, &sd, render_buffer);
|
|
}
|
|
|
|
/* Perform path termination. Most paths have already been terminated in
|
|
* the intersect_closest kernel, this is just for emission and for dividing
|
|
* throughput by the probability at the right moment.
|
|
*
|
|
* Also ensure we don't do it twice for SSS at both the entry and exit point. */
|
|
if (integrate_surface_terminate(state, path_flag)) {
|
|
return LABEL_NONE;
|
|
}
|
|
|
|
/* Write render passes. */
|
|
#ifdef __PASSES__
|
|
PROFILING_EVENT(PROFILING_SHADE_SURFACE_PASSES);
|
|
film_write_data_passes(kg, state, &sd, render_buffer);
|
|
#endif
|
|
|
|
#ifdef __DENOISING_FEATURES__
|
|
film_write_denoising_features_surface(kg, state, &sd, render_buffer);
|
|
#endif
|
|
}
|
|
|
|
/* Load random number state. */
|
|
RNGState rng_state;
|
|
path_state_rng_load(state, &rng_state);
|
|
|
|
#if defined(__PATH_GUIDING__) && PATH_GUIDING_LEVEL >= 4
|
|
surface_shader_prepare_guiding(kg, state, &sd, &rng_state);
|
|
guiding_write_debug_passes(kg, state, &sd, render_buffer);
|
|
#endif
|
|
/* Direct light. */
|
|
PROFILING_EVENT(PROFILING_SHADE_SURFACE_DIRECT_LIGHT);
|
|
integrate_surface_direct_light<node_feature_mask>(kg, state, &sd, &rng_state);
|
|
|
|
#if defined(__AO__)
|
|
/* Ambient occlusion pass. */
|
|
if (kernel_data.kernel_features & KERNEL_FEATURE_AO) {
|
|
PROFILING_EVENT(PROFILING_SHADE_SURFACE_AO);
|
|
integrate_surface_ao(kg, state, &sd, &rng_state, render_buffer);
|
|
}
|
|
#endif
|
|
|
|
PROFILING_EVENT(PROFILING_SHADE_SURFACE_INDIRECT_LIGHT);
|
|
continue_path_label = integrate_surface_bsdf_bssrdf_bounce(kg, state, &sd, &rng_state);
|
|
#ifdef __VOLUME__
|
|
}
|
|
else {
|
|
if (integrate_surface_terminate(state, path_flag)) {
|
|
return LABEL_NONE;
|
|
}
|
|
|
|
PROFILING_EVENT(PROFILING_SHADE_SURFACE_INDIRECT_LIGHT);
|
|
continue_path_label = integrate_surface_volume_only_bounce(state, &sd);
|
|
}
|
|
|
|
if (continue_path_label & LABEL_TRANSMIT) {
|
|
/* Enter/Exit volume. */
|
|
volume_stack_enter_exit(kg, state, &sd);
|
|
}
|
|
#endif
|
|
|
|
return continue_path_label;
|
|
}
|
|
|
|
template<DeviceKernel current_kernel>
|
|
ccl_device_forceinline void integrator_shade_surface_next_kernel(KernelGlobals kg,
|
|
IntegratorState state)
|
|
{
|
|
if (INTEGRATOR_STATE(state, path, flag) & PATH_RAY_SUBSURFACE) {
|
|
integrator_path_next(kg, state, current_kernel, DEVICE_KERNEL_INTEGRATOR_INTERSECT_SUBSURFACE);
|
|
}
|
|
else {
|
|
kernel_assert(INTEGRATOR_STATE(state, ray, tmax) != 0.0f);
|
|
integrator_path_next(kg, state, current_kernel, DEVICE_KERNEL_INTEGRATOR_INTERSECT_CLOSEST);
|
|
}
|
|
}
|
|
|
|
template<uint node_feature_mask = KERNEL_FEATURE_NODE_MASK_SURFACE & ~KERNEL_FEATURE_NODE_RAYTRACE,
|
|
DeviceKernel current_kernel = DEVICE_KERNEL_INTEGRATOR_SHADE_SURFACE>
|
|
ccl_device_forceinline void integrator_shade_surface(KernelGlobals kg,
|
|
IntegratorState state,
|
|
ccl_global float *ccl_restrict render_buffer)
|
|
{
|
|
const int continue_path_label = integrate_surface<node_feature_mask>(kg, state, render_buffer);
|
|
if (continue_path_label == LABEL_NONE) {
|
|
integrator_path_terminate(kg, state, current_kernel);
|
|
return;
|
|
}
|
|
|
|
#ifdef __SHADOW_LINKING__
|
|
/* No need to cast shadow linking rays at a transparent bounce: the lights will be accumulated
|
|
* via the main path in this case. */
|
|
if ((continue_path_label & LABEL_TRANSPARENT) == 0) {
|
|
if (shadow_linking_schedule_intersection_kernel<current_kernel>(kg, state)) {
|
|
return;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
integrator_shade_surface_next_kernel<current_kernel>(kg, state);
|
|
}
|
|
|
|
ccl_device_forceinline void integrator_shade_surface_raytrace(
|
|
KernelGlobals kg, IntegratorState state, ccl_global float *ccl_restrict render_buffer)
|
|
{
|
|
integrator_shade_surface<KERNEL_FEATURE_NODE_MASK_SURFACE,
|
|
DEVICE_KERNEL_INTEGRATOR_SHADE_SURFACE_RAYTRACE>(
|
|
kg, state, render_buffer);
|
|
}
|
|
|
|
ccl_device_forceinline void integrator_shade_surface_mnee(
|
|
KernelGlobals kg, IntegratorState state, ccl_global float *ccl_restrict render_buffer)
|
|
{
|
|
integrator_shade_surface<(KERNEL_FEATURE_NODE_MASK_SURFACE & ~KERNEL_FEATURE_NODE_RAYTRACE) |
|
|
KERNEL_FEATURE_MNEE,
|
|
DEVICE_KERNEL_INTEGRATOR_SHADE_SURFACE_MNEE>(kg, state, render_buffer);
|
|
}
|
|
|
|
CCL_NAMESPACE_END
|