forked from bartvdbraak/blender
Cycles: change volume step size controls, auto adjust based on voxel size
By default it will now set the step size to the voxel size for smoke and volume objects, and 1/10th the bounding box for procedural volume shaders. New settings are: * Scene render/preview step rate: to globally adjust detail and performance * Material step rate: multiplied with auto detected per-object step size * World step size: distance to steo for world shader Differential Revision: https://developer.blender.org/D1777
This commit is contained in:
parent
9d20f170c7
commit
1162ba206d
@ -444,13 +444,20 @@ class CyclesRenderSettings(bpy.types.PropertyGroup):
|
||||
default=8,
|
||||
)
|
||||
|
||||
volume_step_size: FloatProperty(
|
||||
name="Step Size",
|
||||
description="Distance between volume shader samples when rendering the volume "
|
||||
"(lower values give more accurate and detailed results, but also increased render time)",
|
||||
default=0.1,
|
||||
min=0.0000001, max=100000.0, soft_min=0.01, soft_max=1.0, precision=4,
|
||||
unit='LENGTH'
|
||||
volume_step_rate: FloatProperty(
|
||||
name="Step Rate",
|
||||
description="Globally adjust detail for volume rendering, on top of automatically estimated step size. "
|
||||
"Higher values reduce render time, lower values render with more detail",
|
||||
default=1.0,
|
||||
min=0.01, max=100.0, soft_min=0.1, soft_max=10.0, precision=2
|
||||
)
|
||||
|
||||
volume_preview_step_rate: FloatProperty(
|
||||
name="Step Rate",
|
||||
description="Globally adjust detail for volume rendering, on top of automatically estimated step size. "
|
||||
"Higher values reduce render time, lower values render with more detail",
|
||||
default=1.0,
|
||||
min=0.01, max=100.0, soft_min=0.1, soft_max=10.0, precision=2
|
||||
)
|
||||
|
||||
volume_max_steps: IntProperty(
|
||||
@ -934,6 +941,14 @@ class CyclesMaterialSettings(bpy.types.PropertyGroup):
|
||||
default='LINEAR',
|
||||
)
|
||||
|
||||
volume_step_rate: FloatProperty(
|
||||
name="Step Rate",
|
||||
description="Scale the distance between volume shader samples when rendering the volume "
|
||||
"(lower values give more accurate and detailed results, but also increased render time)",
|
||||
default=1.0,
|
||||
min=0.001, max=1000.0, soft_min=0.1, soft_max=10.0, precision=4
|
||||
)
|
||||
|
||||
displacement_method: EnumProperty(
|
||||
name="Displacement Method",
|
||||
description="Method to use for the displacement",
|
||||
@ -1044,6 +1059,13 @@ class CyclesWorldSettings(bpy.types.PropertyGroup):
|
||||
items=enum_volume_interpolation,
|
||||
default='LINEAR',
|
||||
)
|
||||
volume_step_size: FloatProperty(
|
||||
name="Step Size",
|
||||
description="Distance between volume shader samples when rendering the volume "
|
||||
"(lower values give more accurate and detailed results, but also increased render time)",
|
||||
default=1.0,
|
||||
min=0.0000001, max=100000.0, soft_min=0.1, soft_max=100.0, precision=4
|
||||
)
|
||||
|
||||
@classmethod
|
||||
def register(cls):
|
||||
|
@ -373,7 +373,7 @@ class CYCLES_RENDER_PT_subdivision(CyclesButtonsPanel, Panel):
|
||||
col = layout.column()
|
||||
sub = col.column(align=True)
|
||||
sub.prop(cscene, "dicing_rate", text="Dicing Rate Render")
|
||||
sub.prop(cscene, "preview_dicing_rate", text="Preview")
|
||||
sub.prop(cscene, "preview_dicing_rate", text="Viewport")
|
||||
|
||||
col.separator()
|
||||
|
||||
@ -428,9 +428,11 @@ class CYCLES_RENDER_PT_volumes(CyclesButtonsPanel, Panel):
|
||||
scene = context.scene
|
||||
cscene = scene.cycles
|
||||
|
||||
col = layout.column()
|
||||
col.prop(cscene, "volume_step_size", text="Step Size")
|
||||
col.prop(cscene, "volume_max_steps", text="Max Steps")
|
||||
col = layout.column(align=True)
|
||||
col.prop(cscene, "volume_step_rate", text="Step Rate Render")
|
||||
col.prop(cscene, "volume_preview_step_rate", text="Viewport")
|
||||
|
||||
layout.prop(cscene, "volume_max_steps", text="Max Steps")
|
||||
|
||||
|
||||
class CYCLES_RENDER_PT_light_paths(CyclesButtonsPanel, Panel):
|
||||
@ -1696,6 +1698,9 @@ class CYCLES_WORLD_PT_settings_volume(CyclesButtonsPanel, Panel):
|
||||
sub.prop(cworld, "volume_sampling", text="Sampling")
|
||||
col.prop(cworld, "volume_interpolation", text="Interpolation")
|
||||
col.prop(cworld, "homogeneous_volume", text="Homogeneous")
|
||||
sub = col.column()
|
||||
sub.active = not cworld.homogeneous_volume
|
||||
sub.prop(cworld, "volume_step_size")
|
||||
|
||||
|
||||
class CYCLES_MATERIAL_PT_preview(CyclesButtonsPanel, Panel):
|
||||
@ -1827,6 +1832,9 @@ class CYCLES_MATERIAL_PT_settings_volume(CyclesButtonsPanel, Panel):
|
||||
sub.prop(cmat, "volume_sampling", text="Sampling")
|
||||
col.prop(cmat, "volume_interpolation", text="Interpolation")
|
||||
col.prop(cmat, "homogeneous_volume", text="Homogeneous")
|
||||
sub = col.column()
|
||||
sub.active = not cmat.homogeneous_volume
|
||||
sub.prop(cmat, "volume_step_rate")
|
||||
|
||||
def draw(self, context):
|
||||
self.draw_shared(self, context, context.material)
|
||||
|
@ -1260,6 +1260,7 @@ void BlenderSync::sync_materials(BL::Depsgraph &b_depsgraph, bool update_all)
|
||||
shader->heterogeneous_volume = !get_boolean(cmat, "homogeneous_volume");
|
||||
shader->volume_sampling_method = get_volume_sampling(cmat);
|
||||
shader->volume_interpolation_method = get_volume_interpolation(cmat);
|
||||
shader->volume_step_rate = get_float(cmat, "volume_step_rate");
|
||||
shader->displacement_method = get_displacement_method(cmat);
|
||||
|
||||
shader->set_graph(graph);
|
||||
@ -1324,6 +1325,7 @@ void BlenderSync::sync_world(BL::Depsgraph &b_depsgraph, BL::SpaceView3D &b_v3d,
|
||||
shader->heterogeneous_volume = !get_boolean(cworld, "homogeneous_volume");
|
||||
shader->volume_sampling_method = get_volume_sampling(cworld);
|
||||
shader->volume_interpolation_method = get_volume_interpolation(cworld);
|
||||
shader->volume_step_rate = get_float(cworld, "volume_step_size");
|
||||
}
|
||||
else if (new_viewport_parameters.use_scene_world && b_world) {
|
||||
BackgroundNode *background = new BackgroundNode();
|
||||
|
@ -262,7 +262,8 @@ void BlenderSync::sync_integrator()
|
||||
integrator->transparent_max_bounce = get_int(cscene, "transparent_max_bounces");
|
||||
|
||||
integrator->volume_max_steps = get_int(cscene, "volume_max_steps");
|
||||
integrator->volume_step_size = get_float(cscene, "volume_step_size");
|
||||
integrator->volume_step_rate = (preview) ? get_float(cscene, "volume_preview_step_rate") :
|
||||
get_float(cscene, "volume_step_rate");
|
||||
|
||||
integrator->caustics_reflective = get_boolean(cscene, "caustics_reflective");
|
||||
integrator->caustics_refractive = get_boolean(cscene, "caustics_refractive");
|
||||
|
@ -320,6 +320,17 @@ ccl_device_inline uint object_patch_map_offset(KernelGlobals *kg, int object)
|
||||
return kernel_tex_fetch(__objects, object).patch_map_offset;
|
||||
}
|
||||
|
||||
/* Volume step size */
|
||||
|
||||
ccl_device_inline float object_volume_step_size(KernelGlobals *kg, int object)
|
||||
{
|
||||
if (object == OBJECT_NONE) {
|
||||
return kernel_data.background.volume_step_size;
|
||||
}
|
||||
|
||||
return kernel_tex_fetch(__object_volume_step, object);
|
||||
}
|
||||
|
||||
/* Pass ID for shader */
|
||||
|
||||
ccl_device int shader_pass_id(KernelGlobals *kg, const ShaderData *sd)
|
||||
|
@ -171,19 +171,19 @@ ccl_device_forceinline VolumeIntegrateResult kernel_path_volume(KernelGlobals *k
|
||||
Ray volume_ray = *ray;
|
||||
volume_ray.t = (hit) ? isect->t : FLT_MAX;
|
||||
|
||||
bool heterogeneous = volume_stack_is_heterogeneous(kg, state->volume_stack);
|
||||
float step_size = volume_stack_step_size(kg, state->volume_stack);
|
||||
|
||||
# ifdef __VOLUME_DECOUPLED__
|
||||
int sampling_method = volume_stack_sampling_method(kg, state->volume_stack);
|
||||
bool direct = (state->flag & PATH_RAY_CAMERA) != 0;
|
||||
bool decoupled = kernel_volume_use_decoupled(kg, heterogeneous, direct, sampling_method);
|
||||
bool decoupled = kernel_volume_use_decoupled(kg, step_size, direct, sampling_method);
|
||||
|
||||
if (decoupled) {
|
||||
/* cache steps along volume for repeated sampling */
|
||||
VolumeSegment volume_segment;
|
||||
|
||||
shader_setup_from_volume(kg, sd, &volume_ray);
|
||||
kernel_volume_decoupled_record(kg, state, &volume_ray, sd, &volume_segment, heterogeneous);
|
||||
kernel_volume_decoupled_record(kg, state, &volume_ray, sd, &volume_segment, step_size);
|
||||
|
||||
volume_segment.sampling_method = sampling_method;
|
||||
|
||||
@ -229,7 +229,7 @@ ccl_device_forceinline VolumeIntegrateResult kernel_path_volume(KernelGlobals *k
|
||||
{
|
||||
/* integrate along volume segment with distance sampling */
|
||||
VolumeIntegrateResult result = kernel_volume_integrate(
|
||||
kg, state, sd, &volume_ray, L, throughput, heterogeneous);
|
||||
kg, state, sd, &volume_ray, L, throughput, step_size);
|
||||
|
||||
# ifdef __VOLUME_SCATTER__
|
||||
if (result == VOLUME_PATH_SCATTERED) {
|
||||
|
@ -91,7 +91,7 @@ ccl_device_forceinline void kernel_branched_path_volume(KernelGlobals *kg,
|
||||
Ray volume_ray = *ray;
|
||||
volume_ray.t = (hit) ? isect->t : FLT_MAX;
|
||||
|
||||
bool heterogeneous = volume_stack_is_heterogeneous(kg, state->volume_stack);
|
||||
float step_size = volume_stack_step_size(kg, state->volume_stack);
|
||||
|
||||
# ifdef __VOLUME_DECOUPLED__
|
||||
/* decoupled ray marching only supported on CPU */
|
||||
@ -100,7 +100,7 @@ ccl_device_forceinline void kernel_branched_path_volume(KernelGlobals *kg,
|
||||
VolumeSegment volume_segment;
|
||||
|
||||
shader_setup_from_volume(kg, sd, &volume_ray);
|
||||
kernel_volume_decoupled_record(kg, state, &volume_ray, sd, &volume_segment, heterogeneous);
|
||||
kernel_volume_decoupled_record(kg, state, &volume_ray, sd, &volume_segment, step_size);
|
||||
|
||||
/* direct light sampling */
|
||||
if (volume_segment.closure_flag & SD_SCATTER) {
|
||||
@ -171,7 +171,7 @@ ccl_device_forceinline void kernel_branched_path_volume(KernelGlobals *kg,
|
||||
path_state_branch(&ps, j, num_samples);
|
||||
|
||||
VolumeIntegrateResult result = kernel_volume_integrate(
|
||||
kg, &ps, sd, &volume_ray, L, &tp, heterogeneous);
|
||||
kg, &ps, sd, &volume_ray, L, &tp, step_size);
|
||||
|
||||
# ifdef __VOLUME_SCATTER__
|
||||
if (result == VOLUME_PATH_SCATTERED) {
|
||||
|
@ -35,6 +35,7 @@ KERNEL_TEX(KernelObject, __objects)
|
||||
KERNEL_TEX(Transform, __object_motion_pass)
|
||||
KERNEL_TEX(DecomposedTransform, __object_motion)
|
||||
KERNEL_TEX(uint, __object_flag)
|
||||
KERNEL_TEX(float, __object_volume_step)
|
||||
|
||||
/* cameras */
|
||||
KERNEL_TEX(DecomposedTransform, __camera_motion)
|
||||
|
@ -887,13 +887,13 @@ enum ShaderDataFlag {
|
||||
SD_HAS_DISPLACEMENT = (1 << 26),
|
||||
/* Has constant emission (value stored in __shaders) */
|
||||
SD_HAS_CONSTANT_EMISSION = (1 << 27),
|
||||
/* Needs to access attributes */
|
||||
SD_NEED_ATTRIBUTES = (1 << 28),
|
||||
/* Needs to access attributes for volume rendering */
|
||||
SD_NEED_VOLUME_ATTRIBUTES = (1 << 28),
|
||||
|
||||
SD_SHADER_FLAGS = (SD_USE_MIS | SD_HAS_TRANSPARENT_SHADOW | SD_HAS_VOLUME | SD_HAS_ONLY_VOLUME |
|
||||
SD_HETEROGENEOUS_VOLUME | SD_HAS_BSSRDF_BUMP | SD_VOLUME_EQUIANGULAR |
|
||||
SD_VOLUME_MIS | SD_VOLUME_CUBIC | SD_HAS_BUMP | SD_HAS_DISPLACEMENT |
|
||||
SD_HAS_CONSTANT_EMISSION | SD_NEED_ATTRIBUTES)
|
||||
SD_HAS_CONSTANT_EMISSION | SD_NEED_VOLUME_ATTRIBUTES)
|
||||
};
|
||||
|
||||
/* Object flags. */
|
||||
@ -1275,6 +1275,7 @@ typedef struct KernelBackground {
|
||||
/* only shader index */
|
||||
int surface_shader;
|
||||
int volume_shader;
|
||||
float volume_step_size;
|
||||
int transparent;
|
||||
float transparent_roughness_squared_threshold;
|
||||
|
||||
@ -1282,7 +1283,6 @@ typedef struct KernelBackground {
|
||||
float ao_factor;
|
||||
float ao_distance;
|
||||
float ao_bounces_factor;
|
||||
float ao_pad;
|
||||
} KernelBackground;
|
||||
static_assert_align(KernelBackground, 16);
|
||||
|
||||
@ -1355,7 +1355,7 @@ typedef struct KernelIntegrator {
|
||||
/* volume render */
|
||||
int use_volumes;
|
||||
int volume_max_steps;
|
||||
float volume_step_size;
|
||||
float volume_step_rate;
|
||||
int volume_samples;
|
||||
|
||||
int start_sample;
|
||||
|
@ -101,15 +101,19 @@ ccl_device float kernel_volume_channel_get(float3 value, int channel)
|
||||
|
||||
#ifdef __VOLUME__
|
||||
|
||||
ccl_device bool volume_stack_is_heterogeneous(KernelGlobals *kg, ccl_addr_space VolumeStack *stack)
|
||||
ccl_device float volume_stack_step_size(KernelGlobals *kg, ccl_addr_space VolumeStack *stack)
|
||||
{
|
||||
float step_size = FLT_MAX;
|
||||
|
||||
for (int i = 0; stack[i].shader != SHADER_NONE; i++) {
|
||||
int shader_flag = kernel_tex_fetch(__shaders, (stack[i].shader & SHADER_MASK)).flags;
|
||||
|
||||
bool heterogeneous = false;
|
||||
|
||||
if (shader_flag & SD_HETEROGENEOUS_VOLUME) {
|
||||
return true;
|
||||
heterogeneous = true;
|
||||
}
|
||||
else if (shader_flag & SD_NEED_ATTRIBUTES) {
|
||||
else if (shader_flag & SD_NEED_VOLUME_ATTRIBUTES) {
|
||||
/* We want to render world or objects without any volume grids
|
||||
* as homogeneous, but can only verify this at run-time since other
|
||||
* heterogeneous volume objects may be using the same shader. */
|
||||
@ -117,13 +121,19 @@ ccl_device bool volume_stack_is_heterogeneous(KernelGlobals *kg, ccl_addr_space
|
||||
if (object != OBJECT_NONE) {
|
||||
int object_flag = kernel_tex_fetch(__object_flag, object);
|
||||
if (object_flag & SD_OBJECT_HAS_VOLUME_ATTRIBUTES) {
|
||||
return true;
|
||||
heterogeneous = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (heterogeneous) {
|
||||
float object_step_size = object_volume_step_size(kg, stack[i].object);
|
||||
object_step_size *= kernel_data.integrator.volume_step_rate;
|
||||
step_size = fminf(object_step_size, step_size);
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
return step_size;
|
||||
}
|
||||
|
||||
ccl_device int volume_stack_sampling_method(KernelGlobals *kg, VolumeStack *stack)
|
||||
@ -158,12 +168,13 @@ ccl_device int volume_stack_sampling_method(KernelGlobals *kg, VolumeStack *stac
|
||||
|
||||
ccl_device_inline void kernel_volume_step_init(KernelGlobals *kg,
|
||||
ccl_addr_space PathState *state,
|
||||
const float object_step_size,
|
||||
float t,
|
||||
float *step_size,
|
||||
float *step_offset)
|
||||
{
|
||||
const int max_steps = kernel_data.integrator.volume_max_steps;
|
||||
float step = min(kernel_data.integrator.volume_step_size, t);
|
||||
float step = min(object_step_size, t);
|
||||
|
||||
/* compute exact steps in advance for malloc */
|
||||
if (t > max_steps * step) {
|
||||
@ -199,7 +210,8 @@ ccl_device void kernel_volume_shadow_heterogeneous(KernelGlobals *kg,
|
||||
ccl_addr_space PathState *state,
|
||||
Ray *ray,
|
||||
ShaderData *sd,
|
||||
float3 *throughput)
|
||||
float3 *throughput,
|
||||
const float object_step_size)
|
||||
{
|
||||
float3 tp = *throughput;
|
||||
const float tp_eps = 1e-6f; /* todo: this is likely not the right value */
|
||||
@ -207,7 +219,7 @@ ccl_device void kernel_volume_shadow_heterogeneous(KernelGlobals *kg,
|
||||
/* prepare for stepping */
|
||||
int max_steps = kernel_data.integrator.volume_max_steps;
|
||||
float step_offset, step_size;
|
||||
kernel_volume_step_init(kg, state, ray->t, &step_size, &step_offset);
|
||||
kernel_volume_step_init(kg, state, object_step_size, ray->t, &step_size, &step_offset);
|
||||
|
||||
/* compute extinction at the start */
|
||||
float t = 0.0f;
|
||||
@ -264,8 +276,9 @@ ccl_device_noinline void kernel_volume_shadow(KernelGlobals *kg,
|
||||
{
|
||||
shader_setup_from_volume(kg, shadow_sd, ray);
|
||||
|
||||
if (volume_stack_is_heterogeneous(kg, state->volume_stack))
|
||||
kernel_volume_shadow_heterogeneous(kg, state, ray, shadow_sd, throughput);
|
||||
float step_size = volume_stack_step_size(kg, state->volume_stack);
|
||||
if (step_size != FLT_MAX)
|
||||
kernel_volume_shadow_heterogeneous(kg, state, ray, shadow_sd, throughput, step_size);
|
||||
else
|
||||
kernel_volume_shadow_homogeneous(kg, state, ray, shadow_sd, throughput);
|
||||
}
|
||||
@ -533,7 +546,8 @@ kernel_volume_integrate_heterogeneous_distance(KernelGlobals *kg,
|
||||
Ray *ray,
|
||||
ShaderData *sd,
|
||||
PathRadiance *L,
|
||||
ccl_addr_space float3 *throughput)
|
||||
ccl_addr_space float3 *throughput,
|
||||
const float object_step_size)
|
||||
{
|
||||
float3 tp = *throughput;
|
||||
const float tp_eps = 1e-6f; /* todo: this is likely not the right value */
|
||||
@ -541,7 +555,7 @@ kernel_volume_integrate_heterogeneous_distance(KernelGlobals *kg,
|
||||
/* prepare for stepping */
|
||||
int max_steps = kernel_data.integrator.volume_max_steps;
|
||||
float step_offset, step_size;
|
||||
kernel_volume_step_init(kg, state, ray->t, &step_size, &step_offset);
|
||||
kernel_volume_step_init(kg, state, object_step_size, ray->t, &step_size, &step_offset);
|
||||
|
||||
/* compute coefficients at the start */
|
||||
float t = 0.0f;
|
||||
@ -679,12 +693,13 @@ kernel_volume_integrate(KernelGlobals *kg,
|
||||
Ray *ray,
|
||||
PathRadiance *L,
|
||||
ccl_addr_space float3 *throughput,
|
||||
bool heterogeneous)
|
||||
float step_size)
|
||||
{
|
||||
shader_setup_from_volume(kg, sd, ray);
|
||||
|
||||
if (heterogeneous)
|
||||
return kernel_volume_integrate_heterogeneous_distance(kg, state, ray, sd, L, throughput);
|
||||
if (step_size != FLT_MAX)
|
||||
return kernel_volume_integrate_heterogeneous_distance(
|
||||
kg, state, ray, sd, L, throughput, step_size);
|
||||
else
|
||||
return kernel_volume_integrate_homogeneous(kg, state, ray, sd, L, throughput, true);
|
||||
}
|
||||
@ -735,7 +750,7 @@ ccl_device void kernel_volume_decoupled_record(KernelGlobals *kg,
|
||||
Ray *ray,
|
||||
ShaderData *sd,
|
||||
VolumeSegment *segment,
|
||||
bool heterogeneous)
|
||||
const float object_step_size)
|
||||
{
|
||||
const float tp_eps = 1e-6f; /* todo: this is likely not the right value */
|
||||
|
||||
@ -743,9 +758,9 @@ ccl_device void kernel_volume_decoupled_record(KernelGlobals *kg,
|
||||
int max_steps;
|
||||
float step_size, step_offset;
|
||||
|
||||
if (heterogeneous) {
|
||||
if (object_step_size != FLT_MAX) {
|
||||
max_steps = kernel_data.integrator.volume_max_steps;
|
||||
kernel_volume_step_init(kg, state, ray->t, &step_size, &step_offset);
|
||||
kernel_volume_step_init(kg, state, object_step_size, ray->t, &step_size, &step_offset);
|
||||
|
||||
# ifdef __KERNEL_CPU__
|
||||
/* NOTE: For the branched path tracing it's possible to have direct
|
||||
|
@ -44,7 +44,7 @@ ccl_device_noinline bool kernel_split_branched_path_volume_indirect_light_iter(K
|
||||
branched_state->isect.t :
|
||||
FLT_MAX;
|
||||
|
||||
bool heterogeneous = volume_stack_is_heterogeneous(kg, branched_state->path_state.volume_stack);
|
||||
float step_size = volume_stack_step_size(kg, branched_state->path_state.volume_stack);
|
||||
|
||||
for (int j = branched_state->next_sample; j < num_samples; j++) {
|
||||
ccl_global PathState *ps = &kernel_split_state.path_state[ray_index];
|
||||
@ -61,7 +61,7 @@ ccl_device_noinline bool kernel_split_branched_path_volume_indirect_light_iter(K
|
||||
|
||||
/* integrate along volume segment with distance sampling */
|
||||
VolumeIntegrateResult result = kernel_volume_integrate(
|
||||
kg, ps, sd, &volume_ray, L, tp, heterogeneous);
|
||||
kg, ps, sd, &volume_ray, L, tp, step_size);
|
||||
|
||||
# ifdef __VOLUME_SCATTER__
|
||||
if (result == VOLUME_PATH_SCATTERED) {
|
||||
@ -164,12 +164,12 @@ ccl_device void kernel_do_volume(KernelGlobals *kg)
|
||||
if (!kernel_data.integrator.branched ||
|
||||
IS_FLAG(ray_state, ray_index, RAY_BRANCHED_INDIRECT)) {
|
||||
# endif /* __BRANCHED_PATH__ */
|
||||
bool heterogeneous = volume_stack_is_heterogeneous(kg, state->volume_stack);
|
||||
float step_size = volume_stack_step_size(kg, state->volume_stack);
|
||||
|
||||
{
|
||||
/* integrate along volume segment with distance sampling */
|
||||
VolumeIntegrateResult result = kernel_volume_integrate(
|
||||
kg, state, sd, &volume_ray, L, throughput, heterogeneous);
|
||||
kg, state, sd, &volume_ray, L, throughput, step_size);
|
||||
|
||||
# ifdef __VOLUME_SCATTER__
|
||||
if (result == VOLUME_PATH_SCATTERED) {
|
||||
|
@ -43,6 +43,8 @@ NODE_DEFINE(Background)
|
||||
SOCKET_BOOLEAN(transparent_glass, "Transparent Glass", false);
|
||||
SOCKET_FLOAT(transparent_roughness_threshold, "Transparent Roughness Threshold", 0.0f);
|
||||
|
||||
SOCKET_FLOAT(volume_step_size, "Volume Step Size", 0.1f);
|
||||
|
||||
SOCKET_NODE(shader, "Shader", &Shader::node_type);
|
||||
|
||||
return type;
|
||||
@ -91,6 +93,8 @@ void Background::device_update(Device *device, DeviceScene *dscene, Scene *scene
|
||||
else
|
||||
kbackground->volume_shader = SHADER_NONE;
|
||||
|
||||
kbackground->volume_step_size = volume_step_size * scene->integrator->volume_step_rate;
|
||||
|
||||
/* No background node, make world shader invisible to all rays, to skip evaluation in kernel. */
|
||||
if (bg_shader->graph->nodes.size() <= 1) {
|
||||
kbackground->surface_shader |= SHADER_EXCLUDE_ANY;
|
||||
|
@ -45,6 +45,8 @@ class Background : public Node {
|
||||
bool transparent_glass;
|
||||
float transparent_roughness_threshold;
|
||||
|
||||
float volume_step_size;
|
||||
|
||||
bool need_update;
|
||||
|
||||
Background();
|
||||
|
@ -50,7 +50,7 @@ NODE_DEFINE(Integrator)
|
||||
SOCKET_INT(ao_bounces, "AO Bounces", 0);
|
||||
|
||||
SOCKET_INT(volume_max_steps, "Volume Max Steps", 1024);
|
||||
SOCKET_FLOAT(volume_step_size, "Volume Step Size", 0.1f);
|
||||
SOCKET_FLOAT(volume_step_rate, "Volume Step Rate", 1.0f);
|
||||
|
||||
SOCKET_BOOLEAN(caustics_reflective, "Reflective Caustics", true);
|
||||
SOCKET_BOOLEAN(caustics_refractive, "Refractive Caustics", true);
|
||||
@ -143,7 +143,7 @@ void Integrator::device_update(Device *device, DeviceScene *dscene, Scene *scene
|
||||
}
|
||||
|
||||
kintegrator->volume_max_steps = volume_max_steps;
|
||||
kintegrator->volume_step_size = volume_step_size;
|
||||
kintegrator->volume_step_rate = volume_step_rate;
|
||||
|
||||
kintegrator->caustics_reflective = caustics_reflective;
|
||||
kintegrator->caustics_refractive = caustics_refractive;
|
||||
|
@ -45,7 +45,7 @@ class Integrator : public Node {
|
||||
int ao_bounces;
|
||||
|
||||
int volume_max_steps;
|
||||
float volume_step_size;
|
||||
float volume_step_rate;
|
||||
|
||||
bool caustics_reflective;
|
||||
bool caustics_refractive;
|
||||
|
@ -17,6 +17,7 @@
|
||||
#include "render/camera.h"
|
||||
#include "device/device.h"
|
||||
#include "render/hair.h"
|
||||
#include "render/integrator.h"
|
||||
#include "render/light.h"
|
||||
#include "render/mesh.h"
|
||||
#include "render/curves.h"
|
||||
@ -65,6 +66,7 @@ struct UpdateObjectTransformState {
|
||||
KernelObject *objects;
|
||||
Transform *object_motion_pass;
|
||||
DecomposedTransform *object_motion;
|
||||
float *object_volume_step;
|
||||
|
||||
/* Flags which will be synchronized to Integrator. */
|
||||
bool have_motion;
|
||||
@ -266,6 +268,65 @@ uint Object::visibility_for_tracing() const
|
||||
return trace_visibility;
|
||||
}
|
||||
|
||||
float Object::compute_volume_step_size() const
|
||||
{
|
||||
if (!geometry->has_volume) {
|
||||
return FLT_MAX;
|
||||
}
|
||||
|
||||
/* Compute step rate from shaders. */
|
||||
float step_rate = FLT_MAX;
|
||||
|
||||
foreach (Shader *shader, geometry->used_shaders) {
|
||||
if (shader->has_volume) {
|
||||
if ((shader->heterogeneous_volume && shader->has_volume_spatial_varying) ||
|
||||
(shader->has_volume_attribute_dependency)) {
|
||||
step_rate = fminf(shader->volume_step_rate, step_rate);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (step_rate == FLT_MAX) {
|
||||
return FLT_MAX;
|
||||
}
|
||||
|
||||
/* Compute step size from voxel grids. */
|
||||
float step_size = FLT_MAX;
|
||||
|
||||
foreach (Attribute &attr, geometry->attributes.attributes) {
|
||||
if (attr.element == ATTR_ELEMENT_VOXEL) {
|
||||
ImageHandle &handle = attr.data_voxel();
|
||||
const ImageMetaData &metadata = handle.metadata();
|
||||
if (metadata.width == 0 || metadata.height == 0 || metadata.depth == 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Step size is transformed from voxel to world space. */
|
||||
Transform voxel_tfm = tfm;
|
||||
if (metadata.use_transform_3d) {
|
||||
voxel_tfm = tfm * transform_inverse(metadata.transform_3d);
|
||||
}
|
||||
|
||||
float3 size = make_float3(
|
||||
1.0f / metadata.width, 1.0f / metadata.height, 1.0f / metadata.depth);
|
||||
float voxel_step_size = min3(fabs(transform_direction(&voxel_tfm, size)));
|
||||
|
||||
if (voxel_step_size > 0.0f) {
|
||||
step_size = fminf(voxel_step_size, step_size);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (step_size == FLT_MAX) {
|
||||
/* Fall back to 1/10th of bounds for procedural volumes. */
|
||||
step_size = 0.1f * average(bounds.size());
|
||||
}
|
||||
|
||||
step_size *= step_rate;
|
||||
|
||||
return step_size;
|
||||
}
|
||||
|
||||
int Object::get_device_index() const
|
||||
{
|
||||
return index;
|
||||
@ -451,6 +512,7 @@ void ObjectManager::device_update_object_transform(UpdateObjectTransformState *s
|
||||
flag |= SD_OBJECT_HOLDOUT_MASK;
|
||||
}
|
||||
state->object_flag[ob->index] = flag;
|
||||
state->object_volume_step[ob->index] = FLT_MAX;
|
||||
|
||||
/* Have curves. */
|
||||
if (geom->type == Geometry::HAIR) {
|
||||
@ -504,6 +566,7 @@ void ObjectManager::device_update_transforms(DeviceScene *dscene, Scene *scene,
|
||||
|
||||
state.objects = dscene->objects.alloc(scene->objects.size());
|
||||
state.object_flag = dscene->object_flag.alloc(scene->objects.size());
|
||||
state.object_volume_step = dscene->object_volume_step.alloc(scene->objects.size());
|
||||
state.object_motion = NULL;
|
||||
state.object_motion_pass = NULL;
|
||||
|
||||
@ -624,6 +687,7 @@ void ObjectManager::device_update_flags(
|
||||
|
||||
/* Object info flag. */
|
||||
uint *object_flag = dscene->object_flag.data();
|
||||
float *object_volume_step = dscene->object_volume_step.data();
|
||||
|
||||
/* Object volume intersection. */
|
||||
vector<Object *> volume_objects;
|
||||
@ -634,6 +698,10 @@ void ObjectManager::device_update_flags(
|
||||
volume_objects.push_back(object);
|
||||
}
|
||||
has_volume_objects = true;
|
||||
object_volume_step[object->index] = object->compute_volume_step_size();
|
||||
}
|
||||
else {
|
||||
object_volume_step[object->index] = FLT_MAX;
|
||||
}
|
||||
}
|
||||
|
||||
@ -651,6 +719,7 @@ void ObjectManager::device_update_flags(
|
||||
else {
|
||||
object_flag[object->index] &= ~(SD_OBJECT_HAS_VOLUME | SD_OBJECT_HAS_VOLUME_ATTRIBUTES);
|
||||
}
|
||||
|
||||
if (object->is_shadow_catcher) {
|
||||
object_flag[object->index] |= SD_OBJECT_SHADOW_CATCHER;
|
||||
}
|
||||
@ -679,6 +748,7 @@ void ObjectManager::device_update_flags(
|
||||
|
||||
/* Copy object flag. */
|
||||
dscene->object_flag.copy_to_device();
|
||||
dscene->object_volume_step.copy_to_device();
|
||||
}
|
||||
|
||||
void ObjectManager::device_update_mesh_offsets(Device *, DeviceScene *dscene, Scene *scene)
|
||||
@ -725,6 +795,7 @@ void ObjectManager::device_free(Device *, DeviceScene *dscene)
|
||||
dscene->object_motion_pass.free();
|
||||
dscene->object_motion.free();
|
||||
dscene->object_flag.free();
|
||||
dscene->object_volume_step.free();
|
||||
}
|
||||
|
||||
void ObjectManager::apply_static_transforms(DeviceScene *dscene, Scene *scene, Progress &progress)
|
||||
|
@ -97,6 +97,9 @@ class Object : public Node {
|
||||
/* Returns the index that is used in the kernel for this object. */
|
||||
int get_device_index() const;
|
||||
|
||||
/* Compute step size from attributes, shaders, transforms. */
|
||||
float compute_volume_step_size() const;
|
||||
|
||||
protected:
|
||||
/* Specifies the position of the object in scene->objects and
|
||||
* in the device vectors. Gets set in device_update. */
|
||||
|
@ -760,16 +760,14 @@ void OSLCompiler::add(ShaderNode *node, const char *name, bool isfilepath)
|
||||
else if (current_type == SHADER_TYPE_VOLUME) {
|
||||
if (node->has_spatial_varying())
|
||||
current_shader->has_volume_spatial_varying = true;
|
||||
if (node->has_attribute_dependency())
|
||||
current_shader->has_volume_attribute_dependency = true;
|
||||
}
|
||||
|
||||
if (node->has_object_dependency()) {
|
||||
current_shader->has_object_dependency = true;
|
||||
}
|
||||
|
||||
if (node->has_attribute_dependency()) {
|
||||
current_shader->has_attribute_dependency = true;
|
||||
}
|
||||
|
||||
if (node->has_integrator_dependency()) {
|
||||
current_shader->has_integrator_dependency = true;
|
||||
}
|
||||
@ -1143,8 +1141,8 @@ void OSLCompiler::compile(OSLGlobals *og, Shader *shader)
|
||||
shader->has_displacement = false;
|
||||
shader->has_surface_spatial_varying = false;
|
||||
shader->has_volume_spatial_varying = false;
|
||||
shader->has_volume_attribute_dependency = false;
|
||||
shader->has_object_dependency = false;
|
||||
shader->has_attribute_dependency = false;
|
||||
shader->has_integrator_dependency = false;
|
||||
|
||||
/* generate surface shader */
|
||||
|
@ -63,6 +63,7 @@ DeviceScene::DeviceScene(Device *device)
|
||||
object_motion_pass(device, "__object_motion_pass", MEM_GLOBAL),
|
||||
object_motion(device, "__object_motion", MEM_GLOBAL),
|
||||
object_flag(device, "__object_flag", MEM_GLOBAL),
|
||||
object_volume_step(device, "__object_volume_step", MEM_GLOBAL),
|
||||
camera_motion(device, "__camera_motion", MEM_GLOBAL),
|
||||
attributes_map(device, "__attributes_map", MEM_GLOBAL),
|
||||
attributes_float(device, "__attributes_float", MEM_GLOBAL),
|
||||
|
@ -91,6 +91,7 @@ class DeviceScene {
|
||||
device_vector<Transform> object_motion_pass;
|
||||
device_vector<DecomposedTransform> object_motion;
|
||||
device_vector<uint> object_flag;
|
||||
device_vector<float> object_volume_step;
|
||||
|
||||
/* cameras */
|
||||
device_vector<DecomposedTransform> camera_motion;
|
||||
|
@ -178,6 +178,8 @@ NODE_DEFINE(Shader)
|
||||
volume_interpolation_method_enum,
|
||||
VOLUME_INTERPOLATION_LINEAR);
|
||||
|
||||
SOCKET_FLOAT(volume_step_rate, "Volume Step Rate", 1.0f);
|
||||
|
||||
static NodeEnum displacement_method_enum;
|
||||
displacement_method_enum.insert("bump", DISPLACE_BUMP);
|
||||
displacement_method_enum.insert("true", DISPLACE_TRUE);
|
||||
@ -203,10 +205,11 @@ Shader::Shader() : Node(node_type)
|
||||
has_bssrdf_bump = false;
|
||||
has_surface_spatial_varying = false;
|
||||
has_volume_spatial_varying = false;
|
||||
has_volume_attribute_dependency = false;
|
||||
has_object_dependency = false;
|
||||
has_attribute_dependency = false;
|
||||
has_integrator_dependency = false;
|
||||
has_volume_connected = false;
|
||||
prev_volume_step_rate = 0.0f;
|
||||
|
||||
displacement_method = DISPLACE_BUMP;
|
||||
|
||||
@ -353,9 +356,10 @@ void Shader::tag_update(Scene *scene)
|
||||
scene->geometry_manager->need_update = true;
|
||||
}
|
||||
|
||||
if (has_volume != prev_has_volume) {
|
||||
if (has_volume != prev_has_volume || volume_step_rate != prev_volume_step_rate) {
|
||||
scene->geometry_manager->need_flags_update = true;
|
||||
scene->object_manager->need_flags_update = true;
|
||||
prev_volume_step_rate = volume_step_rate;
|
||||
}
|
||||
}
|
||||
|
||||
@ -533,10 +537,12 @@ void ShaderManager::device_update_common(Device *device,
|
||||
/* in this case we can assume transparent surface */
|
||||
if (shader->has_volume_connected && !shader->has_surface)
|
||||
flag |= SD_HAS_ONLY_VOLUME;
|
||||
if (shader->heterogeneous_volume && shader->has_volume_spatial_varying)
|
||||
flag |= SD_HETEROGENEOUS_VOLUME;
|
||||
if (shader->has_attribute_dependency)
|
||||
flag |= SD_NEED_ATTRIBUTES;
|
||||
if (shader->has_volume) {
|
||||
if (shader->heterogeneous_volume && shader->has_volume_spatial_varying)
|
||||
flag |= SD_HETEROGENEOUS_VOLUME;
|
||||
}
|
||||
if (shader->has_volume_attribute_dependency)
|
||||
flag |= SD_NEED_VOLUME_ATTRIBUTES;
|
||||
if (shader->has_bssrdf_bump)
|
||||
flag |= SD_HAS_BSSRDF_BUMP;
|
||||
if (device->info.has_volume_decoupled) {
|
||||
|
@ -92,6 +92,8 @@ class Shader : public Node {
|
||||
bool heterogeneous_volume;
|
||||
VolumeSampling volume_sampling_method;
|
||||
int volume_interpolation_method;
|
||||
float volume_step_rate;
|
||||
float prev_volume_step_rate;
|
||||
|
||||
/* synchronization */
|
||||
bool need_update;
|
||||
@ -118,8 +120,8 @@ class Shader : public Node {
|
||||
bool has_bssrdf_bump;
|
||||
bool has_surface_spatial_varying;
|
||||
bool has_volume_spatial_varying;
|
||||
bool has_volume_attribute_dependency;
|
||||
bool has_object_dependency;
|
||||
bool has_attribute_dependency;
|
||||
bool has_integrator_dependency;
|
||||
|
||||
/* displacement */
|
||||
|
@ -444,16 +444,14 @@ void SVMCompiler::generate_node(ShaderNode *node, ShaderNodeSet &done)
|
||||
else if (current_type == SHADER_TYPE_VOLUME) {
|
||||
if (node->has_spatial_varying())
|
||||
current_shader->has_volume_spatial_varying = true;
|
||||
if (node->has_attribute_dependency())
|
||||
current_shader->has_volume_attribute_dependency = true;
|
||||
}
|
||||
|
||||
if (node->has_object_dependency()) {
|
||||
current_shader->has_object_dependency = true;
|
||||
}
|
||||
|
||||
if (node->has_attribute_dependency()) {
|
||||
current_shader->has_attribute_dependency = true;
|
||||
}
|
||||
|
||||
if (node->has_integrator_dependency()) {
|
||||
current_shader->has_integrator_dependency = true;
|
||||
}
|
||||
@ -864,8 +862,8 @@ void SVMCompiler::compile(Shader *shader, array<int4> &svm_nodes, int index, Sum
|
||||
shader->has_displacement = false;
|
||||
shader->has_surface_spatial_varying = false;
|
||||
shader->has_volume_spatial_varying = false;
|
||||
shader->has_volume_attribute_dependency = false;
|
||||
shader->has_object_dependency = false;
|
||||
shader->has_attribute_dependency = false;
|
||||
shader->has_integrator_dependency = false;
|
||||
|
||||
/* generate bump shader */
|
||||
|
Loading…
Reference in New Issue
Block a user