Welcome to mirror list, hosted at ThFree Co, Russian Federation.

git.blender.org/blender.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'intern/cycles')
-rw-r--r--intern/cycles/app/io_export_cycles_xml.py41
-rw-r--r--intern/cycles/blender/addon/camera.py22
-rw-r--r--intern/cycles/blender/addon/engine.py14
-rw-r--r--intern/cycles/blender/addon/operators.py6
-rw-r--r--intern/cycles/blender/addon/presets.py1
-rw-r--r--intern/cycles/blender/addon/properties.py25
-rw-r--r--intern/cycles/blender/addon/ui.py33
-rw-r--r--intern/cycles/blender/addon/version_update.py32
-rw-r--r--intern/cycles/blender/camera.cpp10
-rw-r--r--intern/cycles/blender/object.cpp7
-rw-r--r--intern/cycles/blender/sync.cpp2
-rw-r--r--intern/cycles/blender/volume.cpp49
-rw-r--r--intern/cycles/device/cuda/queue.cpp5
-rw-r--r--intern/cycles/device/cuda/queue.h2
-rw-r--r--intern/cycles/device/hip/queue.cpp5
-rw-r--r--intern/cycles/device/hip/queue.h2
-rw-r--r--intern/cycles/device/metal/queue.h2
-rw-r--r--intern/cycles/device/metal/queue.mm5
-rw-r--r--intern/cycles/device/queue.h3
-rw-r--r--intern/cycles/kernel/device/hip/compat.h2
-rw-r--r--intern/cycles/kernel/integrator/mnee.h272
-rw-r--r--intern/cycles/kernel/integrator/shader_eval.h59
-rw-r--r--intern/cycles/kernel/types.h25
-rw-r--r--intern/cycles/scene/attribute.cpp9
-rw-r--r--intern/cycles/scene/camera.cpp1
-rw-r--r--intern/cycles/scene/camera.h12
-rw-r--r--intern/cycles/scene/geometry.cpp2
-rw-r--r--intern/cycles/scene/geometry.h2
-rw-r--r--intern/cycles/scene/image_vdb.cpp5
-rw-r--r--intern/cycles/scene/image_vdb.h3
-rw-r--r--intern/cycles/scene/object.cpp8
-rw-r--r--intern/cycles/scene/scene.cpp6
-rw-r--r--intern/cycles/scene/scene.h2
-rw-r--r--intern/cycles/scene/volume.cpp152
-rw-r--r--intern/cycles/scene/volume.h1
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;
};