WIP: Cycles: Implement volume stack priority and nested IOR support #118478

Draft
Lukas Stockner wants to merge 1 commits from LukasStockner/blender:volume-stack-priority into main

When changing the target branch, be careful to rebase the branch in your fork to match. See documentation.
19 changed files with 216 additions and 35 deletions

View File

@ -1040,6 +1040,12 @@ class CyclesMaterialSettings(bpy.types.PropertyGroup):
min=0.001, max=1000.0, soft_min=0.1, soft_max=10.0, precision=4
)
volume_stack_priority: IntProperty(
name="Volume Stack Priority",
default=0,
min=0,
)
@classmethod
def register(cls):
bpy.types.Material.cycles = PointerProperty(

View File

@ -1965,7 +1965,10 @@ class CYCLES_MATERIAL_PT_settings(CyclesButtonsPanel, Panel):
layout.use_property_split = True
layout.use_property_decorate = False
cmat = mat.cycles
layout.prop(mat, "pass_index")
layout.prop(cmat, "volume_stack_priority")
def draw(self, context):
self.draw_shared(self, context.material)

View File

@ -1549,6 +1549,7 @@ void BlenderSync::sync_materials(BL::Depsgraph &b_depsgraph, bool update_all)
shader->set_volume_interpolation_method(get_volume_interpolation(cmat));
shader->set_volume_step_rate(get_float(cmat, "volume_step_rate"));
shader->set_displacement_method(get_displacement_method(b_mat));
shader->set_volume_stack_priority(get_int(cmat, "volume_stack_priority"));
shader->set_graph(graph);

View File

@ -148,9 +148,8 @@ ccl_device_inline int bsdf_sample(KernelGlobals kg,
*eta = 1.0f;
break;
case CLOSURE_BSDF_TRANSPARENT_ID:
label = bsdf_transparent_sample(sc, Ng, sd->wi, eval, wo, pdf);
label = bsdf_transparent_sample(sc, Ng, sd->wi, eval, wo, pdf, eta);
*sampled_roughness = zero_float2();
*eta = 1.0f;
break;
case CLOSURE_BSDF_MICROFACET_GGX_ID:
case CLOSURE_BSDF_MICROFACET_GGX_REFRACTION_ID:

View File

@ -9,9 +9,20 @@
CCL_NAMESPACE_BEGIN
typedef struct TransparentBsdf {
SHADER_CLOSURE_BASE;
/* For nested dielectrics, we need the ability to skip a surface while still recording
* its IOR so it can be added to the volume stack. */
float ior;
} TransparentBsdf;
static_assert(sizeof(ShaderClosure) >= sizeof(TransparentBsdf), "TransparentBsdf is too large!");
ccl_device void bsdf_transparent_setup(ccl_private ShaderData *sd,
const Spectrum weight,
uint32_t path_flag)
uint32_t path_flag,
float ior)
{
/* Check cutoff weight. */
float sample_weight = fabsf(average(weight));
@ -27,8 +38,11 @@ ccl_device void bsdf_transparent_setup(ccl_private ShaderData *sd,
ccl_private ShaderClosure *sc = &sd->closure[i];
if (sc->type == CLOSURE_BSDF_TRANSPARENT_ID) {
sc->weight += weight;
sc->sample_weight += sample_weight;
ccl_private TransparentBsdf *bsdf = (ccl_private TransparentBsdf *)sc;
/* If there are multiple skipped refractive closures, average their IORs. */
bsdf->ior = (bsdf->ior * bsdf->sample_weight + ior * sample_weight) / (bsdf->sample_weight + sample_weight);
bsdf->weight += weight;
bsdf->sample_weight += sample_weight;
break;
}
}
@ -45,12 +59,13 @@ ccl_device void bsdf_transparent_setup(ccl_private ShaderData *sd,
}
/* Create new transparent BSDF. */
ccl_private ShaderClosure *bsdf = closure_alloc(
sd, sizeof(ShaderClosure), CLOSURE_BSDF_TRANSPARENT_ID, weight);
ccl_private TransparentBsdf *bsdf = (ccl_private TransparentBsdf *)closure_alloc(
sd, sizeof(TransparentBsdf), CLOSURE_BSDF_TRANSPARENT_ID, weight);
if (bsdf) {
bsdf->sample_weight = sample_weight;
bsdf->N = sd->N;
bsdf->ior = ior;
}
else if (path_flag & PATH_RAY_TERMINATE) {
sd->num_closure_left = 0;
@ -58,6 +73,13 @@ ccl_device void bsdf_transparent_setup(ccl_private ShaderData *sd,
}
}
ccl_device void bsdf_transparent_setup(ccl_private ShaderData *sd,
const Spectrum weight,
uint32_t path_flag)
{
bsdf_transparent_setup(sd, weight, path_flag, 1.0f);
}
ccl_device Spectrum bsdf_transparent_eval(ccl_private const ShaderClosure *sc,
const float3 wi,
const float3 wo,
@ -72,13 +94,16 @@ ccl_device int bsdf_transparent_sample(ccl_private const ShaderClosure *sc,
float3 wi,
ccl_private Spectrum *eval,
ccl_private float3 *wo,
ccl_private float *pdf)
ccl_private float *pdf,
ccl_private float *ior)
{
ccl_private const TransparentBsdf *bsdf = (ccl_private const TransparentBsdf *)sc;
// only one direction is possible
*wo = -wi;
/* Some high number for MIS. */
*pdf = 1e6f;
*eval = one_spectrum() * 1e6f;
*ior = bsdf->ior;
return LABEL_TRANSMIT | LABEL_TRANSPARENT;
}

View File

@ -47,6 +47,7 @@ ccl_device_inline void shader_setup_from_ray(KernelGlobals kg,
sd->prim = isect->prim;
sd->lamp = LAMP_NONE;
sd->flag = 0;
sd->opposite_ior = 1.0f;
/* Read matrices and time. */
sd->time = ray->time;
@ -175,6 +176,7 @@ ccl_device_inline void shader_setup_from_sample(KernelGlobals kg,
sd->v = v;
sd->time = time;
sd->ray_length = t;
sd->opposite_ior = 1.0f;
sd->flag = kernel_data_fetch(shaders, (sd->shader & SHADER_MASK)).flags;
sd->object_flag = 0;
@ -304,6 +306,7 @@ ccl_device void shader_setup_from_curve(KernelGlobals kg,
sd->v = 0.0f;
sd->time = 0.5f;
sd->ray_length = 0.0f;
sd->opposite_ior = 1.0f;
/* Shader */
sd->shader = kernel_data_fetch(curves, prim).shader_id;
@ -383,6 +386,7 @@ ccl_device_inline void shader_setup_from_background(KernelGlobals kg,
sd->object_flag = 0;
sd->time = ray_time;
sd->ray_length = 0.0f;
sd->opposite_ior = 1.0f;
sd->object = OBJECT_NONE;
sd->lamp = LAMP_NONE;
@ -423,6 +427,7 @@ ccl_device_inline void shader_setup_from_volume(KernelGlobals kg,
sd->object_flag = 0;
sd->time = ray->time;
sd->ray_length = 0.0f; /* todo: can we set this to some useful value? */
sd->opposite_ior = 1.0f;
sd->object = OBJECT_NONE; /* todo: fill this for texture coordinates */
sd->lamp = LAMP_NONE;

View File

@ -51,7 +51,7 @@ ccl_device void integrator_volume_stack_update_for_subsurface(KernelGlobals kg,
continue;
}
shader_setup_from_ray(kg, stack_sd, &volume_ray, isect);
volume_stack_enter_exit(kg, state, stack_sd);
volume_stack_enter_exit(kg, state, stack_sd, 1.0f);
}
}
#else
@ -63,7 +63,7 @@ ccl_device void integrator_volume_stack_update_for_subsurface(KernelGlobals kg,
/* Ignore self, SSS itself already enters and exits the object. */
if (isect.object != volume_ray.self.object) {
shader_setup_from_ray(kg, stack_sd, &volume_ray, &isect);
volume_stack_enter_exit(kg, state, stack_sd);
volume_stack_enter_exit(kg, state, stack_sd, 1.0f);
}
/* Move ray forward. */
volume_ray.tmin = intersection_t_offset(isect.t);

View File

@ -80,9 +80,14 @@ ccl_device_inline void path_state_init_integrator(KernelGlobals kg,
INTEGRATOR_STATE_ARRAY_WRITE(state, volume_stack, 0, object) = OBJECT_NONE;
INTEGRATOR_STATE_ARRAY_WRITE(
state, volume_stack, 0, shader) = kernel_data.background.volume_shader;
INTEGRATOR_STATE_ARRAY_WRITE(state, volume_stack, 0, ior) = 1.0f;
INTEGRATOR_STATE_ARRAY_WRITE(state, volume_stack, 1, object) = OBJECT_NONE;
INTEGRATOR_STATE_ARRAY_WRITE(state, volume_stack, 1, shader) = SHADER_NONE;
}
else {
INTEGRATOR_STATE_ARRAY_WRITE(state, volume_stack, 0, object) = OBJECT_NONE;
INTEGRATOR_STATE_ARRAY_WRITE(state, volume_stack, 0, shader) = SHADER_NONE;
}
#ifdef __DENOISING_FEATURES__
if (kernel_data.kernel_features & KERNEL_FEATURE_DENOISING) {

View File

@ -40,6 +40,9 @@ ccl_device_inline Spectrum integrate_transparent_surface_shadow(KernelGlobals kg
shader_setup_from_ray(kg, shadow_sd, &ray, &isect);
VOLUME_READ_LAMBDA(integrator_state_read_shadow_volume_stack(state, i))
volume_stack_set_surface_priority(kg, shadow_sd, volume_read_lambda_pass);
/* Evaluate shader. */
if (!(shadow_sd->flag & SD_HAS_ONLY_VOLUME)) {
surface_shader_eval<KERNEL_FEATURE_NODE_MASK_SURFACE_SHADOW>(

View File

@ -33,6 +33,9 @@ ccl_device_forceinline void integrate_surface_shader_setup(KernelGlobals kg,
integrator_state_read_ray(state, &ray);
shader_setup_from_ray(kg, sd, &ray, &isect);
VOLUME_READ_LAMBDA(integrator_state_read_volume_stack(state, i))
volume_stack_set_surface_priority(kg, sd, volume_read_lambda_pass);
}
ccl_device_forceinline float3 integrate_surface_ray_offset(KernelGlobals kg,
@ -397,7 +400,8 @@ ccl_device_forceinline int integrate_surface_bsdf_bssrdf_bounce(
KernelGlobals kg,
IntegratorState state,
ccl_private ShaderData *sd,
ccl_private const RNGState *rng_state)
ccl_private const RNGState *rng_state,
ccl_private float *bsdf_eta)
{
/* Sample BSDF or BSSRDF. */
if (!(sd->flag & (SD_BSDF | SD_BSSRDF))) {
@ -421,7 +425,7 @@ ccl_device_forceinline int integrate_surface_bsdf_bssrdf_bounce(
int label;
float2 bsdf_sampled_roughness = make_float2(1.0f, 1.0f);
float bsdf_eta = 1.0f;
*bsdf_eta = 1.0f;
float mis_pdf = 1.0f;
#if defined(__PATH_GUIDING__) && PATH_GUIDING_LEVEL >= 4
@ -437,7 +441,7 @@ ccl_device_forceinline int integrate_surface_bsdf_bssrdf_bounce(
&mis_pdf,
&unguided_bsdf_pdf,
&bsdf_sampled_roughness,
&bsdf_eta,
bsdf_eta,
rng_state);
if (bsdf_pdf == 0.0f || bsdf_eval_is_zero(&bsdf_eval)) {
@ -458,7 +462,7 @@ ccl_device_forceinline int integrate_surface_bsdf_bssrdf_bounce(
&bsdf_wo,
&bsdf_pdf,
&bsdf_sampled_roughness,
&bsdf_eta);
bsdf_eta);
if (bsdf_pdf == 0.0f || bsdf_eval_is_zero(&bsdf_eval)) {
return LABEL_NONE;
@ -520,7 +524,7 @@ ccl_device_forceinline int integrate_surface_bsdf_bssrdf_bounce(
sd->N,
normalize(bsdf_wo),
bsdf_sampled_roughness,
bsdf_eta);
*bsdf_eta);
return label;
}
@ -658,6 +662,7 @@ ccl_device int integrate_surface(KernelGlobals kg,
PROFILING_SHADER(sd.object, sd.shader);
int continue_path_label = 0;
float ior = 1.0f;
const uint32_t path_flag = INTEGRATOR_STATE(state, path, flag);
@ -749,7 +754,7 @@ ccl_device int integrate_surface(KernelGlobals kg,
#endif
PROFILING_EVENT(PROFILING_SHADE_SURFACE_INDIRECT_LIGHT);
continue_path_label = integrate_surface_bsdf_bssrdf_bounce(kg, state, &sd, &rng_state);
continue_path_label = integrate_surface_bsdf_bssrdf_bounce(kg, state, &sd, &rng_state, &ior);
#ifdef __VOLUME__
}
else {
@ -763,7 +768,7 @@ ccl_device int integrate_surface(KernelGlobals kg,
if (continue_path_label & LABEL_TRANSMIT) {
/* Enter/Exit volume. */
volume_stack_enter_exit(kg, state, &sd);
volume_stack_enter_exit(kg, state, &sd, ior);
}
#endif

View File

@ -103,6 +103,7 @@ KERNEL_STRUCT_END(subsurface)
KERNEL_STRUCT_BEGIN(volume_stack)
KERNEL_STRUCT_ARRAY_MEMBER(volume_stack, int, object, KERNEL_FEATURE_VOLUME)
KERNEL_STRUCT_ARRAY_MEMBER(volume_stack, int, shader, KERNEL_FEATURE_VOLUME)
KERNEL_STRUCT_ARRAY_MEMBER(volume_stack, float, ior, KERNEL_FEATURE_VOLUME)
KERNEL_STRUCT_END_ARRAY(volume_stack,
KERNEL_STRUCT_VOLUME_STACK_SIZE,
KERNEL_STRUCT_VOLUME_STACK_SIZE)

View File

@ -119,7 +119,8 @@ ccl_device_forceinline VolumeStack integrator_state_read_volume_stack(ConstInteg
int i)
{
VolumeStack entry = {INTEGRATOR_STATE_ARRAY(state, volume_stack, i, object),
INTEGRATOR_STATE_ARRAY(state, volume_stack, i, shader)};
INTEGRATOR_STATE_ARRAY(state, volume_stack, i, shader),
INTEGRATOR_STATE_ARRAY(state, volume_stack, i, ior)};
return entry;
}
@ -129,6 +130,7 @@ ccl_device_forceinline void integrator_state_write_volume_stack(IntegratorState
{
INTEGRATOR_STATE_ARRAY_WRITE(state, volume_stack, i, object) = entry.object;
INTEGRATOR_STATE_ARRAY_WRITE(state, volume_stack, i, shader) = entry.shader;
INTEGRATOR_STATE_ARRAY_WRITE(state, volume_stack, i, ior) = entry.ior;
}
ccl_device_forceinline bool integrator_state_volume_stack_is_empty(KernelGlobals kg,
@ -181,7 +183,7 @@ ccl_device_forceinline void integrator_state_copy_volume_stack_to_shadow(
INTEGRATOR_STATE_ARRAY_WRITE(shadow_state, shadow_volume_stack, index, shader) = shader;
++index;
} while (shader != OBJECT_NONE);
} while (shader != SHADER_NONE);
}
}
@ -198,6 +200,7 @@ ccl_device_forceinline void integrator_state_copy_volume_stack(KernelGlobals kg,
INTEGRATOR_STATE_ARRAY_WRITE(to_state, volume_stack, index, object) = INTEGRATOR_STATE_ARRAY(
state, volume_stack, index, object);
INTEGRATOR_STATE_ARRAY_WRITE(to_state, volume_stack, index, shader) = shader;
INTEGRATOR_STATE_ARRAY_WRITE(to_state, volume_stack, index, ior) = INTEGRATOR_STATE_ARRAY(state, volume_stack, index, ior);
++index;
} while (shader != OBJECT_NONE);
@ -208,7 +211,8 @@ ccl_device_forceinline VolumeStack
integrator_state_read_shadow_volume_stack(ConstIntegratorShadowState state, int i)
{
VolumeStack entry = {INTEGRATOR_STATE_ARRAY(state, shadow_volume_stack, i, object),
INTEGRATOR_STATE_ARRAY(state, shadow_volume_stack, i, shader)};
INTEGRATOR_STATE_ARRAY(state, shadow_volume_stack, i, shader),
0.0f};
return entry;
}

View File

@ -461,11 +461,15 @@ ccl_device_inline void volume_shader_eval(KernelGlobals kg,
sd->flag = 0;
sd->object_flag = 0;
int active_priority = volume_stack_active_priority(kg, stack_read);
for (int i = 0;; i++) {
const VolumeStack entry = stack_read(i);
if (entry.shader == SHADER_NONE) {
break;
}
if (!volume_stack_volume_active(kg, entry.shader, active_priority)) {
continue;
}
/* Setup shader-data from stack. it's mostly setup already in
* shader_setup_from_volume, this switching should be quick. */

View File

@ -19,16 +19,83 @@ CCL_NAMESPACE_BEGIN
* This is an array of object/shared ID's that the current segment of the path
* is inside of. */
/* Gets the volume stack priority of the given shader. */
ccl_device_inline int volume_stack_shader_priority(KernelGlobals kg, int shader)
{
return kernel_data_fetch(shaders, (shader & SHADER_MASK)).volume_stack_priority;
}
/* Checks whether the given shader should be skipped
* (if we're currently in a volume with a higher priority). */
ccl_device_inline int volume_stack_skip_shader(KernelGlobals kg, int shader, int active_priority)
{
int priority = volume_stack_shader_priority(kg, shader);
return (priority > 0) && (priority < active_priority);
}
/* Checks whether the given shader should be considered when iterating over all volumes at
* the current point (during ray marching).
* Skips volumes that are being masked by priority, as well as ones that don't actually have
* a volume shader. */
ccl_device_inline bool volume_stack_volume_active(KernelGlobals kg, int shader, int active_priority)
{
int flags = kernel_data_fetch(shaders, (shader & SHADER_MASK)).flags;
return (flags & SD_HAS_VOLUME) && !volume_stack_skip_shader(kg, shader, active_priority);
}
/* Gets the currently active priority (as in, the highest priority of all volumes on the stack). */
template<typename StackReadOp>
ccl_device_inline int volume_stack_active_priority(KernelGlobals kg, StackReadOp stack_read)
{
/* Since the stack is sorted by priority, just look at the first entry. */
int shader = stack_read(0).shader;
return (shader == SHADER_NONE) ? 0 : volume_stack_shader_priority(kg, shader);
}
/* Checks whether the current surface should be skipped, and sets the SD flag accordingly. */
template<typename StackReadOp>
ccl_device_inline void volume_stack_set_surface_priority(KernelGlobals kg, ccl_private ShaderData *sd, StackReadOp stack_read)
{
int priority = volume_stack_shader_priority(kg, sd->shader);
if (priority == 0) {
return;
}
VolumeStack active = stack_read(0);
if (active.shader == SHADER_NONE) {
return;
}
int active_priority = volume_stack_shader_priority(kg, active.shader);
if (priority < active_priority) {
sd->flag |= SD_BSDF_PRIORIITY_MASKED;
}
else if (active_priority > 0) {
if (sd->flag & SD_BACKFACING) {
/* We're leaving the currently active volume, so the opposite medium is the next in line. */
VolumeStack next = stack_read(1);
sd->opposite_ior = (next.shader == SHADER_NONE)? 1.0f : next.ior;
}
else {
/* We're entering a volume with higher priority, so the opposite medium is the currently highest one. */
sd->opposite_ior = active.ior;
}
}
}
template<typename StackReadOp, typename StackWriteOp>
ccl_device void volume_stack_enter_exit(KernelGlobals kg,
ccl_private const ShaderData *sd,
const float ior,
StackReadOp stack_read,
StackWriteOp stack_write)
{
/* todo: we should have some way for objects to indicate if they want the
* world shader to work inside them. excluding it by default is problematic
* because non-volume objects can't be assumed to be closed manifolds */
if (!(sd->flag & SD_HAS_VOLUME)) {
const int priority = volume_stack_shader_priority(kg, sd->shader);
if (!(sd->flag & SD_HAS_VOLUME) && priority == 0) {
return;
}
@ -55,6 +122,7 @@ ccl_device void volume_stack_enter_exit(KernelGlobals kg,
else {
/* Enter volume object: add to stack. */
int i;
int insert_pos = 0;
for (i = 0;; i++) {
VolumeStack entry = stack_read(i);
if (entry.shader == SHADER_NONE) {
@ -65,6 +133,11 @@ ccl_device void volume_stack_enter_exit(KernelGlobals kg,
if (entry.object == sd->object && entry.shader == sd->shader) {
return;
}
/* Keep advancing the insert position until we reach entries with lower priority. */
if (volume_stack_shader_priority(kg, entry.shader) > priority) {
insert_pos = i + 1;
}
}
/* If we exceed the stack limit, ignore. */
@ -72,21 +145,25 @@ ccl_device void volume_stack_enter_exit(KernelGlobals kg,
return;
}
/* Add to the end of the stack. */
const VolumeStack new_entry = {sd->object, sd->shader};
const VolumeStack empty_entry = {OBJECT_NONE, SHADER_NONE};
stack_write(i, new_entry);
stack_write(i + 1, empty_entry);
/* Shift back stack entries starting at (and including) insert_pos. */
for (int j = i; j >= insert_pos; j--) {
stack_write(j + 1, stack_read(j));
}
/* Add entry at insert position. */
const VolumeStack new_entry = {sd->object, sd->shader, ior};
stack_write(insert_pos, new_entry);
}
}
ccl_device void volume_stack_enter_exit(KernelGlobals kg,
IntegratorState state,
ccl_private const ShaderData *sd)
ccl_private const ShaderData *sd,
const float ior)
{
VOLUME_READ_LAMBDA(integrator_state_read_volume_stack(state, i))
VOLUME_WRITE_LAMBDA(integrator_state_write_volume_stack(state, i, entry))
volume_stack_enter_exit(kg, sd, volume_read_lambda_pass, volume_write_lambda_pass);
volume_stack_enter_exit(kg, sd, ior, volume_read_lambda_pass, volume_write_lambda_pass);
}
ccl_device void shadow_volume_stack_enter_exit(KernelGlobals kg,
@ -95,7 +172,8 @@ ccl_device void shadow_volume_stack_enter_exit(KernelGlobals kg,
{
VOLUME_READ_LAMBDA(integrator_state_read_shadow_volume_stack(state, i))
VOLUME_WRITE_LAMBDA(integrator_state_write_shadow_volume_stack(state, i, entry))
volume_stack_enter_exit(kg, sd, volume_read_lambda_pass, volume_write_lambda_pass);
/* IOR does not matter in shadow stack, refractive interfaces block the shadow ray anyways. */
volume_stack_enter_exit(kg, sd, 0.0f, volume_read_lambda_pass, volume_write_lambda_pass);
}
/* Clean stack after the last bounce.
@ -127,12 +205,16 @@ template<typename StackReadOp>
ccl_device float volume_stack_step_size(KernelGlobals kg, StackReadOp stack_read)
{
float step_size = FLT_MAX;
int active_priority = volume_stack_active_priority(kg, stack_read);
for (int i = 0;; i++) {
VolumeStack entry = stack_read(i);
if (entry.shader == SHADER_NONE) {
break;
}
if (!volume_stack_volume_active(kg, entry.shader, active_priority)) {
continue;
}
int shader_flag = kernel_data_fetch(shaders, (entry.shader & SHADER_MASK)).flags;
@ -174,12 +256,17 @@ typedef enum VolumeSampleMethod {
ccl_device VolumeSampleMethod volume_stack_sample_method(KernelGlobals kg, IntegratorState state)
{
VolumeSampleMethod method = VOLUME_SAMPLE_NONE;
VOLUME_READ_LAMBDA(integrator_state_read_volume_stack(state, i))
int active_priority = volume_stack_active_priority(kg, volume_read_lambda_pass);
for (int i = 0;; i++) {
VolumeStack entry = integrator_state_read_volume_stack(state, i);
if (entry.shader == SHADER_NONE) {
break;
}
if (!volume_stack_volume_active(kg, entry.shader, active_priority)) {
continue;
}
int shader_flag = kernel_data_fetch(shaders, (entry.shader & SHADER_MASK)).flags;

View File

@ -80,6 +80,7 @@ ccl_device
switch (type) {
case CLOSURE_BSDF_PRINCIPLED_ID: {
uint specular_ior_level_offset, roughness_offset, specular_tint_offset, anisotropic_offset,
sheen_weight_offset, sheen_tint_offset, sheen_roughness_offset, coat_weight_offset,
coat_roughness_offset, coat_ior_offset, eta_offset, transmission_weight_offset,
@ -164,6 +165,11 @@ ccl_device
Spectrum weight = closure_weight * mix_weight;
if (sd->flag & SD_BSDF_PRIORIITY_MASKED) {
bsdf_transparent_setup(sd, closure_weight * mix_weight, path_flag, ior);
break;
}
float alpha_x = sqr(roughness), alpha_y = sqr(roughness);
if (anisotropic > 0.0f) {
float aspect = sqrtf(1.0f - anisotropic * 0.9f);
@ -314,11 +320,12 @@ ccl_device
bsdf->T = zero_float3();
bsdf->alpha_x = bsdf->alpha_y = sqr(roughness);
bsdf->ior = (sd->flag & SD_BACKFACING) ? 1.0f / ior : ior;
float eta = ior / sd->opposite_ior;
bsdf->ior = (sd->flag & SD_BACKFACING) ? 1.0f / eta : eta;
fresnel->f0 = make_float3(F0_from_ior(ior)) * specular_tint;
fresnel->f0 = make_float3(F0_from_ior(eta)) * specular_tint;
fresnel->f90 = one_spectrum();
fresnel->exponent = -ior;
fresnel->exponent = -eta;
fresnel->reflection_tint = reflective_caustics ? one_spectrum() : zero_spectrum();
fresnel->transmission_tint = refractive_caustics ?
sqrt(rgb_to_spectrum(clamped_base_color)) :
@ -556,6 +563,13 @@ ccl_device
case CLOSURE_BSDF_MICROFACET_GGX_GLASS_ID:
case CLOSURE_BSDF_MICROFACET_BECKMANN_GLASS_ID:
case CLOSURE_BSDF_MICROFACET_MULTI_GGX_GLASS_ID: {
float ior = fmaxf(param2, 1e-5f);
if (sd->flag & SD_BSDF_PRIORIITY_MASKED) {
bsdf_transparent_setup(sd, closure_weight * mix_weight, path_flag, ior);
break;
}
#ifdef __CAUSTICS_TRICKS__
const bool reflective_caustics = (kernel_data.integrator.caustics_reflective ||
(path_flag & PATH_RAY_DIFFUSE) == 0);
@ -578,7 +592,7 @@ ccl_device
bsdf->N = maybe_ensure_valid_specular_reflection(sd, N);
bsdf->T = zero_float3();
float ior = fmaxf(param2, 1e-5f);
ior = ior / sd->opposite_ior;
bsdf->ior = (sd->flag & SD_BACKFACING) ? 1.0f / ior : ior;
bsdf->alpha_x = bsdf->alpha_y = sqr(param1);

View File

@ -831,9 +831,12 @@ enum ShaderDataFlag {
SD_BSDF_NEEDS_LCG = (1 << 10),
/* BSDF has a transmissive component. */
SD_BSDF_HAS_TRANSMISSION = (1 << 11),
/* Shader is masked by volume with higher priority. */
SD_BSDF_PRIORIITY_MASKED = (1 << 12),
SD_CLOSURE_FLAGS = (SD_EMISSION | SD_BSDF | SD_BSDF_HAS_EVAL | SD_BSSRDF | SD_HOLDOUT |
SD_EXTINCTION | SD_SCATTER | SD_BSDF_NEEDS_LCG | SD_BSDF_HAS_TRANSMISSION),
SD_EXTINCTION | SD_SCATTER | SD_BSDF_NEEDS_LCG | SD_BSDF_HAS_TRANSMISSION |
SD_BSDF_PRIORIITY_MASKED),
/* Shader flags. */
@ -991,6 +994,9 @@ typedef struct ccl_align(16) ShaderData
# endif
#endif
/* IOR of medium on the opposite side of the surface. */
float opposite_ior;
/* LCG state for closures that require additional random numbers. */
uint lcg_state;
@ -1048,6 +1054,8 @@ typedef struct ShaderVolumePhases {
typedef struct VolumeStack {
int object;
int shader;
/* TODO: ior is wasted space in the Shadow Volume Stack. */
float ior;
} VolumeStack;
#endif
@ -1537,7 +1545,8 @@ typedef struct KernelShader {
float cryptomatte_id;
int flags;
int pass_id;
int pad2, pad3;
int volume_stack_priority;
int pad2;
} KernelShader;
static_assert_align(KernelShader, 16);

View File

@ -680,6 +680,9 @@ int Scene::get_max_closure_count()
int Scene::get_volume_stack_size() const
{
/* TODO: Determine this properly. Just count "has volume or priority"? */
return MAX_VOLUME_STACK_SIZE;
int volume_stack_size = 0;
/* Space for background volume and terminator.

View File

@ -56,6 +56,7 @@ NODE_DEFINE(Shader)
SOCKET_BOOLEAN(use_transparent_shadow, "Use Transparent Shadow", true);
SOCKET_BOOLEAN(use_bump_map_correction, "Bump Map Correction", true);
SOCKET_BOOLEAN(heterogeneous_volume, "Heterogeneous Volume", true);
SOCKET_INT(volume_stack_priority, "Volume Stack Priority", 0);
static NodeEnum volume_sampling_method_enum;
volume_sampling_method_enum.insert("distance", VOLUME_SAMPLING_DISTANCE);
@ -605,6 +606,7 @@ void ShaderManager::device_update_common(Device * /*device*/,
/* regular shader */
kshader->flags = flag;
kshader->pass_id = shader->get_pass_id();
kshader->volume_stack_priority = shader->get_volume_stack_priority();
kshader->constant_emission[0] = shader->emission_estimate.x;
kshader->constant_emission[1] = shader->emission_estimate.y;
kshader->constant_emission[2] = shader->emission_estimate.z;
@ -785,6 +787,10 @@ uint ShaderManager::get_kernel_features(Scene *scene)
if (shader->has_volume_connected) {
kernel_features |= KERNEL_FEATURE_VOLUME;
}
/* Volume stack priority requires the volume stack even if no volumetric shaders are used. */
if (shader->get_volume_stack_priority() != 0) {
kernel_features |= KERNEL_FEATURE_VOLUME;
}
}
if (use_osl()) {

View File

@ -82,6 +82,7 @@ class Shader : public Node {
NODE_SOCKET_API(VolumeSampling, volume_sampling_method)
NODE_SOCKET_API(int, volume_interpolation_method)
NODE_SOCKET_API(float, volume_step_rate)
NODE_SOCKET_API(int, volume_stack_priority)
/* displacement */
NODE_SOCKET_API(DisplacementMethod, displacement_method)