diff options
author | Gaia Clary <gaia.clary@machinimatrix.org> | 2019-05-22 23:35:40 +0300 |
---|---|---|
committer | Gaia Clary <gaia.clary@machinimatrix.org> | 2019-05-22 23:35:40 +0300 |
commit | 4755c2345854dd97234de2438e00008311537dde (patch) | |
tree | 3f7f5a029c50fda39630bdb6ef518f925df01f18 /intern/cycles | |
parent | 43e6bb85cee0802887eae9489a2bd73836daf41d (diff) | |
parent | b471e48c305b6fdee69a862b50547a59dd368c4d (diff) |
Merge branch 'master' into collada
Diffstat (limited to 'intern/cycles')
43 files changed, 1574 insertions, 641 deletions
diff --git a/intern/cycles/blender/addon/__init__.py b/intern/cycles/blender/addon/__init__.py index 93a1271b4b4..776a73dabd8 100644 --- a/intern/cycles/blender/addon/__init__.py +++ b/intern/cycles/blender/addon/__init__.py @@ -87,16 +87,16 @@ class CyclesRender(bpy.types.RenderEngine): engine.bake(self, depsgraph, obj, pass_type, pass_filter, object_id, pixel_array, num_pixels, depth, result) # viewport render - def view_update(self, context): + def view_update(self, context, depsgraph): if not self.session: engine.create(self, context.blend_data, context.region, context.space_data, context.region_data) - engine.reset(self, context.blend_data, context.depsgraph) - engine.sync(self, context.depsgraph, context.blend_data) + engine.reset(self, context.blend_data, depsgraph) + engine.sync(self, depsgraph, context.blend_data) - def view_draw(self, context): - engine.draw(self, context.depsgraph, context.region, context.space_data, context.region_data) + def view_draw(self, context, depsgraph): + engine.draw(self, depsgraph, context.region, context.space_data, context.region_data) def update_script_node(self, node): if engine.with_osl(): diff --git a/intern/cycles/blender/addon/properties.py b/intern/cycles/blender/addon/properties.py index 6da88a769f5..d9e145c8b75 100644 --- a/intern/cycles/blender/addon/properties.py +++ b/intern/cycles/blender/addon/properties.py @@ -68,11 +68,6 @@ enum_filter_types = ( ('BLACKMAN_HARRIS', "Blackman-Harris", "Blackman-Harris filter"), ) -enum_aperture_types = ( - ('RADIUS', "Radius", "Directly change the size of the aperture"), - ('FSTOP', "F-stop", "Change the size of the aperture by f-stop"), -) - enum_panorama_types = ( ('EQUIRECTANGULAR', "Equirectangular", "Render the scene with a spherical camera, also known as Lat Long panorama"), ('FISHEYE_EQUIDISTANT', "Fisheye Equidistant", "Ideal for fulldomes, ignore the sensor dimensions"), @@ -417,11 +412,6 @@ class CyclesRenderSettings(bpy.types.PropertyGroup): min=0.0, max=10.0, default=1.0, ) - film_transparent: BoolProperty( - name="Transparent", - description="World background is transparent, for compositing the render over another background", - default=False, - ) film_transparent_glass: BoolProperty( name="Transparent Glass", description="Render transmissive surfaces as transparent, for compositing glass over another background", @@ -747,49 +737,6 @@ class CyclesRenderSettings(bpy.types.PropertyGroup): class CyclesCameraSettings(bpy.types.PropertyGroup): - aperture_type: EnumProperty( - name="Aperture Type", - description="Use f-stop number or aperture radius", - items=enum_aperture_types, - default='RADIUS', - ) - aperture_fstop: FloatProperty( - name="Aperture f-stop", - description="F-stop ratio (lower numbers give more defocus, higher numbers give a sharper image)", - min=0.0, soft_min=0.1, soft_max=64.0, - default=5.6, - step=10, - precision=1, - ) - aperture_size: FloatProperty( - name="Aperture Size", - description="Radius of the aperture for depth of field (higher values give more defocus)", - min=0.0, soft_max=10.0, - default=0.0, - step=1, - precision=4, - subtype='DISTANCE', - ) - aperture_blades: IntProperty( - name="Aperture Blades", - description="Number of blades in aperture for polygonal bokeh (at least 3)", - min=0, max=100, - default=0, - ) - aperture_rotation: FloatProperty( - name="Aperture Rotation", - description="Rotation of blades in aperture", - soft_min=-pi, soft_max=pi, - subtype='ANGLE', - default=0, - ) - aperture_ratio: FloatProperty( - name="Aperture Ratio", - description="Distortion to simulate anamorphic lens bokeh", - min=0.01, soft_min=1.0, soft_max=2.0, - default=1.0, - precision=4, - ) panorama_type: EnumProperty( name="Panorama Type", description="Distortion to use for the calculation", @@ -1490,7 +1437,7 @@ class CyclesPreferences(bpy.types.AddonPreferences): break if not found_device: - col = box.column(align=True); + col = box.column(align=True) col.label(text="No compatible GPUs found for path tracing", icon='INFO') col.label(text="Cycles will render on the CPU", icon='BLANK1') return diff --git a/intern/cycles/blender/addon/ui.py b/intern/cycles/blender/addon/ui.py index 0845f567056..a49efb3567f 100644 --- a/intern/cycles/blender/addon/ui.py +++ b/intern/cycles/blender/addon/ui.py @@ -57,7 +57,7 @@ def node_panel(cls): node_cls.bl_space_type = 'NODE_EDITOR' node_cls.bl_region_type = 'UI' - node_cls.bl_category = "Node" + node_cls.bl_category = "Options" if hasattr(node_cls, 'bl_parent_id'): node_cls.bl_parent_id = 'NODE_' + node_cls.bl_parent_id @@ -528,31 +528,32 @@ class CYCLES_RENDER_PT_film(CyclesButtonsPanel, Panel): class CYCLES_RENDER_PT_film_transparency(CyclesButtonsPanel, Panel): - bl_label = "Transparency" + bl_label = "Transparent" bl_parent_id = "CYCLES_RENDER_PT_film" def draw_header(self, context): layout = self.layout scene = context.scene - cscene = scene.cycles + rd = scene.render - layout.prop(cscene, "film_transparent", text="") + layout.prop(rd, "film_transparent", text="") def draw(self, context): layout = self.layout layout.use_property_split = True layout.use_property_decorate = False scene = context.scene + rd = scene.render cscene = scene.cycles - layout.active = cscene.film_transparent + layout.active = rd.film_transparent col = layout.column() col.prop(cscene, "film_transparent_glass", text="Transparent Glass") sub = col.column() - sub.active = cscene.film_transparent and cscene.film_transparent_glass + sub.active = rd.film_transparent and cscene.film_transparent_glass sub.prop(cscene, "film_transparent_roughness", text="Roughness Threshold") @@ -1015,20 +1016,27 @@ class CYCLES_CAMERA_PT_dof(CyclesButtonsPanel, Panel): def poll(cls, context): return context.camera and CyclesButtonsPanel.poll(context) + def draw_header(self, context): + cam = context.camera + dof = cam.dof + self.layout.prop(dof, "use_dof", text="") + def draw(self, context): layout = self.layout layout.use_property_split = True cam = context.camera + dof = cam.dof + layout.active = dof.use_dof split = layout.split() col = split.column() - col.prop(cam, "dof_object", text="Focus Object") + col.prop(dof, "focus_object", text="Focus Object") sub = col.row() - sub.active = cam.dof_object is None - sub.prop(cam, "dof_distance", text="Distance") + sub.active = dof.focus_object is None + sub.prop(dof, "focus_distance", text="Distance") class CYCLES_CAMERA_PT_dof_aperture(CyclesButtonsPanel, Panel): @@ -1042,44 +1050,17 @@ class CYCLES_CAMERA_PT_dof_aperture(CyclesButtonsPanel, Panel): def draw(self, context): layout = self.layout layout.use_property_split = True - flow = layout.grid_flow(row_major=True, columns=0, even_columns=True, even_rows=False, align=False) cam = context.camera - ccam = cam.cycles - - col = flow.column() - col.prop(ccam, "aperture_type") - if ccam.aperture_type == 'RADIUS': - col.prop(ccam, "aperture_size", text="Size") - elif ccam.aperture_type == 'FSTOP': - col.prop(ccam, "aperture_fstop", text="Number") - col.separator() - - col = flow.column() - col.prop(ccam, "aperture_blades", text="Blades") - col.prop(ccam, "aperture_rotation", text="Rotation") - col.prop(ccam, "aperture_ratio", text="Ratio") - - -class CYCLES_CAMERA_PT_dof_viewport(CyclesButtonsPanel, Panel): - bl_label = "Viewport" - bl_parent_id = "CYCLES_CAMERA_PT_dof" - - @classmethod - def poll(cls, context): - return context.camera and CyclesButtonsPanel.poll(context) - - def draw(self, context): - layout = self.layout - layout.use_property_split = True + dof = cam.dof + layout.active = dof.use_dof flow = layout.grid_flow(row_major=True, columns=0, even_columns=True, even_rows=False, align=False) - cam = context.camera - dof_options = cam.gpu_dof - - sub = flow.column(align=True) - sub.prop(dof_options, "fstop") - sub.prop(dof_options, "blades") + col = flow.column() + col.prop(dof, "aperture_fstop") + col.prop(dof, "aperture_blades") + col.prop(dof, "aperture_rotation") + col.prop(dof, "aperture_ratio") class CYCLES_PT_context_material(CyclesButtonsPanel, Panel): @@ -1191,27 +1172,53 @@ class CYCLES_OBJECT_PT_motion_blur(CyclesButtonsPanel, Panel): row.prop(cob, "motion_steps", text="Steps") -class CYCLES_OBJECT_PT_cycles_settings(CyclesButtonsPanel, Panel): - bl_label = "Cycles Settings" +def has_geometry_visibility(ob): + return ob and ((ob.type in {'MESH', 'CURVE', 'SURFACE', 'FONT', 'META', 'LIGHT'}) or + (ob.instance_type == 'COLLECTION' and ob.instance_collection)) + + +class CYCLES_OBJECT_PT_visibility(CyclesButtonsPanel, Panel): + bl_label = "Visibility" bl_context = "object" bl_options = {'DEFAULT_CLOSED'} @classmethod def poll(cls, context): - ob = context.object - return (CyclesButtonsPanel.poll(context) and - ob and ((ob.type in {'MESH', 'CURVE', 'SURFACE', 'FONT', 'META', 'LIGHT'}) or - (ob.instance_type == 'COLLECTION' and ob.instance_collection))) + return CyclesButtonsPanel.poll(context) and (context.object) def draw(self, context): - pass + layout = self.layout + layout.use_property_split = True + flow = layout.grid_flow(row_major=False, columns=0, even_columns=True, even_rows=False, align=False) + layout = self.layout + ob = context.object -class CYCLES_OBJECT_PT_cycles_settings_ray_visibility(CyclesButtonsPanel, Panel): + col = flow.column() + col.prop(ob, "hide_viewport", text="Show in Viewports", invert_checkbox=True) + col = flow.column() + col.prop(ob, "hide_render", text="Show in Renders", invert_checkbox=True) + col = flow.column() + col.prop(ob, "hide_select", text="Selectable", invert_checkbox=True) + + if has_geometry_visibility(ob): + cob = ob.cycles + col = flow.column() + col.prop(cob, "is_shadow_catcher") + col = flow.column() + col.prop(cob, "is_holdout") + + +class CYCLES_OBJECT_PT_visibility_ray_visibility(CyclesButtonsPanel, Panel): bl_label = "Ray Visibility" - bl_parent_id = "CYCLES_OBJECT_PT_cycles_settings" + bl_parent_id = "CYCLES_OBJECT_PT_visibility" bl_context = "object" + @classmethod + def poll(cls, context): + ob = context.object + return CyclesButtonsPanel.poll(context) and has_geometry_visibility(ob) + def draw(self, context): layout = self.layout layout.use_property_split = True @@ -1241,19 +1248,16 @@ class CYCLES_OBJECT_PT_cycles_settings_ray_visibility(CyclesButtonsPanel, Panel) layout.separator() - flow = layout.grid_flow(row_major=True, columns=0, even_columns=True, even_rows=False, align=False) - - col = flow.column() - col.prop(cob, "is_shadow_catcher") - col = flow.column() - col.prop(cob, "is_holdout") - -class CYCLES_OBJECT_PT_cycles_settings_performance(CyclesButtonsPanel, Panel): - bl_label = "Performance" - bl_parent_id = "CYCLES_OBJECT_PT_cycles_settings" +class CYCLES_OBJECT_PT_visibility_culling(CyclesButtonsPanel, Panel): + bl_label = "Culling" + bl_parent_id = "CYCLES_OBJECT_PT_visibility" bl_context = "object" + @classmethod + def poll(cls, context): + ob = context.object + return CyclesButtonsPanel.poll(context) and has_geometry_visibility(ob) def draw(self, context): layout = self.layout @@ -1341,8 +1345,14 @@ class CYCLES_LIGHT_PT_light(CyclesButtonsPanel, Panel): col = layout.column() - if light.type in {'POINT', 'SUN', 'SPOT'}: + col.prop(light, "color") + col.prop(light, "energy") + col.separator() + + if light.type in {'POINT', 'SPOT'}: col.prop(light, "shadow_soft_size", text="Size") + elif light.type == 'SUN': + col.prop(light, "angle") elif light.type == 'AREA': col.prop(light, "shape", text="Shape") sub = col.column(align=True) @@ -1384,8 +1394,7 @@ class CYCLES_LIGHT_PT_nodes(CyclesButtonsPanel, Panel): layout = self.layout light = context.light - if not panel_node_draw(layout, light, 'OUTPUT_LIGHT', 'Surface'): - layout.prop(light, "color") + panel_node_draw(layout, light, 'OUTPUT_LIGHT', 'Surface') class CYCLES_LIGHT_PT_spot(CyclesButtonsPanel, Panel): @@ -1714,7 +1723,7 @@ class CYCLES_MATERIAL_PT_settings_surface(CyclesButtonsPanel, Panel): col = layout.column() col.prop(cmat, "sample_as_light", text="Multiple Importance") col.prop(cmat, "use_transparent_shadow") - col.prop(cmat, "displacement_method", text="Displacement Method") + col.prop(cmat, "displacement_method", text="Displacement") def draw(self, context): self.draw_shared(self, context.material) @@ -2087,6 +2096,7 @@ def get_panels(): 'MATERIAL_PT_preview', 'NODE_DATA_PT_light', 'NODE_DATA_PT_spot', + 'OBJECT_PT_visibility', 'VIEWLAYER_PT_filter', 'VIEWLAYER_PT_layer_passes', 'RENDER_PT_post_processing', @@ -2141,12 +2151,11 @@ classes = ( CYCLES_PT_post_processing, CYCLES_CAMERA_PT_dof, CYCLES_CAMERA_PT_dof_aperture, - CYCLES_CAMERA_PT_dof_viewport, CYCLES_PT_context_material, CYCLES_OBJECT_PT_motion_blur, - CYCLES_OBJECT_PT_cycles_settings, - CYCLES_OBJECT_PT_cycles_settings_ray_visibility, - CYCLES_OBJECT_PT_cycles_settings_performance, + CYCLES_OBJECT_PT_visibility, + CYCLES_OBJECT_PT_visibility_ray_visibility, + CYCLES_OBJECT_PT_visibility_culling, CYCLES_LIGHT_PT_preview, CYCLES_LIGHT_PT_light, CYCLES_LIGHT_PT_nodes, diff --git a/intern/cycles/blender/blender_camera.cpp b/intern/cycles/blender/blender_camera.cpp index b3bfaa992a9..92dc8a8a4c6 100644 --- a/intern/cycles/blender/blender_camera.cpp +++ b/intern/cycles/blender/blender_camera.cpp @@ -119,10 +119,10 @@ static float blender_camera_focal_distance(BL::RenderEngine &b_engine, BL::Camera &b_camera, BlenderCamera *bcam) { - BL::Object b_dof_object = b_camera.dof_object(); + BL::Object b_dof_object = b_camera.dof().focus_object(); if (!b_dof_object) - return b_camera.dof_distance(); + return b_camera.dof().focus_distance(); /* for dof object, return distance along camera Z direction */ BL::Array<float, 16> b_ob_matrix; @@ -191,26 +191,30 @@ static void blender_camera_from_object(BlenderCamera *bcam, bcam->lens = b_camera.lens(); - /* allow f/stop number to change aperture_size but still - * give manual control over aperture radius */ - int aperture_type = get_enum(ccamera, "aperture_type"); - - if (aperture_type == 1) { - float fstop = RNA_float_get(&ccamera, "aperture_fstop"); + if (b_camera.dof().use_dof()) { + /* allow f/stop number to change aperture_size but still + * give manual control over aperture radius */ + float fstop = b_camera.dof().aperture_fstop(); fstop = max(fstop, 1e-5f); if (bcam->type == CAMERA_ORTHOGRAPHIC) bcam->aperturesize = 1.0f / (2.0f * fstop); else bcam->aperturesize = (bcam->lens * 1e-3f) / (2.0f * fstop); - } - else - bcam->aperturesize = RNA_float_get(&ccamera, "aperture_size"); - bcam->apertureblades = RNA_int_get(&ccamera, "aperture_blades"); - bcam->aperturerotation = RNA_float_get(&ccamera, "aperture_rotation"); - bcam->focaldistance = blender_camera_focal_distance(b_engine, b_ob, b_camera, bcam); - bcam->aperture_ratio = RNA_float_get(&ccamera, "aperture_ratio"); + bcam->apertureblades = b_camera.dof().aperture_blades(); + bcam->aperturerotation = b_camera.dof().aperture_rotation(); + bcam->focaldistance = blender_camera_focal_distance(b_engine, b_ob, b_camera, bcam); + bcam->aperture_ratio = b_camera.dof().aperture_ratio(); + } + else { + /* DOF is turned of for the camera. */ + bcam->aperturesize = 0.0f; + bcam->apertureblades = 0; + bcam->aperturerotation = 0.0f; + bcam->focaldistance = 0.0f; + bcam->aperture_ratio = 1.0f; + } bcam->shift.x = b_engine.camera_shift_x(b_ob, bcam->use_spherical_stereo); bcam->shift.y = b_camera.shift_y(); diff --git a/intern/cycles/blender/blender_mesh.cpp b/intern/cycles/blender/blender_mesh.cpp index de594f4fb6c..2a5c163aaeb 100644 --- a/intern/cycles/blender/blender_mesh.cpp +++ b/intern/cycles/blender/blender_mesh.cpp @@ -14,6 +14,7 @@ * limitations under the License. */ +#include "render/colorspace.h" #include "render/mesh.h" #include "render/object.h" #include "render/scene.h" @@ -291,7 +292,6 @@ static void create_mesh_volume_attribute( VoxelAttribute *volume_data = attr->data_voxel(); ImageMetaData metadata; bool animated = false; - bool use_alpha = true; volume_data->manager = image_manager; volume_data->slot = image_manager->add_image(Attribute::standard_name(std), @@ -300,7 +300,8 @@ static void create_mesh_volume_attribute( frame, INTERPOLATION_LINEAR, EXTENSION_CLIP, - use_alpha, + IMAGE_ALPHA_AUTO, + u_colorspace_raw, metadata); } diff --git a/intern/cycles/blender/blender_object.cpp b/intern/cycles/blender/blender_object.cpp index 00f53804e38..c07e896ab33 100644 --- a/intern/cycles/blender/blender_object.cpp +++ b/intern/cycles/blender/blender_object.cpp @@ -149,7 +149,7 @@ void BlenderSync::sync_light(BL::Object &b_parent, // } case BL::Light::type_SUN: { BL::SunLight b_sun_light(b_light); - light->size = b_sun_light.shadow_soft_size(); + light->angle = b_sun_light.angle(); light->type = LIGHT_DISTANT; break; } @@ -182,6 +182,10 @@ void BlenderSync::sync_light(BL::Object &b_parent, } } + /* strength */ + light->strength = get_float3(b_light.color()); + light->strength *= BL::PointLight(b_light).energy(); + /* location and (inverted!) direction */ light->co = transform_get_column(&tfm, 3); light->dir = -transform_get_column(&tfm, 2); diff --git a/intern/cycles/blender/blender_python.cpp b/intern/cycles/blender/blender_python.cpp index ffd1c70a4e4..3ab8c8bd6d9 100644 --- a/intern/cycles/blender/blender_python.cpp +++ b/intern/cycles/blender/blender_python.cpp @@ -937,6 +937,15 @@ static PyObject *set_resumable_chunk_range_func(PyObject * /*self*/, PyObject *a Py_RETURN_NONE; } +static PyObject *clear_resumable_chunk_func(PyObject * /*self*/, PyObject * /*value*/) +{ + VLOG(1) << "Clear resumable render"; + BlenderSession::num_resumable_chunks = 0; + BlenderSession::current_resumable_chunk = 0; + + Py_RETURN_NONE; +} + static PyObject *enable_print_stats_func(PyObject * /*self*/, PyObject * /*args*/) { BlenderSession::print_render_stats = true; @@ -992,6 +1001,7 @@ static PyMethodDef methods[] = { /* Resumable render */ {"set_resumable_chunk", set_resumable_chunk_func, METH_VARARGS, ""}, {"set_resumable_chunk_range", set_resumable_chunk_range_func, METH_VARARGS, ""}, + {"clear_resumable_chunk", clear_resumable_chunk_func, METH_NOARGS, ""}, /* Compute Device selection */ {"get_device_types", get_device_types_func, METH_VARARGS, ""}, diff --git a/intern/cycles/blender/blender_session.cpp b/intern/cycles/blender/blender_session.cpp index 3a7e5f02b1d..11b6a38c195 100644 --- a/intern/cycles/blender/blender_session.cpp +++ b/intern/cycles/blender/blender_session.cpp @@ -16,12 +16,13 @@ #include <stdlib.h> +#include "device/device.h" #include "render/background.h" #include "render/buffers.h" #include "render/camera.h" -#include "device/device.h" -#include "render/integrator.h" +#include "render/colorspace.h" #include "render/film.h" +#include "render/integrator.h" #include "render/light.h" #include "render/mesh.h" #include "render/object.h" @@ -1158,6 +1159,12 @@ void BlenderSession::builtin_image_info(const string &builtin_name, metadata.height = b_image.size()[1]; metadata.depth = 1; metadata.channels = b_image.channels(); + + if (metadata.is_float) { + /* Float images are already converted on the Blender side, + * no need to do anything in Cycles. */ + metadata.colorspace = u_colorspace_raw; + } } else if (b_id.is_a(&RNA_Object)) { /* smoke volume data */ @@ -1433,7 +1440,12 @@ void BlenderSession::builtin_images_load() { /* Force builtin images to be loaded along with Blender data sync. This * is needed because we may be reading from depsgraph evaluated data which - * can be freed by Blender before Cycles reads it. */ + * can be freed by Blender before Cycles reads it. + * + * TODO: the assumption that no further access to builtin image data will + * happen is really weak, and likely to break in the future. We should find + * a better solution to hand over the data directly to the image manager + * instead of through callbacks whose timing is difficult to control. */ ImageManager *manager = session->scene->image_manager; Device *device = session->device; manager->device_load_builtin(device, session->scene, session->progress); diff --git a/intern/cycles/blender/blender_shader.cpp b/intern/cycles/blender/blender_shader.cpp index d1f823bc2b8..13097f6bf8e 100644 --- a/intern/cycles/blender/blender_shader.cpp +++ b/intern/cycles/blender/blender_shader.cpp @@ -15,6 +15,7 @@ */ #include "render/background.h" +#include "render/colorspace.h" #include "render/graph.h" #include "render/light.h" #include "render/nodes.h" @@ -89,6 +90,12 @@ template<typename NodeType> static ExtensionType get_image_extension(NodeType &b return (ExtensionType)validate_enum_value(value, EXTENSION_NUM_TYPES, EXTENSION_REPEAT); } +static ImageAlphaType get_image_alpha_type(BL::Image &b_image) +{ + int value = b_image.alpha_mode(); + return (ImageAlphaType)validate_enum_value(value, IMAGE_ALPHA_NUM_TYPES, IMAGE_ALPHA_AUTO); +} + /* Graph */ static BL::NodeSocket get_node_output(BL::Node &b_node, const string &name) @@ -650,8 +657,11 @@ static ShaderNode *add_node(Scene *scene, image->builtin_data = NULL; } + PointerRNA colorspace_ptr = b_image.colorspace_settings().ptr; + image->colorspace = get_enum_identifier(colorspace_ptr, "name"); + image->animated = b_image_node.image_user().use_auto_refresh(); - image->use_alpha = b_image.use_alpha(); + image->alpha_type = get_image_alpha_type(b_image); /* TODO: restore */ /* TODO(sergey): Does not work properly when we change builtin type. */ @@ -661,11 +671,11 @@ static ShaderNode *add_node(Scene *scene, image->builtin_data, get_image_interpolation(b_image_node), get_image_extension(b_image_node), - image->use_alpha); + image->use_alpha, + image->colorspace); } #endif } - image->color_space = (NodeImageColorSpace)b_image_node.color_space(); image->projection = (NodeImageProjection)b_image_node.projection(); image->interpolation = get_image_interpolation(b_image_node); image->extension = get_image_extension(b_image_node); @@ -695,8 +705,11 @@ static ShaderNode *add_node(Scene *scene, env->builtin_data = NULL; } + PointerRNA colorspace_ptr = b_image.colorspace_settings().ptr; + env->colorspace = get_enum_identifier(colorspace_ptr, "name"); + env->animated = b_env_node.image_user().use_auto_refresh(); - env->use_alpha = b_image.use_alpha(); + env->alpha_type = get_image_alpha_type(b_image); /* TODO: restore */ /* TODO(sergey): Does not work properly when we change builtin type. */ @@ -706,11 +719,11 @@ static ShaderNode *add_node(Scene *scene, env->builtin_data, get_image_interpolation(b_env_node), EXTENSION_REPEAT, - env->use_alpha); + env->use_alpha, + env->colorspace); } #endif } - env->color_space = (NodeImageColorSpace)b_env_node.color_space(); env->interpolation = get_image_interpolation(b_env_node); env->projection = (NodeEnvironmentProjection)b_env_node.projection(); BL::TexMapping b_texture_mapping(b_env_node.texture_mapping()); @@ -861,7 +874,8 @@ static ShaderNode *add_node(Scene *scene, point_density->builtin_data, point_density->interpolation, EXTENSION_CLIP, - true); + IMAGE_ALPHA_AUTO, + u_colorspace_raw); } node = point_density; @@ -1346,16 +1360,7 @@ void BlenderSync::sync_world(BL::Depsgraph &b_depsgraph, bool update_all) } PointerRNA cscene = RNA_pointer_get(&b_scene.ptr, "cycles"); - - /* when doing preview render check for BI's transparency settings, - * this is so because Blender's preview render routines are not able - * to tweak all cycles's settings depending on different circumstances - */ - if (b_engine.is_preview() == false) - background->transparent = get_boolean(cscene, "film_transparent"); - else - background->transparent = b_scene.render().alpha_mode() == - BL::RenderSettings::alpha_mode_TRANSPARENT; + background->transparent = b_scene.render().film_transparent(); if (background->transparent) { background->transparent_glass = get_boolean(cscene, "film_transparent_glass"); @@ -1401,16 +1406,9 @@ void BlenderSync::sync_lights(BL::Depsgraph &b_depsgraph, bool update_all) add_nodes(scene, b_engine, b_data, b_depsgraph, b_scene, graph, b_ntree); } else { - float strength = 1.0f; - - if (b_light.type() == BL::Light::type_POINT || b_light.type() == BL::Light::type_SPOT || - b_light.type() == BL::Light::type_AREA) { - strength = 100.0f; - } - EmissionNode *emission = new EmissionNode(); - emission->color = get_float3(b_light.color()); - emission->strength = strength; + emission->color = make_float3(1.0f, 1.0f, 1.0f); + emission->strength = 1.0f; graph->add(emission); ShaderNode *out = graph->output(); diff --git a/intern/cycles/blender/blender_util.h b/intern/cycles/blender/blender_util.h index 500634e7526..972d7296727 100644 --- a/intern/cycles/blender/blender_util.h +++ b/intern/cycles/blender/blender_util.h @@ -43,10 +43,10 @@ CCL_NAMESPACE_BEGIN void python_thread_state_save(void **python_thread_state); void python_thread_state_restore(void **python_thread_state); -static inline BL::Mesh object_to_mesh(BL::BlendData &data, +static inline BL::Mesh object_to_mesh(BL::BlendData & /*data*/, BL::Object &object, - BL::Depsgraph &depsgraph, - bool calc_undeformed, + BL::Depsgraph & /*depsgraph*/, + bool /*calc_undeformed*/, Mesh::SubdivisionType subdivision_type) { /* TODO: make this work with copy-on-write, modifiers are already evaluated. */ @@ -75,11 +75,11 @@ static inline BL::Mesh object_to_mesh(BL::BlendData &data, * UV are not empty. */ if (mesh.is_editmode() || (mesh.use_auto_smooth() && subdivision_type == Mesh::SUBDIVISION_NONE)) { - mesh = data.meshes.new_from_object(depsgraph, object, false, false); + mesh = object.to_mesh(); } } else { - mesh = data.meshes.new_from_object(depsgraph, object, true, calc_undeformed); + mesh = object.to_mesh(); } #if 0 @@ -102,11 +102,13 @@ static inline BL::Mesh object_to_mesh(BL::BlendData &data, return mesh; } -static inline void free_object_to_mesh(BL::BlendData &data, BL::Object &object, BL::Mesh &mesh) +static inline void free_object_to_mesh(BL::BlendData & /*data*/, + BL::Object &object, + BL::Mesh &mesh) { /* Free mesh if we didn't just use the existing one. */ if (object.data().ptr.data != mesh.ptr.data) { - data.meshes.remove(mesh, false, true, false); + object.to_mesh_clear(); } } diff --git a/intern/cycles/device/device_cpu.cpp b/intern/cycles/device/device_cpu.cpp index 837a8186064..dc9adcb1537 100644 --- a/intern/cycles/device/device_cpu.cpp +++ b/intern/cycles/device/device_cpu.cpp @@ -332,9 +332,11 @@ class CPUDevice : public Device { if (DebugFlags().cpu.has_sse2() && system_cpu_support_sse2()) { bvh_layout_mask |= BVH_LAYOUT_BVH4; } +#if defined(__x86_64__) || defined(_M_X64) if (DebugFlags().cpu.has_avx2() && system_cpu_support_avx2()) { bvh_layout_mask |= BVH_LAYOUT_BVH8; } +#endif #ifdef WITH_EMBREE bvh_layout_mask |= BVH_LAYOUT_EMBREE; #endif /* WITH_EMBREE */ diff --git a/intern/cycles/kernel/kernel_emission.h b/intern/cycles/kernel/kernel_emission.h index f2eaa7b50a5..34300543f91 100644 --- a/intern/cycles/kernel/kernel_emission.h +++ b/intern/cycles/kernel/kernel_emission.h @@ -90,6 +90,11 @@ ccl_device_noinline float3 direct_emissive_eval(KernelGlobals *kg, eval *= ls->eval_fac; + if (ls->lamp != LAMP_NONE) { + const ccl_global KernelLight *klight = &kernel_tex_fetch(__lights, ls->lamp); + eval *= make_float3(klight->strength[0], klight->strength[1], klight->strength[2]); + } + return eval; } diff --git a/intern/cycles/kernel/kernel_shader.h b/intern/cycles/kernel/kernel_shader.h index 351b623addb..c80124d3eb2 100644 --- a/intern/cycles/kernel/kernel_shader.h +++ b/intern/cycles/kernel/kernel_shader.h @@ -1092,7 +1092,7 @@ ccl_device void shader_eval_surface(KernelGlobals *kg, #ifdef __OSL__ if (kg->osl) { - if (sd->object == OBJECT_NONE) { + if (sd->object == OBJECT_NONE && sd->lamp == LAMP_NONE) { OSLShader::eval_background(kg, sd, state, path_flag); } else { diff --git a/intern/cycles/kernel/kernel_types.h b/intern/cycles/kernel/kernel_types.h index 0c6b4b401f0..18d2a216f72 100644 --- a/intern/cycles/kernel/kernel_types.h +++ b/intern/cycles/kernel/kernel_types.h @@ -1456,6 +1456,8 @@ typedef struct KernelLight { int samples; float max_bounces; float random; + float strength[3]; + float pad1; Transform tfm; Transform itfm; union { diff --git a/intern/cycles/kernel/osl/osl_globals.h b/intern/cycles/kernel/osl/osl_globals.h index 641c9967586..0e6c8d21534 100644 --- a/intern/cycles/kernel/osl/osl_globals.h +++ b/intern/cycles/kernel/osl/osl_globals.h @@ -21,10 +21,14 @@ # include <OSL/oslexec.h> +# include <OpenImageIO/refcnt.h> +# include <OpenImageIO/unordered_map_concurrent.h> + # include "util/util_map.h" # include "util/util_param.h" # include "util/util_thread.h" # include "util/util_vector.h" +# include "util/util_unique_ptr.h" # ifndef WIN32 using std::isfinite; @@ -33,6 +37,13 @@ using std::isfinite; CCL_NAMESPACE_BEGIN class OSLRenderServices; +class ColorSpaceProcessor; + +/* OSL Globals + * + * Data needed by OSL render services, that is global to a rendering session. + * This includes all OSL shaders, name to attribute mapping and texture handles. + * */ struct OSLGlobals { OSLGlobals() diff --git a/intern/cycles/kernel/osl/osl_services.cpp b/intern/cycles/kernel/osl/osl_services.cpp index 6404690224a..08821ffa099 100644 --- a/intern/cycles/kernel/osl/osl_services.cpp +++ b/intern/cycles/kernel/osl/osl_services.cpp @@ -25,6 +25,7 @@ #include <string.h> +#include "render/colorspace.h" #include "render/mesh.h" #include "render/object.h" #include "render/scene.h" @@ -55,10 +56,6 @@ #include "kernel/kernel_accumulate.h" #include "kernel/kernel_shader.h" -#ifdef WITH_PTEX -# include <Ptexture.h> -#endif - CCL_NAMESPACE_BEGIN /* RenderServices implementation */ @@ -124,34 +121,17 @@ ustring OSLRenderServices::u_I("I"); ustring OSLRenderServices::u_u("u"); ustring OSLRenderServices::u_v("v"); ustring OSLRenderServices::u_empty; -ustring OSLRenderServices::u_at_bevel("@bevel"); -ustring OSLRenderServices::u_at_ao("@ao"); -OSLRenderServices::OSLRenderServices() +OSLRenderServices::OSLRenderServices(OSL::TextureSystem *texture_system) + : texture_system(texture_system) { - kernel_globals = NULL; - osl_ts = NULL; - -#ifdef WITH_PTEX - size_t maxmem = 16384 * 1024; - ptex_cache = PtexCache::create(0, maxmem); -#endif } OSLRenderServices::~OSLRenderServices() { - if (osl_ts) { - VLOG(2) << "OSL texture system stats:\n" << osl_ts->getstats(); + if (texture_system) { + VLOG(2) << "OSL texture system stats:\n" << texture_system->getstats(); } -#ifdef WITH_PTEX - ptex_cache->release(); -#endif -} - -void OSLRenderServices::thread_init(KernelGlobals *kernel_globals_, OSL::TextureSystem *osl_ts_) -{ - kernel_globals = kernel_globals_; - osl_ts = osl_ts_; } bool OSLRenderServices::get_matrix(OSL::ShaderGlobals *sg, @@ -233,7 +213,8 @@ bool OSLRenderServices::get_matrix(OSL::ShaderGlobals *sg, ustring from, float time) { - KernelGlobals *kg = kernel_globals; + ShaderData *sd = (ShaderData *)(sg->renderstate); + KernelGlobals *kg = sd->osl_globals; if (from == u_ndc) { copy_matrix(result, kernel_data.cam.ndctoworld); @@ -264,7 +245,8 @@ bool OSLRenderServices::get_inverse_matrix(OSL::ShaderGlobals *sg, ustring to, float time) { - KernelGlobals *kg = kernel_globals; + ShaderData *sd = (ShaderData *)(sg->renderstate); + KernelGlobals *kg = sd->osl_globals; if (to == u_ndc) { copy_matrix(result, kernel_data.cam.worldtondc); @@ -354,7 +336,8 @@ bool OSLRenderServices::get_inverse_matrix(OSL::ShaderGlobals *sg, bool OSLRenderServices::get_matrix(OSL::ShaderGlobals *sg, OSL::Matrix44 &result, ustring from) { - KernelGlobals *kg = kernel_globals; + ShaderData *sd = (ShaderData *)(sg->renderstate); + KernelGlobals *kg = sd->osl_globals; if (from == u_ndc) { copy_matrix(result, kernel_data.cam.ndctoworld); @@ -380,7 +363,8 @@ bool OSLRenderServices::get_inverse_matrix(OSL::ShaderGlobals *sg, OSL::Matrix44 &result, ustring to) { - KernelGlobals *kg = kernel_globals; + ShaderData *sd = (ShaderData *)(sg->renderstate); + KernelGlobals *kg = sd->osl_globals; if (to == u_ndc) { copy_matrix(result, kernel_data.cam.worldtondc); @@ -956,19 +940,44 @@ bool OSLRenderServices::get_userdata( TextureSystem::TextureHandle *OSLRenderServices::get_texture_handle(ustring filename) { - if (filename.length() && filename[0] == '@') { - /* Dummy, we don't use texture handles for builtin textures but need - * to tell the OSL runtime optimizer that this is a valid texture. */ + OSLTextureHandleMap::iterator it = textures.find(filename); + + /* For non-OIIO textures, just return a pointer to our own OSLTextureHandle. */ + if (it != textures.end()) { + if (it->second->type != OSLTextureHandle::OIIO) { + return (TextureSystem::TextureHandle *)it->second.get(); + } + } + + /* Get handle from OpenImageIO. */ + OSL::TextureSystem *ts = texture_system; + TextureSystem::TextureHandle *handle = ts->get_texture_handle(filename); + if (handle == NULL) { return NULL; } - else { - return texturesys()->get_texture_handle(filename); + + /* Insert new OSLTextureHandle if needed. */ + if (it == textures.end()) { + textures.insert(filename, new OSLTextureHandle(OSLTextureHandle::OIIO)); + it = textures.find(filename); } + + /* Assign OIIO texture handle and return. */ + it->second->oiio_handle = handle; + return (TextureSystem::TextureHandle *)it->second.get(); } bool OSLRenderServices::good(TextureSystem::TextureHandle *texture_handle) { - return texturesys()->good(texture_handle); + OSLTextureHandle *handle = (OSLTextureHandle *)texture_handle; + + if (handle->oiio_handle) { + OSL::TextureSystem *ts = texture_system; + return ts->good(handle->oiio_handle); + } + else { + return true; + } } bool OSLRenderServices::texture(ustring filename, @@ -988,69 +997,28 @@ bool OSLRenderServices::texture(ustring filename, float *dresultdt, ustring *errormessage) { - OSL::TextureSystem *ts = osl_ts; + OSLTextureHandle *handle = (OSLTextureHandle *)texture_handle; + OSLTextureHandle::Type texture_type = (handle) ? handle->type : OSLTextureHandle::OIIO; ShaderData *sd = (ShaderData *)(sg->renderstate); - KernelGlobals *kg = sd->osl_globals; - - if (texture_thread_info == NULL) { - OSLThreadData *tdata = kg->osl_tdata; - texture_thread_info = tdata->oiio_thread_info; - } - -#ifdef WITH_PTEX - /* todo: this is just a quick hack, only works with particular files and options */ - if (string_endswith(filename.string(), ".ptx")) { - float2 uv; - int faceid; - - if (!primitive_ptex(kg, sd, &uv, &faceid)) - return false; - - float u = uv.x; - float v = uv.y; - float dudx = 0.0f; - float dvdx = 0.0f; - float dudy = 0.0f; - float dvdy = 0.0f; - - Ptex::String error; - PtexPtr<PtexTexture> r(ptex_cache->get(filename.c_str(), error)); - - if (!r) { - // std::cerr << error.c_str() << std::endl; - return false; - } - - bool mipmaplerp = false; - float sharpness = 1.0f; - PtexFilter::Options opts(PtexFilter::f_bicubic, mipmaplerp, sharpness); - PtexPtr<PtexFilter> f(PtexFilter::getFilter(r, opts)); - - f->eval(result, options.firstchannel, nchannels, faceid, u, v, dudx, dvdx, dudy, dvdy); - - for (int c = r->numChannels(); c < nchannels; c++) - result[c] = result[0]; - - return true; - } -#endif + KernelGlobals *kernel_globals = sd->osl_globals; bool status = false; - if (filename.length() && filename[0] == '@') { - if (filename == u_at_bevel) { + switch (texture_type) { + case OSLTextureHandle::BEVEL: { /* Bevel shader hack. */ if (nchannels >= 3) { PathState *state = sd->osl_path_state; int num_samples = (int)s; float radius = t; - float3 N = svm_bevel(kg, sd, state, radius, num_samples); + float3 N = svm_bevel(kernel_globals, sd, state, radius, num_samples); result[0] = N.x; result[1] = N.y; result[2] = N.z; status = true; } + break; } - else if (filename == u_at_ao) { + case OSLTextureHandle::AO: { /* AO shader hack. */ PathState *state = sd->osl_path_state; int num_samples = (int)s; @@ -1066,19 +1034,13 @@ bool OSLRenderServices::texture(ustring filename, if ((int)options.tblur) { flags |= NODE_AO_GLOBAL_RADIUS; } - result[0] = svm_ao(kg, sd, N, state, radius, num_samples, flags); + result[0] = svm_ao(kernel_globals, sd, N, state, radius, num_samples, flags); status = true; + break; } - else if (filename[1] == 'l') { - /* IES light. */ - int slot = atoi(filename.c_str() + 2); - result[0] = kernel_ies_interp(kg, slot, s, t); - status = true; - } - else { + case OSLTextureHandle::SVM: { /* Packed texture. */ - int slot = atoi(filename.c_str() + 2); - float4 rgba = kernel_tex_image_interp(kg, slot, s, 1.0f - t); + float4 rgba = kernel_tex_image_interp(kernel_globals, handle->svm_slot, s, 1.0f - t); result[0] = rgba[0]; if (nchannels > 1) @@ -1088,37 +1050,62 @@ bool OSLRenderServices::texture(ustring filename, if (nchannels > 3) result[3] = rgba[3]; status = true; + break; } - } - else { - if (texture_handle != NULL) { - status = ts->texture(texture_handle, - texture_thread_info, - options, - s, - t, - dsdx, - dtdx, - dsdy, - dtdy, - nchannels, - result, - dresultds, - dresultdt); + case OSLTextureHandle::IES: { + /* IES light. */ + result[0] = kernel_ies_interp(kernel_globals, handle->svm_slot, s, t); + status = true; + break; } - else { - status = ts->texture(filename, - options, - s, - t, - dsdx, - dtdx, - dsdy, - dtdy, - nchannels, - result, - dresultds, - dresultdt); + case OSLTextureHandle::OIIO: { + /* OpenImageIO texture cache. */ + OSL::TextureSystem *ts = texture_system; + + if (handle && handle->oiio_handle) { + if (texture_thread_info == NULL) { + OSLThreadData *tdata = kernel_globals->osl_tdata; + texture_thread_info = tdata->oiio_thread_info; + } + + status = ts->texture(handle->oiio_handle, + texture_thread_info, + options, + s, + t, + dsdx, + dtdx, + dsdy, + dtdy, + nchannels, + result, + dresultds, + dresultdt); + } + else { + status = ts->texture(filename, + options, + s, + t, + dsdx, + dtdx, + dsdy, + dtdy, + nchannels, + result, + dresultds, + dresultdt); + } + + if (!status) { + /* This might be slow, but prevents error messages leak and + * other nasty stuff happening. */ + ts->geterror(); + } + else if (handle && handle->processor) { + ColorSpaceManager::to_scene_linear(handle->processor, result, nchannels); + } + break; } } @@ -1131,11 +1118,6 @@ bool OSLRenderServices::texture(ustring filename, if (nchannels == 4) result[3] = 1.0f; } - /* This might be slow, but prevents error messages leak and - * other nasty stuff happening. - */ - string err = ts->geterror(); - (void)err; } return status; @@ -1157,56 +1139,83 @@ bool OSLRenderServices::texture3d(ustring filename, float *dresultdr, ustring *errormessage) { - OSL::TextureSystem *ts = osl_ts; - ShaderData *sd = (ShaderData *)(sg->renderstate); - KernelGlobals *kg = sd->osl_globals; + OSLTextureHandle *handle = (OSLTextureHandle *)texture_handle; + OSLTextureHandle::Type texture_type = (handle) ? handle->type : OSLTextureHandle::OIIO; + bool status = false; - if (texture_thread_info == NULL) { - OSLThreadData *tdata = kg->osl_tdata; - texture_thread_info = tdata->oiio_thread_info; - } + switch (texture_type) { + case OSLTextureHandle::SVM: { + /* Packed texture. */ + ShaderData *sd = (ShaderData *)(sg->renderstate); + KernelGlobals *kernel_globals = sd->osl_globals; + int slot = handle->svm_slot; + float4 rgba = kernel_tex_image_interp_3d( + kernel_globals, slot, P.x, P.y, P.z, INTERPOLATION_NONE); - bool status; - if (filename.length() && filename[0] == '@') { - int slot = atoi(filename.c_str() + 1); - float4 rgba = kernel_tex_image_interp_3d(kg, slot, P.x, P.y, P.z, INTERPOLATION_NONE); + result[0] = rgba[0]; + if (nchannels > 1) + result[1] = rgba[1]; + if (nchannels > 2) + result[2] = rgba[2]; + if (nchannels > 3) + result[3] = rgba[3]; + status = true; + break; + } + case OSLTextureHandle::OIIO: { + /* OpenImageIO texture cache. */ + OSL::TextureSystem *ts = texture_system; + + if (handle && handle->oiio_handle) { + if (texture_thread_info == NULL) { + ShaderData *sd = (ShaderData *)(sg->renderstate); + KernelGlobals *kernel_globals = sd->osl_globals; + OSLThreadData *tdata = kernel_globals->osl_tdata; + texture_thread_info = tdata->oiio_thread_info; + } - result[0] = rgba[0]; - if (nchannels > 1) - result[1] = rgba[1]; - if (nchannels > 2) - result[2] = rgba[2]; - if (nchannels > 3) - result[3] = rgba[3]; - status = true; - } - else { - if (texture_handle != NULL) { - status = ts->texture3d(texture_handle, - texture_thread_info, - options, - P, - dPdx, - dPdy, - dPdz, - nchannels, - result, - dresultds, - dresultdt, - dresultdr); + status = ts->texture3d(handle->oiio_handle, + texture_thread_info, + options, + P, + dPdx, + dPdy, + dPdz, + nchannels, + result, + dresultds, + dresultdt, + dresultdr); + } + else { + status = ts->texture3d(filename, + options, + P, + dPdx, + dPdy, + dPdz, + nchannels, + result, + dresultds, + dresultdt, + dresultdr); + } + + if (!status) { + /* This might be slow, but prevents error messages leak and + * other nasty stuff happening. */ + ts->geterror(); + } + else if (handle && handle->processor) { + ColorSpaceManager::to_scene_linear(handle->processor, result, nchannels); + } + break; } - else { - status = ts->texture3d(filename, - options, - P, - dPdx, - dPdy, - dPdz, - nchannels, - result, - dresultds, - dresultdt, - dresultdr); + case OSLTextureHandle::IES: + case OSLTextureHandle::AO: + case OSLTextureHandle::BEVEL: { + status = false; + break; } } @@ -1219,18 +1228,13 @@ bool OSLRenderServices::texture3d(ustring filename, if (nchannels == 4) result[3] = 1.0f; } - /* This might be slow, but prevents error messages leak and - * other nasty stuff happening. - */ - string err = ts->geterror(); - (void)err; } return status; } bool OSLRenderServices::environment(ustring filename, - TextureHandle *th, + TextureHandle *texture_handle, TexturePerthread *thread_info, TextureOpt &options, OSL::ShaderGlobals *sg, @@ -1243,21 +1247,33 @@ bool OSLRenderServices::environment(ustring filename, float *dresultdt, ustring *errormessage) { - OSL::TextureSystem *ts = osl_ts; + OSLTextureHandle *handle = (OSLTextureHandle *)texture_handle; + OSL::TextureSystem *ts = texture_system; + bool status = false; - if (thread_info == NULL) { - ShaderData *sd = (ShaderData *)(sg->renderstate); - KernelGlobals *kg = sd->osl_globals; - OSLThreadData *tdata = kg->osl_tdata; - thread_info = tdata->oiio_thread_info; - } + if (handle && handle->oiio_handle) { + if (thread_info == NULL) { + ShaderData *sd = (ShaderData *)(sg->renderstate); + KernelGlobals *kernel_globals = sd->osl_globals; + OSLThreadData *tdata = kernel_globals->osl_tdata; + thread_info = tdata->oiio_thread_info; + } - if (th == NULL) { - th = ts->get_texture_handle(filename, thread_info); + status = ts->environment(handle->oiio_handle, + thread_info, + options, + R, + dRdx, + dRdy, + nchannels, + result, + dresultds, + dresultdt); + } + else { + status = ts->environment( + filename, options, R, dRdx, dRdy, nchannels, result, dresultds, dresultdt); } - - bool status = ts->environment( - th, thread_info, options, R, dRdx, dRdy, nchannels, result, dresultds, dresultdt); if (!status) { if (nchannels == 3 || nchannels == 4) { @@ -1269,26 +1285,31 @@ bool OSLRenderServices::environment(ustring filename, result[3] = 1.0f; } } + else if (handle && handle->processor) { + ColorSpaceManager::to_scene_linear(handle->processor, result, nchannels); + } return status; } bool OSLRenderServices::get_texture_info(OSL::ShaderGlobals *sg, ustring filename, - TextureHandle *th, + TextureHandle *texture_handle, int subimage, ustring dataname, TypeDesc datatype, void *data) { - OSL::TextureSystem *ts = osl_ts; - if (filename.length() && filename[0] == '@') { - /* Special builtin textures. */ + OSLTextureHandle *handle = (OSLTextureHandle *)texture_handle; + + /* No texture info for other texture types. */ + if (handle && handle->type != OSLTextureHandle::OIIO) { return false; } - else { - return ts->get_texture_info(filename, subimage, dataname, datatype, data); - } + + /* Get texture info from OpenImageIO. */ + OSL::TextureSystem *ts = texture_system; + return ts->get_texture_info(filename, subimage, dataname, datatype, data); } int OSLRenderServices::pointcloud_search(OSL::ShaderGlobals *sg, diff --git a/intern/cycles/kernel/osl/osl_services.h b/intern/cycles/kernel/osl/osl_services.h index 2fad5833fc9..024ef656be1 100644 --- a/intern/cycles/kernel/osl/osl_services.h +++ b/intern/cycles/kernel/osl/osl_services.h @@ -40,13 +40,46 @@ class Shader; struct ShaderData; struct float3; struct KernelGlobals; + +/* OSL Texture Handle + * + * OSL texture lookups are string based. If those strings are known at compile + * time, the OSL compiler can cache a texture handle to use instead of a string. + * + * By default it uses TextureSystem::TextureHandle. But since we want to support + * different kinds of textures and color space conversions, this is our own handle + * with additional data. + * + * These are stored in a concurrent hash map, because OSL can compile multiple + * shaders in parallel. */ + +struct OSLTextureHandle : public OIIO::RefCnt { + enum Type { OIIO, SVM, IES, BEVEL, AO }; + + OSLTextureHandle(Type type = OIIO, int svm_slot = -1) + : type(type), svm_slot(svm_slot), oiio_handle(NULL), processor(NULL) + { + } + + Type type; + int svm_slot; + OSL::TextureSystem::TextureHandle *oiio_handle; + ColorSpaceProcessor *processor; +}; + +typedef OIIO::intrusive_ptr<OSLTextureHandle> OSLTextureHandleRef; +typedef OIIO::unordered_map_concurrent<ustring, OSLTextureHandleRef, ustringHash> + OSLTextureHandleMap; + +/* OSL Render Services + * + * Interface for OSL to access attributes, textures and other scene data. */ + class OSLRenderServices : public OSL::RendererServices { public: - OSLRenderServices(); + OSLRenderServices(OSL::TextureSystem *texture_system); ~OSLRenderServices(); - void thread_init(KernelGlobals *kernel_globals, OSL::TextureSystem *ts); - bool get_matrix(OSL::ShaderGlobals *sg, OSL::Matrix44 &result, OSL::TransformationPtr xform, @@ -255,12 +288,12 @@ class OSLRenderServices : public OSL::RendererServices { static ustring u_at_bevel; static ustring u_at_ao; - private: - KernelGlobals *kernel_globals; - OSL::TextureSystem *osl_ts; -#ifdef WITH_PTEX - PtexCache *ptex_cache; -#endif + /* Texture system and texture handle map are part of the services instead of + * globals to be shared between different render sessions. This saves memory, + * and is required because texture handles are cached as part of the shared + * shading system. */ + OSL::TextureSystem *texture_system; + OSLTextureHandleMap textures; }; CCL_NAMESPACE_END diff --git a/intern/cycles/kernel/osl/osl_shader.cpp b/intern/cycles/kernel/osl/osl_shader.cpp index 3d9c579c9ff..db5ad06d3fc 100644 --- a/intern/cycles/kernel/osl/osl_shader.cpp +++ b/intern/cycles/kernel/osl/osl_shader.cpp @@ -49,7 +49,6 @@ void OSLShader::thread_init(KernelGlobals *kg, /* per thread kernel data init*/ kg->osl = osl_globals; - kg->osl->services->thread_init(kernel_globals, osl_globals->ts); OSL::ShadingSystem *ss = kg->osl->ss; OSLThreadData *tdata = new OSLThreadData(); diff --git a/intern/cycles/kernel/shaders/node_environment_texture.osl b/intern/cycles/kernel/shaders/node_environment_texture.osl index eb32dad392f..43f607f7cb0 100644 --- a/intern/cycles/kernel/shaders/node_environment_texture.osl +++ b/intern/cycles/kernel/shaders/node_environment_texture.osl @@ -47,9 +47,10 @@ shader node_environment_texture( string filename = "", string projection = "equirectangular", string interpolation = "linear", - string color_space = "sRGB", + int compress_as_srgb = 0, + int ignore_alpha = 0, + int unassociate_alpha = 0, int is_float = 1, - int use_alpha = 1, output color Color = 0.0, output float Alpha = 1.0) { @@ -69,13 +70,16 @@ shader node_environment_texture( Color = (color)texture( filename, p[0], 1.0 - p[1], "wrap", "periodic", "interp", interpolation, "alpha", Alpha); - if (use_alpha) { + if (ignore_alpha) { + Alpha = 1.0; + } + else if (unassociate_alpha) { Color = color_unpremultiply(Color, Alpha); if (!is_float) Color = min(Color, 1.0); } - if (color_space == "sRGB") + if (compress_as_srgb) Color = color_srgb_to_scene_linear(Color); } diff --git a/intern/cycles/kernel/shaders/node_ies_light.osl b/intern/cycles/kernel/shaders/node_ies_light.osl index ea8c44e09de..ce0173451da 100644 --- a/intern/cycles/kernel/shaders/node_ies_light.osl +++ b/intern/cycles/kernel/shaders/node_ies_light.osl @@ -21,7 +21,7 @@ shader node_ies_light(int use_mapping = 0, matrix mapping = matrix(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0), - int slot = 0, + string filename = "", float Strength = 1.0, point Vector = I, output float Fac = 0.0) @@ -37,5 +37,5 @@ shader node_ies_light(int use_mapping = 0, float v_angle = acos(-p[2]); float h_angle = atan2(p[0], p[1]) + M_PI; - Fac = Strength * texture(format("@l%d", slot), h_angle, v_angle); + Fac = Strength * texture(filename, h_angle, v_angle); } diff --git a/intern/cycles/kernel/shaders/node_image_texture.osl b/intern/cycles/kernel/shaders/node_image_texture.osl index df5eda39985..f78ca7ec0e8 100644 --- a/intern/cycles/kernel/shaders/node_image_texture.osl +++ b/intern/cycles/kernel/shaders/node_image_texture.osl @@ -56,11 +56,12 @@ point map_to_sphere(vector dir) } color image_texture_lookup(string filename, - string color_space, float u, float v, output float Alpha, - int use_alpha, + int compress_as_srgb, + int ignore_alpha, + int unassociate_alpha, int is_float, string interpolation, string extension) @@ -68,14 +69,17 @@ color image_texture_lookup(string filename, color rgb = (color)texture( filename, u, 1.0 - v, "wrap", extension, "interp", interpolation, "alpha", Alpha); - if (use_alpha) { + if (ignore_alpha) { + Alpha = 1.0; + } + else if (unassociate_alpha) { rgb = color_unpremultiply(rgb, Alpha); if (!is_float) rgb = min(rgb, 1.0); } - if (color_space == "sRGB") { + if (compress_as_srgb) { rgb = color_srgb_to_scene_linear(rgb); } @@ -86,13 +90,14 @@ shader node_image_texture(int use_mapping = 0, matrix mapping = matrix(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0), point Vector = P, string filename = "", - string color_space = "sRGB", string projection = "flat", string interpolation = "smartcubic", string extension = "periodic", float projection_blend = 0.0, + int compress_as_srgb = 0, + int ignore_alpha = 0, + int unassociate_alpha = 0, int is_float = 1, - int use_alpha = 1, output color Color = 0.0, output float Alpha = 1.0) { @@ -102,8 +107,16 @@ shader node_image_texture(int use_mapping = 0, p = transform(mapping, p); if (projection == "flat") { - Color = image_texture_lookup( - filename, color_space, p[0], p[1], Alpha, use_alpha, is_float, interpolation, extension); + Color = image_texture_lookup(filename, + p[0], + p[1], + Alpha, + compress_as_srgb, + ignore_alpha, + unassociate_alpha, + is_float, + interpolation, + extension); } else if (projection == "box") { /* object space normal */ @@ -173,11 +186,12 @@ shader node_image_texture(int use_mapping = 0, if (weight[0] > 0.0) { Color += weight[0] * image_texture_lookup(filename, - color_space, p[1], p[2], tmp_alpha, - use_alpha, + compress_as_srgb, + ignore_alpha, + unassociate_alpha, is_float, interpolation, extension); @@ -185,11 +199,12 @@ shader node_image_texture(int use_mapping = 0, } if (weight[1] > 0.0) { Color += weight[1] * image_texture_lookup(filename, - color_space, p[0], p[2], tmp_alpha, - use_alpha, + compress_as_srgb, + ignore_alpha, + unassociate_alpha, is_float, interpolation, extension); @@ -197,11 +212,12 @@ shader node_image_texture(int use_mapping = 0, } if (weight[2] > 0.0) { Color += weight[2] * image_texture_lookup(filename, - color_space, p[1], p[0], tmp_alpha, - use_alpha, + compress_as_srgb, + ignore_alpha, + unassociate_alpha, is_float, interpolation, extension); @@ -211,11 +227,12 @@ shader node_image_texture(int use_mapping = 0, else if (projection == "sphere") { point projected = map_to_sphere(texco_remap_square(p)); Color = image_texture_lookup(filename, - color_space, projected[0], projected[1], Alpha, - use_alpha, + compress_as_srgb, + ignore_alpha, + unassociate_alpha, is_float, interpolation, extension); @@ -223,11 +240,12 @@ shader node_image_texture(int use_mapping = 0, else if (projection == "tube") { point projected = map_to_tube(texco_remap_square(p)); Color = image_texture_lookup(filename, - color_space, projected[0], projected[1], Alpha, - use_alpha, + compress_as_srgb, + ignore_alpha, + unassociate_alpha, is_float, interpolation, extension); diff --git a/intern/cycles/kernel/svm/svm_image.h b/intern/cycles/kernel/svm/svm_image.h index ee4b8b6e50c..2ef64662d0e 100644 --- a/intern/cycles/kernel/svm/svm_image.h +++ b/intern/cycles/kernel/svm/svm_image.h @@ -16,13 +16,12 @@ CCL_NAMESPACE_BEGIN -ccl_device float4 -svm_image_texture(KernelGlobals *kg, int id, float x, float y, uint srgb, uint use_alpha) +ccl_device float4 svm_image_texture(KernelGlobals *kg, int id, float x, float y, uint flags) { float4 r = kernel_tex_image_interp(kg, id, x, y); const float alpha = r.w; - if (use_alpha && alpha != 1.0f && alpha != 0.0f) { + if ((flags & NODE_IMAGE_ALPHA_UNASSOCIATE) && alpha != 1.0f && alpha != 0.0f) { r /= alpha; const int texture_type = kernel_tex_type(id); if (texture_type == IMAGE_DATA_TYPE_BYTE4 || texture_type == IMAGE_DATA_TYPE_BYTE) { @@ -31,8 +30,7 @@ svm_image_texture(KernelGlobals *kg, int id, float x, float y, uint srgb, uint u r.w = alpha; } - if (srgb) { - /* TODO(lukas): Implement proper conversion for image textures. */ + if (flags & NODE_IMAGE_COMPRESS_AS_SRGB) { r = color_srgb_to_linear_v4(r); } @@ -48,13 +46,12 @@ ccl_device_inline float3 texco_remap_square(float3 co) ccl_device void svm_node_tex_image(KernelGlobals *kg, ShaderData *sd, float *stack, uint4 node) { uint id = node.y; - uint co_offset, out_offset, alpha_offset, srgb; + uint co_offset, out_offset, alpha_offset, flags; - decode_node_uchar4(node.z, &co_offset, &out_offset, &alpha_offset, &srgb); + decode_node_uchar4(node.z, &co_offset, &out_offset, &alpha_offset, &flags); float3 co = stack_load_float3(stack, co_offset); float2 tex_co; - uint use_alpha = stack_valid(alpha_offset); if (node.w == NODE_IMAGE_PROJ_SPHERE) { co = texco_remap_square(co); tex_co = map_to_sphere(co); @@ -66,7 +63,7 @@ ccl_device void svm_node_tex_image(KernelGlobals *kg, ShaderData *sd, float *sta else { tex_co = make_float2(co.x, co.y); } - float4 f = svm_image_texture(kg, id, tex_co.x, tex_co.y, srgb, use_alpha); + float4 f = svm_image_texture(kg, id, tex_co.x, tex_co.y, flags); if (stack_valid(out_offset)) stack_store_float3(stack, out_offset, make_float3(f.x, f.y, f.z)); @@ -145,27 +142,26 @@ ccl_device void svm_node_tex_image_box(KernelGlobals *kg, ShaderData *sd, float } /* now fetch textures */ - uint co_offset, out_offset, alpha_offset, srgb; - decode_node_uchar4(node.z, &co_offset, &out_offset, &alpha_offset, &srgb); + uint co_offset, out_offset, alpha_offset, flags; + decode_node_uchar4(node.z, &co_offset, &out_offset, &alpha_offset, &flags); float3 co = stack_load_float3(stack, co_offset); uint id = node.y; float4 f = make_float4(0.0f, 0.0f, 0.0f, 0.0f); - uint use_alpha = stack_valid(alpha_offset); /* Map so that no textures are flipped, rotation is somewhat arbitrary. */ if (weight.x > 0.0f) { float2 uv = make_float2((signed_N.x < 0.0f) ? 1.0f - co.y : co.y, co.z); - f += weight.x * svm_image_texture(kg, id, uv.x, uv.y, srgb, use_alpha); + f += weight.x * svm_image_texture(kg, id, uv.x, uv.y, flags); } if (weight.y > 0.0f) { float2 uv = make_float2((signed_N.y > 0.0f) ? 1.0f - co.x : co.x, co.z); - f += weight.y * svm_image_texture(kg, id, uv.x, uv.y, srgb, use_alpha); + f += weight.y * svm_image_texture(kg, id, uv.x, uv.y, flags); } if (weight.z > 0.0f) { float2 uv = make_float2((signed_N.z > 0.0f) ? 1.0f - co.y : co.y, co.x); - f += weight.z * svm_image_texture(kg, id, uv.x, uv.y, srgb, use_alpha); + f += weight.z * svm_image_texture(kg, id, uv.x, uv.y, flags); } if (stack_valid(out_offset)) @@ -180,10 +176,10 @@ ccl_device void svm_node_tex_environment(KernelGlobals *kg, uint4 node) { uint id = node.y; - uint co_offset, out_offset, alpha_offset, srgb; + uint co_offset, out_offset, alpha_offset, flags; uint projection = node.w; - decode_node_uchar4(node.z, &co_offset, &out_offset, &alpha_offset, &srgb); + decode_node_uchar4(node.z, &co_offset, &out_offset, &alpha_offset, &flags); float3 co = stack_load_float3(stack, co_offset); float2 uv; @@ -195,8 +191,7 @@ ccl_device void svm_node_tex_environment(KernelGlobals *kg, else uv = direction_to_mirrorball(co); - uint use_alpha = stack_valid(alpha_offset); - float4 f = svm_image_texture(kg, id, uv.x, uv.y, srgb, use_alpha); + float4 f = svm_image_texture(kg, id, uv.x, uv.y, flags); if (stack_valid(out_offset)) stack_store_float3(stack, out_offset, make_float3(f.x, f.y, f.z)); diff --git a/intern/cycles/kernel/svm/svm_types.h b/intern/cycles/kernel/svm/svm_types.h index d31e4f93696..ea92fd7ce59 100644 --- a/intern/cycles/kernel/svm/svm_types.h +++ b/intern/cycles/kernel/svm/svm_types.h @@ -373,11 +373,6 @@ typedef enum NodeNormalMapSpace { NODE_NORMAL_MAP_BLENDER_WORLD, } NodeNormalMapSpace; -typedef enum NodeImageColorSpace { - NODE_COLOR_SPACE_NONE = 0, - NODE_COLOR_SPACE_COLOR = 1, -} NodeImageColorSpace; - typedef enum NodeImageProjection { NODE_IMAGE_PROJ_FLAT = 0, NODE_IMAGE_PROJ_BOX = 1, @@ -385,6 +380,11 @@ typedef enum NodeImageProjection { NODE_IMAGE_PROJ_TUBE = 3, } NodeImageProjection; +typedef enum NodeImageFlags { + NODE_IMAGE_COMPRESS_AS_SRGB = 1, + NODE_IMAGE_ALPHA_UNASSOCIATE = 2, +} NodeImageFlags; + typedef enum NodeEnvironmentProjection { NODE_ENVIRONMENT_EQUIRECTANGULAR = 0, NODE_ENVIRONMENT_MIRROR_BALL = 1, diff --git a/intern/cycles/render/CMakeLists.txt b/intern/cycles/render/CMakeLists.txt index 378957d21f4..c79e5a23ea1 100644 --- a/intern/cycles/render/CMakeLists.txt +++ b/intern/cycles/render/CMakeLists.txt @@ -14,6 +14,7 @@ set(SRC bake.cpp buffers.cpp camera.cpp + colorspace.cpp constant_fold.cpp coverage.cpp denoising.cpp @@ -48,6 +49,7 @@ set(SRC_HEADERS background.h buffers.h camera.h + colorspace.h constant_fold.h coverage.h denoising.h diff --git a/intern/cycles/render/colorspace.cpp b/intern/cycles/render/colorspace.cpp new file mode 100644 index 00000000000..f4c217bc9fb --- /dev/null +++ b/intern/cycles/render/colorspace.cpp @@ -0,0 +1,410 @@ +/* + * Copyright 2011-2013 Blender Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "render/colorspace.h" + +#include "util/util_color.h" +#include "util/util_image.h" +#include "util/util_half.h" +#include "util/util_logging.h" +#include "util/util_math.h" +#include "util/util_thread.h" +#include "util/util_vector.h" + +#ifdef WITH_OCIO +# include <OpenColorIO/OpenColorIO.h> +namespace OCIO = OCIO_NAMESPACE; +#endif + +CCL_NAMESPACE_BEGIN + +/* Builtin colorspaces. */ +ustring u_colorspace_auto; +ustring u_colorspace_raw("__builtin_raw"); +ustring u_colorspace_srgb("__builtin_srgb"); + +/* Cached data. */ +#ifdef WITH_OCIO +static thread_mutex cache_colorspaces_mutex; +static thread_mutex cache_processors_mutex; +static unordered_map<ustring, ustring, ustringHash> cached_colorspaces; +static unordered_map<ustring, OCIO::ConstProcessorRcPtr, ustringHash> cached_processors; +#endif + +ColorSpaceProcessor *ColorSpaceManager::get_processor(ustring colorspace) +{ +#ifdef WITH_OCIO + /* Only use this for OpenColorIO color spaces, not the builtin ones. */ + assert(colorspace != u_colorspace_srgb && colorspace != u_colorspace_auto); + + if (colorspace == u_colorspace_raw) { + return NULL; + } + + OCIO::ConstConfigRcPtr config = OCIO::GetCurrentConfig(); + if (!config) { + return NULL; + } + + /* Cache processor until free_memory(), memory overhead is expected to be + * small and the processor is likely to be reused. */ + thread_scoped_lock cache_processors_lock(cache_processors_mutex); + if (cached_processors.find(colorspace) == cached_processors.end()) { + try { + cached_processors[colorspace] = config->getProcessor(colorspace.c_str(), "scene_linear"); + } + catch (OCIO::Exception &exception) { + cached_processors[colorspace] = OCIO::ConstProcessorRcPtr(); + VLOG(1) << "Colorspace " << colorspace.c_str() + << " can't be converted to scene_linear: " << exception.what(); + } + } + + const OCIO::Processor *processor = cached_processors[colorspace].get(); + return (ColorSpaceProcessor *)processor; +#else + /* No OpenColorIO. */ + (void)colorspace; + return NULL; +#endif +} + +bool ColorSpaceManager::colorspace_is_data(ustring colorspace) +{ + if (colorspace == u_colorspace_auto || colorspace == u_colorspace_raw || + colorspace == u_colorspace_srgb) { + return false; + } + +#ifdef WITH_OCIO + OCIO::ConstConfigRcPtr config = OCIO::GetCurrentConfig(); + if (!config) { + return false; + } + + try { + OCIO::ConstColorSpaceRcPtr space = config->getColorSpace(colorspace.c_str()); + return space && space->isData(); + } + catch (OCIO::Exception &exception) { + return false; + } +#else + return false; +#endif +} + +ustring ColorSpaceManager::detect_known_colorspace(ustring colorspace, + const char *file_format, + bool is_float) +{ + if (colorspace == u_colorspace_auto) { + /* Auto detect sRGB or raw if none specified. */ + if (is_float) { + bool srgb = (colorspace == "sRGB" || colorspace == "GammaCorrected" || + (colorspace.empty() && + (strcmp(file_format, "png") == 0 || strcmp(file_format, "tiff") == 0 || + strcmp(file_format, "dpx") == 0 || strcmp(file_format, "jpeg2000") == 0))); + return srgb ? u_colorspace_srgb : u_colorspace_raw; + } + else { + return u_colorspace_srgb; + } + } + else if (colorspace == u_colorspace_srgb || colorspace == u_colorspace_raw) { + /* Builtin colorspaces. */ + return colorspace; + } + else { + /* Use OpenColorIO. */ +#ifdef WITH_OCIO + { + thread_scoped_lock cache_lock(cache_colorspaces_mutex); + /* Cached lookup. */ + if (cached_colorspaces.find(colorspace) != cached_colorspaces.end()) { + return cached_colorspaces[colorspace]; + } + } + + /* Detect if it matches a simple builtin colorspace. */ + bool is_scene_linear, is_srgb; + is_builtin_colorspace(colorspace, is_scene_linear, is_srgb); + + thread_scoped_lock cache_lock(cache_colorspaces_mutex); + if (is_scene_linear) { + VLOG(1) << "Colorspace " << colorspace.string() << " is no-op"; + cached_colorspaces[colorspace] = u_colorspace_raw; + return u_colorspace_raw; + } + else if (is_srgb) { + VLOG(1) << "Colorspace " << colorspace.string() << " is sRGB"; + cached_colorspaces[colorspace] = u_colorspace_srgb; + return u_colorspace_srgb; + } + + /* Verify if we can convert from the requested color space. */ + if (!get_processor(colorspace)) { + OCIO::ConstConfigRcPtr config = OCIO::GetCurrentConfig(); + if (!config || !config->getColorSpace(colorspace.c_str())) { + VLOG(1) << "Colorspace " << colorspace.c_str() << " not found, using raw instead"; + } + else { + VLOG(1) << "Colorspace " << colorspace.c_str() + << " can't be converted to scene_linear, using raw instead"; + } + cached_colorspaces[colorspace] = u_colorspace_raw; + return u_colorspace_raw; + } + + /* Convert to/from colorspace with OpenColorIO. */ + VLOG(1) << "Colorspace " << colorspace.string() << " handled through OpenColorIO"; + cached_colorspaces[colorspace] = colorspace; + return colorspace; +#else + VLOG(1) << "Colorspace " << colorspace.c_str() << " not available, built without OpenColorIO"; + return u_colorspace_raw; +#endif + } +} + +void ColorSpaceManager::is_builtin_colorspace(ustring colorspace, + bool &is_scene_linear, + bool &is_srgb) +{ +#ifdef WITH_OCIO + const OCIO::Processor *processor = (const OCIO::Processor *)get_processor(colorspace); + if (!processor) { + is_scene_linear = false; + is_srgb = false; + return; + } + + is_scene_linear = true; + is_srgb = true; + for (int i = 0; i < 256; i++) { + float v = i / 255.0f; + + float cR[3] = {v, 0, 0}; + float cG[3] = {0, v, 0}; + float cB[3] = {0, 0, v}; + float cW[3] = {v, v, v}; + processor->applyRGB(cR); + processor->applyRGB(cG); + processor->applyRGB(cB); + processor->applyRGB(cW); + + /* Make sure that there is no channel crosstalk. */ + if (fabsf(cR[1]) > 1e-5f || fabsf(cR[2]) > 1e-5f || fabsf(cG[0]) > 1e-5f || + fabsf(cG[2]) > 1e-5f || fabsf(cB[0]) > 1e-5f || fabsf(cB[1]) > 1e-5f) { + is_scene_linear = false; + is_srgb = false; + break; + } + /* Make sure that the three primaries combine linearly. */ + if (!compare_floats(cR[0], cW[0], 1e-6f, 64) || !compare_floats(cG[1], cW[1], 1e-6f, 64) || + !compare_floats(cB[2], cW[2], 1e-6f, 64)) { + is_scene_linear = false; + is_srgb = false; + break; + } + /* Make sure that the three channels behave identically. */ + if (!compare_floats(cW[0], cW[1], 1e-6f, 64) || !compare_floats(cW[1], cW[2], 1e-6f, 64)) { + is_scene_linear = false; + is_srgb = false; + break; + } + + float out_v = average(make_float3(cW[0], cW[1], cW[2])); + if (!compare_floats(v, out_v, 1e-6f, 64)) { + is_scene_linear = false; + } + if (!compare_floats(color_srgb_to_linear(v), out_v, 1e-6f, 64)) { + is_srgb = false; + } + } +#else + (void)colorspace; + is_scene_linear = false; + is_srgb = false; +#endif +} + +#ifdef WITH_OCIO + +template<typename T> inline float4 cast_to_float4(T *data) +{ + return make_float4(util_image_cast_to_float(data[0]), + util_image_cast_to_float(data[1]), + util_image_cast_to_float(data[2]), + util_image_cast_to_float(data[3])); +} + +template<typename T> inline void cast_from_float4(T *data, float4 value) +{ + data[0] = util_image_cast_from_float<T>(value.x); + data[1] = util_image_cast_from_float<T>(value.y); + data[2] = util_image_cast_from_float<T>(value.z); + data[3] = util_image_cast_from_float<T>(value.w); +} + +/* Slower versions for other all data types, which needs to convert to float and back. */ +template<typename T, bool compress_as_srgb = false> +inline void processor_apply_pixels(const OCIO::Processor *processor, + T *pixels, + size_t width, + size_t height) +{ + /* TODO: implement faster version for when we know the conversion + * is a simple matrix transform between linear spaces. In that case + * unpremultiply is not needed. */ + + /* Process large images in chunks to keep temporary memory requirement down. */ + size_t y_chunk_size = max(1, 16 * 1024 * 1024 / (sizeof(float4) * width)); + vector<float4> float_pixels(y_chunk_size * width); + + for (size_t y0 = 0; y0 < height; y0 += y_chunk_size) { + size_t y1 = std::min(y0 + y_chunk_size, height); + size_t i = 0; + + for (size_t y = y0; y < y1; y++) { + for (size_t x = 0; x < width; x++, i++) { + float4 value = cast_to_float4(pixels + 4 * (y * width + x)); + + if (!(value.w == 0.0f || value.w == 1.0f)) { + float inv_alpha = 1.0f / value.w; + value.x *= inv_alpha; + value.y *= inv_alpha; + value.z *= inv_alpha; + } + + float_pixels[i] = value; + } + } + + OCIO::PackedImageDesc desc((float *)float_pixels.data(), width, y_chunk_size, 4); + processor->apply(desc); + + i = 0; + for (size_t y = y0; y < y1; y++) { + for (size_t x = 0; x < width; x++, i++) { + float4 value = float_pixels[i]; + + value.x *= value.w; + value.y *= value.w; + value.z *= value.w; + + if (compress_as_srgb) { + value = color_linear_to_srgb_v4(value); + } + + cast_from_float4(pixels + 4 * (y * width + x), value); + } + } + } +} +#endif + +template<typename T> +void ColorSpaceManager::to_scene_linear(ustring colorspace, + T *pixels, + size_t width, + size_t height, + size_t depth, + bool compress_as_srgb) +{ +#ifdef WITH_OCIO + const OCIO::Processor *processor = (const OCIO::Processor *)get_processor(colorspace); + + if (processor) { + if (compress_as_srgb) { + /* Compress output as sRGB. */ + for (size_t z = 0; z < depth; z++) { + processor_apply_pixels<T, true>(processor, &pixels[z * width * height], width, height); + } + } + else { + /* Write output as scene linear directly. */ + for (size_t z = 0; z < depth; z++) { + processor_apply_pixels<T>(processor, &pixels[z * width * height], width, height); + } + } + } +#else + (void)colorspace; + (void)pixels; + (void)width; + (void)height; + (void)depth; + (void)compress_as_srgb; +#endif +} + +void ColorSpaceManager::to_scene_linear(ColorSpaceProcessor *processor_, + float *pixel, + int channels) +{ +#ifdef WITH_OCIO + const OCIO::Processor *processor = (const OCIO::Processor *)processor_; + + if (processor) { + if (channels == 3) { + processor->applyRGB(pixel); + } + else if (channels == 4) { + if (pixel[3] == 1.0f || pixel[3] == 0.0f) { + /* Fast path for RGBA. */ + processor->applyRGB(pixel); + } + else { + /* Unassociate and associate alpha since color management should not + * be affected by transparency. */ + float alpha = pixel[3]; + float inv_alpha = 1.0f / alpha; + + pixel[0] *= inv_alpha; + pixel[1] *= inv_alpha; + pixel[2] *= inv_alpha; + + processor->applyRGB(pixel); + + pixel[0] *= alpha; + pixel[1] *= alpha; + pixel[2] *= alpha; + } + } + } +#else + (void)processor_; + (void)pixel; + (void)channels; +#endif +} + +void ColorSpaceManager::free_memory() +{ +#ifdef WITH_OCIO + map_free_memory(cached_colorspaces); + map_free_memory(cached_colorspaces); +#endif +} + +/* Template instanstations so we don't have to inline functions. */ +template void ColorSpaceManager::to_scene_linear(ustring, uchar *, size_t, size_t, size_t, bool); +template void ColorSpaceManager::to_scene_linear(ustring, ushort *, size_t, size_t, size_t, bool); +template void ColorSpaceManager::to_scene_linear(ustring, half *, size_t, size_t, size_t, bool); +template void ColorSpaceManager::to_scene_linear(ustring, float *, size_t, size_t, size_t, bool); + +CCL_NAMESPACE_END diff --git a/intern/cycles/render/colorspace.h b/intern/cycles/render/colorspace.h new file mode 100644 index 00000000000..9fea2d6efc6 --- /dev/null +++ b/intern/cycles/render/colorspace.h @@ -0,0 +1,68 @@ +/* + * Copyright 2011-2013 Blender Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __COLORSPACE_H__ +#define __COLORSPACE_H__ + +#include "util/util_map.h" +#include "util/util_param.h" + +CCL_NAMESPACE_BEGIN + +extern ustring u_colorspace_auto; +extern ustring u_colorspace_raw; +extern ustring u_colorspace_srgb; + +class ColorSpaceProcessor; + +class ColorSpaceManager { + public: + /* Convert used specified colorspace to a colorspace that we are able to + * convert to and from. If the colorspace is u_colorspace_auto, we auto + * detect a colospace. */ + static ustring detect_known_colorspace(ustring colorspace, + const char *file_format, + bool is_float); + + /* Test if colorspace is for non-color data. */ + static bool colorspace_is_data(ustring colorspace); + + /* Convert pixels in the specified colorspace to scene linear color for + * rendering. Must be a colorspace returned from detect_known_colorspace. */ + template<typename T> + static void to_scene_linear(ustring colorspace, + T *pixels, + size_t width, + size_t height, + size_t depth, + bool compress_as_srgb); + + /* Efficiently convert pixels to scene linear colorspace at render time, + * for OSL where the image texture cache contains original pixels. The + * handle is valid for the lifetime of the application. */ + static ColorSpaceProcessor *get_processor(ustring colorspace); + static void to_scene_linear(ColorSpaceProcessor *processor, float *pixel, int channels); + + /* Clear memory when the application exits. Invalidates all processors. */ + static void free_memory(); + + private: + static void is_builtin_colorspace(ustring colorspace, bool &is_no_op, bool &is_srgb); +}; + +CCL_NAMESPACE_END + +#endif /* __COLORSPACE_H__ */ diff --git a/intern/cycles/render/graph.cpp b/intern/cycles/render/graph.cpp index e5fd39f08b7..9203c4468d2 100644 --- a/intern/cycles/render/graph.cpp +++ b/intern/cycles/render/graph.cpp @@ -127,6 +127,12 @@ ShaderOutput *ShaderNode::output(ustring name) return NULL; } +void ShaderNode::remove_input(ShaderInput *input) +{ + assert(input->link == NULL); + inputs.erase(remove(inputs.begin(), inputs.end(), input), inputs.end()); +} + void ShaderNode::attributes(Shader *shader, AttributeRequestSet *attributes) { foreach (ShaderInput *input, inputs) { @@ -297,6 +303,28 @@ void ShaderGraph::disconnect(ShaderInput *to) from->links.erase(remove(from->links.begin(), from->links.end(), to), from->links.end()); } +void ShaderGraph::relink(ShaderInput *from, ShaderInput *to) +{ + ShaderOutput *out = from->link; + if (out) { + disconnect(from); + connect(out, to); + } + to->parent->copy_value(to->socket_type, *(from->parent), from->socket_type); +} + +void ShaderGraph::relink(ShaderOutput *from, ShaderOutput *to) +{ + /* Copy because disconnect modifies this list. */ + vector<ShaderInput *> outputs = from->links; + + foreach (ShaderInput *sock, outputs) { + disconnect(sock); + if (to) + connect(to, sock); + } +} + void ShaderGraph::relink(ShaderNode *node, ShaderOutput *from, ShaderOutput *to) { simplified = false; @@ -320,6 +348,7 @@ void ShaderGraph::relink(ShaderNode *node, ShaderOutput *from, ShaderOutput *to) void ShaderGraph::simplify(Scene *scene) { if (!simplified) { + expand(); default_inputs(scene->shader_manager->use_osl()); clean(scene); refine_bump_nodes(); @@ -780,6 +809,14 @@ void ShaderGraph::clean(Scene *scene) nodes = newnodes; } +void ShaderGraph::expand() +{ + /* Call expand on all nodes, to generate additional nodes. */ + foreach (ShaderNode *node, nodes) { + node->expand(this); + } +} + void ShaderGraph::default_inputs(bool do_osl) { /* nodes can specify default texture coordinates, for now we give diff --git a/intern/cycles/render/graph.h b/intern/cycles/render/graph.h index b1aa5cf3168..cade04de374 100644 --- a/intern/cycles/render/graph.h +++ b/intern/cycles/render/graph.h @@ -147,6 +147,7 @@ class ShaderNode : public Node { virtual ~ShaderNode(); void create_inputs_outputs(const NodeType *type); + void remove_input(ShaderInput *input); ShaderInput *input(const char *name); ShaderOutput *output(const char *name); @@ -158,6 +159,11 @@ class ShaderNode : public Node { virtual void compile(SVMCompiler &compiler) = 0; virtual void compile(OSLCompiler &compiler) = 0; + /* Expand node into additional nodes. */ + virtual void expand(ShaderGraph * /* graph */) + { + } + /* ** Node optimization ** */ /* Check whether the node can be replaced with single constant. */ virtual void constant_fold(const ConstantFolder & /*folder*/) @@ -322,6 +328,8 @@ class ShaderGraph { void connect(ShaderOutput *from, ShaderInput *to); void disconnect(ShaderOutput *from); void disconnect(ShaderInput *to); + void relink(ShaderInput *from, ShaderInput *to); + void relink(ShaderOutput *from, ShaderOutput *to); void relink(ShaderNode *node, ShaderOutput *from, ShaderOutput *to); void remove_proxy_nodes(); @@ -346,6 +354,7 @@ class ShaderGraph { void break_cycles(ShaderNode *node, vector<bool> &visited, vector<bool> &on_stack); void bump_from_displacement(bool use_object_space); void refine_bump_nodes(); + void expand(); void default_inputs(bool do_osl); void transform_multi_closure(ShaderNode *node, ShaderOutput *weight_out, bool volume); diff --git a/intern/cycles/render/image.cpp b/intern/cycles/render/image.cpp index ae219e912e0..160e7d9ff1e 100644 --- a/intern/cycles/render/image.cpp +++ b/intern/cycles/render/image.cpp @@ -14,12 +14,14 @@ * limitations under the License. */ -#include "device/device.h" #include "render/image.h" +#include "device/device.h" +#include "render/colorspace.h" #include "render/scene.h" #include "render/stats.h" #include "util/util_foreach.h" +#include "util/util_image_impl.h" #include "util/util_logging.h" #include "util/util_path.h" #include "util/util_progress.h" @@ -164,11 +166,45 @@ bool ImageManager::get_image_metadata(int flat_slot, ImageMetaData &metadata) return false; } +void ImageManager::metadata_detect_colorspace(ImageMetaData &metadata, const char *file_format) +{ + /* Convert used specified color spaces to one we know how to handle. */ + metadata.colorspace = ColorSpaceManager::detect_known_colorspace( + metadata.colorspace, file_format, metadata.is_float || metadata.is_half); + + if (metadata.colorspace == u_colorspace_raw) { + /* Nothing to do. */ + } + else if (metadata.colorspace == u_colorspace_srgb) { + /* Keep sRGB colorspace stored as sRGB, to save memory and/or loading time + * for the common case of 8bit sRGB images like PNG. */ + metadata.compress_as_srgb = true; + } + else { + /* Always compress non-raw 8bit images as scene linear + sRGB, as a + * heuristic to keep memory usage the same without too much data loss + * due to quantization in common cases. */ + metadata.compress_as_srgb = (metadata.type == IMAGE_DATA_TYPE_BYTE || + metadata.type == IMAGE_DATA_TYPE_BYTE4); + + /* If colorspace conversion needed, use half instead of short so we can + * represent HDR values that might result from conversion. */ + if (metadata.type == IMAGE_DATA_TYPE_USHORT) { + metadata.type = IMAGE_DATA_TYPE_HALF; + } + else if (metadata.type == IMAGE_DATA_TYPE_USHORT4) { + metadata.type = IMAGE_DATA_TYPE_HALF4; + } + } +} + bool ImageManager::get_image_metadata(const string &filename, void *builtin_data, + ustring colorspace, ImageMetaData &metadata) { - memset(&metadata, 0, sizeof(metadata)); + metadata = ImageMetaData(); + metadata.colorspace = colorspace; if (builtin_data) { if (builtin_image_info_cb) { @@ -179,13 +215,14 @@ bool ImageManager::get_image_metadata(const string &filename, } if (metadata.is_float) { - metadata.is_linear = true; metadata.type = (metadata.channels > 1) ? IMAGE_DATA_TYPE_FLOAT4 : IMAGE_DATA_TYPE_FLOAT; } else { metadata.type = (metadata.channels > 1) ? IMAGE_DATA_TYPE_BYTE4 : IMAGE_DATA_TYPE_BYTE; } + metadata_detect_colorspace(metadata, ""); + return true; } @@ -213,20 +250,19 @@ bool ImageManager::get_image_metadata(const string &filename, metadata.width = spec.width; metadata.height = spec.height; metadata.depth = spec.depth; + metadata.compress_as_srgb = false; /* Check the main format, and channel formats. */ size_t channel_size = spec.format.basesize(); if (spec.format.is_floating_point()) { metadata.is_float = true; - metadata.is_linear = true; } for (size_t channel = 0; channel < spec.channelformats.size(); channel++) { channel_size = max(channel_size, spec.channelformats[channel].basesize()); if (spec.channelformats[channel].is_floating_point()) { metadata.is_float = true; - metadata.is_linear = true; } } @@ -235,21 +271,6 @@ bool ImageManager::get_image_metadata(const string &filename, metadata.is_half = true; } - /* basic color space detection, not great but better than nothing - * before we do OpenColorIO integration */ - if (metadata.is_float) { - string colorspace = spec.get_string_attribute("oiio:ColorSpace"); - - metadata.is_linear = !( - colorspace == "sRGB" || colorspace == "GammaCorrected" || - (colorspace == "" && - (strcmp(in->format_name(), "png") == 0 || strcmp(in->format_name(), "tiff") == 0 || - strcmp(in->format_name(), "dpx") == 0 || strcmp(in->format_name(), "jpeg2000") == 0))); - } - else { - metadata.is_linear = false; - } - /* set type and channels */ metadata.channels = spec.nchannels; @@ -266,6 +287,8 @@ bool ImageManager::get_image_metadata(const string &filename, metadata.type = (metadata.channels > 1) ? IMAGE_DATA_TYPE_BYTE4 : IMAGE_DATA_TYPE_BYTE; } + metadata_detect_colorspace(metadata, in->format_name()); + in->close(); return true; @@ -276,11 +299,12 @@ static bool image_equals(ImageManager::Image *image, void *builtin_data, InterpolationType interpolation, ExtensionType extension, - bool use_alpha) + ImageAlphaType alpha_type, + ustring colorspace) { return image->filename == filename && image->builtin_data == builtin_data && image->interpolation == interpolation && image->extension == extension && - image->use_alpha == use_alpha; + image->alpha_type == alpha_type && image->colorspace == colorspace; } int ImageManager::add_image(const string &filename, @@ -289,13 +313,14 @@ int ImageManager::add_image(const string &filename, float frame, InterpolationType interpolation, ExtensionType extension, - bool use_alpha, + ImageAlphaType alpha_type, + ustring colorspace, ImageMetaData &metadata) { Image *img; size_t slot; - get_image_metadata(filename, builtin_data, metadata); + get_image_metadata(filename, builtin_data, colorspace, metadata); ImageDataType type = metadata.type; thread_scoped_lock device_lock(device_mutex); @@ -313,13 +338,19 @@ int ImageManager::add_image(const string &filename, /* Fnd existing image. */ for (slot = 0; slot < images[type].size(); slot++) { img = images[type][slot]; - if (img && image_equals(img, filename, builtin_data, interpolation, extension, use_alpha)) { + if (img && + image_equals( + img, filename, builtin_data, interpolation, extension, alpha_type, colorspace)) { if (img->frame != frame) { img->frame = frame; img->need_load = true; } - if (img->use_alpha != use_alpha) { - img->use_alpha = use_alpha; + if (img->alpha_type != alpha_type) { + img->alpha_type = alpha_type; + img->need_load = true; + } + if (img->colorspace != colorspace) { + img->colorspace = colorspace; img->need_load = true; } if (!(img->metadata == metadata)) { @@ -369,7 +400,8 @@ int ImageManager::add_image(const string &filename, img->interpolation = interpolation; img->extension = extension; img->users = 1; - img->use_alpha = use_alpha; + img->alpha_type = alpha_type; + img->colorspace = colorspace; img->mem = NULL; images[type][slot] = img; @@ -381,6 +413,17 @@ int ImageManager::add_image(const string &filename, return type_index_to_flattened_slot(slot, type); } +void ImageManager::add_image_user(int flat_slot) +{ + ImageDataType type; + int slot = flattened_slot_to_type_index(flat_slot, &type); + + Image *image = images[type][slot]; + assert(image && image->users >= 1); + + image->users++; +} + void ImageManager::remove_image(int flat_slot) { ImageDataType type; @@ -403,15 +446,20 @@ void ImageManager::remove_image(const string &filename, void *builtin_data, InterpolationType interpolation, ExtensionType extension, - bool use_alpha) + ImageAlphaType alpha_type, + ustring colorspace) { size_t slot; for (int type = 0; type < IMAGE_DATA_NUM_TYPES; type++) { for (slot = 0; slot < images[type].size(); slot++) { - if (images[type][slot] && - image_equals( - images[type][slot], filename, builtin_data, interpolation, extension, use_alpha)) { + if (images[type][slot] && image_equals(images[type][slot], + filename, + builtin_data, + interpolation, + extension, + alpha_type, + colorspace)) { remove_image(type_index_to_flattened_slot(slot, (ImageDataType)type)); return; } @@ -427,13 +475,18 @@ void ImageManager::tag_reload_image(const string &filename, void *builtin_data, InterpolationType interpolation, ExtensionType extension, - bool use_alpha) + ImageAlphaType alpha_type, + ustring colorspace) { for (size_t type = 0; type < IMAGE_DATA_NUM_TYPES; type++) { for (size_t slot = 0; slot < images[type].size(); slot++) { - if (images[type][slot] && - image_equals( - images[type][slot], filename, builtin_data, interpolation, extension, use_alpha)) { + if (images[type][slot] && image_equals(images[type][slot], + filename, + builtin_data, + interpolation, + extension, + alpha_type, + colorspace)) { images[type][slot]->need_load = true; break; } @@ -461,8 +514,15 @@ bool ImageManager::file_load_image_generic(Image *img, unique_ptr<ImageInput> *i ImageSpec spec = ImageSpec(); ImageSpec config = ImageSpec(); - if (img->use_alpha == false) + /* For typical RGBA images we let OIIO convert to associated alpha, + * but some types we want to leave the RGB channels untouched. */ + const bool associate_alpha = !(ColorSpaceManager::colorspace_is_data(img->colorspace) || + img->alpha_type == IMAGE_ALPHA_IGNORE || + img->alpha_type == IMAGE_ALPHA_CHANNEL_PACKED); + + if (!associate_alpha) { config.attribute("oiio:UnassociatedAlpha", 1); + } if (!(*in)->open(img->filename, spec, config)) { return false; @@ -502,14 +562,16 @@ bool ImageManager::file_load_image(Image *img, int depth = img->metadata.depth; int components = img->metadata.channels; - /* Read RGBA pixels. */ + /* Read pixels. */ vector<StorageType> pixels_storage; StorageType *pixels; const size_t max_size = max(max(width, height), depth); if (max_size == 0) { - /* Don't bother with invalid images. */ + /* Don't bother with empty images. */ return false; } + + /* Allocate memory as needed, may be smaller to resize down. */ if (texture_limit > 0 && max_size > texture_limit) { pixels_storage.resize(((size_t)width) * height * depth * 4); pixels = &pixels_storage[0]; @@ -518,19 +580,23 @@ bool ImageManager::file_load_image(Image *img, thread_scoped_lock device_lock(device_mutex); pixels = (StorageType *)tex_img.alloc(width, height, depth); } + if (pixels == NULL) { /* Could be that we've run out of memory. */ return false; } + bool cmyk = false; const size_t num_pixels = ((size_t)width) * height * depth; if (in) { + /* Read pixels through OpenImageIO. */ StorageType *readpixels = pixels; vector<StorageType> tmppixels; if (components > 4) { tmppixels.resize(((size_t)width) * height * components); readpixels = &tmppixels[0]; } + if (depth <= 1) { size_t scanlinesize = ((size_t)width) * components * sizeof(StorageType); in->read_image(FileFormat, @@ -542,6 +608,7 @@ bool ImageManager::file_load_image(Image *img, else { in->read_image(FileFormat, (uchar *)readpixels); } + if (components > 4) { size_t dimensions = ((size_t)width) * height; for (size_t i = dimensions - 1, pixel = 0; pixel < dimensions; pixel++, i--) { @@ -552,10 +619,12 @@ bool ImageManager::file_load_image(Image *img, } tmppixels.clear(); } + cmyk = strcmp(in->format_name(), "jpeg") == 0 && components == 4; in->close(); } else { + /* Read pixels through callback. */ if (FileFormat == TypeDesc::FLOAT) { builtin_image_float_pixels_cb(img->filename, img->builtin_data, @@ -574,16 +643,17 @@ bool ImageManager::file_load_image(Image *img, /* TODO(dingto): Support half for ImBuf. */ } } - /* Check if we actually have a float4 slot, in case components == 1, - * but device doesn't support single channel textures. - */ + + /* The kernel can handle 1 and 4 channel images. Anything that is not a single + * channel image is converted to RGBA format. */ bool is_rgba = (type == IMAGE_DATA_TYPE_FLOAT4 || type == IMAGE_DATA_TYPE_HALF4 || type == IMAGE_DATA_TYPE_BYTE4 || type == IMAGE_DATA_TYPE_USHORT4); + if (is_rgba) { const StorageType one = util_image_cast_from_float<StorageType>(1.0f); if (cmyk) { - /* CMYK */ + /* CMYK to RGBA. */ for (size_t i = num_pixels - 1, pixel = 0; pixel < num_pixels; pixel++, i--) { float c = util_image_cast_to_float(pixels[i * 4 + 0]); float m = util_image_cast_to_float(pixels[i * 4 + 1]); @@ -596,7 +666,7 @@ bool ImageManager::file_load_image(Image *img, } } else if (components == 2) { - /* grayscale + alpha */ + /* Grayscale + alpha to RGBA. */ for (size_t i = num_pixels - 1, pixel = 0; pixel < num_pixels; pixel++, i--) { pixels[i * 4 + 3] = pixels[i * 2 + 1]; pixels[i * 4 + 2] = pixels[i * 2 + 0]; @@ -605,7 +675,7 @@ bool ImageManager::file_load_image(Image *img, } } else if (components == 3) { - /* RGB */ + /* RGB to RGBA. */ for (size_t i = num_pixels - 1, pixel = 0; pixel < num_pixels; pixel++, i--) { pixels[i * 4 + 3] = one; pixels[i * 4 + 2] = pixels[i * 3 + 2]; @@ -614,7 +684,7 @@ bool ImageManager::file_load_image(Image *img, } } else if (components == 1) { - /* grayscale */ + /* Grayscale to RGBA. */ for (size_t i = num_pixels - 1, pixel = 0; pixel < num_pixels; pixel++, i--) { pixels[i * 4 + 3] = one; pixels[i * 4 + 2] = pixels[i]; @@ -622,18 +692,27 @@ bool ImageManager::file_load_image(Image *img, pixels[i * 4 + 0] = pixels[i]; } } - if (img->use_alpha == false) { + + /* Disable alpha if requested by the user. */ + if (img->alpha_type == IMAGE_ALPHA_IGNORE) { for (size_t i = num_pixels - 1, pixel = 0; pixel < num_pixels; pixel++, i--) { pixels[i * 4 + 3] = one; } } + + if (img->metadata.colorspace != u_colorspace_raw && + img->metadata.colorspace != u_colorspace_srgb) { + /* Convert to scene linear. */ + ColorSpaceManager::to_scene_linear( + img->metadata.colorspace, pixels, width, height, depth, img->metadata.compress_as_srgb); + } } + /* Make sure we don't have buggy values. */ if (FileFormat == TypeDesc::FLOAT) { /* For RGBA buffers we put all channels to 0 if either of them is not * finite. This way we avoid possible artifacts caused by fully changed - * hue. - */ + * hue. */ if (is_rgba) { for (size_t i = 0; i < num_pixels; i += 4) { StorageType *pixel = &pixels[i * 4]; @@ -655,6 +734,7 @@ bool ImageManager::file_load_image(Image *img, } } } + /* Scale image down if needed. */ if (pixels_storage.size() > 0) { float scale_factor = 1.0f; @@ -684,6 +764,7 @@ bool ImageManager::file_load_image(Image *img, memcpy(texture_pixels, &scaled_pixels[0], scaled_pixels.size() * sizeof(StorageType)); } + return true; } diff --git a/intern/cycles/render/image.h b/intern/cycles/render/image.h index 34f046692f6..ed2780f8471 100644 --- a/intern/cycles/render/image.h +++ b/intern/cycles/render/image.h @@ -20,6 +20,8 @@ #include "device/device.h" #include "device/device_memory.h" +#include "render/colorspace.h" + #include "util/util_image.h" #include "util/util_string.h" #include "util/util_thread.h" @@ -32,6 +34,7 @@ class Device; class Progress; class RenderStats; class Scene; +class ColorSpaceProcessor; class ImageMetaData { public: @@ -43,13 +46,29 @@ class ImageMetaData { /* Automatically set. */ ImageDataType type; - bool is_linear; + ustring colorspace; + bool compress_as_srgb; + + ImageMetaData() + : is_float(false), + is_half(false), + channels(0), + width(0), + height(0), + depth(0), + builtin_free_cache(false), + type((ImageDataType)0), + colorspace(u_colorspace_raw), + compress_as_srgb(false) + { + } bool operator==(const ImageMetaData &other) const { return is_float == other.is_float && is_half == other.is_half && channels == other.channels && width == other.width && height == other.height && depth == other.depth && - type == other.type && is_linear == other.is_linear; + type == other.type && colorspace == other.colorspace && + compress_as_srgb == other.compress_as_srgb; } }; @@ -64,20 +83,27 @@ class ImageManager { float frame, InterpolationType interpolation, ExtensionType extension, - bool use_alpha, + ImageAlphaType alpha_type, + ustring colorspace, ImageMetaData &metadata); + void add_image_user(int flat_slot); void remove_image(int flat_slot); void remove_image(const string &filename, void *builtin_data, InterpolationType interpolation, ExtensionType extension, - bool use_alpha); + ImageAlphaType alpha_type, + ustring colorspace); void tag_reload_image(const string &filename, void *builtin_data, InterpolationType interpolation, ExtensionType extension, - bool use_alpha); - bool get_image_metadata(const string &filename, void *builtin_data, ImageMetaData &metadata); + ImageAlphaType alpha_type, + ustring colorspace); + bool get_image_metadata(const string &filename, + void *builtin_data, + ustring colorspace, + ImageMetaData &metadata); bool get_image_metadata(int flat_slot, ImageMetaData &metadata); void device_update(Device *device, Scene *scene, Progress &progress); @@ -120,7 +146,8 @@ class ImageManager { void *builtin_data; ImageMetaData metadata; - bool use_alpha; + ustring colorspace; + ImageAlphaType alpha_type; bool need_load; bool animated; float frame; @@ -152,6 +179,8 @@ class ImageManager { int texture_limit, device_vector<DeviceType> &tex_img); + void metadata_detect_colorspace(ImageMetaData &metadata, const char *file_format); + void device_load_image( Device *device, Scene *scene, ImageDataType type, int slot, Progress *progress); void device_free_image(Device *device, ImageDataType type, int slot); diff --git a/intern/cycles/render/light.cpp b/intern/cycles/render/light.cpp index d4c233e3bb3..5c3f1c35bdc 100644 --- a/intern/cycles/render/light.cpp +++ b/intern/cycles/render/light.cpp @@ -114,6 +114,8 @@ NODE_DEFINE(Light) type_enum.insert("spot", LIGHT_SPOT); SOCKET_ENUM(type, "Type", type_enum, LIGHT_POINT); + SOCKET_COLOR(strength, "Strength", make_float3(1.0f, 1.0f, 1.0f)); + SOCKET_POINT(co, "Co", make_float3(0.0f, 0.0f, 0.0f)); SOCKET_VECTOR(dir, "Dir", make_float3(0.0f, 0.0f, 0.0f)); @@ -162,6 +164,9 @@ void Light::tag_update(Scene *scene) bool Light::has_contribution(Scene *scene) { + if (strength == make_float3(0.0f, 0.0f, 0.0f)) { + return false; + } if (is_portal) { return false; } @@ -393,11 +398,18 @@ void LightManager::device_update_distribution(Device *, distribution[offset].lamp.size = light->size; totarea += lightarea; - if (light->size > 0.0f && light->use_mis) - use_lamp_mis = true; - if (light->type == LIGHT_BACKGROUND) { + if (light->type == LIGHT_DISTANT) { + use_lamp_mis |= (light->angle > 0.0f && light->use_mis); + } + else if (light->type == LIGHT_POINT || light->type == LIGHT_SPOT) { + use_lamp_mis |= (light->size > 0.0f && light->use_mis); + } + else if (light->type == LIGHT_AREA) { + use_lamp_mis |= light->use_mis; + } + else if (light->type == LIGHT_BACKGROUND) { num_background_lights++; - background_mis = light->use_mis; + background_mis |= light->use_mis; } light_index++; @@ -672,7 +684,6 @@ void LightManager::device_update_points(Device *, DeviceScene *dscene, Scene *sc float3 co = light->co; Shader *shader = (light->shader) ? light->shader : scene->default_light; int shader_id = scene->shader_manager->get_shader_id(shader); - int samples = light->samples; int max_bounces = light->max_bounces; float random = (float)light->random_id * (1.0f / (float)0xFFFFFFFF); @@ -697,7 +708,10 @@ void LightManager::device_update_points(Device *, DeviceScene *dscene, Scene *sc } klights[light_index].type = light->type; - klights[light_index].samples = samples; + klights[light_index].samples = light->samples; + klights[light_index].strength[0] = light->strength.x; + klights[light_index].strength[1] = light->strength.y; + klights[light_index].strength[2] = light->strength.z; if (light->type == LIGHT_POINT) { shader_id &= ~SHADER_AREA_LIGHT; @@ -718,8 +732,8 @@ void LightManager::device_update_points(Device *, DeviceScene *dscene, Scene *sc else if (light->type == LIGHT_DISTANT) { shader_id &= ~SHADER_AREA_LIGHT; - float radius = light->size; - float angle = atanf(radius); + float angle = light->angle / 2.0f; + float radius = tanf(angle); float cosangle = cosf(angle); float area = M_PI_F * radius * radius; float invarea = (area > 0.0f) ? 1.0f / area : 1.0f; diff --git a/intern/cycles/render/light.h b/intern/cycles/render/light.h index 66732000f3b..79450ea5f8d 100644 --- a/intern/cycles/render/light.h +++ b/intern/cycles/render/light.h @@ -42,10 +42,12 @@ class Light : public Node { Light(); LightType type; + float3 strength; float3 co; float3 dir; float size; + float angle; float3 axisu; float sizeu; diff --git a/intern/cycles/render/nodes.cpp b/intern/cycles/render/nodes.cpp index 16416a9a009..c5b0f6a1e79 100644 --- a/intern/cycles/render/nodes.cpp +++ b/intern/cycles/render/nodes.cpp @@ -14,6 +14,7 @@ * limitations under the License. */ +#include "render/colorspace.h" #include "render/film.h" #include "render/image.h" #include "render/integrator.h" @@ -207,13 +208,15 @@ NODE_DEFINE(ImageTextureNode) TEXTURE_MAPPING_DEFINE(ImageTextureNode); SOCKET_STRING(filename, "Filename", ustring()); + SOCKET_STRING(colorspace, "Colorspace", u_colorspace_auto); - static NodeEnum color_space_enum; - color_space_enum.insert("none", NODE_COLOR_SPACE_NONE); - color_space_enum.insert("color", NODE_COLOR_SPACE_COLOR); - SOCKET_ENUM(color_space, "Color Space", color_space_enum, NODE_COLOR_SPACE_COLOR); - - SOCKET_BOOLEAN(use_alpha, "Use Alpha", true); + static NodeEnum alpha_type_enum; + alpha_type_enum.insert("auto", IMAGE_ALPHA_AUTO); + alpha_type_enum.insert("unassociated", IMAGE_ALPHA_UNASSOCIATED); + alpha_type_enum.insert("associated", IMAGE_ALPHA_ASSOCIATED); + alpha_type_enum.insert("channel_packed", IMAGE_ALPHA_CHANNEL_PACKED); + alpha_type_enum.insert("ignore", IMAGE_ALPHA_IGNORE); + SOCKET_ENUM(alpha_type, "Alpha Type", alpha_type_enum, IMAGE_ALPHA_AUTO); static NodeEnum interpolation_enum; interpolation_enum.insert("closest", INTERPOLATION_CLOSEST); @@ -250,7 +253,8 @@ ImageTextureNode::ImageTextureNode() : ImageSlotTextureNode(node_type) image_manager = NULL; slot = -1; is_float = -1; - is_linear = false; + compress_as_srgb = false; + colorspace = u_colorspace_raw; builtin_data = NULL; animated = false; } @@ -259,18 +263,17 @@ ImageTextureNode::~ImageTextureNode() { if (image_manager) { image_manager->remove_image( - filename.string(), builtin_data, interpolation, extension, use_alpha); + filename.string(), builtin_data, interpolation, extension, alpha_type, colorspace); } } ShaderNode *ImageTextureNode::clone() const { - ImageTextureNode *node = new ImageTextureNode(*this); - node->image_manager = NULL; - node->slot = -1; - node->is_float = -1; - node->is_linear = false; - return node; + /* Increase image user count for new node. */ + if (slot != -1) { + image_manager->add_image_user(slot); + } + return new ImageTextureNode(*this); } void ImageTextureNode::attributes(Shader *shader, AttributeRequestSet *attributes) @@ -303,15 +306,30 @@ void ImageTextureNode::compile(SVMCompiler &compiler) 0, interpolation, extension, - use_alpha, + alpha_type, + colorspace, metadata); is_float = metadata.is_float; - is_linear = metadata.is_linear; + compress_as_srgb = metadata.compress_as_srgb; + known_colorspace = metadata.colorspace; } if (slot != -1) { - int srgb = (is_linear || color_space != NODE_COLOR_SPACE_COLOR) ? 0 : 1; int vector_offset = tex_mapping.compile_begin(compiler, vector_in); + uint flags = 0; + + if (compress_as_srgb) { + flags |= NODE_IMAGE_COMPRESS_AS_SRGB; + } + if (!alpha_out->links.empty()) { + const bool unassociate_alpha = !(ColorSpaceManager::colorspace_is_data(colorspace) || + alpha_type == IMAGE_ALPHA_CHANNEL_PACKED || + alpha_type == IMAGE_ALPHA_IGNORE); + + if (unassociate_alpha) { + flags |= NODE_IMAGE_ALPHA_UNASSOCIATE; + } + } if (projection != NODE_IMAGE_PROJ_BOX) { compiler.add_node(NODE_TEX_IMAGE, @@ -319,7 +337,7 @@ void ImageTextureNode::compile(SVMCompiler &compiler) compiler.encode_uchar4(vector_offset, compiler.stack_assign_if_linked(color_out), compiler.stack_assign_if_linked(alpha_out), - srgb), + flags), projection); } else { @@ -328,7 +346,7 @@ void ImageTextureNode::compile(SVMCompiler &compiler) compiler.encode_uchar4(vector_offset, compiler.stack_assign_if_linked(color_out), compiler.stack_assign_if_linked(alpha_out), - srgb), + flags), __float_as_int(projection_blend)); } @@ -358,7 +376,7 @@ void ImageTextureNode::compile(OSLCompiler &compiler) if (is_float == -1) { ImageMetaData metadata; if (builtin_data == NULL) { - image_manager->get_image_metadata(filename.string(), NULL, metadata); + image_manager->get_image_metadata(filename.string(), NULL, colorspace, metadata); } else { slot = image_manager->add_image(filename.string(), @@ -367,33 +385,32 @@ void ImageTextureNode::compile(OSLCompiler &compiler) 0, interpolation, extension, - use_alpha, + alpha_type, + colorspace, metadata); } is_float = metadata.is_float; - is_linear = metadata.is_linear; + compress_as_srgb = metadata.compress_as_srgb; + known_colorspace = metadata.colorspace; } if (slot == -1) { - compiler.parameter(this, "filename"); + compiler.parameter_texture("filename", filename, known_colorspace); } else { - /* TODO(sergey): It's not so simple to pass custom attribute - * to the texture() function in order to make builtin images - * support more clear. So we use special file name which is - * "@i<slot_number>" and check whether file name matches this - * mask in the OSLRenderServices::texture(). - */ - compiler.parameter("filename", string_printf("@i%d", slot).c_str()); + compiler.parameter_texture("filename", slot); } - if (is_linear || color_space != NODE_COLOR_SPACE_COLOR) - compiler.parameter("color_space", "linear"); - else - compiler.parameter("color_space", "sRGB"); + + const bool unassociate_alpha = !(ColorSpaceManager::colorspace_is_data(colorspace) || + alpha_type == IMAGE_ALPHA_CHANNEL_PACKED || + alpha_type == IMAGE_ALPHA_IGNORE); + compiler.parameter(this, "projection"); compiler.parameter(this, "projection_blend"); + compiler.parameter("compress_as_srgb", compress_as_srgb); + compiler.parameter("ignore_alpha", alpha_type == IMAGE_ALPHA_IGNORE); + compiler.parameter("unassociate_alpha", !alpha_out->links.empty() && unassociate_alpha); compiler.parameter("is_float", is_float); - compiler.parameter("use_alpha", !alpha_out->links.empty()); compiler.parameter(this, "interpolation"); compiler.parameter(this, "extension"); @@ -409,13 +426,15 @@ NODE_DEFINE(EnvironmentTextureNode) TEXTURE_MAPPING_DEFINE(EnvironmentTextureNode); SOCKET_STRING(filename, "Filename", ustring()); + SOCKET_STRING(colorspace, "Colorspace", u_colorspace_auto); - static NodeEnum color_space_enum; - color_space_enum.insert("none", NODE_COLOR_SPACE_NONE); - color_space_enum.insert("color", NODE_COLOR_SPACE_COLOR); - SOCKET_ENUM(color_space, "Color Space", color_space_enum, NODE_COLOR_SPACE_COLOR); - - SOCKET_BOOLEAN(use_alpha, "Use Alpha", true); + static NodeEnum alpha_type_enum; + alpha_type_enum.insert("auto", IMAGE_ALPHA_AUTO); + alpha_type_enum.insert("unassociated", IMAGE_ALPHA_UNASSOCIATED); + alpha_type_enum.insert("associated", IMAGE_ALPHA_ASSOCIATED); + alpha_type_enum.insert("channel_packed", IMAGE_ALPHA_CHANNEL_PACKED); + alpha_type_enum.insert("ignore", IMAGE_ALPHA_IGNORE); + SOCKET_ENUM(alpha_type, "Alpha Type", alpha_type_enum, IMAGE_ALPHA_AUTO); static NodeEnum interpolation_enum; interpolation_enum.insert("closest", INTERPOLATION_CLOSEST); @@ -442,7 +461,8 @@ EnvironmentTextureNode::EnvironmentTextureNode() : ImageSlotTextureNode(node_typ image_manager = NULL; slot = -1; is_float = -1; - is_linear = false; + compress_as_srgb = false; + colorspace = u_colorspace_raw; builtin_data = NULL; animated = false; } @@ -451,18 +471,17 @@ EnvironmentTextureNode::~EnvironmentTextureNode() { if (image_manager) { image_manager->remove_image( - filename.string(), builtin_data, interpolation, EXTENSION_REPEAT, use_alpha); + filename.string(), builtin_data, interpolation, EXTENSION_REPEAT, alpha_type, colorspace); } } ShaderNode *EnvironmentTextureNode::clone() const { - EnvironmentTextureNode *node = new EnvironmentTextureNode(*this); - node->image_manager = NULL; - node->slot = -1; - node->is_float = -1; - node->is_linear = false; - return node; + /* Increase image user count for new node. */ + if (slot != -1) { + image_manager->add_image_user(slot); + } + return new EnvironmentTextureNode(*this); } void EnvironmentTextureNode::attributes(Shader *shader, AttributeRequestSet *attributes) @@ -493,22 +512,28 @@ void EnvironmentTextureNode::compile(SVMCompiler &compiler) 0, interpolation, EXTENSION_REPEAT, - use_alpha, + alpha_type, + colorspace, metadata); is_float = metadata.is_float; - is_linear = metadata.is_linear; + compress_as_srgb = metadata.compress_as_srgb; + known_colorspace = metadata.colorspace; } if (slot != -1) { - int srgb = (is_linear || color_space != NODE_COLOR_SPACE_COLOR) ? 0 : 1; int vector_offset = tex_mapping.compile_begin(compiler, vector_in); + uint flags = 0; + + if (compress_as_srgb) { + flags |= NODE_IMAGE_COMPRESS_AS_SRGB; + } compiler.add_node(NODE_TEX_ENVIRONMENT, slot, compiler.encode_uchar4(vector_offset, compiler.stack_assign_if_linked(color_out), compiler.stack_assign_if_linked(alpha_out), - srgb), + flags), projection); tex_mapping.compile_end(compiler, vector_in, vector_offset); @@ -529,8 +554,6 @@ void EnvironmentTextureNode::compile(SVMCompiler &compiler) void EnvironmentTextureNode::compile(OSLCompiler &compiler) { - ShaderOutput *alpha_out = output("Alpha"); - tex_mapping.compile(compiler); /* See comments in ImageTextureNode::compile about support @@ -540,7 +563,7 @@ void EnvironmentTextureNode::compile(OSLCompiler &compiler) if (is_float == -1) { ImageMetaData metadata; if (builtin_data == NULL) { - image_manager->get_image_metadata(filename.string(), NULL, metadata); + image_manager->get_image_metadata(filename.string(), NULL, colorspace, metadata); } else { slot = image_manager->add_image(filename.string(), @@ -549,28 +572,27 @@ void EnvironmentTextureNode::compile(OSLCompiler &compiler) 0, interpolation, EXTENSION_REPEAT, - use_alpha, + alpha_type, + colorspace, metadata); } is_float = metadata.is_float; - is_linear = metadata.is_linear; + compress_as_srgb = metadata.compress_as_srgb; + known_colorspace = metadata.colorspace; } if (slot == -1) { - compiler.parameter(this, "filename"); + compiler.parameter_texture("filename", filename, known_colorspace); } else { - compiler.parameter("filename", string_printf("@i%d", slot).c_str()); + compiler.parameter_texture("filename", slot); } - compiler.parameter(this, "projection"); - if (is_linear || color_space != NODE_COLOR_SPACE_COLOR) - compiler.parameter("color_space", "linear"); - else - compiler.parameter("color_space", "sRGB"); + compiler.parameter(this, "projection"); compiler.parameter(this, "interpolation"); + compiler.parameter("compress_as_srgb", compress_as_srgb); + compiler.parameter("ignore_alpha", alpha_type == IMAGE_ALPHA_IGNORE); compiler.parameter("is_float", is_float); - compiler.parameter("use_alpha", !alpha_out->links.empty()); compiler.add(this, "node_environment_texture"); } @@ -1080,7 +1102,7 @@ void IESLightNode::compile(OSLCompiler &compiler) tex_mapping.compile(compiler); - compiler.parameter("slot", slot); + compiler.parameter_texture_ies("filename", slot); compiler.add(this, "node_ies_light"); } @@ -1482,17 +1504,24 @@ PointDensityTextureNode::PointDensityTextureNode() : ShaderNode(node_type) PointDensityTextureNode::~PointDensityTextureNode() { if (image_manager) { - image_manager->remove_image( - filename.string(), builtin_data, interpolation, EXTENSION_CLIP, true); + image_manager->remove_image(filename.string(), + builtin_data, + interpolation, + EXTENSION_CLIP, + IMAGE_ALPHA_AUTO, + ustring()); } } ShaderNode *PointDensityTextureNode::clone() const { - PointDensityTextureNode *node = new PointDensityTextureNode(*this); - node->image_manager = NULL; - node->slot = -1; - return node; + /* Increase image user count for new node. We need to ensure to not call + * add_image again, to work around access of freed data on the Blender + * side. A better solution should be found to avoid this. */ + if (slot != -1) { + image_manager->add_image_user(slot); + } + return new PointDensityTextureNode(*this); } void PointDensityTextureNode::attributes(Shader *shader, AttributeRequestSet *attributes) @@ -1507,8 +1536,15 @@ void PointDensityTextureNode::add_image() { if (slot == -1) { ImageMetaData metadata; - slot = image_manager->add_image( - filename.string(), builtin_data, false, 0, interpolation, EXTENSION_CLIP, true, metadata); + slot = image_manager->add_image(filename.string(), + builtin_data, + false, + 0, + interpolation, + EXTENSION_CLIP, + IMAGE_ALPHA_AUTO, + u_colorspace_raw, + metadata); } } @@ -1567,9 +1603,7 @@ void PointDensityTextureNode::compile(OSLCompiler &compiler) if (use_density || use_color) { add_image(); - if (slot != -1) { - compiler.parameter("filename", string_printf("@i%d", slot).c_str()); - } + compiler.parameter_texture("filename", slot); if (space == NODE_TEX_VOXEL_SPACE_WORLD) { compiler.parameter("mapping", tfm); compiler.parameter("use_mapping", 1); @@ -2430,6 +2464,8 @@ NODE_DEFINE(PrincipledBsdfNode) SOCKET_IN_FLOAT(transmission, "Transmission", 0.0f); SOCKET_IN_FLOAT(transmission_roughness, "Transmission Roughness", 0.0f); SOCKET_IN_FLOAT(anisotropic_rotation, "Anisotropic Rotation", 0.0f); + SOCKET_IN_COLOR(emission, "Emission", make_float3(0.0f, 0.0f, 0.0f)); + SOCKET_IN_FLOAT(alpha, "Alpha", 1.0f); SOCKET_IN_NORMAL(normal, "Normal", make_float3(0.0f, 0.0f, 0.0f), SocketType::LINK_NORMAL); SOCKET_IN_NORMAL(clearcoat_normal, "Clearcoat Normal", @@ -2450,6 +2486,48 @@ PrincipledBsdfNode::PrincipledBsdfNode() : BsdfBaseNode(node_type) distribution_orig = NBUILTIN_CLOSURES; } +void PrincipledBsdfNode::expand(ShaderGraph *graph) +{ + ShaderOutput *principled_out = output("BSDF"); + + ShaderInput *emission_in = input("Emission"); + if (emission_in->link || emission != make_float3(0.0f, 0.0f, 0.0f)) { + /* Create add closure and emission. */ + AddClosureNode *add = new AddClosureNode(); + EmissionNode *emission_node = new EmissionNode(); + ShaderOutput *new_out = add->output("Closure"); + + graph->add(add); + graph->add(emission_node); + + emission_node->strength = 1.0f; + graph->relink(emission_in, emission_node->input("Color")); + graph->relink(principled_out, new_out); + graph->connect(emission_node->output("Emission"), add->input("Closure1")); + graph->connect(principled_out, add->input("Closure2")); + + principled_out = new_out; + } + + ShaderInput *alpha_in = input("Alpha"); + if (alpha_in->link || alpha != 1.0f) { + /* Create mix and transparent BSDF for alpha transparency. */ + MixClosureNode *mix = new MixClosureNode(); + TransparentBsdfNode *transparent = new TransparentBsdfNode(); + + graph->add(mix); + graph->add(transparent); + + graph->relink(alpha_in, mix->input("Fac")); + graph->relink(principled_out, mix->output("Closure")); + graph->connect(transparent->output("BSDF"), mix->input("Closure1")); + graph->connect(principled_out, mix->input("Closure2")); + } + + remove_input(emission_in); + remove_input(alpha_in); +} + bool PrincipledBsdfNode::has_surface_bssrdf() { ShaderInput *subsurface_in = input("Subsurface"); @@ -2630,7 +2708,7 @@ NODE_DEFINE(TransparentBsdfNode) { NodeType *type = NodeType::add("transparent_bsdf", create, NodeType::SHADER); - SOCKET_IN_COLOR(color, "Color", make_float3(0.8f, 0.8f, 0.8f)); + SOCKET_IN_COLOR(color, "Color", make_float3(1.0f, 1.0f, 1.0f)); SOCKET_IN_FLOAT(surface_mix_weight, "SurfaceMixWeight", 0.0f, SocketType::SVM_INTERNAL); SOCKET_OUT_CLOSURE(BSDF, "BSDF"); diff --git a/intern/cycles/render/nodes.h b/intern/cycles/render/nodes.h index 7796711115e..6b21be88663 100644 --- a/intern/cycles/render/nodes.h +++ b/intern/cycles/render/nodes.h @@ -92,13 +92,18 @@ class ImageTextureNode : public ImageSlotTextureNode { return true; } - ImageManager *image_manager; - int is_float; - bool is_linear; - bool use_alpha; + virtual bool equals(const ShaderNode &other) + { + const ImageTextureNode &image_node = (const ImageTextureNode &)other; + return ImageSlotTextureNode::equals(other) && builtin_data == image_node.builtin_data && + animated == image_node.animated; + } + + /* Parameters. */ ustring filename; void *builtin_data; - NodeImageColorSpace color_space; + ustring colorspace; + ImageAlphaType alpha_type; NodeImageProjection projection; InterpolationType interpolation; ExtensionType extension; @@ -106,12 +111,11 @@ class ImageTextureNode : public ImageSlotTextureNode { bool animated; float3 vector; - virtual bool equals(const ShaderNode &other) - { - const ImageTextureNode &image_node = (const ImageTextureNode &)other; - return ImageSlotTextureNode::equals(other) && builtin_data == image_node.builtin_data && - animated == image_node.animated; - } + /* Runtime. */ + ImageManager *image_manager; + int is_float; + bool compress_as_srgb; + ustring known_colorspace; }; class EnvironmentTextureNode : public ImageSlotTextureNode { @@ -129,24 +133,28 @@ class EnvironmentTextureNode : public ImageSlotTextureNode { return NODE_GROUP_LEVEL_2; } - ImageManager *image_manager; - int is_float; - bool is_linear; - bool use_alpha; + virtual bool equals(const ShaderNode &other) + { + const EnvironmentTextureNode &env_node = (const EnvironmentTextureNode &)other; + return ImageSlotTextureNode::equals(other) && builtin_data == env_node.builtin_data && + animated == env_node.animated; + } + + /* Parameters. */ ustring filename; void *builtin_data; - NodeImageColorSpace color_space; + ustring colorspace; + ImageAlphaType alpha_type; NodeEnvironmentProjection projection; InterpolationType interpolation; bool animated; float3 vector; - virtual bool equals(const ShaderNode &other) - { - const EnvironmentTextureNode &env_node = (const EnvironmentTextureNode &)other; - return ImageSlotTextureNode::equals(other) && builtin_data == env_node.builtin_data && - animated == env_node.animated; - } + /* Runtime. */ + ImageManager *image_manager; + int is_float; + bool compress_as_srgb; + ustring known_colorspace; }; class SkyTextureNode : public TextureNode { @@ -319,15 +327,17 @@ class PointDensityTextureNode : public ShaderNode { void add_image(); + /* Parameters. */ ustring filename; NodeTexVoxelSpace space; InterpolationType interpolation; Transform tfm; float3 vector; + void *builtin_data; + /* Runtime. */ ImageManager *image_manager; int slot; - void *builtin_data; virtual bool equals(const ShaderNode &other) { @@ -477,6 +487,7 @@ class PrincipledBsdfNode : public BsdfBaseNode { public: SHADER_NODE_CLASS(PrincipledBsdfNode) + void expand(ShaderGraph *graph); bool has_surface_bssrdf(); bool has_bssrdf_bump(); void compile(SVMCompiler &compiler, @@ -505,6 +516,8 @@ class PrincipledBsdfNode : public BsdfBaseNode { float surface_mix_weight; ClosureType distribution, distribution_orig; ClosureType subsurface_method; + float3 emission; + float alpha; bool has_integrator_dependency(); void attributes(Shader *shader, AttributeRequestSet *attributes); diff --git a/intern/cycles/render/osl.cpp b/intern/cycles/render/osl.cpp index b66a46938be..6f927bd5c42 100644 --- a/intern/cycles/render/osl.cpp +++ b/intern/cycles/render/osl.cpp @@ -16,6 +16,7 @@ #include "device/device.h" +#include "render/colorspace.h" #include "render/graph.h" #include "render/light.h" #include "render/osl.h" @@ -29,6 +30,7 @@ # include "kernel/osl/osl_services.h" # include "kernel/osl/osl_shader.h" +# include "util/util_aligned_malloc.h" # include "util/util_foreach.h" # include "util/util_logging.h" # include "util/util_md5.h" @@ -53,6 +55,7 @@ OSLRenderServices *OSLShaderManager::services_shared = NULL; int OSLShaderManager::ss_shared_users = 0; thread_mutex OSLShaderManager::ss_shared_mutex; thread_mutex OSLShaderManager::ss_mutex; +int OSLCompiler::texture_shared_unique_id = 0; /* Shader Manager */ @@ -115,7 +118,7 @@ void OSLShaderManager::device_update(Device *device, * compile shaders alternating */ thread_scoped_lock lock(ss_mutex); - OSLCompiler compiler((void *)this, (void *)ss, scene->image_manager, scene->light_manager); + OSLCompiler compiler(this, services, ss, scene->image_manager, scene->light_manager); compiler.background = (shader == scene->default_background); compiler.compile(scene, og, shader); @@ -140,6 +143,10 @@ void OSLShaderManager::device_update(Device *device, /* set texture system */ scene->image_manager->set_osl_texture_system((void *)ts); + /* add special builtin texture types */ + services->textures.insert(ustring("@ao"), new OSLTextureHandle(OSLTextureHandle::AO)); + services->textures.insert(ustring("@bevel"), new OSLTextureHandle(OSLTextureHandle::BEVEL)); + device_update_common(device, dscene, scene, progress); { @@ -218,7 +225,8 @@ void OSLShaderManager::shading_system_init() thread_scoped_lock lock(ss_shared_mutex); if (ss_shared_users == 0) { - services_shared = new OSLRenderServices(); + /* Must use aligned new due to concurrent hash map. */ + services_shared = util_aligned_new<OSLRenderServices>(ts_shared); string shader_path = path_get("shader"); # ifdef _WIN32 @@ -287,7 +295,7 @@ void OSLShaderManager::shading_system_free() delete ss_shared; ss_shared = NULL; - delete services_shared; + util_aligned_delete(services_shared); services_shared = NULL; } @@ -555,15 +563,17 @@ OSLNode *OSLShaderManager::osl_node(const std::string &filepath, /* Graph Compiler */ -OSLCompiler::OSLCompiler(void *manager_, - void *shadingsys_, - ImageManager *image_manager_, - LightManager *light_manager_) +OSLCompiler::OSLCompiler(OSLShaderManager *manager, + OSLRenderServices *services, + OSL::ShadingSystem *ss, + ImageManager *image_manager, + LightManager *light_manager) + : image_manager(image_manager), + light_manager(light_manager), + manager(manager), + services(services), + ss(ss) { - manager = manager_; - shadingsys = shadingsys_; - image_manager = image_manager_; - light_manager = light_manager_; current_type = SHADER_TYPE_SURFACE; current_shader = NULL; background = false; @@ -649,11 +659,9 @@ bool OSLCompiler::node_skip_input(ShaderNode *node, ShaderInput *input) void OSLCompiler::add(ShaderNode *node, const char *name, bool isfilepath) { - OSL::ShadingSystem *ss = (OSL::ShadingSystem *)shadingsys; - /* load filepath */ if (isfilepath) { - name = ((OSLShaderManager *)manager)->shader_load_filepath(name); + name = manager->shader_load_filepath(name); if (name == NULL) return; @@ -731,7 +739,7 @@ void OSLCompiler::add(ShaderNode *node, const char *name, bool isfilepath) } /* test if we shader contains specific closures */ - OSLShaderInfo *info = ((OSLShaderManager *)manager)->shader_loaded_info(name); + OSLShaderInfo *info = manager->shader_loaded_info(name); if (current_type == SHADER_TYPE_SURFACE) { if (info) { @@ -778,7 +786,6 @@ static TypeDesc array_typedesc(TypeDesc typedesc, int arraylength) void OSLCompiler::parameter(ShaderNode *node, const char *name) { - OSL::ShadingSystem *ss = (OSL::ShadingSystem *)shadingsys; ustring uname = ustring(name); const SocketType &socket = *(node->type->find_input(uname)); @@ -930,56 +937,47 @@ void OSLCompiler::parameter(ShaderNode *node, const char *name) void OSLCompiler::parameter(const char *name, float f) { - OSL::ShadingSystem *ss = (OSL::ShadingSystem *)shadingsys; ss->Parameter(name, TypeDesc::TypeFloat, &f); } void OSLCompiler::parameter_color(const char *name, float3 f) { - OSL::ShadingSystem *ss = (OSL::ShadingSystem *)shadingsys; ss->Parameter(name, TypeDesc::TypeColor, &f); } void OSLCompiler::parameter_point(const char *name, float3 f) { - OSL::ShadingSystem *ss = (OSL::ShadingSystem *)shadingsys; ss->Parameter(name, TypeDesc::TypePoint, &f); } void OSLCompiler::parameter_normal(const char *name, float3 f) { - OSL::ShadingSystem *ss = (OSL::ShadingSystem *)shadingsys; ss->Parameter(name, TypeDesc::TypeNormal, &f); } void OSLCompiler::parameter_vector(const char *name, float3 f) { - OSL::ShadingSystem *ss = (OSL::ShadingSystem *)shadingsys; ss->Parameter(name, TypeDesc::TypeVector, &f); } void OSLCompiler::parameter(const char *name, int f) { - OSL::ShadingSystem *ss = (OSL::ShadingSystem *)shadingsys; ss->Parameter(name, TypeDesc::TypeInt, &f); } void OSLCompiler::parameter(const char *name, const char *s) { - OSL::ShadingSystem *ss = (OSL::ShadingSystem *)shadingsys; ss->Parameter(name, TypeDesc::TypeString, &s); } void OSLCompiler::parameter(const char *name, ustring s) { - OSL::ShadingSystem *ss = (OSL::ShadingSystem *)shadingsys; const char *str = s.c_str(); ss->Parameter(name, TypeDesc::TypeString, &str); } void OSLCompiler::parameter(const char *name, const Transform &tfm) { - OSL::ShadingSystem *ss = (OSL::ShadingSystem *)shadingsys; ProjectionTransform projection(tfm); projection = projection_transpose(projection); ss->Parameter(name, TypeDesc::TypeMatrix, (float *)&projection); @@ -987,7 +985,6 @@ void OSLCompiler::parameter(const char *name, const Transform &tfm) void OSLCompiler::parameter_array(const char *name, const float f[], int arraylen) { - OSL::ShadingSystem *ss = (OSL::ShadingSystem *)shadingsys; TypeDesc type = TypeDesc::TypeFloat; type.arraylen = arraylen; ss->Parameter(name, type, f); @@ -1004,7 +1001,6 @@ void OSLCompiler::parameter_color_array(const char *name, const array<float3> &f table[i][2] = f[i].z; } - OSL::ShadingSystem *ss = (OSL::ShadingSystem *)shadingsys; TypeDesc type = TypeDesc::TypeColor; type.arraylen = table.size(); ss->Parameter(name, type, table.data()); @@ -1082,8 +1078,6 @@ void OSLCompiler::generate_nodes(const ShaderNodeSet &nodes) OSL::ShaderGroupRef OSLCompiler::compile_type(Shader *shader, ShaderGraph *graph, ShaderType type) { - OSL::ShadingSystem *ss = (OSL::ShadingSystem *)shadingsys; - current_type = type; OSL::ShaderGroupRef group = ss->ShaderGroupBegin(shader->name.c_str()); @@ -1194,6 +1188,35 @@ void OSLCompiler::compile(Scene *scene, OSLGlobals *og, Shader *shader) og->bump_state.push_back(shader->osl_surface_bump_ref); } +void OSLCompiler::parameter_texture(const char *name, ustring filename, ustring colorspace) +{ + /* Textured loaded through the OpenImageIO texture cache. For this + * case we need to do runtime color space conversion. */ + OSLTextureHandle *handle = new OSLTextureHandle(OSLTextureHandle::OIIO); + handle->processor = ColorSpaceManager::get_processor(colorspace); + services->textures.insert(filename, handle); + parameter(name, filename); +} + +void OSLCompiler::parameter_texture(const char *name, int svm_slot) +{ + /* Texture loaded through SVM image texture system. We generate a unique + * name, which ends up being used in OSLRenderServices::get_texture_handle + * to get handle again. Note that this name must be unique between multiple + * render sessions as the render services are shared. */ + ustring filename(string_printf("@svm%d", texture_shared_unique_id++).c_str()); + services->textures.insert(filename, new OSLTextureHandle(OSLTextureHandle::SVM, svm_slot)); + parameter(name, filename); +} + +void OSLCompiler::parameter_texture_ies(const char *name, int svm_slot) +{ + /* IES light textures stored in SVM. */ + ustring filename(string_printf("@svm%d", texture_shared_unique_id++).c_str()); + services->textures.insert(filename, new OSLTextureHandle(OSLTextureHandle::IES, svm_slot)); + parameter(name, filename); +} + #else void OSLCompiler::add(ShaderNode * /*node*/, const char * /*name*/, bool /*isfilepath*/) @@ -1248,6 +1271,20 @@ void OSLCompiler::parameter_color_array(const char * /*name*/, const array<float { } +void OSLCompiler::parameter_texture(const char * /* name */, + ustring /* filename */, + ustring /* colorspace */) +{ +} + +void OSLCompiler::parameter_texture(const char * /* name */, int /* svm_slot */) +{ +} + +void OSLCompiler::parameter_texture_ies(const char * /* name */, int /* svm_slot */) +{ +} + #endif /* WITH_OSL */ CCL_NAMESPACE_END diff --git a/intern/cycles/render/osl.h b/intern/cycles/render/osl.h index aec518a6c2b..17bf98a3433 100644 --- a/intern/cycles/render/osl.h +++ b/intern/cycles/render/osl.h @@ -127,10 +127,13 @@ class OSLShaderManager : public ShaderManager { class OSLCompiler { public: - OSLCompiler(void *manager, - void *shadingsys, +#ifdef WITH_OSL + OSLCompiler(OSLShaderManager *manager, + OSLRenderServices *services, + OSL::ShadingSystem *shadingsys, ImageManager *image_manager, LightManager *light_manager); +#endif void compile(Scene *scene, OSLGlobals *og, Shader *shader); void add(ShaderNode *node, const char *name, bool isfilepath = false); @@ -152,6 +155,10 @@ class OSLCompiler { void parameter_attribute(const char *name, ustring s); + void parameter_texture(const char *name, ustring filename, ustring colorspace); + void parameter_texture(const char *name, int svm_slot); + void parameter_texture_ies(const char *name, int svm_slot); + ShaderType output_type() { return current_type; @@ -171,12 +178,16 @@ class OSLCompiler { void find_dependencies(ShaderNodeSet &dependencies, ShaderInput *input); void generate_nodes(const ShaderNodeSet &nodes); + + OSLShaderManager *manager; + OSLRenderServices *services; + OSL::ShadingSystem *ss; #endif - void *shadingsys; - void *manager; ShaderType current_type; Shader *current_shader; + + static int texture_shared_unique_id; }; CCL_NAMESPACE_END diff --git a/intern/cycles/render/shader.cpp b/intern/cycles/render/shader.cpp index ac3303cbfeb..ec85e516832 100644 --- a/intern/cycles/render/shader.cpp +++ b/intern/cycles/render/shader.cpp @@ -14,9 +14,11 @@ * limitations under the License. */ +#include "device/device.h" + #include "render/background.h" #include "render/camera.h" -#include "device/device.h" +#include "render/colorspace.h" #include "render/graph.h" #include "render/integrator.h" #include "render/light.h" @@ -717,6 +719,8 @@ void ShaderManager::free_memory() #ifdef WITH_OSL OSLShaderManager::free_memory(); #endif + + ColorSpaceManager::free_memory(); } float ShaderManager::linear_rgb_to_gray(float3 c) diff --git a/intern/cycles/util/util_aligned_malloc.h b/intern/cycles/util/util_aligned_malloc.h index 0f006e95f6a..df7d93c056d 100644 --- a/intern/cycles/util/util_aligned_malloc.h +++ b/intern/cycles/util/util_aligned_malloc.h @@ -30,6 +30,21 @@ void *util_aligned_malloc(size_t size, int alignment); /* Free memory allocated by util_aligned_malloc. */ void util_aligned_free(void *ptr); +/* Aligned new operator. */ +template<typename T, typename... Args> T *util_aligned_new(Args... args) +{ + void *mem = util_aligned_malloc(sizeof(T), alignof(T)); + return new (mem) T(args...); +} + +template<typename T> void util_aligned_delete(T *t) +{ + if (t) { + t->~T(); + util_aligned_free(t); + } +} + CCL_NAMESPACE_END #endif /* __UTIL_ALIGNED_MALLOC_H__ */ diff --git a/intern/cycles/util/util_color.h b/intern/cycles/util/util_color.h index 85f241c6221..83410db13c6 100644 --- a/intern/cycles/util/util_color.h +++ b/intern/cycles/util/util_color.h @@ -236,6 +236,12 @@ ccl_device float3 color_linear_to_srgb_v3(float3 c) color_linear_to_srgb(c.x), color_linear_to_srgb(c.y), color_linear_to_srgb(c.z)); } +ccl_device float4 color_linear_to_srgb_v4(float4 c) +{ + return make_float4( + color_linear_to_srgb(c.x), color_linear_to_srgb(c.y), color_linear_to_srgb(c.z), c.w); +} + ccl_device float4 color_srgb_to_linear_v4(float4 c) { #ifdef __KERNEL_SSE2__ diff --git a/intern/cycles/util/util_image.h b/intern/cycles/util/util_image.h index 8962c09d098..27ec7ffb423 100644 --- a/intern/cycles/util/util_image.h +++ b/intern/cycles/util/util_image.h @@ -21,6 +21,7 @@ # include <OpenImageIO/imageio.h> +# include "util/util_half.h" # include "util/util_vector.h" CCL_NAMESPACE_BEGIN diff --git a/intern/cycles/util/util_map.h b/intern/cycles/util/util_map.h index 3c9288417cf..8385b08dd5a 100644 --- a/intern/cycles/util/util_map.h +++ b/intern/cycles/util/util_map.h @@ -26,6 +26,13 @@ using std::map; using std::pair; using std::unordered_map; +template<typename T> static void map_free_memory(T &data) +{ + /* Use swap() trick to actually free all internal memory. */ + T empty_data; + data.swap(empty_data); +} + CCL_NAMESPACE_END #endif /* __UTIL_MAP_H__ */ diff --git a/intern/cycles/util/util_math.h b/intern/cycles/util/util_math.h index 92cab29346a..dde0d31f467 100644 --- a/intern/cycles/util/util_math.h +++ b/intern/cycles/util/util_math.h @@ -651,6 +651,26 @@ ccl_device_inline float2 map_to_sphere(const float3 co) return make_float2(u, v); } +/* Compares two floats. + * Returns true if their absolute difference is smaller than abs_diff (for numbers near zero) + * or their relative difference is less than ulp_diff ULPs. + * Based on + * https://randomascii.wordpress.com/2012/02/25/comparing-floating-point-numbers-2012-edition/ + */ + +ccl_device_inline float compare_floats(float a, float b, float abs_diff, int ulp_diff) +{ + if (fabsf(a - b) < abs_diff) { + return true; + } + + if ((a < 0.0f) != (b < 0.0f)) { + return false; + } + + return (abs(__float_as_int(a) - __float_as_int(b)) < ulp_diff); +} + CCL_NAMESPACE_END #endif /* __UTIL_MATH_H__ */ diff --git a/intern/cycles/util/util_texture.h b/intern/cycles/util/util_texture.h index 5ce16e0095a..d43852480d1 100644 --- a/intern/cycles/util/util_texture.h +++ b/intern/cycles/util/util_texture.h @@ -59,6 +59,18 @@ typedef enum ImageDataType { IMAGE_DATA_NUM_TYPES } ImageDataType; +/* Alpha types + * How to treat alpha in images. */ +typedef enum ImageAlphaType { + IMAGE_ALPHA_UNASSOCIATED = 0, + IMAGE_ALPHA_ASSOCIATED = 1, + IMAGE_ALPHA_CHANNEL_PACKED = 2, + IMAGE_ALPHA_IGNORE = 3, + IMAGE_ALPHA_AUTO = 4, + + IMAGE_ALPHA_NUM_TYPES, +} ImageAlphaType; + #define IMAGE_DATA_TYPE_SHIFT 3 #define IMAGE_DATA_TYPE_MASK 0x7 |