Cycles: better path termination for transparency.
We now continue transparent paths after diffuse/glossy/transmission/volume bounces are exceeded. This avoids unexpected boundaries in volumes with transparent boundaries. It is also required for MIS to work correctly with transparent surfaces, as we also continue through these in shadow rays. The main visible changes is that volumes will now be lit by the background even at volume bounces 0, same as surfaces. Fixes T53914 and T54103.
This commit is contained in:
parent
84e92f9b3c
commit
2d81758aa6
Notes:
blender-bot
2023-02-14 07:31:32 +01:00
Referenced by issue #54103, Solid color backgrounds have no multiple importance sampling causing volumes not to render properly Referenced by issue #53914, Transparency swallows volume scatter bounce
|
@ -35,21 +35,38 @@
|
|||
|
||||
CCL_NAMESPACE_BEGIN
|
||||
|
||||
ccl_device void bsdf_transparent_setup(ShaderData *sd, const float3 weight)
|
||||
ccl_device void bsdf_transparent_setup(ShaderData *sd, const float3 weight, int path_flag)
|
||||
{
|
||||
if(sd->flag & SD_TRANSPARENT) {
|
||||
sd->closure_transparent_extinction += weight;
|
||||
|
||||
for(int i = 0; i < sd->num_closure; i++) {
|
||||
ShaderClosure *sc = &sd->closure[i];
|
||||
|
||||
if(sc->type == CLOSURE_BSDF_TRANSPARENT_ID) {
|
||||
sc->weight += weight;
|
||||
sc->sample_weight += fabsf(average(weight));
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
sd->flag |= SD_BSDF|SD_TRANSPARENT;
|
||||
sd->closure_transparent_extinction = weight;
|
||||
}
|
||||
|
||||
ShaderClosure *bsdf = bsdf_alloc(sd, sizeof(ShaderClosure), weight);
|
||||
if(path_flag & PATH_RAY_TERMINATE) {
|
||||
/* In this case the number of closures is set to zero to disable
|
||||
* all others, but we still want to get transparency so increase
|
||||
* the number just for this. */
|
||||
sd->num_closure_left = 1;
|
||||
}
|
||||
|
||||
if(bsdf) {
|
||||
bsdf->N = sd->N;
|
||||
bsdf->type = CLOSURE_BSDF_TRANSPARENT_ID;
|
||||
ShaderClosure *bsdf = bsdf_alloc(sd, sizeof(ShaderClosure), weight);
|
||||
|
||||
if(bsdf) {
|
||||
bsdf->N = sd->N;
|
||||
bsdf->type = CLOSURE_BSDF_TRANSPARENT_ID;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -51,7 +51,7 @@ ccl_device_inline void compute_light_pass(KernelGlobals *kg,
|
|||
path_state_init(kg, &emission_sd, &state, rng_hash, sample, NULL);
|
||||
|
||||
/* evaluate surface shader */
|
||||
shader_eval_surface(kg, sd, &state, state.flag, kernel_data.integrator.max_closures);
|
||||
shader_eval_surface(kg, sd, &state, state.flag);
|
||||
|
||||
/* TODO, disable more closures we don't need besides transparent */
|
||||
shader_bsdf_disable_transparency(kg, sd);
|
||||
|
@ -228,12 +228,12 @@ ccl_device float3 kernel_bake_evaluate_direct_indirect(KernelGlobals *kg,
|
|||
}
|
||||
else {
|
||||
/* surface color of the pass only */
|
||||
shader_eval_surface(kg, sd, state, 0, kernel_data.integrator.max_closures);
|
||||
shader_eval_surface(kg, sd, state, 0);
|
||||
return kernel_bake_shader_bsdf(kg, sd, type);
|
||||
}
|
||||
}
|
||||
else {
|
||||
shader_eval_surface(kg, sd, state, 0, kernel_data.integrator.max_closures);
|
||||
shader_eval_surface(kg, sd, state, 0);
|
||||
color = kernel_bake_shader_bsdf(kg, sd, type);
|
||||
}
|
||||
|
||||
|
@ -333,7 +333,7 @@ ccl_device void kernel_bake_evaluate(KernelGlobals *kg, ccl_global uint4 *input,
|
|||
{
|
||||
float3 N = sd.N;
|
||||
if((sd.flag & SD_HAS_BUMP)) {
|
||||
shader_eval_surface(kg, &sd, &state, 0, kernel_data.integrator.max_closures);
|
||||
shader_eval_surface(kg, &sd, &state, 0);
|
||||
N = shader_bsdf_average_normal(kg, &sd);
|
||||
}
|
||||
|
||||
|
@ -348,7 +348,7 @@ ccl_device void kernel_bake_evaluate(KernelGlobals *kg, ccl_global uint4 *input,
|
|||
}
|
||||
case SHADER_EVAL_EMISSION:
|
||||
{
|
||||
shader_eval_surface(kg, &sd, &state, 0, 0);
|
||||
shader_eval_surface(kg, &sd, &state, PATH_RAY_EMISSION);
|
||||
out = shader_emissive_eval(kg, &sd);
|
||||
break;
|
||||
}
|
||||
|
|
|
@ -67,13 +67,13 @@ ccl_device_noinline float3 direct_emissive_eval(KernelGlobals *kg,
|
|||
|
||||
ls->Ng = emission_sd->Ng;
|
||||
|
||||
/* no path flag, we're evaluating this for all closures. that's weak but
|
||||
* we'd have to do multiple evaluations otherwise */
|
||||
/* No proper path flag, we're evaluating this for all closures. that's
|
||||
* weak but we'd have to do multiple evaluations otherwise. */
|
||||
path_state_modify_bounce(state, true);
|
||||
shader_eval_surface(kg, emission_sd, state, 0, 0);
|
||||
shader_eval_surface(kg, emission_sd, state, PATH_RAY_EMISSION);
|
||||
path_state_modify_bounce(state, false);
|
||||
|
||||
/* evaluate emissive closure */
|
||||
/* Evaluate emissive closure. */
|
||||
eval = shader_emissive_eval(kg, emission_sd);
|
||||
}
|
||||
|
||||
|
|
|
@ -446,11 +446,8 @@ ccl_device void kernel_path_indirect(KernelGlobals *kg,
|
|||
}
|
||||
|
||||
/* Setup and evaluate shader. */
|
||||
shader_setup_from_ray(kg,
|
||||
sd,
|
||||
&isect,
|
||||
ray);
|
||||
shader_eval_surface(kg, sd, state, state->flag, kernel_data.integrator.max_closures);
|
||||
shader_setup_from_ray(kg, sd, &isect, ray);
|
||||
shader_eval_surface(kg, sd, state, state->flag);
|
||||
shader_prepare_closures(sd, state);
|
||||
|
||||
/* Apply shadow catcher, holdout, emission. */
|
||||
|
@ -610,7 +607,7 @@ ccl_device_forceinline void kernel_path_integrate(
|
|||
|
||||
/* Setup and evaluate shader. */
|
||||
shader_setup_from_ray(kg, &sd, &isect, ray);
|
||||
shader_eval_surface(kg, &sd, state, state->flag, kernel_data.integrator.max_closures);
|
||||
shader_eval_surface(kg, &sd, state, state->flag);
|
||||
shader_prepare_closures(&sd, state);
|
||||
|
||||
/* Apply shadow catcher, holdout, emission. */
|
||||
|
|
|
@ -480,7 +480,7 @@ ccl_device void kernel_branched_path_integrate(KernelGlobals *kg,
|
|||
|
||||
/* Setup and evaluate shader. */
|
||||
shader_setup_from_ray(kg, &sd, &isect, &ray);
|
||||
shader_eval_surface(kg, &sd, &state, state.flag, kernel_data.integrator.max_closures);
|
||||
shader_eval_surface(kg, &sd, &state, state.flag);
|
||||
shader_merge_closures(&sd);
|
||||
|
||||
/* Apply shadow catcher, holdout, emission. */
|
||||
|
|
|
@ -75,6 +75,9 @@ ccl_device_inline void path_state_next(KernelGlobals *kg, ccl_addr_space PathSta
|
|||
if(label & LABEL_TRANSPARENT) {
|
||||
state->flag |= PATH_RAY_TRANSPARENT;
|
||||
state->transparent_bounce++;
|
||||
if(state->transparent_bounce >= kernel_data.integrator.transparent_max_bounce) {
|
||||
state->flag |= PATH_RAY_TERMINATE_IMMEDIATE;
|
||||
}
|
||||
|
||||
if(!kernel_data.integrator.transparent_shadows)
|
||||
state->flag |= PATH_RAY_MIS_SKIP;
|
||||
|
@ -86,6 +89,10 @@ ccl_device_inline void path_state_next(KernelGlobals *kg, ccl_addr_space PathSta
|
|||
}
|
||||
|
||||
state->bounce++;
|
||||
if(state->bounce >= kernel_data.integrator.max_bounce) {
|
||||
state->flag |= PATH_RAY_TERMINATE_AFTER_TRANSPARENT;
|
||||
}
|
||||
|
||||
state->flag &= ~(PATH_RAY_ALL_VISIBILITY|PATH_RAY_MIS_SKIP);
|
||||
|
||||
#ifdef __VOLUME__
|
||||
|
@ -95,6 +102,9 @@ ccl_device_inline void path_state_next(KernelGlobals *kg, ccl_addr_space PathSta
|
|||
state->flag &= ~PATH_RAY_TRANSPARENT_BACKGROUND;
|
||||
|
||||
state->volume_bounce++;
|
||||
if(state->volume_bounce >= kernel_data.integrator.max_volume_bounce) {
|
||||
state->flag |= PATH_RAY_TERMINATE_AFTER_TRANSPARENT;
|
||||
}
|
||||
}
|
||||
else
|
||||
#endif
|
||||
|
@ -104,10 +114,18 @@ ccl_device_inline void path_state_next(KernelGlobals *kg, ccl_addr_space PathSta
|
|||
state->flag |= PATH_RAY_REFLECT;
|
||||
state->flag &= ~PATH_RAY_TRANSPARENT_BACKGROUND;
|
||||
|
||||
if(label & LABEL_DIFFUSE)
|
||||
if(label & LABEL_DIFFUSE) {
|
||||
state->diffuse_bounce++;
|
||||
else
|
||||
if(state->diffuse_bounce >= kernel_data.integrator.max_diffuse_bounce) {
|
||||
state->flag |= PATH_RAY_TERMINATE_AFTER_TRANSPARENT;
|
||||
}
|
||||
}
|
||||
else {
|
||||
state->glossy_bounce++;
|
||||
if(state->glossy_bounce >= kernel_data.integrator.max_glossy_bounce) {
|
||||
state->flag |= PATH_RAY_TERMINATE_AFTER_TRANSPARENT;
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
kernel_assert(label & LABEL_TRANSMIT);
|
||||
|
@ -119,6 +137,9 @@ ccl_device_inline void path_state_next(KernelGlobals *kg, ccl_addr_space PathSta
|
|||
}
|
||||
|
||||
state->transmission_bounce++;
|
||||
if(state->transmission_bounce >= kernel_data.integrator.max_transmission_bounce) {
|
||||
state->flag |= PATH_RAY_TERMINATE_AFTER_TRANSPARENT;
|
||||
}
|
||||
}
|
||||
|
||||
/* diffuse/glossy/singular */
|
||||
|
@ -162,13 +183,13 @@ ccl_device_inline float path_state_continuation_probability(KernelGlobals *kg,
|
|||
ccl_addr_space PathState *state,
|
||||
const float3 throughput)
|
||||
{
|
||||
if(state->flag & PATH_RAY_TRANSPARENT) {
|
||||
/* Transparent rays are treated separately with own max bounces. */
|
||||
if(state->transparent_bounce >= kernel_data.integrator.transparent_max_bounce) {
|
||||
return 0.0f;
|
||||
}
|
||||
if(state->flag & PATH_RAY_TERMINATE_IMMEDIATE) {
|
||||
/* Ray is to be terminated immediately. */
|
||||
return 0.0f;
|
||||
}
|
||||
else if(state->flag & PATH_RAY_TRANSPARENT) {
|
||||
/* Do at least one bounce without RR. */
|
||||
else if(state->transparent_bounce <= 1) {
|
||||
if(state->transparent_bounce <= 1) {
|
||||
return 1.0f;
|
||||
}
|
||||
#ifdef __SHADOW_TRICKS__
|
||||
|
@ -179,19 +200,8 @@ ccl_device_inline float path_state_continuation_probability(KernelGlobals *kg,
|
|||
#endif
|
||||
}
|
||||
else {
|
||||
/* Test max bounces for various ray types. */
|
||||
if((state->bounce >= kernel_data.integrator.max_bounce) ||
|
||||
(state->diffuse_bounce >= kernel_data.integrator.max_diffuse_bounce) ||
|
||||
(state->glossy_bounce >= kernel_data.integrator.max_glossy_bounce) ||
|
||||
#ifdef __VOLUME__
|
||||
(state->volume_bounce >= kernel_data.integrator.max_volume_bounce) ||
|
||||
#endif
|
||||
(state->transmission_bounce >= kernel_data.integrator.max_transmission_bounce))
|
||||
{
|
||||
return 0.0f;
|
||||
}
|
||||
/* Do at least one bounce without RR. */
|
||||
else if(state->bounce <= 1) {
|
||||
if(state->bounce <= 1) {
|
||||
return 1.0f;
|
||||
}
|
||||
#ifdef __SHADOW_TRICKS__
|
||||
|
|
|
@ -966,10 +966,21 @@ ccl_device float3 shader_holdout_eval(KernelGlobals *kg, ShaderData *sd)
|
|||
/* Surface Evaluation */
|
||||
|
||||
ccl_device void shader_eval_surface(KernelGlobals *kg, ShaderData *sd,
|
||||
ccl_addr_space PathState *state, int path_flag, int max_closure)
|
||||
ccl_addr_space PathState *state, int path_flag)
|
||||
{
|
||||
/* If path is being terminated, we are tracing a shadow ray or evaluating
|
||||
* emission, then we don't need to store closures. The emission and shadow
|
||||
* shader data also do not have a closure array to save GPU memory. */
|
||||
int max_closures;
|
||||
if(path_flag & (PATH_RAY_TERMINATE|PATH_RAY_SHADOW|PATH_RAY_EMISSION)) {
|
||||
max_closures = 0;
|
||||
}
|
||||
else {
|
||||
max_closures = kernel_data.integrator.max_closures;
|
||||
}
|
||||
|
||||
sd->num_closure = 0;
|
||||
sd->num_closure_left = max_closure;
|
||||
sd->num_closure_left = max_closures;
|
||||
|
||||
#ifdef __OSL__
|
||||
if(kg->osl)
|
||||
|
@ -1140,13 +1151,23 @@ ccl_device_inline void shader_eval_volume(KernelGlobals *kg,
|
|||
ShaderData *sd,
|
||||
ccl_addr_space PathState *state,
|
||||
ccl_addr_space VolumeStack *stack,
|
||||
int path_flag,
|
||||
int max_closure)
|
||||
int path_flag)
|
||||
{
|
||||
/* If path is being terminated, we are tracing a shadow ray or evaluating
|
||||
* emission, then we don't need to store closures. The emission and shadow
|
||||
* shader data also do not have a closure array to save GPU memory. */
|
||||
int max_closures;
|
||||
if(path_flag & (PATH_RAY_TERMINATE|PATH_RAY_SHADOW|PATH_RAY_EMISSION)) {
|
||||
max_closures = 0;
|
||||
}
|
||||
else {
|
||||
max_closures = kernel_data.integrator.max_closures;
|
||||
}
|
||||
|
||||
/* reset closures once at the start, we will be accumulating the closures
|
||||
* for all volumes in the stack into a single array of closures */
|
||||
sd->num_closure = 0;
|
||||
sd->num_closure_left = max_closure;
|
||||
sd->num_closure_left = max_closures;
|
||||
sd->flag = 0;
|
||||
sd->object_flag = 0;
|
||||
|
||||
|
|
|
@ -86,8 +86,7 @@ ccl_device_forceinline bool shadow_handle_transparent_isect(
|
|||
shader_eval_surface(kg,
|
||||
shadow_sd,
|
||||
state,
|
||||
PATH_RAY_SHADOW,
|
||||
0);
|
||||
PATH_RAY_SHADOW);
|
||||
path_state_modify_bounce(state, false);
|
||||
*throughput *= shader_bsdf_transparency(kg, shadow_sd);
|
||||
}
|
||||
|
|
|
@ -146,7 +146,7 @@ ccl_device void subsurface_color_bump_blur(KernelGlobals *kg,
|
|||
|
||||
if(bump || texture_blur > 0.0f) {
|
||||
/* average color and normal at incoming point */
|
||||
shader_eval_surface(kg, sd, state, state->flag, kernel_data.integrator.max_closures);
|
||||
shader_eval_surface(kg, sd, state, state->flag);
|
||||
float3 in_color = shader_bssrdf_sum(sd, (bump)? N: NULL, NULL);
|
||||
|
||||
/* we simply divide out the average color and multiply with the average
|
||||
|
|
|
@ -361,6 +361,17 @@ enum PathRayFlag {
|
|||
PATH_RAY_STORE_SHADOW_INFO = (1 << 18),
|
||||
/* Zero background alpha, for camera or transparent glass rays. */
|
||||
PATH_RAY_TRANSPARENT_BACKGROUND = (1 << 19),
|
||||
/* Terminate ray immediately at next bounce. */
|
||||
PATH_RAY_TERMINATE_IMMEDIATE = (1 << 20),
|
||||
/* Ray is to be terminated, but continue with transparent bounces and
|
||||
* emission as long as we encounter them. This is required to make the
|
||||
* MIS between direct and indirect light rays match, as shadow rays go
|
||||
* through transparent surfaces to reach emisison too. */
|
||||
PATH_RAY_TERMINATE_AFTER_TRANSPARENT = (1 << 21),
|
||||
/* Ray is to be terminated. */
|
||||
PATH_RAY_TERMINATE = (PATH_RAY_TERMINATE_IMMEDIATE|PATH_RAY_TERMINATE_AFTER_TRANSPARENT),
|
||||
/* Path and shader is being evaluated for direct lighting emission. */
|
||||
PATH_RAY_EMISSION = (1 << 22)
|
||||
};
|
||||
|
||||
/* Closure Label */
|
||||
|
|
|
@ -45,7 +45,7 @@ ccl_device_inline bool volume_shader_extinction_sample(KernelGlobals *kg,
|
|||
float3 *extinction)
|
||||
{
|
||||
sd->P = P;
|
||||
shader_eval_volume(kg, sd, state, state->volume_stack, PATH_RAY_SHADOW, 0);
|
||||
shader_eval_volume(kg, sd, state, state->volume_stack, PATH_RAY_SHADOW);
|
||||
|
||||
if(sd->flag & SD_EXTINCTION) {
|
||||
*extinction = sd->closure_transparent_extinction;
|
||||
|
@ -64,7 +64,7 @@ ccl_device_inline bool volume_shader_sample(KernelGlobals *kg,
|
|||
VolumeShaderCoefficients *coeff)
|
||||
{
|
||||
sd->P = P;
|
||||
shader_eval_volume(kg, sd, state, state->volume_stack, state->flag, kernel_data.integrator.max_closures);
|
||||
shader_eval_volume(kg, sd, state, state->volume_stack, state->flag);
|
||||
|
||||
if(!(sd->flag & (SD_EXTINCTION|SD_SCATTER|SD_EMISSION)))
|
||||
return false;
|
||||
|
@ -76,18 +76,11 @@ ccl_device_inline bool volume_shader_sample(KernelGlobals *kg,
|
|||
make_float3(0.0f, 0.0f, 0.0f);
|
||||
|
||||
if(sd->flag & SD_SCATTER) {
|
||||
if(state->bounce < kernel_data.integrator.max_bounce &&
|
||||
state->volume_bounce < kernel_data.integrator.max_volume_bounce) {
|
||||
for(int i = 0; i < sd->num_closure; i++) {
|
||||
const ShaderClosure *sc = &sd->closure[i];
|
||||
for(int i = 0; i < sd->num_closure; i++) {
|
||||
const ShaderClosure *sc = &sd->closure[i];
|
||||
|
||||
if(CLOSURE_IS_VOLUME(sc->type))
|
||||
coeff->sigma_s += sc->weight;
|
||||
}
|
||||
}
|
||||
else {
|
||||
/* When at the max number of bounces, clear scattering. */
|
||||
sd->flag &= ~SD_SCATTER;
|
||||
if(CLOSURE_IS_VOLUME(sc->type))
|
||||
coeff->sigma_s += sc->weight;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -705,7 +705,7 @@ public:
|
|||
|
||||
void setup(ShaderData *sd, int path_flag, float3 weight)
|
||||
{
|
||||
bsdf_transparent_setup(sd, weight);
|
||||
bsdf_transparent_setup(sd, weight, path_flag);
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
@ -50,7 +50,7 @@ ccl_device void kernel_shader_eval(KernelGlobals *kg)
|
|||
if(IS_STATE(ray_state, ray_index, RAY_ACTIVE)) {
|
||||
ccl_global PathState *state = &kernel_split_state.path_state[ray_index];
|
||||
|
||||
shader_eval_surface(kg, kernel_split_sd(sd, ray_index), state, state->flag, kernel_data.integrator.max_closures);
|
||||
shader_eval_surface(kg, kernel_split_sd(sd, ray_index), state, state->flag);
|
||||
#ifdef __BRANCHED_PATH__
|
||||
if(kernel_data.integrator.branched) {
|
||||
shader_merge_closures(kernel_split_sd(sd, ray_index));
|
||||
|
|
|
@ -449,7 +449,7 @@ ccl_device void svm_node_closure_bsdf(KernelGlobals *kg, ShaderData *sd, float *
|
|||
}
|
||||
case CLOSURE_BSDF_TRANSPARENT_ID: {
|
||||
float3 weight = sd->svm_closure_weight * mix_weight;
|
||||
bsdf_transparent_setup(sd, weight);
|
||||
bsdf_transparent_setup(sd, weight, path_flag);
|
||||
break;
|
||||
}
|
||||
case CLOSURE_BSDF_REFLECTION_ID:
|
||||
|
@ -728,7 +728,7 @@ ccl_device void svm_node_closure_bsdf(KernelGlobals *kg, ShaderData *sd, float *
|
|||
* the throughput can blow up after multiple bounces. we
|
||||
* better figure out a way to skip backfaces from rays
|
||||
* spawned by transmission from the front */
|
||||
bsdf_transparent_setup(sd, make_float3(1.0f, 1.0f, 1.0f));
|
||||
bsdf_transparent_setup(sd, make_float3(1.0f, 1.0f, 1.0f), path_flag);
|
||||
}
|
||||
else {
|
||||
HairBsdf *bsdf = (HairBsdf*)bsdf_alloc(sd, sizeof(HairBsdf), weight);
|
||||
|
|
|
@ -249,6 +249,9 @@ void OSLShaderManager::shading_system_init()
|
|||
"__unused__",
|
||||
"__unused__",
|
||||
"__unused__",
|
||||
"__unused__",
|
||||
"__unused__",
|
||||
"__unused__",
|
||||
};
|
||||
|
||||
const int nraytypes = sizeof(raytypes)/sizeof(raytypes[0]);
|
||||
|
|
Loading…
Reference in New Issue