diff options
Diffstat (limited to 'intern/cycles')
35 files changed, 549 insertions, 278 deletions
diff --git a/intern/cycles/app/io_export_cycles_xml.py b/intern/cycles/app/io_export_cycles_xml.py index 0009995653e..ceb30f8fb56 100644 --- a/intern/cycles/app/io_export_cycles_xml.py +++ b/intern/cycles/app/io_export_cycles_xml.py @@ -10,6 +10,7 @@ import bpy from bpy_extras.io_utils import ExportHelper from bpy.props import PointerProperty, StringProperty + def strip(root): root.text = None root.tail = None @@ -17,6 +18,7 @@ def strip(root): for elem in root: strip(elem) + def write(node, fname): strip(node) @@ -26,25 +28,31 @@ def write(node, fname): f = open(fname, "w") f.write(s) + class CyclesXMLSettings(bpy.types.PropertyGroup): @classmethod def register(cls): bpy.types.Scene.cycles_xml = PointerProperty( - type=cls, - name="Cycles XML export Settings", - description="Cycles XML export settings") + type=cls, + name="Cycles XML export Settings", + description="Cycles XML export settings", + ) cls.filepath = StringProperty( - name='Filepath', - description='Filepath for the .xml file', - maxlen=256, - default='', - subtype='FILE_PATH') + name='Filepath', + description='Filepath for the .xml file', + maxlen=256, + default='', + subtype='FILE_PATH', + ) @classmethod def unregister(cls): del bpy.types.Scene.cycles_xml -# User Interface Drawing Code + +# User Interface Drawing Code. + + class RenderButtonsPanel(): bl_space_type = 'PROPERTIES' bl_region_type = 'WINDOW' @@ -114,22 +122,31 @@ class ExportCyclesXML(bpy.types.Operator, ExportHelper): uvs += str(uvf.uv1[0]) + " " + str(uvf.uv1[1]) + " " uvs += str(uvf.uv2[0]) + " " + str(uvf.uv2[1]) + " " uvs += str(uvf.uv3[0]) + " " + str(uvf.uv3[1]) + " " - if vcount==4: + if vcount == 4: uvs += " " + str(uvf.uv4[0]) + " " + str(uvf.uv4[1]) + " " - - node = etree.Element('mesh', attrib={'nverts': nverts.strip(), 'verts': verts.strip(), 'P': P, 'UV' : uvs.strip()}) + node = etree.Element( + 'mesh', + attrib={ + 'nverts': nverts.strip(), + 'verts': verts.strip(), + 'P': P, + 'UV': uvs.strip(), + }) # write to file write(node, filepath) return {'FINISHED'} + def register(): bpy.utils.register_module(__name__) + def unregister(): bpy.utils.unregister_module(__name__) + if __name__ == "__main__": register() diff --git a/intern/cycles/blender/addon/camera.py b/intern/cycles/blender/addon/camera.py index 9841e031201..0e78112699e 100644 --- a/intern/cycles/blender/addon/camera.py +++ b/intern/cycles/blender/addon/camera.py @@ -4,11 +4,14 @@ # <pep8 compliant> # Fit to match default projective camera with focal_length 50 and sensor_width 36. -default_fisheye_polynomial = [-1.1735143712967577e-05, - -0.019988736953434998, - -3.3525322965709175e-06, - 3.099275275886036e-06, - -2.6064646454854524e-08] +default_fisheye_polynomial = [ + -1.1735143712967577e-05, + -0.019988736953434998, + -3.3525322965709175e-06, + 3.099275275886036e-06, + -2.6064646454854524e-08, +] + # Utilities to generate lens polynomials to match built-in camera types, only here # for reference at the moment, not used by the code. @@ -51,7 +54,9 @@ def fisheye_lens_polynomial_from_equidistant(fov=180, sensor_width=36, sensor_he return [0, -np.radians(fov) / sensor_width, 0, 0, 0] -def fisheye_lens_polynomial_from_distorted_projective_polynomial(k1, k2, k3, focal_length=50, sensor_width=36, sensor_height=None): +def fisheye_lens_polynomial_from_distorted_projective_polynomial( + k1, k2, k3, focal_length=50, sensor_width=36, sensor_height=None, +): import numpy as np rr = create_grid(sensor_height, sensor_width) r2 = (rr / focal_length) ** 2 @@ -61,7 +66,10 @@ def fisheye_lens_polynomial_from_distorted_projective_polynomial(k1, k2, k3, foc polynomial = np.polyfit(rr.flat, (-np.arctan(rr / focal_length * r_coeff)).flat, 4) return list(reversed(polynomial)) -def fisheye_lens_polynomial_from_distorted_projective_divisions(k1, k2, focal_length=50, sensor_width=36, sensor_height=None): + +def fisheye_lens_polynomial_from_distorted_projective_divisions( + k1, k2, focal_length=50, sensor_width=36, sensor_height=None, +): import numpy as np rr = create_grid(sensor_height, sensor_width) r2 = (rr / focal_length) ** 2 diff --git a/intern/cycles/blender/addon/engine.py b/intern/cycles/blender/addon/engine.py index 1b03581ae03..724e1b8f727 100644 --- a/intern/cycles/blender/addon/engine.py +++ b/intern/cycles/blender/addon/engine.py @@ -98,6 +98,7 @@ def render_frame_finish(engine): import _cycles _cycles.render_frame_finish(engine.session) + def draw(engine, depsgraph, space_image): if not engine.session: return @@ -168,6 +169,9 @@ def list_render_passes(scene, srl): # Combined pass. yield ("Combined", "RGBA", 'COLOR') + # Keep alignment for readability. + # autopep8: off + # Data passes. if srl.use_pass_z: yield ("Depth", "Z", 'VALUE') if srl.use_pass_mist: yield ("Mist", "Z", 'VALUE') @@ -195,9 +199,11 @@ def list_render_passes(scene, srl): if srl.use_pass_shadow: yield ("Shadow", "RGB", 'COLOR') if srl.use_pass_ambient_occlusion: yield ("AO", "RGB", 'COLOR') if crl.use_pass_shadow_catcher: yield ("Shadow Catcher", "RGB", 'COLOR') + # autopep8: on # Debug passes. - if crl.pass_debug_sample_count: yield ("Debug Sample Count", "X", 'VALUE') + if crl.pass_debug_sample_count: + yield ("Debug Sample Count", "X", 'VALUE') # Cryptomatte passes. crypto_depth = (srl.pass_cryptomatte_depth + 1) // 2 @@ -217,9 +223,9 @@ def list_render_passes(scene, srl): if crl.use_pass_shadow_catcher: yield ("Noisy Shadow Catcher", "RGBA", 'COLOR') if crl.denoising_store_passes: - yield ("Denoising Normal", "XYZ", 'VECTOR') - yield ("Denoising Albedo", "RGB", 'COLOR') - yield ("Denoising Depth", "Z", 'VALUE') + yield ("Denoising Normal", "XYZ", 'VECTOR') + yield ("Denoising Albedo", "RGB", 'COLOR') + yield ("Denoising Depth", "Z", 'VALUE') # Custom AOV passes. for aov in srl.aovs: diff --git a/intern/cycles/blender/addon/operators.py b/intern/cycles/blender/addon/operators.py index 973088ac3e7..e5d7f00a381 100644 --- a/intern/cycles/blender/addon/operators.py +++ b/intern/cycles/blender/addon/operators.py @@ -34,8 +34,8 @@ class CYCLES_OT_use_shading_nodes(Operator): class CYCLES_OT_denoise_animation(Operator): "Denoise rendered animation sequence using current scene and view " \ - "layer settings. Requires denoising data passes and output to " \ - "OpenEXR multilayer files" + "layer settings. Requires denoising data passes and output to " \ + "OpenEXR multilayer files" bl_idname = "cycles.denoise_animation" bl_label = "Denoise Animation" @@ -117,7 +117,7 @@ class CYCLES_OT_denoise_animation(Operator): class CYCLES_OT_merge_images(Operator): "Combine OpenEXR multilayer images rendered with different sample " \ - "ranges into one image with reduced noise" + "ranges into one image with reduced noise" bl_idname = "cycles.merge_images" bl_label = "Merge Images" diff --git a/intern/cycles/blender/addon/presets.py b/intern/cycles/blender/addon/presets.py index a84c9f07c55..5eaa592a9de 100644 --- a/intern/cycles/blender/addon/presets.py +++ b/intern/cycles/blender/addon/presets.py @@ -85,6 +85,7 @@ class AddPresetViewportSampling(AddPresetBase, Operator): preset_subdir = "cycles/viewport_sampling" + classes = ( AddPresetIntegrator, AddPresetSampling, diff --git a/intern/cycles/blender/addon/properties.py b/intern/cycles/blender/addon/properties.py index ed054b041d8..a0ceb9c3c29 100644 --- a/intern/cycles/blender/addon/properties.py +++ b/intern/cycles/blender/addon/properties.py @@ -83,7 +83,8 @@ enum_sampling_pattern = ( enum_volume_sampling = ( ('DISTANCE', "Distance", "Use distance sampling, best for dense volumes with lights far away"), ('EQUIANGULAR', "Equiangular", "Use equiangular sampling, best for volumes with low density with light inside or near the volume"), - ('MULTIPLE_IMPORTANCE', "Multiple Importance", "Combine distance and equi-angular sampling for volumes where neither method is ideal"), + ('MULTIPLE_IMPORTANCE', "Multiple Importance", + "Combine distance and equi-angular sampling for volumes where neither method is ideal"), ) enum_volume_interpolation = ( @@ -181,7 +182,12 @@ def enum_preview_denoiser(self, context): oidn_items = enum_openimagedenoise_denoiser(self, context) if len(optix_items) or len(oidn_items): - items = [('AUTO', "Automatic", "Use the fastest available denoiser for viewport rendering (OptiX if available, OpenImageDenoise otherwise)", 0)] + items = [ + ('AUTO', + "Automatic", + ("Use the fastest available denoiser for viewport rendering " + "(OptiX if available, OpenImageDenoise otherwise)"), + 0)] else: items = [('AUTO', "None", "Blender was compiled without a viewport denoiser", 0)] @@ -210,11 +216,14 @@ enum_denoising_prefilter = ( ) enum_direct_light_sampling_type = ( - ('MULTIPLE_IMPORTANCE_SAMPLING', "Multiple Importance Sampling", "Multiple importance sampling is used to combine direct light contributions from next-event estimation and forward path tracing", 0), + ('MULTIPLE_IMPORTANCE_SAMPLING', "Multiple Importance Sampling", + "Multiple importance sampling is used to combine direct light contributions from next-event estimation and forward path tracing", 0), ('FORWARD_PATH_TRACING', "Forward Path Tracing", "Direct light contributions are only sampled using forward path tracing", 1), - ('NEXT_EVENT_ESTIMATION', "Next-Event Estimation", "Direct light contributions are only sampled using next-event estimation", 2), + ('NEXT_EVENT_ESTIMATION', "Next-Event Estimation", + "Direct light contributions are only sampled using next-event estimation", 2), ) + def update_render_passes(self, context): view_layer = context.view_layer view_layer.update_render_passes() @@ -262,7 +271,7 @@ class CyclesRenderSettings(bpy.types.PropertyGroup): description="Denoise the image with the selected denoiser. " "For denoising the image after rendering", items=enum_denoiser, - default=4, # Use integer to avoid error in builds without OpenImageDenoise. + default=4, # Use integer to avoid error in builds without OpenImageDenoise. update=update_render_passes, ) denoising_prefilter: EnumProperty( @@ -1507,9 +1516,12 @@ class CyclesPreferences(bpy.types.AddonPreferences): col.label(text="and NVIDIA driver version 470 or newer", icon='BLANK1') elif device_type == 'HIP': import sys - col.label(text="Requires discrete AMD GPU with RDNA architecture", icon='BLANK1') if sys.platform[:3] == "win": + col.label(text="Requires discrete AMD GPU with RDNA architecture", icon='BLANK1') col.label(text="and AMD Radeon Pro 21.Q4 driver or newer", icon='BLANK1') + elif sys.platform.startswith("linux"): + col.label(text="Requires discrete AMD GPU with RDNA architecture", icon='BLANK1') + col.label(text="and AMD driver version 22.10 or newer", icon='BLANK1') elif device_type == 'METAL': col.label(text="Requires Apple Silicon with macOS 12.2 or newer", icon='BLANK1') col.label(text="or AMD with macOS 12.3 or newer", icon='BLANK1') @@ -1547,7 +1559,6 @@ class CyclesPreferences(bpy.types.AddonPreferences): row.use_property_split = True row.prop(self, "use_metalrt") - def draw(self, context): self.draw_impl(self.layout, context) diff --git a/intern/cycles/blender/addon/ui.py b/intern/cycles/blender/addon/ui.py index 739a555f037..131b849a094 100644 --- a/intern/cycles/blender/addon/ui.py +++ b/intern/cycles/blender/addon/ui.py @@ -14,6 +14,7 @@ from bl_ui.properties_grease_pencil_common import GreasePencilSimplifyPanel from bl_ui.properties_render import draw_curves_settings from bl_ui.properties_view_layer import ViewLayerCryptomattePanel, ViewLayerAOVPanel, ViewLayerLightgroupsPanel + class CyclesPresetPanel(PresetPanel, Panel): COMPAT_ENGINES = {'CYCLES'} preset_operator = "script.execute_preset" @@ -25,16 +26,19 @@ class CyclesPresetPanel(PresetPanel, Panel): render = context.scene.render render.filter_size = render.filter_size + class CYCLES_PT_sampling_presets(CyclesPresetPanel): bl_label = "Sampling Presets" preset_subdir = "cycles/sampling" preset_add_operator = "render.cycles_sampling_preset_add" + class CYCLES_PT_viewport_sampling_presets(CyclesPresetPanel): bl_label = "Viewport Sampling Presets" preset_subdir = "cycles/viewport_sampling" preset_add_operator = "render.cycles_viewport_sampling_preset_add" + class CYCLES_PT_integrator_presets(CyclesPresetPanel): bl_label = "Integrator Presets" preset_subdir = "cycles/integrator" @@ -90,6 +94,7 @@ def use_metal(context): return (get_device_type(context) == 'METAL' and cscene.device == 'GPU') + def use_cuda(context): cscene = context.scene.cycles @@ -101,11 +106,13 @@ def use_hip(context): return (get_device_type(context) == 'HIP' and cscene.device == 'GPU') + def use_optix(context): cscene = context.scene.cycles return (get_device_type(context) == 'OPTIX' and cscene.device == 'GPU') + def use_multi_device(context): cscene = context.scene.cycles if cscene.device != 'GPU': @@ -133,7 +140,6 @@ def get_effective_preview_denoiser(context): return 'OIDN' - class CYCLES_RENDER_PT_sampling(CyclesButtonsPanel, Panel): bl_label = "Sampling" @@ -353,6 +359,7 @@ class CYCLES_RENDER_PT_curves(CyclesButtonsPanel, Panel): if ccscene.shape == 'RIBBONS': col.prop(ccscene, "subdivisions", text="Curve Subdivisions") + class CYCLES_RENDER_PT_curves_viewport_display(CyclesButtonsPanel, Panel): bl_label = "Viewport Display" bl_parent_id = "CYCLES_RENDER_PT_curves" @@ -361,6 +368,7 @@ class CYCLES_RENDER_PT_curves_viewport_display(CyclesButtonsPanel, Panel): def draw(self, context): draw_curves_settings(self, context) + class CYCLES_RENDER_PT_volumes(CyclesButtonsPanel, Panel): bl_label = "Volumes" bl_options = {'DEFAULT_CLOSED'} @@ -478,10 +486,10 @@ class CYCLES_RENDER_PT_light_paths_fast_gi(CyclesButtonsPanel, Panel): col.prop(cscene, "fast_gi_method", text="Method") if world: - light = world.light_settings - col = layout.column(align=True) - col.prop(light, "ao_factor", text="AO Factor") - col.prop(light, "distance", text="AO Distance") + light = world.light_settings + col = layout.column(align=True) + col.prop(light, "ao_factor", text="AO Factor") + col.prop(light, "distance", text="AO Distance") if cscene.fast_gi_method == 'REPLACE': col = layout.column(align=True) @@ -1034,7 +1042,8 @@ class CYCLES_OBJECT_PT_motion_blur(CyclesButtonsPanel, Panel): def poll(cls, context): ob = context.object if CyclesButtonsPanel.poll(context) and ob: - if ob.type in {'MESH', 'CURVE', 'CURVE', 'SURFACE', 'FONT', 'META', 'CAMERA', 'CURVES', 'POINTCLOUD'}: + if ob.type in {'MESH', 'CURVE', 'CURVE', 'SURFACE', 'FONT', + 'META', 'CAMERA', 'CURVES', 'POINTCLOUD', 'VOLUME'}: return True if ob.instance_type == 'COLLECTION' and ob.instance_collection: return True @@ -1446,7 +1455,14 @@ class CYCLES_WORLD_PT_surface(CyclesButtonsPanel, Panel): row.use_property_decorate = False sub = row.column(align=True) - sub.prop_search(world, "lightgroup", view_layer, "lightgroups", text="Light Group", results_are_suggestions=True) + sub.prop_search( + world, + "lightgroup", + view_layer, + "lightgroups", + text="Light Group", + results_are_suggestions=True, + ) sub = row.column(align=True) sub.active = bool(world.lightgroup) and not any(lg.name == world.lightgroup for lg in view_layer.lightgroups) @@ -1572,7 +1588,6 @@ class CYCLES_WORLD_PT_settings_surface(CyclesButtonsPanel, Panel): sub.prop(cworld, "is_caustics_light", text="Shadow Caustics") - class CYCLES_WORLD_PT_settings_volume(CyclesButtonsPanel, Panel): bl_label = "Volume" bl_parent_id = "CYCLES_WORLD_PT_settings" @@ -1894,6 +1909,7 @@ class CYCLES_RENDER_PT_bake_output(CyclesButtonsPanel, Panel): if cbk.target == 'IMAGE_TEXTURES': layout.prop(cbk, "use_clear", text="Clear Image") + class CYCLES_RENDER_PT_bake_output_margin(CyclesButtonsPanel, Panel): bl_label = "Margin" bl_context = "render" @@ -1932,7 +1948,6 @@ class CYCLES_RENDER_PT_bake_output_margin(CyclesButtonsPanel, Panel): layout.prop(cbk, "margin", text="Size") - class CYCLES_RENDER_PT_debug(CyclesDebugButtonsPanel, Panel): bl_label = "Debug" bl_context = "render" diff --git a/intern/cycles/blender/addon/version_update.py b/intern/cycles/blender/addon/version_update.py index aade864e4c7..651613b0407 100644 --- a/intern/cycles/blender/addon/version_update.py +++ b/intern/cycles/blender/addon/version_update.py @@ -72,7 +72,7 @@ def do_versions(self): # Device might not currently be available so this can fail try: if system.legacy_compute_device_type == 1: - prop.compute_device_type = 'NONE' # Was OpenCL + prop.compute_device_type = 'NONE' # Was OpenCL elif system.legacy_compute_device_type == 2: prop.compute_device_type = 'CUDA' else: @@ -181,24 +181,24 @@ def do_versions(self): if version <= (2, 92, 4): if scene.render.engine == 'CYCLES': - for view_layer in scene.view_layers: - cview_layer = view_layer.cycles - view_layer.use_pass_cryptomatte_object = cview_layer.get("use_pass_crypto_object", False) - view_layer.use_pass_cryptomatte_material = cview_layer.get("use_pass_crypto_material", False) - view_layer.use_pass_cryptomatte_asset = cview_layer.get("use_pass_crypto_asset", False) - view_layer.pass_cryptomatte_depth = cview_layer.get("pass_crypto_depth", 6) + for view_layer in scene.view_layers: + cview_layer = view_layer.cycles + view_layer.use_pass_cryptomatte_object = cview_layer.get("use_pass_crypto_object", False) + view_layer.use_pass_cryptomatte_material = cview_layer.get("use_pass_crypto_material", False) + view_layer.use_pass_cryptomatte_asset = cview_layer.get("use_pass_crypto_asset", False) + view_layer.pass_cryptomatte_depth = cview_layer.get("pass_crypto_depth", 6) if version <= (2, 93, 7): if scene.render.engine == 'CYCLES': - for view_layer in scene.view_layers: - cview_layer = view_layer.cycles - for caov in cview_layer.get("aovs", []): - aov_name = caov.get("name", "AOV") - if aov_name in view_layer.aovs: - continue - baov = view_layer.aovs.add() - baov.name = caov.get("name", "AOV") - baov.type = "COLOR" if caov.get("type", 1) == 1 else "VALUE" + for view_layer in scene.view_layers: + cview_layer = view_layer.cycles + for caov in cview_layer.get("aovs", []): + aov_name = caov.get("name", "AOV") + if aov_name in view_layer.aovs: + continue + baov = view_layer.aovs.add() + baov.name = caov.get("name", "AOV") + baov.type = "COLOR" if caov.get("type", 1) == 1 else "VALUE" if version <= (2, 93, 16): cscene = scene.cycles diff --git a/intern/cycles/blender/camera.cpp b/intern/cycles/blender/camera.cpp index cdd19341534..402fd7c4ec6 100644 --- a/intern/cycles/blender/camera.cpp +++ b/intern/cycles/blender/camera.cpp @@ -23,7 +23,7 @@ struct BlenderCamera { float lens; float shuttertime; - Camera::MotionPosition motion_position; + MotionPosition motion_position; array<float> shutter_curve; Camera::RollingShutterType rolling_shutter_type; @@ -114,7 +114,7 @@ static void blender_camera_init(BlenderCamera *bcam, BL::RenderSettings &b_rende bcam->sensor_width = 36.0f; bcam->sensor_height = 24.0f; bcam->sensor_fit = BlenderCamera::AUTO; - bcam->motion_position = Camera::MOTION_POSITION_CENTER; + bcam->motion_position = MOTION_POSITION_CENTER; bcam->border.right = 1.0f; bcam->border.top = 1.0f; bcam->viewport_camera_border.right = 1.0f; @@ -555,10 +555,8 @@ void BlenderSync::sync_camera(BL::RenderSettings &b_render, curvemapping_to_array(b_shutter_curve, bcam.shutter_curve, RAMP_TABLE_SIZE); PointerRNA cscene = RNA_pointer_get(&b_scene.ptr, "cycles"); - bcam.motion_position = (Camera::MotionPosition)get_enum(cscene, - "motion_blur_position", - Camera::MOTION_NUM_POSITIONS, - Camera::MOTION_POSITION_CENTER); + bcam.motion_position = (MotionPosition)get_enum( + cscene, "motion_blur_position", MOTION_NUM_POSITIONS, MOTION_POSITION_CENTER); bcam.rolling_shutter_type = (Camera::RollingShutterType)get_enum( cscene, "rolling_shutter_type", diff --git a/intern/cycles/blender/object.cpp b/intern/cycles/blender/object.cpp index f77cbdf847d..9b08b564b25 100644 --- a/intern/cycles/blender/object.cpp +++ b/intern/cycles/blender/object.cpp @@ -16,6 +16,7 @@ #include "scene/shader.h" #include "scene/shader_graph.h" #include "scene/shader_nodes.h" +#include "scene/volume.h" #include "util/foreach.h" #include "util/hash.h" @@ -715,13 +716,13 @@ void BlenderSync::sync_motion(BL::RenderSettings &b_render, float frame_center_delta = 0.0f; if (scene->need_motion() != Scene::MOTION_PASS && - scene->camera->get_motion_position() != Camera::MOTION_POSITION_CENTER) { + scene->camera->get_motion_position() != MOTION_POSITION_CENTER) { float shuttertime = scene->camera->get_shuttertime(); - if (scene->camera->get_motion_position() == Camera::MOTION_POSITION_END) { + if (scene->camera->get_motion_position() == MOTION_POSITION_END) { frame_center_delta = -shuttertime * 0.5f; } else { - assert(scene->camera->get_motion_position() == Camera::MOTION_POSITION_START); + assert(scene->camera->get_motion_position() == MOTION_POSITION_START); frame_center_delta = shuttertime * 0.5f; } diff --git a/intern/cycles/blender/sync.cpp b/intern/cycles/blender/sync.cpp index bd6bfafedeb..1028c940772 100644 --- a/intern/cycles/blender/sync.cpp +++ b/intern/cycles/blender/sync.cpp @@ -272,7 +272,7 @@ void BlenderSync::sync_data(BL::RenderSettings &b_render, geometry_synced.clear(); /* use for objects and motion sync */ if (scene->need_motion() == Scene::MOTION_PASS || scene->need_motion() == Scene::MOTION_NONE || - scene->camera->get_motion_position() == Camera::MOTION_POSITION_CENTER) { + scene->camera->get_motion_position() == MOTION_POSITION_CENTER) { sync_objects(b_depsgraph, b_v3d); } sync_motion(b_render, b_depsgraph, b_v3d, b_override, width, height, python_thread_state); diff --git a/intern/cycles/blender/volume.cpp b/intern/cycles/blender/volume.cpp index 381b3385a5a..8dd2d45c0b6 100644 --- a/intern/cycles/blender/volume.cpp +++ b/intern/cycles/blender/volume.cpp @@ -168,7 +168,8 @@ class BlenderSmokeLoader : public ImageLoader { AttributeStandard attribute; }; -static void sync_smoke_volume(Scene *scene, BObjectInfo &b_ob_info, Volume *volume, float frame) +static void sync_smoke_volume( + BL::Scene &b_scene, Scene *scene, BObjectInfo &b_ob_info, Volume *volume, float frame) { if (!b_ob_info.is_real_object_data()) { return; @@ -178,6 +179,18 @@ static void sync_smoke_volume(Scene *scene, BObjectInfo &b_ob_info, Volume *volu return; } + float velocity_scale = b_domain.velocity_scale(); + /* Motion blur attribute is relative to seconds, we need it relative to frames. */ + const bool need_motion = object_need_motion_attribute(b_ob_info, scene); + const float motion_scale = (need_motion) ? + scene->motion_shutter_time() / + (b_scene.render().fps() / b_scene.render().fps_base()) : + 0.0f; + + velocity_scale *= motion_scale; + + volume->set_velocity_scale(velocity_scale); + AttributeStandard attributes[] = {ATTR_STD_VOLUME_DENSITY, ATTR_STD_VOLUME_COLOR, ATTR_STD_VOLUME_FLAME, @@ -234,6 +247,7 @@ class BlenderVolumeLoader : public VDBImageLoader { }; static void sync_volume_object(BL::BlendData &b_data, + BL::Scene &b_scene, BObjectInfo &b_ob_info, Scene *scene, Volume *volume) @@ -247,6 +261,20 @@ static void sync_volume_object(BL::BlendData &b_data, volume->set_step_size(b_render.step_size()); volume->set_object_space((b_render.space() == BL::VolumeRender::space_OBJECT)); + float velocity_scale = b_volume.velocity_scale(); + if (b_volume.velocity_unit() == BL::Volume::velocity_unit_SECOND) { + /* Motion blur attribute is relative to seconds, we need it relative to frames. */ + const bool need_motion = object_need_motion_attribute(b_ob_info, scene); + const float motion_scale = (need_motion) ? + scene->motion_shutter_time() / + (b_scene.render().fps() / b_scene.render().fps_base()) : + 0.0f; + + velocity_scale *= motion_scale; + } + + volume->set_velocity_scale(velocity_scale); + /* Find grid with matching name. */ for (BL::VolumeGrid &b_grid : b_volume.grids) { ustring name = ustring(b_grid.name()); @@ -267,9 +295,22 @@ static void sync_volume_object(BL::BlendData &b_data, else if (name == Attribute::standard_name(ATTR_STD_VOLUME_TEMPERATURE)) { std = ATTR_STD_VOLUME_TEMPERATURE; } - else if (name == Attribute::standard_name(ATTR_STD_VOLUME_VELOCITY)) { + else if (name == Attribute::standard_name(ATTR_STD_VOLUME_VELOCITY) || + name == b_volume.velocity_grid()) { std = ATTR_STD_VOLUME_VELOCITY; } + else if (name == Attribute::standard_name(ATTR_STD_VOLUME_VELOCITY_X) || + name == b_volume.velocity_x_grid()) { + std = ATTR_STD_VOLUME_VELOCITY_X; + } + else if (name == Attribute::standard_name(ATTR_STD_VOLUME_VELOCITY_Y) || + name == b_volume.velocity_y_grid()) { + std = ATTR_STD_VOLUME_VELOCITY_Y; + } + else if (name == Attribute::standard_name(ATTR_STD_VOLUME_VELOCITY_Z) || + name == b_volume.velocity_z_grid()) { + std = ATTR_STD_VOLUME_VELOCITY_Z; + } if ((std != ATTR_STD_NONE && volume->need_attribute(scene, std)) || volume->need_attribute(scene, name)) { @@ -294,11 +335,11 @@ void BlenderSync::sync_volume(BObjectInfo &b_ob_info, Volume *volume) if (b_ob_info.object_data.is_a(&RNA_Volume)) { /* Volume object. Create only attributes, bounding mesh will then * be automatically generated later. */ - sync_volume_object(b_data, b_ob_info, scene, volume); + sync_volume_object(b_data, b_scene, b_ob_info, scene, volume); } else { /* Smoke domain. */ - sync_smoke_volume(scene, b_ob_info, volume, b_scene.frame_current()); + sync_smoke_volume(b_scene, scene, b_ob_info, volume, b_scene.frame_current()); } } diff --git a/intern/cycles/device/cuda/queue.cpp b/intern/cycles/device/cuda/queue.cpp index a2ca70611bf..38c71866ad0 100644 --- a/intern/cycles/device/cuda/queue.cpp +++ b/intern/cycles/device/cuda/queue.cpp @@ -71,11 +71,6 @@ void CUDADeviceQueue::init_execution() debug_init_execution(); } -bool CUDADeviceQueue::kernel_available(DeviceKernel kernel) const -{ - return cuda_device_->kernels.available(kernel); -} - bool CUDADeviceQueue::enqueue(DeviceKernel kernel, const int work_size, DeviceKernelArguments const &args) diff --git a/intern/cycles/device/cuda/queue.h b/intern/cycles/device/cuda/queue.h index 4612e517de7..b450f5b3592 100644 --- a/intern/cycles/device/cuda/queue.h +++ b/intern/cycles/device/cuda/queue.h @@ -27,8 +27,6 @@ class CUDADeviceQueue : public DeviceQueue { virtual void init_execution() override; - virtual bool kernel_available(DeviceKernel kernel) const override; - virtual bool enqueue(DeviceKernel kernel, const int work_size, DeviceKernelArguments const &args) override; diff --git a/intern/cycles/device/hip/queue.cpp b/intern/cycles/device/hip/queue.cpp index 42778bc31cf..6c2c2c29624 100644 --- a/intern/cycles/device/hip/queue.cpp +++ b/intern/cycles/device/hip/queue.cpp @@ -71,11 +71,6 @@ void HIPDeviceQueue::init_execution() debug_init_execution(); } -bool HIPDeviceQueue::kernel_available(DeviceKernel kernel) const -{ - return hip_device_->kernels.available(kernel); -} - bool HIPDeviceQueue::enqueue(DeviceKernel kernel, const int work_size, DeviceKernelArguments const &args) diff --git a/intern/cycles/device/hip/queue.h b/intern/cycles/device/hip/queue.h index 11f60d04ad4..729d8a19acb 100644 --- a/intern/cycles/device/hip/queue.h +++ b/intern/cycles/device/hip/queue.h @@ -27,8 +27,6 @@ class HIPDeviceQueue : public DeviceQueue { virtual void init_execution() override; - virtual bool kernel_available(DeviceKernel kernel) const override; - virtual bool enqueue(DeviceKernel kernel, const int work_size, DeviceKernelArguments const &args) override; diff --git a/intern/cycles/device/metal/queue.h b/intern/cycles/device/metal/queue.h index 1e1c6d5fcc1..6cc84a20787 100644 --- a/intern/cycles/device/metal/queue.h +++ b/intern/cycles/device/metal/queue.h @@ -39,8 +39,6 @@ class MetalDeviceQueue : public DeviceQueue { virtual void copy_to_device(device_memory &mem) override; virtual void copy_from_device(device_memory &mem) override; - virtual bool kernel_available(DeviceKernel kernel) const override; - protected: void prepare_resources(DeviceKernel kernel); diff --git a/intern/cycles/device/metal/queue.mm b/intern/cycles/device/metal/queue.mm index c0f53386dff..1686ab95ffa 100644 --- a/intern/cycles/device/metal/queue.mm +++ b/intern/cycles/device/metal/queue.mm @@ -492,11 +492,6 @@ void MetalDeviceQueue::copy_from_device(device_memory &mem) } } -bool MetalDeviceQueue::kernel_available(DeviceKernel kernel) const -{ - return metal_device->kernels.available(kernel); -} - void MetalDeviceQueue::prepare_resources(DeviceKernel kernel) { std::lock_guard<std::recursive_mutex> lock(metal_device->metal_mem_map_mutex); diff --git a/intern/cycles/device/queue.h b/intern/cycles/device/queue.h index 2bd6e7ae460..14a5db3a204 100644 --- a/intern/cycles/device/queue.h +++ b/intern/cycles/device/queue.h @@ -112,9 +112,6 @@ class DeviceQueue { * Use this method after device synchronization has finished before enqueueing any kernels. */ virtual void init_execution() = 0; - /* Test if an optional device kernel is available. */ - virtual bool kernel_available(DeviceKernel kernel) const = 0; - /* Enqueue kernel execution. * * Execute the kernel work_size times on the device. diff --git a/intern/cycles/kernel/device/hip/compat.h b/intern/cycles/kernel/device/hip/compat.h index 9c93d87fd87..667352ed12e 100644 --- a/intern/cycles/kernel/device/hip/compat.h +++ b/intern/cycles/kernel/device/hip/compat.h @@ -62,7 +62,7 @@ typedef unsigned long long uint64_t; #define ccl_gpu_block_idx_x (blockIdx.x) #define ccl_gpu_grid_dim_x (gridDim.x) #define ccl_gpu_warp_size (warpSize) -#define ccl_gpu_thread_mask(thread_warp) uint64_t(0xFFFFFFFFFFFFFFFF >> (64 - thread_warp)) +#define ccl_gpu_thread_mask(thread_warp) uint(0xFFFFFFFF >> (ccl_gpu_warp_size - thread_warp)) #define ccl_gpu_global_id_x() (ccl_gpu_block_idx_x * ccl_gpu_block_dim_x + ccl_gpu_thread_idx_x) #define ccl_gpu_global_size_x() (ccl_gpu_grid_dim_x * ccl_gpu_block_dim_x) diff --git a/intern/cycles/kernel/integrator/mnee.h b/intern/cycles/kernel/integrator/mnee.h index 7218165c67c..c523da8d3be 100644 --- a/intern/cycles/kernel/integrator/mnee.h +++ b/intern/cycles/kernel/integrator/mnee.h @@ -36,11 +36,13 @@ * https://cg.ivd.kit.edu/english/HSLT.php */ -# define MNEE_MAX_ITERATIONS 50 +# define MNEE_MAX_ITERATIONS 32 # define MNEE_MAX_INTERSECTION_COUNT 10 # define MNEE_SOLVER_THRESHOLD 0.001f +# define MNEE_MINIMUM_STEP_SIZE 0.0001f # define MNEE_MAX_CAUSTIC_CASTERS 6 # define MNEE_MIN_DISTANCE 0.001f +# define MNEE_MIN_PROGRESS_DISTANCE 0.0001f # define MNEE_MIN_DETERMINANT 0.0001f # define MNEE_PROJECTION_DISTANCE_MULTIPLIER 2.f @@ -168,7 +170,8 @@ ccl_device_forceinline void mnee_setup_manifold_vertex(KernelGlobals kg, const float2 n_offset, ccl_private const Ray *ray, ccl_private const Intersection *isect, - ccl_private ShaderData *sd_vtx) + ccl_private ShaderData *sd_vtx, + bool seed) { sd_vtx->object = (isect->object == OBJECT_NONE) ? kernel_tex_fetch(__prim_object, isect->prim) : isect->object; @@ -177,7 +180,7 @@ ccl_device_forceinline void mnee_setup_manifold_vertex(KernelGlobals kg, sd_vtx->flag = 0; sd_vtx->object_flag = kernel_tex_fetch(__object_flag, sd_vtx->object); - /* matrices and time */ + /* Matrices and time. */ shader_setup_object_transforms(kg, sd_vtx, ray->time); sd_vtx->time = ray->time; @@ -191,83 +194,29 @@ ccl_device_forceinline void mnee_setup_manifold_vertex(KernelGlobals kg, float3 verts[3]; float3 normals[3]; - uint4 tri_vindex = kernel_tex_fetch(__tri_vindex, sd_vtx->prim); - if (sd_vtx->type & PRIMITIVE_TRIANGLE) { - /* Static triangle. */ - - /* Load triangle vertices. */ - verts[0] = kernel_tex_fetch(__tri_verts, tri_vindex.w + 0); - verts[1] = kernel_tex_fetch(__tri_verts, tri_vindex.w + 1); - verts[2] = kernel_tex_fetch(__tri_verts, tri_vindex.w + 2); - - /* Vectors. */ - sd_vtx->P = triangle_point_from_uv(kg, sd_vtx, isect->object, isect->prim, isect->u, isect->v); - - /* Smooth normal. */ - if (sd_vtx->shader & SHADER_SMOOTH_NORMAL) { - /* Load triangle vertices. */ - normals[0] = kernel_tex_fetch(__tri_vnormal, tri_vindex.x); - normals[1] = kernel_tex_fetch(__tri_vnormal, tri_vindex.y); - normals[2] = kernel_tex_fetch(__tri_vnormal, tri_vindex.z); + /* Load triangle vertices and normals. */ + triangle_vertices_and_normals(kg, sd_vtx->prim, verts, normals); + + /* Compute refined position (same code as in triangle_point_from_uv). */ + sd_vtx->P = isect->u * verts[0] + isect->v * verts[1] + (1.f - isect->u - isect->v) * verts[2]; + if (!(sd_vtx->object_flag & SD_OBJECT_TRANSFORM_APPLIED)) { + const Transform tfm = object_get_transform(kg, sd_vtx); + sd_vtx->P = transform_point(&tfm, sd_vtx->P); } } else { /* if (sd_vtx->type & PRIMITIVE_MOTION_TRIANGLE) */ - /* Motion triangle. */ - - /* Get motion info. */ - int numsteps, numverts; - object_motion_info(kg, sd_vtx->object, &numsteps, &numverts, NULL); - - /* Figure out which steps we need to fetch and their interpolation factor. */ - int maxstep = numsteps * 2; - int step = min((int)(sd_vtx->time * maxstep), maxstep - 1); - float t = sd_vtx->time * maxstep - step; - - /* Find attribute. */ - int offset = intersection_find_attribute(kg, sd_vtx->object, ATTR_STD_MOTION_VERTEX_POSITION); - kernel_assert(offset != ATTR_STD_NOT_FOUND); - - /* Fetch vertex coordinates. */ - float3 next_verts[3]; - uint4 tri_vindex = kernel_tex_fetch(__tri_vindex, sd_vtx->prim); - motion_triangle_verts_for_step(kg, tri_vindex, offset, numverts, numsteps, step, verts); - motion_triangle_verts_for_step( - kg, tri_vindex, offset, numverts, numsteps, step + 1, next_verts); - - /* Interpolate between steps. */ - verts[0] = (1.0f - t) * verts[0] + t * next_verts[0]; - verts[1] = (1.0f - t) * verts[1] + t * next_verts[1]; - verts[2] = (1.0f - t) * verts[2] + t * next_verts[2]; + /* Load triangle vertices and normals. */ + motion_triangle_vertices_and_normals( + kg, sd_vtx->object, sd_vtx->prim, sd_vtx->time, verts, normals); /* Compute refined position. */ sd_vtx->P = motion_triangle_point_from_uv( kg, sd_vtx, isect->object, isect->prim, isect->u, isect->v, verts); - - /* Compute smooth normal. */ - if (sd_vtx->shader & SHADER_SMOOTH_NORMAL) { - /* Find attribute. */ - int offset = intersection_find_attribute(kg, sd_vtx->object, ATTR_STD_MOTION_VERTEX_NORMAL); - kernel_assert(offset != ATTR_STD_NOT_FOUND); - - /* Fetch vertex coordinates. */ - float3 next_normals[3]; - motion_triangle_normals_for_step(kg, tri_vindex, offset, numverts, numsteps, step, normals); - motion_triangle_normals_for_step( - kg, tri_vindex, offset, numverts, numsteps, step + 1, next_normals); - - /* Interpolate between steps. */ - normals[0] = (1.0f - t) * normals[0] + t * next_normals[0]; - normals[1] = (1.0f - t) * normals[1] + t * next_normals[1]; - normals[2] = (1.0f - t) * normals[2] + t * next_normals[2]; - } } - /* manifold vertex position */ - vtx->p = sd_vtx->P; - + /* Instance transform. */ if (!(sd_vtx->object_flag & SD_OBJECT_TRANSFORM_APPLIED)) { - /* Instance transform. */ object_position_transform_auto(kg, sd_vtx, &verts[0]); object_position_transform_auto(kg, sd_vtx, &verts[1]); object_position_transform_auto(kg, sd_vtx, &verts[2]); @@ -277,94 +226,70 @@ ccl_device_forceinline void mnee_setup_manifold_vertex(KernelGlobals kg, } /* Tangent space (position derivatives) WRT barycentric (u, v). */ - vtx->dp_du = verts[0] - verts[2]; - vtx->dp_dv = verts[1] - verts[2]; + float3 dp_du = verts[0] - verts[2]; + float3 dp_dv = verts[1] - verts[2]; /* Geometric normal. */ - vtx->ng = normalize(cross(vtx->dp_du, vtx->dp_dv)); + vtx->ng = normalize(cross(dp_du, dp_dv)); if (sd_vtx->object_flag & SD_OBJECT_NEGATIVE_SCALE_APPLIED) vtx->ng = -vtx->ng; - /* Shading normal. */ - if (!(sd_vtx->shader & SHADER_SMOOTH_NORMAL)) { - vtx->n = vtx->ng; - vtx->dn_du = vtx->dn_dv = zero_float3(); - } - else { - /* Interpolate normals between vertices. */ - float n_len; - vtx->n = normalize_len(normals[0] * sd_vtx->u + normals[1] * sd_vtx->v + - normals[2] * (1.0f - sd_vtx->u - sd_vtx->v), - &n_len); - - /* Shading normal derivatives WRT barycentric (u, v) - * we calculate the derivative of n = |u*n0 + v*n1 + (1-u-v)*n2| using: - * d/du [f(u)/|f(u)|] = [d/du f(u)]/|f(u)| - f(u)/|f(u)|^3 <f(u), d/du f(u)>. */ - const float inv_n_len = 1.f / n_len; - vtx->dn_du = inv_n_len * (normals[0] - normals[2]); - vtx->dn_dv = inv_n_len * (normals[1] - normals[2]); - vtx->dn_du -= vtx->n * dot(vtx->n, vtx->dn_du); - vtx->dn_dv -= vtx->n * dot(vtx->n, vtx->dn_dv); - } + /* Shading normals: Interpolate normals between vertices. */ + float n_len; + vtx->n = normalize_len(normals[0] * sd_vtx->u + normals[1] * sd_vtx->v + + normals[2] * (1.0f - sd_vtx->u - sd_vtx->v), + &n_len); + + /* Shading normal derivatives WRT barycentric (u, v) + * we calculate the derivative of n = |u*n0 + v*n1 + (1-u-v)*n2| using: + * d/du [f(u)/|f(u)|] = [d/du f(u)]/|f(u)| - f(u)/|f(u)|^3 <f(u), d/du f(u)>. */ + const float inv_n_len = 1.f / n_len; + float3 dn_du = inv_n_len * (normals[0] - normals[2]); + float3 dn_dv = inv_n_len * (normals[1] - normals[2]); + dn_du -= vtx->n * dot(vtx->n, dn_du); + dn_dv -= vtx->n * dot(vtx->n, dn_dv); - /* dp_du and dp_dv need to be continuous across triangles for the h normal - * offset to yield a consistent halfvector while walking on the manifold. - * It's usually best to rely on the mesh uv layout, which is assumed to be - * continuous across the mesh. */ - float2 duv0, duv1; - bool found_uv = false; - AttributeDescriptor uv_desc = find_attribute(kg, sd_vtx, ATTR_STD_GENERATED); - if (uv_desc.offset != ATTR_STD_NOT_FOUND) { - float3 uvs[3]; - uvs[0] = kernel_tex_fetch(__attributes_float3, uv_desc.offset + tri_vindex.x); - uvs[1] = kernel_tex_fetch(__attributes_float3, uv_desc.offset + tri_vindex.y); - uvs[2] = kernel_tex_fetch(__attributes_float3, uv_desc.offset + tri_vindex.z); - duv0 = make_float2(uvs[0].x - uvs[2].x, uvs[0].y - uvs[2].y); - duv1 = make_float2(uvs[1].x - uvs[2].x, uvs[1].y - uvs[2].y); - found_uv = true; + /* Orthonormalize (dp_du,dp_dv) using a linear transformation, which + * we use on (dn_du,dn_dv) as well so the new (u,v) are consistent. */ + const float inv_len_dp_du = 1.f / len(dp_du); + dp_du *= inv_len_dp_du; + dn_du *= inv_len_dp_du; + + const float dpdu_dot_dpdv = dot(dp_du, dp_dv); + dp_dv -= dpdu_dot_dpdv * dp_du; + dn_dv -= dpdu_dot_dpdv * dn_du; + + const float inv_len_dp_dv = 1.f / len(dp_dv); + dp_dv *= inv_len_dp_dv; + dn_dv *= inv_len_dp_dv; + + /* Final local differential geometry. */ + if (seed) { + vtx->dp_du = dp_du; + vtx->dp_dv = dp_dv; + vtx->dn_du = dn_du; + vtx->dn_dv = dn_dv; } else { - uv_desc = find_attribute(kg, sd_vtx, ATTR_STD_UV); - if (uv_desc.offset != ATTR_STD_NOT_FOUND) { - float2 uvs[3]; - uvs[0] = kernel_tex_fetch(__attributes_float2, uv_desc.offset + tri_vindex.x); - uvs[1] = kernel_tex_fetch(__attributes_float2, uv_desc.offset + tri_vindex.y); - uvs[2] = kernel_tex_fetch(__attributes_float2, uv_desc.offset + tri_vindex.z); - duv0 = make_float2(uvs[0].x - uvs[2].x, uvs[0].y - uvs[2].y); - duv1 = make_float2(uvs[1].x - uvs[2].x, uvs[1].y - uvs[2].y); - found_uv = true; - } - } - if (found_uv) { - const float det = duv0.x * duv1.y - duv0.y * duv1.x; - if (det != 0.f) { - const float inv_det = 1.f / det; - - /* Tangent space (position derivatives) WRT texture (u, v). */ - const float3 dp_du = vtx->dp_du; - const float3 dp_dv = vtx->dp_dv; - vtx->dp_du = (duv1.y * dp_du - duv0.y * dp_dv) * inv_det; - vtx->dp_dv = (-duv1.x * dp_du + duv0.x * dp_dv) * inv_det; - - /* Shading normal derivatives WRT texture (u, v). */ - const float3 dn_du = vtx->dn_du; - const float3 dn_dv = vtx->dn_dv; - vtx->dn_du = (duv1.y * dn_du - duv0.y * dn_dv) * inv_det; - vtx->dn_dv = (-duv1.x * dn_du + duv0.x * dn_dv) * inv_det; - } + /* Find angle subtended by reference direction (travel direction). */ + const float3 reference_direction = normalize(sd_vtx->P - vtx->p); + const float reference_theta = atan2(dot(reference_direction, vtx->dp_dv), + dot(reference_direction, vtx->dp_du)); + const float current_theta = atan2(dot(reference_direction, dp_dv), + dot(reference_direction, dp_du)); + const float theta = reference_theta - current_theta; + + /* Rotate (dp_du,dp_dv) to be consistent with previous tangent frame. */ + float cos_theta, sin_theta; + fast_sincosf(theta, &sin_theta, &cos_theta); + vtx->dp_du = cos_theta * dp_du - sin_theta * dp_dv; + vtx->dp_dv = sin_theta * dp_du + cos_theta * dp_dv; + vtx->dn_du = cos_theta * dn_du - sin_theta * dn_dv; + vtx->dn_dv = sin_theta * dn_du + cos_theta * dn_dv; } - /* Orthonormalize (dp_du,dp_dv) using a linear transformation, which - * we use on (dn_du,dn_dv) as well so the new (u,v) are consistent. */ - const float inv_len_dp_du = 1.f / len(vtx->dp_du); - vtx->dp_du *= inv_len_dp_du; - vtx->dn_du *= inv_len_dp_du; - const float dpdu_dot_dpdv = dot(vtx->dp_du, vtx->dp_dv); - const float3 dp_dv = vtx->dp_dv - dpdu_dot_dpdv * vtx->dp_du; - const float3 dn_dv = vtx->dn_dv - dpdu_dot_dpdv * vtx->dn_du; - const float inv_len_dp_dv = 1.f / len(dp_dv); - vtx->dp_dv = dp_dv * inv_len_dp_dv; - vtx->dn_dv = dn_dv * inv_len_dp_dv; + /* Manifold vertex position. */ + vtx->p = sd_vtx->P; /* Initialize constraint and its derivates. */ vtx->a = vtx->c = zero_float4(); @@ -638,15 +563,28 @@ ccl_device_forceinline bool mnee_newton_solver(KernelGlobals kg, break; } + /* Initialize tangent frame, which will be used as reference. */ + ccl_private ManifoldVertex &tv = tentative[vi]; + tv.p = mv.p; + tv.dp_du = mv.dp_du; + tv.dp_dv = mv.dp_dv; + /* Setup corrected manifold vertex. */ mnee_setup_manifold_vertex(kg, - &tentative[vi], + &tv, mv.bsdf, mv.eta, mv.n_offset, &projection_ray, &projection_isect, - sd_vtx); + sd_vtx, + false); + + /* Fail newton solve if we are not making progress, probably stuck trying to move off the + * edge of the mesh. */ + const float distance = len(tv.p - mv.p); + if (distance < MNEE_MIN_PROGRESS_DISTANCE) + return false; } /* Check that tentative path is still transmissive. */ @@ -672,6 +610,11 @@ ccl_device_forceinline bool mnee_newton_solver(KernelGlobals kg, reduce_stepsize = false; resolve_constraint = false; beta *= .5f; + + /* Fail newton solve if the stepsize is too small. */ + if (beta < MNEE_MINIMUM_STEP_SIZE) + return false; + continue; } @@ -714,8 +657,7 @@ mnee_sample_bsdf_dh(ClosureType type, float alpha_x, float alpha_y, float sample if (type == CLOSURE_BSDF_MICROFACET_BECKMANN_REFRACTION_ID) { tan2_theta *= -logf(1.0f - sample_u); } - else { /* if (type == CLOSURE_BSDF_MICROFACET_GGX_REFRACTION_ID || type == - CLOSURE_BSDF_MICROFACET_GGX_REFRACTION_FRESNEL_ID) */ + else { /* type == CLOSURE_BSDF_MICROFACET_GGX_REFRACTION_ID assumed */ tan2_theta *= sample_u / (1.0f - sample_u); } float cos2_theta = 1.0f / (1.0f + tan2_theta); @@ -749,8 +691,7 @@ ccl_device_forceinline float3 mnee_eval_bsdf_contribution(ccl_private ShaderClos /* Eq. 26, 27: now calculate G1(i,m) and G1(o,m). */ G = bsdf_beckmann_G1(bsdf->alpha_x, cosNO) * bsdf_beckmann_G1(bsdf->alpha_x, cosNI); } - else { /* if (type == CLOSURE_BSDF_MICROFACET_GGX_REFRACTION_ID || type == - CLOSURE_BSDF_MICROFACET_GGX_REFRACTION_FRESNEL_ID) */ + else { /* bsdf->type == CLOSURE_BSDF_MICROFACET_GGX_REFRACTION_ID assumed */ /* Eq. 34: now calculate G1(i,m) and G1(o,m). */ G = (2.f / (1.f + safe_sqrtf(1.f + alpha2 * (1.f - cosNO * cosNO) / (cosNO * cosNO)))) * (2.f / (1.f + safe_sqrtf(1.f + alpha2 * (1.f - cosNI * cosNI) / (cosNI * cosNI)))); @@ -929,7 +870,8 @@ ccl_device_forceinline bool mnee_path_contribution(KernelGlobals kg, /* Receiver bsdf eval above already contains |n.wo|. */ const float dw0_dx1 = fabsf(dot(wo, vertices[0].n)) / sqr(wo_len); - const float G = dw0_dx1 * dx1_dxlight; + /* Clamp since it has a tendency to be unstable. */ + const float G = fminf(dw0_dx1 * dx1_dxlight, 2.f); bsdf_eval_mul(throughput, G); /* Specular reflectance. */ @@ -1058,21 +1000,36 @@ ccl_device_forceinline bool kernel_path_mnee_sample(KernelGlobals kg, if (vertex_count >= MNEE_MAX_CAUSTIC_CASTERS) return false; + /* Reject caster if it is not a triangles mesh. */ + if (!(probe_isect.type & PRIMITIVE_TRIANGLE)) + return false; + ccl_private ManifoldVertex &mv = vertices[vertex_count++]; /* Setup shader data on caustic caster and evaluate context. */ shader_setup_from_ray(kg, sd_mnee, &probe_ray, &probe_isect); + /* Reject caster if smooth normals are not available: Manifold exploration assumes local + * differential geometry can be created at any point on the surface which is not possible if + * normals are not smooth. */ + if (!(sd_mnee->shader & SHADER_SMOOTH_NORMAL)) + return false; + /* Last bool argument is the MNEE flag (for TINY_MAX_CLOSURE cap in kernel_shader.h). */ shader_eval_surface<KERNEL_FEATURE_NODE_MASK_SURFACE_SHADOW>( kg, state, sd_mnee, NULL, PATH_RAY_DIFFUSE, true); - /* get and sample refraction bsdf. */ + /* Get and sample refraction bsdf */ bool found_transimissive_microfacet_bsdf = false; for (int ci = 0; ci < sd_mnee->num_closure; ci++) { ccl_private ShaderClosure *bsdf = &sd_mnee->closure[ci]; if (bsdf->type == CLOSURE_BSDF_MICROFACET_BECKMANN_REFRACTION_ID || - bsdf->type == CLOSURE_BSDF_MICROFACET_GGX_REFRACTION_ID) { + bsdf->type == CLOSURE_BSDF_MICROFACET_GGX_REFRACTION_ID || + bsdf->type == CLOSURE_BSDF_MICROFACET_MULTI_GGX_GLASS_ID || + bsdf->type == CLOSURE_BSDF_MICROFACET_MULTI_GGX_GLASS_FRESNEL_ID) { + /* Note that CLOSURE_BSDF_MICROFACET_MULTI_GGX_GLASS_ID and + * CLOSURE_BSDF_MICROFACET_MULTI_GGX_GLASS_FRESNEL_ID are treated as + * CLOSURE_BSDF_MICROFACET_GGX_REFRACTION_ID further below. */ found_transimissive_microfacet_bsdf = true; ccl_private MicrofacetBsdf *microfacet_bsdf = (ccl_private MicrofacetBsdf *)bsdf; @@ -1088,7 +1045,8 @@ ccl_device_forceinline bool kernel_path_mnee_sample(KernelGlobals kg, bsdf->type, microfacet_bsdf->alpha_x, microfacet_bsdf->alpha_y, bsdf_u, bsdf_v); /* Setup differential geometry on vertex. */ - mnee_setup_manifold_vertex(kg, &mv, bsdf, eta, h, &probe_ray, &probe_isect, sd_mnee); + mnee_setup_manifold_vertex( + kg, &mv, bsdf, eta, h, &probe_ray, &probe_isect, sd_mnee, true); break; } } diff --git a/intern/cycles/kernel/integrator/shader_eval.h b/intern/cycles/kernel/integrator/shader_eval.h index 3066fb661a1..4da92929366 100644 --- a/intern/cycles/kernel/integrator/shader_eval.h +++ b/intern/cycles/kernel/integrator/shader_eval.h @@ -831,6 +831,65 @@ ccl_device_inline void shader_eval_volume(KernelGlobals kg, /* todo: this is inefficient for motion blur, we should be * caching matrices instead of recomputing them each step */ shader_setup_object_transforms(kg, sd, sd->time); + + if ((sd->object_flag & SD_OBJECT_HAS_VOLUME_MOTION) != 0) { + AttributeDescriptor v_desc = find_attribute(kg, sd, ATTR_STD_VOLUME_VELOCITY); + kernel_assert(v_desc.offset != ATTR_STD_NOT_FOUND); + + const float3 P = sd->P; + const float velocity_scale = kernel_tex_fetch(__objects, sd->object).velocity_scale; + const float time_offset = kernel_data.cam.motion_position == MOTION_POSITION_CENTER ? + 0.5f : + 0.0f; + const float time = kernel_data.cam.motion_position == MOTION_POSITION_END ? + (1.0f - kernel_data.cam.shuttertime) + sd->time : + sd->time; + + /* Use a 1st order semi-lagrangian advection scheme to estimate what volume quantity + * existed, or will exist, at the given time: + * + * `phi(x, T) = phi(x - (T - t) * u(x, T), t)` + * + * where + * + * x : position + * T : super-sampled time (or ray time) + * t : current time of the simulation (in rendering we assume this is center frame with + * relative time = 0) + * phi : the volume quantity + * u : the velocity field + * + * But first we need to determine the velocity field `u(x, T)`, which we can estimate also + * using semi-lagrangian advection. + * + * `u(x, T) = u(x - (T - t) * u(x, T), t)` + * + * This is the typical way to model self-advection in fluid dynamics, however, we do not + * account for other forces affecting the velocity during simulation (pressure, buoyancy, + * etc.): this gives a linear interpolation when fluid are mostly "curvy". For better + * results, a higher order interpolation scheme can be used (at the cost of more lookups), + * or an interpolation of the velocity fields for the previous and next frames could also + * be used to estimate `u(x, T)` (which will cost more memory and lookups). + * + * References: + * "Eulerian Motion Blur", Kim and Ko, 2007 + * "Production Volume Rendering", Wreninge et al., 2012 + */ + + /* Find velocity. */ + float3 velocity = primitive_volume_attribute_float3(kg, sd, v_desc); + object_dir_transform(kg, sd, &velocity); + + /* Find advected P. */ + sd->P = P - (time - time_offset) * velocity_scale * velocity; + + /* Find advected velocity. */ + velocity = primitive_volume_attribute_float3(kg, sd, v_desc); + object_dir_transform(kg, sd, &velocity); + + /* Find advected P. */ + sd->P = P - (time - time_offset) * velocity_scale * velocity; + } # endif } diff --git a/intern/cycles/kernel/types.h b/intern/cycles/kernel/types.h index 422285cd346..01df7948241 100644 --- a/intern/cycles/kernel/types.h +++ b/intern/cycles/kernel/types.h @@ -489,6 +489,18 @@ enum PanoramaType { PANORAMA_NUM_TYPES, }; +/* Specifies an offset for the shutter's time interval. */ +enum MotionPosition { + /* Shutter opens at the current frame. */ + MOTION_POSITION_START = 0, + /* Shutter is fully open at the current frame. */ + MOTION_POSITION_CENTER = 1, + /* Shutter closes at the current frame. */ + MOTION_POSITION_END = 2, + + MOTION_NUM_POSITIONS, +}; + /* Direct Light Sampling */ enum DirectLightSamplingType { @@ -635,6 +647,9 @@ typedef enum AttributeStandard { ATTR_STD_VOLUME_HEAT, ATTR_STD_VOLUME_TEMPERATURE, ATTR_STD_VOLUME_VELOCITY, + ATTR_STD_VOLUME_VELOCITY_X, + ATTR_STD_VOLUME_VELOCITY_Y, + ATTR_STD_VOLUME_VELOCITY_Z, ATTR_STD_POINTINESS, ATTR_STD_RANDOM_PER_ISLAND, ATTR_STD_SHADOW_TRANSPARENCY, @@ -808,6 +823,8 @@ enum ShaderDataObjectFlag { SD_OBJECT_CAUSTICS_CASTER = (1 << 9), /* object is caustics receiver */ SD_OBJECT_CAUSTICS_RECEIVER = (1 << 10), + /* object has attribute for volume motion */ + SD_OBJECT_HAS_VOLUME_MOTION = (1 << 11), /* object is using caustics */ SD_OBJECT_CAUSTICS = (SD_OBJECT_CAUSTICS_CASTER | SD_OBJECT_CAUSTICS_RECEIVER), @@ -815,7 +832,8 @@ enum ShaderDataObjectFlag { SD_OBJECT_FLAGS = (SD_OBJECT_HOLDOUT_MASK | SD_OBJECT_MOTION | SD_OBJECT_TRANSFORM_APPLIED | SD_OBJECT_NEGATIVE_SCALE_APPLIED | SD_OBJECT_HAS_VOLUME | SD_OBJECT_INTERSECTS_VOLUME | SD_OBJECT_SHADOW_CATCHER | - SD_OBJECT_HAS_VOLUME_ATTRIBUTES | SD_OBJECT_CAUSTICS) + SD_OBJECT_HAS_VOLUME_ATTRIBUTES | SD_OBJECT_CAUSTICS | + SD_OBJECT_HAS_VOLUME_MOTION) }; typedef struct ccl_align(16) ShaderData @@ -1040,7 +1058,7 @@ typedef struct KernelCamera { int rolling_shutter_type; float rolling_shutter_duration; - int pad; + int motion_position; } KernelCamera; static_assert_align(KernelCamera, 16); @@ -1386,7 +1404,8 @@ typedef struct KernelObject { uint visibility; int primitive_type; - int pad1; + /* Volume velocity scale. */ + float velocity_scale; } KernelObject; static_assert_align(KernelObject, 16); diff --git a/intern/cycles/scene/attribute.cpp b/intern/cycles/scene/attribute.cpp index 0ca602362bc..df01189a54b 100644 --- a/intern/cycles/scene/attribute.cpp +++ b/intern/cycles/scene/attribute.cpp @@ -360,6 +360,12 @@ const char *Attribute::standard_name(AttributeStandard std) return "temperature"; case ATTR_STD_VOLUME_VELOCITY: return "velocity"; + case ATTR_STD_VOLUME_VELOCITY_X: + return "velocity_x"; + case ATTR_STD_VOLUME_VELOCITY_Y: + return "velocity_y"; + case ATTR_STD_VOLUME_VELOCITY_Z: + return "velocity_z"; case ATTR_STD_POINTINESS: return "pointiness"; case ATTR_STD_RANDOM_PER_ISLAND: @@ -587,6 +593,9 @@ Attribute *AttributeSet::add(AttributeStandard std, ustring name) case ATTR_STD_VOLUME_FLAME: case ATTR_STD_VOLUME_HEAT: case ATTR_STD_VOLUME_TEMPERATURE: + case ATTR_STD_VOLUME_VELOCITY_X: + case ATTR_STD_VOLUME_VELOCITY_Y: + case ATTR_STD_VOLUME_VELOCITY_Z: attr = add(name, TypeDesc::TypeFloat, ATTR_ELEMENT_VOXEL); break; case ATTR_STD_VOLUME_COLOR: diff --git a/intern/cycles/scene/camera.cpp b/intern/cycles/scene/camera.cpp index 6aca2fcbb81..710f1c5ee90 100644 --- a/intern/cycles/scene/camera.cpp +++ b/intern/cycles/scene/camera.cpp @@ -397,6 +397,7 @@ void Camera::update(Scene *scene) /* motion blur */ kcam->shuttertime = (need_motion == Scene::MOTION_BLUR) ? shuttertime : -1.0f; + kcam->motion_position = motion_position; /* type */ kcam->type = camera_type; diff --git a/intern/cycles/scene/camera.h b/intern/cycles/scene/camera.h index 97bee430588..c150405acc2 100644 --- a/intern/cycles/scene/camera.h +++ b/intern/cycles/scene/camera.h @@ -30,18 +30,6 @@ class Camera : public Node { public: NODE_DECLARE - /* Specifies an offset for the shutter's time interval. */ - enum MotionPosition { - /* Shutter opens at the current frame. */ - MOTION_POSITION_START = 0, - /* Shutter is fully open at the current frame. */ - MOTION_POSITION_CENTER = 1, - /* Shutter closes at the current frame. */ - MOTION_POSITION_END = 2, - - MOTION_NUM_POSITIONS, - }; - /* Specifies rolling shutter effect. */ enum RollingShutterType { /* No rolling shutter effect. */ diff --git a/intern/cycles/scene/geometry.cpp b/intern/cycles/scene/geometry.cpp index 351ec4f09ae..349d8ad39c7 100644 --- a/intern/cycles/scene/geometry.cpp +++ b/intern/cycles/scene/geometry.cpp @@ -1541,7 +1541,7 @@ void GeometryManager::device_update_preprocess(Device *device, Scene *scene, Pro } Volume *volume = static_cast<Volume *>(geom); - create_volume_mesh(volume, progress); + create_volume_mesh(scene, volume, progress); /* always reallocate when we have a volume, as we need to rebuild the BVH */ device_update_flags |= DEVICE_MESH_DATA_NEEDS_REALLOC; diff --git a/intern/cycles/scene/geometry.h b/intern/cycles/scene/geometry.h index 0c2e70d483d..6210a64509a 100644 --- a/intern/cycles/scene/geometry.h +++ b/intern/cycles/scene/geometry.h @@ -216,7 +216,7 @@ class GeometryManager { protected: bool displace(Device *device, Scene *scene, Mesh *mesh, Progress &progress); - void create_volume_mesh(Volume *volume, Progress &progress); + void create_volume_mesh(const Scene *scene, Volume *volume, Progress &progress); /* Attributes */ void update_osl_attributes(Device *device, diff --git a/intern/cycles/scene/image_vdb.cpp b/intern/cycles/scene/image_vdb.cpp index 9906606a959..b6f0911fa2c 100644 --- a/intern/cycles/scene/image_vdb.cpp +++ b/intern/cycles/scene/image_vdb.cpp @@ -64,6 +64,11 @@ struct ToNanoOp { } }; # endif + +VDBImageLoader::VDBImageLoader(openvdb::GridBase::ConstPtr grid_, const string &grid_name) + : grid_name(grid_name), grid(grid_) +{ +} #endif VDBImageLoader::VDBImageLoader(const string &grid_name) : grid_name(grid_name) diff --git a/intern/cycles/scene/image_vdb.h b/intern/cycles/scene/image_vdb.h index c851ef6250d..a5fd51915ef 100644 --- a/intern/cycles/scene/image_vdb.h +++ b/intern/cycles/scene/image_vdb.h @@ -17,6 +17,9 @@ CCL_NAMESPACE_BEGIN class VDBImageLoader : public ImageLoader { public: +#ifdef WITH_OPENVDB + VDBImageLoader(openvdb::GridBase::ConstPtr grid_, const string &grid_name); +#endif VDBImageLoader(const string &grid_name); ~VDBImageLoader(); diff --git a/intern/cycles/scene/object.cpp b/intern/cycles/scene/object.cpp index 55d89fc3673..676cc78a11f 100644 --- a/intern/cycles/scene/object.cpp +++ b/intern/cycles/scene/object.cpp @@ -439,6 +439,14 @@ void ObjectManager::device_update_object_transform(UpdateObjectTransformState *s flag |= SD_OBJECT_HAS_VERTEX_MOTION; } } + else if (geom->is_volume()) { + Volume *volume = static_cast<Volume *>(geom); + if (volume->attributes.find(ATTR_STD_VOLUME_VELOCITY) && + volume->get_velocity_scale() != 0.0f) { + flag |= SD_OBJECT_HAS_VOLUME_MOTION; + kobject.velocity_scale = volume->get_velocity_scale(); + } + } if (state->need_motion == Scene::MOTION_PASS) { /* Clear motion array if there is no actual motion. */ diff --git a/intern/cycles/scene/scene.cpp b/intern/cycles/scene/scene.cpp index b6b53004816..b35242139ea 100644 --- a/intern/cycles/scene/scene.cpp +++ b/intern/cycles/scene/scene.cpp @@ -381,7 +381,7 @@ void Scene::device_update(Device *device_, Progress &progress) } } -Scene::MotionType Scene::need_motion() +Scene::MotionType Scene::need_motion() const { if (integrator->get_motion_blur()) return MOTION_BLUR; @@ -407,6 +407,10 @@ bool Scene::need_global_attribute(AttributeStandard std) return need_motion() != MOTION_NONE; else if (std == ATTR_STD_MOTION_VERTEX_NORMAL) return need_motion() == MOTION_BLUR; + else if (std == ATTR_STD_VOLUME_VELOCITY || std == ATTR_STD_VOLUME_VELOCITY_X || + std == ATTR_STD_VOLUME_VELOCITY_Y || std == ATTR_STD_VOLUME_VELOCITY_Z) { + return need_motion() != MOTION_NONE; + } return false; } diff --git a/intern/cycles/scene/scene.h b/intern/cycles/scene/scene.h index 54c7d9d93ea..a0d2f4a6c06 100644 --- a/intern/cycles/scene/scene.h +++ b/intern/cycles/scene/scene.h @@ -257,7 +257,7 @@ class Scene : public NodeOwner { void need_global_attributes(AttributeRequestSet &attributes); enum MotionType { MOTION_NONE = 0, MOTION_PASS, MOTION_BLUR }; - MotionType need_motion(); + MotionType need_motion() const; float motion_shutter_time(); bool need_update(); diff --git a/intern/cycles/scene/volume.cpp b/intern/cycles/scene/volume.cpp index 0555fdd2fad..39e9b0bbbf4 100644 --- a/intern/cycles/scene/volume.cpp +++ b/intern/cycles/scene/volume.cpp @@ -10,9 +10,9 @@ # include <openvdb/tools/Dense.h> # include <openvdb/tools/GridTransformer.h> # include <openvdb/tools/Morphology.h> +# include <openvdb/tools/Statistics.h> #endif -#include "util/foreach.h" #include "util/hash.h" #include "util/log.h" #include "util/openvdb.h" @@ -28,6 +28,7 @@ NODE_DEFINE(Volume) SOCKET_FLOAT(clipping, "Clipping", 0.001f); SOCKET_FLOAT(step_size, "Step Size", 0.0f); SOCKET_BOOLEAN(object_space, "Object Space", false); + SOCKET_FLOAT(velocity_scale, "Velocity Scale", 1.0f); return type; } @@ -258,7 +259,8 @@ void VolumeMeshBuilder::add_grid(openvdb::GridBase::ConstPtr grid, void VolumeMeshBuilder::add_padding(int pad_size) { #ifdef WITH_OPENVDB - openvdb::tools::dilateVoxels(topology_grid->tree(), pad_size); + openvdb::tools::dilateActiveValues( + topology_grid->tree(), pad_size, openvdb::tools::NN_FACE, openvdb::tools::IGNORE_TILES); #else (void)pad_size; #endif @@ -482,11 +484,141 @@ static openvdb::GridBase::ConstPtr openvdb_grid_from_device_texture(device_textu return sparse; } + +static int estimate_required_velocity_padding(openvdb::GridBase::ConstPtr grid, + float velocity_scale) +{ + /* TODO: we may need to also find outliers and clamp them to avoid adding too much padding. */ + openvdb::math::Extrema extrema; + openvdb::Vec3d voxel_size; + + /* External .vdb files have a vec3 type for velocity, but the Blender exporter creates a vec4. */ + if (grid->isType<openvdb::Vec3fGrid>()) { + openvdb::Vec3fGrid::ConstPtr vel_grid = openvdb::gridConstPtrCast<openvdb::Vec3fGrid>(grid); + extrema = openvdb::tools::extrema(vel_grid->cbeginValueOn()); + voxel_size = vel_grid->voxelSize(); + } + else if (grid->isType<openvdb::Vec4fGrid>()) { + openvdb::Vec4fGrid::ConstPtr vel_grid = openvdb::gridConstPtrCast<openvdb::Vec4fGrid>(grid); + extrema = openvdb::tools::extrema(vel_grid->cbeginValueOn()); + voxel_size = vel_grid->voxelSize(); + } + else { + assert(0); + return 0; + } + + /* We should only have uniform grids, so x = y = z, but we never know. */ + const double max_voxel_size = openvdb::math::Max(voxel_size.x(), voxel_size.y(), voxel_size.z()); + if (max_voxel_size == 0.0) { + return 0; + } + + const double estimated_padding = extrema.max() * static_cast<double>(velocity_scale) / + max_voxel_size; + + return static_cast<int>(std::ceil(estimated_padding)); +} + +static openvdb::FloatGrid::ConstPtr get_vdb_for_attribute(Volume *volume, AttributeStandard std) +{ + Attribute *attr = volume->attributes.find(std); + if (!attr) { + return nullptr; + } + + ImageHandle &handle = attr->data_voxel(); + VDBImageLoader *vdb_loader = handle.vdb_loader(); + if (!vdb_loader) { + return nullptr; + } + + openvdb::GridBase::ConstPtr grid = vdb_loader->get_grid(); + if (!grid) { + return nullptr; + } + + if (!grid->isType<openvdb::FloatGrid>()) { + return nullptr; + } + + return openvdb::gridConstPtrCast<openvdb::FloatGrid>(grid); +} + +class MergeScalarGrids { + typedef openvdb::FloatTree ScalarTree; + + openvdb::tree::ValueAccessor<const ScalarTree> m_acc_x, m_acc_y, m_acc_z; + + public: + MergeScalarGrids(const ScalarTree *x_tree, const ScalarTree *y_tree, const ScalarTree *z_tree) + : m_acc_x(*x_tree), m_acc_y(*y_tree), m_acc_z(*z_tree) + { + } + + MergeScalarGrids(const MergeScalarGrids &other) + : m_acc_x(other.m_acc_x), m_acc_y(other.m_acc_y), m_acc_z(other.m_acc_z) + { + } + + void operator()(const openvdb::Vec3STree::ValueOnIter &it) const + { + using namespace openvdb; + + const math::Coord xyz = it.getCoord(); + float x = m_acc_x.getValue(xyz); + float y = m_acc_y.getValue(xyz); + float z = m_acc_z.getValue(xyz); + + it.setValue(math::Vec3s(x, y, z)); + } +}; + +static void merge_scalar_grids_for_velocity(const Scene *scene, Volume *volume) +{ + if (volume->attributes.find(ATTR_STD_VOLUME_VELOCITY)) { + /* A vector grid for velocity is already available. */ + return; + } + + openvdb::FloatGrid::ConstPtr vel_x_grid = get_vdb_for_attribute(volume, + ATTR_STD_VOLUME_VELOCITY_X); + openvdb::FloatGrid::ConstPtr vel_y_grid = get_vdb_for_attribute(volume, + ATTR_STD_VOLUME_VELOCITY_Y); + openvdb::FloatGrid::ConstPtr vel_z_grid = get_vdb_for_attribute(volume, + ATTR_STD_VOLUME_VELOCITY_Z); + + if (!(vel_x_grid && vel_y_grid && vel_z_grid)) { + return; + } + + openvdb::Vec3fGrid::Ptr vecgrid = openvdb::Vec3SGrid::create(openvdb::Vec3s(0.0f)); + + /* Activate voxels in the vector grid based on the scalar grids to ensure thread safety during + * the merge. */ + vecgrid->tree().topologyUnion(vel_x_grid->tree()); + vecgrid->tree().topologyUnion(vel_y_grid->tree()); + vecgrid->tree().topologyUnion(vel_z_grid->tree()); + + MergeScalarGrids op(&vel_x_grid->tree(), &vel_y_grid->tree(), &vel_z_grid->tree()); + openvdb::tools::foreach (vecgrid->beginValueOn(), op, true, false); + + /* Assume all grids have the same transformation. */ + openvdb::math::Transform::Ptr transform = openvdb::ConstPtrCast<openvdb::math::Transform>( + vel_x_grid->transformPtr()); + vecgrid->setTransform(transform); + + /* Make an attribute for it. */ + Attribute *attr = volume->attributes.add(ATTR_STD_VOLUME_VELOCITY); + ImageLoader *loader = new VDBImageLoader(vecgrid, "merged_velocity"); + ImageParams params; + attr->data_voxel() = scene->image_manager->add_image(loader, params); +} #endif /* ************************************************************************** */ -void GeometryManager::create_volume_mesh(Volume *volume, Progress &progress) +void GeometryManager::create_volume_mesh(const Scene *scene, Volume *volume, Progress &progress) { string msg = string_printf("Computing Volume Mesh %s", volume->name.c_str()); progress.set_status("Updating Mesh", msg); @@ -495,7 +627,7 @@ void GeometryManager::create_volume_mesh(Volume *volume, Progress &progress) Shader *volume_shader = NULL; int pad_size = 0; - foreach (Node *node, volume->get_used_shaders()) { + for (Node *node : volume->get_used_shaders()) { Shader *shader = static_cast<Shader *>(node); if (!shader->has_volume) { @@ -529,7 +661,9 @@ void GeometryManager::create_volume_mesh(Volume *volume, Progress &progress) VolumeMeshBuilder builder; #ifdef WITH_OPENVDB - foreach (Attribute &attr, volume->attributes.attributes) { + merge_scalar_grids_for_velocity(scene, volume); + + for (Attribute &attr : volume->attributes.attributes) { if (attr.element != ATTR_ELEMENT_VOXEL) { continue; } @@ -567,9 +701,17 @@ void GeometryManager::create_volume_mesh(Volume *volume, Progress &progress) } if (grid) { + /* Add padding based on the maximum velocity vector. */ + if (attr.std == ATTR_STD_VOLUME_VELOCITY && scene->need_motion() != Scene::MOTION_NONE) { + pad_size = max(pad_size, + estimate_required_velocity_padding(grid, volume->get_velocity_scale())); + } + builder.add_grid(grid, do_clipping, volume->get_clipping()); } } +#else + (void)scene; #endif /* If nothing to build, early out. */ diff --git a/intern/cycles/scene/volume.h b/intern/cycles/scene/volume.h index cae0f5f5bce..2b94aaf5253 100644 --- a/intern/cycles/scene/volume.h +++ b/intern/cycles/scene/volume.h @@ -18,6 +18,7 @@ class Volume : public Mesh { NODE_SOCKET_API(float, clipping) NODE_SOCKET_API(float, step_size) NODE_SOCKET_API(bool, object_space) + NODE_SOCKET_API(float, velocity_scale) virtual void clear(bool preserve_shaders = false) override; }; |