WIP: Cycles: Implement volume stack priority and nested IOR support #118478
|
@ -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(
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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>(
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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. */
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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()) {
|
||||
|
|
|
@ -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)
|
||||
|
|
Loading…
Reference in New Issue