diff options
Diffstat (limited to 'intern/cycles')
21 files changed, 397 insertions, 145 deletions
diff --git a/intern/cycles/blender/blender_camera.cpp b/intern/cycles/blender/blender_camera.cpp index b31841801d8..6954c5c2f26 100644 --- a/intern/cycles/blender/blender_camera.cpp +++ b/intern/cycles/blender/blender_camera.cpp @@ -83,6 +83,8 @@ struct BlenderCamera { BoundBox2D pano_viewplane; BoundBox2D viewport_camera_border; + float passepartout_alpha; + Transform matrix; float offscreen_dicing_scale; @@ -125,6 +127,7 @@ static void blender_camera_init(BlenderCamera *bcam, BL::RenderSettings &b_rende bcam->pano_viewplane.top = 1.0f; bcam->viewport_camera_border.right = 1.0f; bcam->viewport_camera_border.top = 1.0f; + bcam->passepartout_alpha = 0.5f; bcam->offscreen_dicing_scale = 1.0f; bcam->matrix = transform_identity(); @@ -212,6 +215,8 @@ static void blender_camera_from_object(BlenderCamera *bcam, bcam->lens = b_camera.lens(); + bcam->passepartout_alpha = b_camera.show_passepartout() ? b_camera.passepartout_alpha() : 0.0f; + if (b_camera.dof().use_dof()) { /* allow f/stop number to change aperture_size but still * give manual control over aperture radius */ @@ -834,15 +839,19 @@ static void blender_camera_border(BlenderCamera *bcam, full_border, &bcam->viewport_camera_border); - if (!b_render.use_border()) { + if (b_render.use_border()) { + bcam->border.left = b_render.border_min_x(); + bcam->border.right = b_render.border_max_x(); + bcam->border.bottom = b_render.border_min_y(); + bcam->border.top = b_render.border_max_y(); + } + else if (bcam->passepartout_alpha == 1.0f) { + bcam->border = full_border; + } + else { return; } - bcam->border.left = b_render.border_min_x(); - bcam->border.right = b_render.border_max_x(); - bcam->border.bottom = b_render.border_min_y(); - bcam->border.top = b_render.border_max_y(); - /* Determine viewport subset matching camera border. */ blender_camera_border_subset(b_engine, b_render, @@ -885,8 +894,7 @@ void BlenderSync::sync_view(BL::SpaceView3D &b_v3d, } } -BufferParams BlenderSync::get_buffer_params(BL::RenderSettings &b_render, - BL::SpaceView3D &b_v3d, +BufferParams BlenderSync::get_buffer_params(BL::SpaceView3D &b_v3d, BL::RegionView3D &b_rv3d, Camera *cam, int width, @@ -902,7 +910,8 @@ BufferParams BlenderSync::get_buffer_params(BL::RenderSettings &b_render, if (b_v3d && b_rv3d && b_rv3d.view_perspective() != BL::RegionView3D::view_perspective_CAMERA) use_border = b_v3d.use_render_border(); else - use_border = b_render.use_border(); + /* the camera can always have a passepartout */ + use_border = true; if (use_border) { /* border render */ diff --git a/intern/cycles/blender/blender_session.cpp b/intern/cycles/blender/blender_session.cpp index 89854f6a0e5..29de886e4ff 100644 --- a/intern/cycles/blender/blender_session.cpp +++ b/intern/cycles/blender/blender_session.cpp @@ -155,7 +155,7 @@ void BlenderSession::create_session() /* set buffer parameters */ BufferParams buffer_params = BlenderSync::get_buffer_params( - b_render, b_v3d, b_rv3d, scene->camera, width, height, session_params.denoising.use); + b_v3d, b_rv3d, scene->camera, width, height, session_params.denoising.use); session->reset(buffer_params, session_params.samples); b_engine.use_highlight_tiles(session_params.progressive_refine == false); @@ -242,8 +242,7 @@ void BlenderSession::reset_session(BL::BlendData &b_data, BL::Depsgraph &b_depsg BL::SpaceView3D b_null_space_view3d(PointerRNA_NULL); BL::RegionView3D b_null_region_view3d(PointerRNA_NULL); - BufferParams buffer_params = BlenderSync::get_buffer_params(b_render, - b_null_space_view3d, + BufferParams buffer_params = BlenderSync::get_buffer_params(b_null_space_view3d, b_null_region_view3d, scene->camera, width, @@ -486,7 +485,7 @@ void BlenderSession::render(BL::Depsgraph &b_depsgraph_) SessionParams session_params = BlenderSync::get_session_params( b_engine, b_userpref, b_scene, background, b_view_layer); BufferParams buffer_params = BlenderSync::get_buffer_params( - b_render, b_v3d, b_rv3d, scene->camera, width, height, session_params.denoising.use); + b_v3d, b_rv3d, scene->camera, width, height, session_params.denoising.use); /* temporary render result to find needed passes and views */ BL::RenderResult b_rr = begin_render_result( @@ -810,7 +809,7 @@ void BlenderSession::synchronize(BL::Depsgraph &b_depsgraph_) /* get buffer parameters */ BufferParams buffer_params = BlenderSync::get_buffer_params( - b_render, b_v3d, b_rv3d, scene->camera, width, height, session_params.denoising.use); + b_v3d, b_rv3d, scene->camera, width, height, session_params.denoising.use); if (!buffer_params.denoising_data_pass) { session_params.denoising.use = false; @@ -889,7 +888,7 @@ bool BlenderSession::draw(int w, int h) SessionParams session_params = BlenderSync::get_session_params( b_engine, b_userpref, b_scene, background); BufferParams buffer_params = BlenderSync::get_buffer_params( - b_render, b_v3d, b_rv3d, scene->camera, width, height, session_params.denoising.use); + b_v3d, b_rv3d, scene->camera, width, height, session_params.denoising.use); bool session_pause = BlenderSync::get_session_pause(b_scene, background); if (session_pause == false) { @@ -907,7 +906,7 @@ bool BlenderSession::draw(int w, int h) /* draw */ BufferParams buffer_params = BlenderSync::get_buffer_params( - b_render, b_v3d, b_rv3d, scene->camera, width, height, session->params.denoising.use); + b_v3d, b_rv3d, scene->camera, width, height, session->params.denoising.use); DeviceDrawParams draw_params; if (session->params.display_buffer_linear) { diff --git a/intern/cycles/blender/blender_shader.cpp b/intern/cycles/blender/blender_shader.cpp index 736c10e8881..7f129310736 100644 --- a/intern/cycles/blender/blender_shader.cpp +++ b/intern/cycles/blender/blender_shader.cpp @@ -1373,7 +1373,7 @@ void BlenderSync::sync_world(BL::Depsgraph &b_depsgraph, BL::SpaceView3D &b_v3d, BlenderViewportParameters new_viewport_parameters(b_v3d); if (world_recalc || update_all || b_world.ptr.data != world_map || - viewport_parameters.modified(new_viewport_parameters)) { + viewport_parameters.shader_modified(new_viewport_parameters)) { Shader *shader = scene->default_background; ShaderGraph *graph = new ShaderGraph(); @@ -1501,8 +1501,8 @@ void BlenderSync::sync_world(BL::Depsgraph &b_depsgraph, BL::SpaceView3D &b_v3d, background->set_transparent_roughness_threshold(0.0f); } - background->set_use_shader(view_layer.use_background_shader | - viewport_parameters.custom_viewport_parameters()); + background->set_use_shader(view_layer.use_background_shader || + viewport_parameters.use_custom_shader()); background->set_use_ao(background->get_use_ao() && view_layer.use_background_ao); background->tag_update(scene); diff --git a/intern/cycles/blender/blender_sync.cpp b/intern/cycles/blender/blender_sync.cpp index 9d0f9f29f94..82b3abd4432 100644 --- a/intern/cycles/blender/blender_sync.cpp +++ b/intern/cycles/blender/blender_sync.cpp @@ -224,9 +224,13 @@ void BlenderSync::sync_recalc(BL::Depsgraph &b_depsgraph, BL::SpaceView3D &b_v3d if (b_v3d) { BlenderViewportParameters new_viewport_parameters(b_v3d); - if (viewport_parameters.modified(new_viewport_parameters)) { + + if (viewport_parameters.shader_modified(new_viewport_parameters)) { world_recalc = true; + has_updates_ = true; } + + has_updates_ |= viewport_parameters.modified(new_viewport_parameters); } } @@ -246,7 +250,7 @@ void BlenderSync::sync_data(BL::RenderSettings &b_render, BL::ViewLayer b_view_layer = b_depsgraph.view_layer_eval(); - sync_view_layer(b_v3d, b_view_layer); + sync_view_layer(b_view_layer); sync_integrator(); sync_film(b_v3d); sync_shaders(b_depsgraph, b_v3d); @@ -441,7 +445,7 @@ void BlenderSync::sync_film(BL::SpaceView3D &b_v3d) /* Render Layer */ -void BlenderSync::sync_view_layer(BL::SpaceView3D & /*b_v3d*/, BL::ViewLayer &b_view_layer) +void BlenderSync::sync_view_layer(BL::ViewLayer &b_view_layer) { view_layer.name = b_view_layer.name(); diff --git a/intern/cycles/blender/blender_sync.h b/intern/cycles/blender/blender_sync.h index 15a10f2b46b..1c98e529190 100644 --- a/intern/cycles/blender/blender_sync.h +++ b/intern/cycles/blender/blender_sync.h @@ -73,7 +73,7 @@ class BlenderSync { int width, int height, void **python_thread_state); - void sync_view_layer(BL::SpaceView3D &b_v3d, BL::ViewLayer &b_view_layer); + void sync_view_layer(BL::ViewLayer &b_view_layer); vector<Pass> sync_render_passes(BL::Scene &b_scene, BL::RenderLayer &b_render_layer, BL::ViewLayer &b_view_layer, @@ -104,8 +104,7 @@ class BlenderSync { bool background, BL::ViewLayer b_view_layer = BL::ViewLayer(PointerRNA_NULL)); static bool get_session_pause(BL::Scene &b_scene, bool background); - static BufferParams get_buffer_params(BL::RenderSettings &b_render, - BL::SpaceView3D &b_v3d, + static BufferParams get_buffer_params(BL::SpaceView3D &b_v3d, BL::RegionView3D &b_rv3d, Camera *cam, int width, diff --git a/intern/cycles/blender/blender_viewport.cpp b/intern/cycles/blender/blender_viewport.cpp index 73ef5f94720..07408fee218 100644 --- a/intern/cycles/blender/blender_viewport.cpp +++ b/intern/cycles/blender/blender_viewport.cpp @@ -13,6 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + #include "blender_viewport.h" #include "blender_util.h" @@ -25,29 +26,39 @@ BlenderViewportParameters::BlenderViewportParameters() studiolight_rotate_z(0.0f), studiolight_intensity(1.0f), studiolight_background_alpha(1.0f), - studiolight_path(ustring()) + display_pass(PASS_COMBINED) { } BlenderViewportParameters::BlenderViewportParameters(BL::SpaceView3D &b_v3d) : BlenderViewportParameters() { - /* We only copy the parameters if we are in look dev mode. otherwise + if (!b_v3d) { + return; + } + + BL::View3DShading shading = b_v3d.shading(); + PointerRNA cshading = RNA_pointer_get(&shading.ptr, "cycles"); + + /* We only copy the shading parameters if we are in look dev mode. otherwise * defaults are being used. These defaults mimic normal render settings */ - if (b_v3d && b_v3d.shading().type() == BL::View3DShading::type_RENDERED) { - use_scene_world = b_v3d.shading().use_scene_world_render(); - use_scene_lights = b_v3d.shading().use_scene_lights_render(); + if (shading.type() == BL::View3DShading::type_RENDERED) { + use_scene_world = shading.use_scene_world_render(); + use_scene_lights = shading.use_scene_lights_render(); + if (!use_scene_world) { - studiolight_rotate_z = b_v3d.shading().studiolight_rotate_z(); - studiolight_intensity = b_v3d.shading().studiolight_intensity(); - studiolight_background_alpha = b_v3d.shading().studiolight_background_alpha(); - studiolight_path = b_v3d.shading().selected_studio_light().path(); + studiolight_rotate_z = shading.studiolight_rotate_z(); + studiolight_intensity = shading.studiolight_intensity(); + studiolight_background_alpha = shading.studiolight_background_alpha(); + studiolight_path = shading.selected_studio_light().path(); } } + + /* Film. */ + display_pass = (PassType)get_enum(cshading, "render_pass", -1, -1); } -/* Check if two instances are different. */ -const bool BlenderViewportParameters::modified(const BlenderViewportParameters &other) const +bool BlenderViewportParameters::shader_modified(const BlenderViewportParameters &other) const { return use_scene_world != other.use_scene_world || use_scene_lights != other.use_scene_lights || studiolight_rotate_z != other.studiolight_rotate_z || @@ -56,26 +67,26 @@ const bool BlenderViewportParameters::modified(const BlenderViewportParameters & studiolight_path != other.studiolight_path; } -const bool BlenderViewportParameters::custom_viewport_parameters() const +bool BlenderViewportParameters::film_modified(const BlenderViewportParameters &other) const { - return !(use_scene_world && use_scene_lights); + return display_pass != other.display_pass; } -PassType BlenderViewportParameters::get_viewport_display_render_pass(BL::SpaceView3D &b_v3d) +bool BlenderViewportParameters::modified(const BlenderViewportParameters &other) const { - PassType display_pass = PASS_NONE; - if (b_v3d) { - BL::View3DShading b_view3dshading = b_v3d.shading(); - PointerRNA cshading = RNA_pointer_get(&b_view3dshading.ptr, "cycles"); - display_pass = (PassType)get_enum(cshading, "render_pass", -1, -1); - } - return display_pass; + return shader_modified(other) || film_modified(other); +} + +bool BlenderViewportParameters::use_custom_shader() const +{ + return !(use_scene_world && use_scene_lights); } PassType update_viewport_display_passes(BL::SpaceView3D &b_v3d, vector<Pass> &passes) { if (b_v3d) { - PassType display_pass = BlenderViewportParameters::get_viewport_display_render_pass(b_v3d); + const BlenderViewportParameters viewport_parameters(b_v3d); + const PassType display_pass = viewport_parameters.display_pass; passes.clear(); Pass::add(display_pass, passes); diff --git a/intern/cycles/blender/blender_viewport.h b/intern/cycles/blender/blender_viewport.h index 7c6c9c4d274..d6518597053 100644 --- a/intern/cycles/blender/blender_viewport.h +++ b/intern/cycles/blender/blender_viewport.h @@ -18,17 +18,18 @@ #define __BLENDER_VIEWPORT_H__ #include "MEM_guardedalloc.h" + #include "RNA_access.h" #include "RNA_blender_cpp.h" #include "RNA_types.h" #include "render/film.h" -#include "util/util_param.h" CCL_NAMESPACE_BEGIN class BlenderViewportParameters { - private: + public: + /* Shader. */ bool use_scene_world; bool use_scene_lights; float studiolight_rotate_z; @@ -36,17 +37,24 @@ class BlenderViewportParameters { float studiolight_background_alpha; ustring studiolight_path; + /* Film. */ + PassType display_pass; + BlenderViewportParameters(); - BlenderViewportParameters(BL::SpaceView3D &b_v3d); + explicit BlenderViewportParameters(BL::SpaceView3D &b_v3d); - const bool modified(const BlenderViewportParameters &other) const; - const bool custom_viewport_parameters() const; - friend class BlenderSync; + /* Check whether any of shading related settings are different from the given parameters. */ + bool shader_modified(const BlenderViewportParameters &other) const; - public: - /* Retrieve the render pass that needs to be displayed on the given `SpaceView3D` - * When the `b_v3d` parameter is not given `PASS_NONE` will be returned. */ - static PassType get_viewport_display_render_pass(BL::SpaceView3D &b_v3d); + /* Check whether any of film related settings are different from the given parameters. */ + bool film_modified(const BlenderViewportParameters &other) const; + + /* Check whether any of settings are different from the given parameters. */ + bool modified(const BlenderViewportParameters &other) const; + + /* Returns truth when a custom shader defined by the viewport is to be used instead of the + * regular background shader or scene light. */ + bool use_custom_shader() const; }; PassType update_viewport_display_passes(BL::SpaceView3D &b_v3d, vector<Pass> &passes); diff --git a/intern/cycles/device/device_optix.cpp b/intern/cycles/device/device_optix.cpp index 01de0724cb2..b008dfa376f 100644 --- a/intern/cycles/device/device_optix.cpp +++ b/intern/cycles/device/device_optix.cpp @@ -726,7 +726,11 @@ class OptiXDevice : public CUDADevice { } } else if (task.type == DeviceTask::SHADER) { - launch_shader_eval(task, thread_index); + // CUDA kernels are used when doing baking + if (optix_module == NULL) + CUDADevice::shader(task); + else + launch_shader_eval(task, thread_index); } else if (task.type == DeviceTask::DENOISE_BUFFER) { // Set up a single tile that covers the whole task and denoise it diff --git a/intern/cycles/kernel/kernel_montecarlo.h b/intern/cycles/kernel/kernel_montecarlo.h index ba25c0e24e4..ce37bd0b15e 100644 --- a/intern/cycles/kernel/kernel_montecarlo.h +++ b/intern/cycles/kernel/kernel_montecarlo.h @@ -195,31 +195,108 @@ ccl_device float2 regular_polygon_sample(float corners, float rotation, float u, ccl_device float3 ensure_valid_reflection(float3 Ng, float3 I, float3 N) { - float3 R; - float NI = dot(N, I); - float NgR, threshold; - - /* Check if the incident ray is coming from behind normal N. */ - if (NI > 0) { - /* Normal reflection */ - R = (2 * NI) * N - I; - NgR = dot(Ng, R); - - /* Reflection rays may always be at least as shallow as the incoming ray. */ - threshold = min(0.9f * dot(Ng, I), 0.01f); - if (NgR >= threshold) { - return N; + float3 R = 2 * dot(N, I) * N - I; + + /* Reflection rays may always be at least as shallow as the incoming ray. */ + float threshold = min(0.9f * dot(Ng, I), 0.01f); + if (dot(Ng, R) >= threshold) { + return N; + } + + /* Form coordinate system with Ng as the Z axis and N inside the X-Z-plane. + * The X axis is found by normalizing the component of N that's orthogonal to Ng. + * The Y axis isn't actually needed. + */ + float NdotNg = dot(N, Ng); + float3 X = normalize(N - NdotNg * Ng); + + /* Keep math expressions. */ + /* clang-format off */ + /* Calculate N.z and N.x in the local coordinate system. + * + * The goal of this computation is to find a N' that is rotated towards Ng just enough + * to lift R' above the threshold (here called t), therefore dot(R', Ng) = t. + * + * According to the standard reflection equation, + * this means that we want dot(2*dot(N', I)*N' - I, Ng) = t. + * + * Since the Z axis of our local coordinate system is Ng, dot(x, Ng) is just x.z, so we get + * 2*dot(N', I)*N'.z - I.z = t. + * + * The rotation is simple to express in the coordinate system we formed - + * since N lies in the X-Z-plane, we know that N' will also lie in the X-Z-plane, + * so N'.y = 0 and therefore dot(N', I) = N'.x*I.x + N'.z*I.z . + * + * Furthermore, we want N' to be normalized, so N'.x = sqrt(1 - N'.z^2). + * + * With these simplifications, + * we get the final equation 2*(sqrt(1 - N'.z^2)*I.x + N'.z*I.z)*N'.z - I.z = t. + * + * The only unknown here is N'.z, so we can solve for that. + * + * The equation has four solutions in general: + * + * N'.z = +-sqrt(0.5*(+-sqrt(I.x^2*(I.x^2 + I.z^2 - t^2)) + t*I.z + I.x^2 + I.z^2)/(I.x^2 + I.z^2)) + * We can simplify this expression a bit by grouping terms: + * + * a = I.x^2 + I.z^2 + * b = sqrt(I.x^2 * (a - t^2)) + * c = I.z*t + a + * N'.z = +-sqrt(0.5*(+-b + c)/a) + * + * Two solutions can immediately be discarded because they're negative so N' would lie in the + * lower hemisphere. + */ + /* clang-format on */ + + float Ix = dot(I, X), Iz = dot(I, Ng); + float Ix2 = sqr(Ix), Iz2 = sqr(Iz); + float a = Ix2 + Iz2; + + float b = safe_sqrtf(Ix2 * (a - sqr(threshold))); + float c = Iz * threshold + a; + + /* Evaluate both solutions. + * In many cases one can be immediately discarded (if N'.z would be imaginary or larger than + * one), so check for that first. If no option is viable (might happen in extreme cases like N + * being in the wrong hemisphere), give up and return Ng. */ + float fac = 0.5f / a; + float N1_z2 = fac * (b + c), N2_z2 = fac * (-b + c); + bool valid1 = (N1_z2 > 1e-5f) && (N1_z2 <= (1.0f + 1e-5f)); + bool valid2 = (N2_z2 > 1e-5f) && (N2_z2 <= (1.0f + 1e-5f)); + + float2 N_new; + if (valid1 && valid2) { + /* If both are possible, do the expensive reflection-based check. */ + float2 N1 = make_float2(safe_sqrtf(1.0f - N1_z2), safe_sqrtf(N1_z2)); + float2 N2 = make_float2(safe_sqrtf(1.0f - N2_z2), safe_sqrtf(N2_z2)); + + float R1 = 2 * (N1.x * Ix + N1.y * Iz) * N1.y - Iz; + float R2 = 2 * (N2.x * Ix + N2.y * Iz) * N2.y - Iz; + + valid1 = (R1 >= 1e-5f); + valid2 = (R2 >= 1e-5f); + if (valid1 && valid2) { + /* If both solutions are valid, return the one with the shallower reflection since it will be + * closer to the input (if the original reflection wasn't shallow, we would not be in this + * part of the function). */ + N_new = (R1 < R2) ? N1 : N2; } + else { + /* If only one reflection is valid (= positive), pick that one. */ + N_new = (R1 > R2) ? N1 : N2; + } + } + else if (valid1 || valid2) { + /* Only one solution passes the N'.z criterium, so pick that one. */ + float Nz2 = valid1 ? N1_z2 : N2_z2; + N_new = make_float2(safe_sqrtf(1.0f - Nz2), safe_sqrtf(Nz2)); } else { - /* Bad incident */ - R = -I; - NgR = dot(Ng, R); - threshold = 0.01f; + return Ng; } - R = R + Ng * (threshold - NgR); /* Lift the reflection above the threshold. */ - return normalize(I * len(R) + R * len(I)); /* Find a bisector. */ + return N_new.x * X + N_new.y * Ng; } CCL_NAMESPACE_END diff --git a/intern/cycles/kernel/shaders/node_vector_math.osl b/intern/cycles/kernel/shaders/node_vector_math.osl index 3963c23ea9c..c08d75b99ef 100644 --- a/intern/cycles/kernel/shaders/node_vector_math.osl +++ b/intern/cycles/kernel/shaders/node_vector_math.osl @@ -52,6 +52,9 @@ shader node_vector_math(string math_type = "add", else if (math_type == "faceforward") { Vector = compatible_faceforward(Vector1, Vector2, Vector3); } + else if (math_type == "multiply_add") { + Vector = Vector1 * Vector2 + Vector3; + } else if (math_type == "dot_product") { Value = dot(Vector1, Vector2); } diff --git a/intern/cycles/kernel/shaders/stdcycles.h b/intern/cycles/kernel/shaders/stdcycles.h index af7b645d9a2..dd604da68ce 100644 --- a/intern/cycles/kernel/shaders/stdcycles.h +++ b/intern/cycles/kernel/shaders/stdcycles.h @@ -84,30 +84,67 @@ closure color principled_hair(normal N, closure color henyey_greenstein(float g) BUILTIN; closure color absorption() BUILTIN; -normal ensure_valid_reflection(normal Ng, normal I, normal N) +normal ensure_valid_reflection(normal Ng, vector I, normal N) { /* The implementation here mirrors the one in kernel_montecarlo.h, * check there for an explanation of the algorithm. */ - vector R; - float NI = dot(N, I); - float NgR, threshold; - - if (NI > 0) { - R = (2 * NI) * N - I; - NgR = dot(Ng, R); - threshold = min(0.9 * dot(Ng, I), 0.01); - if (NgR >= threshold) { - return N; + + float sqr(float x) + { + return x * x; + } + + vector R = 2 * dot(N, I) * N - I; + + float threshold = min(0.9 * dot(Ng, I), 0.01); + if (dot(Ng, R) >= threshold) { + return N; + } + + float NdotNg = dot(N, Ng); + vector X = normalize(N - NdotNg * Ng); + + float Ix = dot(I, X), Iz = dot(I, Ng); + float Ix2 = sqr(Ix), Iz2 = sqr(Iz); + float a = Ix2 + Iz2; + + float b = sqrt(Ix2 * (a - sqr(threshold))); + float c = Iz * threshold + a; + + float fac = 0.5 / a; + float N1_z2 = fac * (b + c), N2_z2 = fac * (-b + c); + int valid1 = (N1_z2 > 1e-5) && (N1_z2 <= (1.0 + 1e-5)); + int valid2 = (N2_z2 > 1e-5) && (N2_z2 <= (1.0 + 1e-5)); + + float N_new_x, N_new_z; + if (valid1 && valid2) { + float N1_x = sqrt(1.0 - N1_z2), N1_z = sqrt(N1_z2); + float N2_x = sqrt(1.0 - N2_z2), N2_z = sqrt(N2_z2); + + float R1 = 2 * (N1_x * Ix + N1_z * Iz) * N1_z - Iz; + float R2 = 2 * (N2_x * Ix + N2_z * Iz) * N2_z - Iz; + + valid1 = (R1 >= 1e-5); + valid2 = (R2 >= 1e-5); + if (valid1 && valid2) { + N_new_x = (R1 < R2) ? N1_x : N2_x; + N_new_z = (R1 < R2) ? N1_z : N2_z; + } + else { + N_new_x = (R1 > R2) ? N1_x : N2_x; + N_new_z = (R1 > R2) ? N1_z : N2_z; } } + else if (valid1 || valid2) { + float Nz2 = valid1 ? N1_z2 : N2_z2; + N_new_x = sqrt(1.0 - Nz2); + N_new_z = sqrt(Nz2); + } else { - R = -I; - NgR = dot(Ng, R); - threshold = 0.01; + return Ng; } - R = R + Ng * (threshold - NgR); - return normalize(I * length(R) + R * length(I)); + return N_new_x * X + N_new_z * Ng; } #endif /* CCL_STDOSL_H */ diff --git a/intern/cycles/kernel/svm/svm_math.h b/intern/cycles/kernel/svm/svm_math.h index dda2e50f916..733ea28f9e5 100644 --- a/intern/cycles/kernel/svm/svm_math.h +++ b/intern/cycles/kernel/svm/svm_math.h @@ -58,7 +58,8 @@ ccl_device void svm_node_vector_math(KernelGlobals *kg, float3 vector; /* 3 Vector Operators */ - if (type == NODE_VECTOR_MATH_WRAP || type == NODE_VECTOR_MATH_FACEFORWARD) { + if (type == NODE_VECTOR_MATH_WRAP || type == NODE_VECTOR_MATH_FACEFORWARD || + type == NODE_VECTOR_MATH_MULTIPLY_ADD) { uint4 extra_node = read_node(kg, offset); c = stack_load_float3(stack, extra_node.x); } diff --git a/intern/cycles/kernel/svm/svm_math_util.h b/intern/cycles/kernel/svm/svm_math_util.h index 19fb1da5a1f..9e654f2247f 100644 --- a/intern/cycles/kernel/svm/svm_math_util.h +++ b/intern/cycles/kernel/svm/svm_math_util.h @@ -52,6 +52,9 @@ ccl_device void svm_vector_math(float *value, case NODE_VECTOR_MATH_FACEFORWARD: *vector = faceforward(a, b, c); break; + case NODE_VECTOR_MATH_MULTIPLY_ADD: + *vector = a * b + c; + break; case NODE_VECTOR_MATH_DOT_PRODUCT: *value = dot(a, b); break; diff --git a/intern/cycles/kernel/svm/svm_types.h b/intern/cycles/kernel/svm/svm_types.h index 64a8f82a094..062afcfa5ac 100644 --- a/intern/cycles/kernel/svm/svm_types.h +++ b/intern/cycles/kernel/svm/svm_types.h @@ -341,6 +341,7 @@ typedef enum NodeVectorMathType { NODE_VECTOR_MATH_TANGENT, NODE_VECTOR_MATH_REFRACT, NODE_VECTOR_MATH_FACEFORWARD, + NODE_VECTOR_MATH_MULTIPLY_ADD, } NodeVectorMathType; typedef enum NodeClampType { diff --git a/intern/cycles/render/alembic.cpp b/intern/cycles/render/alembic.cpp index cf345ee075d..dcb456dc1ce 100644 --- a/intern/cycles/render/alembic.cpp +++ b/intern/cycles/render/alembic.cpp @@ -654,8 +654,7 @@ static void update_attributes(AttributeSet &attributes, CachedData &cached_data, list<Attribute>::iterator it; for (it = attributes.attributes.begin(); it != attributes.attributes.end();) { if (cached_attributes.find(&(*it)) == cached_attributes.end()) { - attributes.attributes.erase(it++); - attributes.modified = true; + attributes.remove(it++); continue; } diff --git a/intern/cycles/render/alembic_read.cpp b/intern/cycles/render/alembic_read.cpp index 4d09c40b07b..33ce3502879 100644 --- a/intern/cycles/render/alembic_read.cpp +++ b/intern/cycles/render/alembic_read.cpp @@ -606,7 +606,8 @@ void read_geometry_data(AlembicProcedural *proc, template<typename T> struct value_type_converter { using cycles_type = float; - static constexpr TypeDesc type_desc = TypeFloat; + /* Use `TypeDesc::FLOAT` instead of `TypeFloat` to work around a compiler bug in gcc 11. */ + static constexpr TypeDesc type_desc = TypeDesc::FLOAT; static constexpr const char *type_name = "float (default)"; static cycles_type convert_value(T value) diff --git a/intern/cycles/render/attribute.cpp b/intern/cycles/render/attribute.cpp index d6a638fd4cd..bf9d69cb47e 100644 --- a/intern/cycles/render/attribute.cpp +++ b/intern/cycles/render/attribute.cpp @@ -383,6 +383,23 @@ AttributeStandard Attribute::name_standard(const char *name) return ATTR_STD_NONE; } +AttrKernelDataType Attribute::kernel_type(const Attribute &attr) +{ + if (attr.element == ATTR_ELEMENT_CORNER) { + return AttrKernelDataType::UCHAR4; + } + + if (attr.type == TypeDesc::TypeFloat) { + return AttrKernelDataType::FLOAT; + } + + if (attr.type == TypeFloat2) { + return AttrKernelDataType::FLOAT2; + } + + return AttrKernelDataType::FLOAT3; +} + void Attribute::get_uv_tiles(Geometry *geom, AttributePrimitive prim, unordered_set<int> &tiles) const @@ -417,7 +434,7 @@ void Attribute::get_uv_tiles(Geometry *geom, /* Attribute Set */ AttributeSet::AttributeSet(Geometry *geometry, AttributePrimitive prim) - : geometry(geometry), prim(prim) + : modified_flag(~0u), geometry(geometry), prim(prim) { } @@ -440,7 +457,7 @@ Attribute *AttributeSet::add(ustring name, TypeDesc type, AttributeElement eleme Attribute new_attr(name, type, element, geometry, prim); attributes.emplace_back(std::move(new_attr)); - modified = true; + tag_modified(attributes.back()); return &attributes.back(); } @@ -462,8 +479,7 @@ void AttributeSet::remove(ustring name) for (it = attributes.begin(); it != attributes.end(); it++) { if (&*it == attr) { - modified = true; - attributes.erase(it); + remove(it); return; } } @@ -608,8 +624,7 @@ void AttributeSet::remove(AttributeStandard std) for (it = attributes.begin(); it != attributes.end(); it++) { if (&*it == attr) { - modified = true; - attributes.erase(it); + remove(it); return; } } @@ -634,6 +649,12 @@ void AttributeSet::remove(Attribute *attribute) } } +void AttributeSet::remove(list<Attribute>::iterator it) +{ + tag_modified(*it); + attributes.erase(it); +} + void AttributeSet::resize(bool reserve_only) { foreach (Attribute &attr, attributes) { @@ -674,15 +695,13 @@ void AttributeSet::update(AttributeSet &&new_attributes) for (it = attributes.begin(); it != attributes.end();) { if (it->std != ATTR_STD_NONE) { if (new_attributes.find(it->std) == nullptr) { - modified = true; - attributes.erase(it++); + remove(it++); continue; } } else if (it->name != "") { if (new_attributes.find(it->name) == nullptr) { - modified = true; - attributes.erase(it++); + remove(it++); continue; } } @@ -699,7 +718,27 @@ void AttributeSet::clear_modified() foreach (Attribute &attr, attributes) { attr.modified = false; } - modified = false; + + modified_flag = 0; +} + +void AttributeSet::tag_modified(const Attribute &attr) +{ + /* Some attributes are not stored in the various kernel attribute arrays + * (DeviceScene::attribute_*), so the modified flags are only set if the associated standard + * corresponds to an attribute which will be stored in the kernel's attribute arrays. */ + const bool modifies_device_array = (attr.std != ATTR_STD_FACE_NORMAL && + attr.std != ATTR_STD_VERTEX_NORMAL); + + if (modifies_device_array) { + AttrKernelDataType kernel_type = Attribute::kernel_type(attr); + modified_flag |= (1u << kernel_type); + } +} + +bool AttributeSet::modified(AttrKernelDataType kernel_type) const +{ + return (modified_flag & (1u << kernel_type)) != 0; } /* AttributeRequest */ diff --git a/intern/cycles/render/attribute.h b/intern/cycles/render/attribute.h index 18c9e5ab83a..004c267cabc 100644 --- a/intern/cycles/render/attribute.h +++ b/intern/cycles/render/attribute.h @@ -39,6 +39,21 @@ class Hair; class Mesh; struct Transform; +/* AttrKernelDataType. + * + * The data type of the device arrays storing the attribute's data. Those data types are different + * than the ones for attributes as some attribute types are stored in the same array, e.g. Point, + * Vector, and Transform are all stored as float3 in the kernel. + * + * The values of this enumeration are also used as flags to detect changes in AttributeSet. */ + +enum AttrKernelDataType { + FLOAT = 0, + FLOAT2 = 1, + FLOAT3 = 2, + UCHAR4 = 3, +}; + /* Attribute * * Arbitrary data layers on meshes. @@ -167,6 +182,8 @@ class Attribute { static const char *standard_name(AttributeStandard std); static AttributeStandard name_standard(const char *name); + static AttrKernelDataType kernel_type(const Attribute &attr); + void get_uv_tiles(Geometry *geom, AttributePrimitive prim, unordered_set<int> &tiles) const; }; @@ -175,11 +192,12 @@ class Attribute { * Set of attributes on a mesh. */ class AttributeSet { + uint32_t modified_flag; + public: Geometry *geometry; AttributePrimitive prim; list<Attribute> attributes; - bool modified = true; AttributeSet(Geometry *geometry, AttributePrimitive prim); AttributeSet(AttributeSet &&) = default; @@ -197,6 +215,8 @@ class AttributeSet { void remove(Attribute *attribute); + void remove(list<Attribute>::iterator it); + void resize(bool reserve_only = false); void clear(bool preserve_voxel_data = false); @@ -204,7 +224,18 @@ class AttributeSet { * and remove any attribute not found on the new set from this. */ void update(AttributeSet &&new_attributes); + /* Return whether the attributes of the given kernel_type are modified, where "modified" means + * that some attributes of the given type were added or removed from this AttributeSet. This does + * not mean that the data of the remaining attributes in this AttributeSet were also modified. To + * check this, use Attribute.modified. */ + bool modified(AttrKernelDataType kernel_type) const; + void clear_modified(); + + private: + /* Set the relevant modified flag for the attribute. Only attributes that are stored in device + * arrays will be considered for tagging this AttributeSet as modified. */ + void tag_modified(const Attribute &attr); }; /* AttributeRequest diff --git a/intern/cycles/render/geometry.cpp b/intern/cycles/render/geometry.cpp index 16fc36231b4..ce76658acb6 100644 --- a/intern/cycles/render/geometry.cpp +++ b/intern/cycles/render/geometry.cpp @@ -830,10 +830,13 @@ void GeometryManager::device_update_attributes(Device *device, dscene->attributes_float3.alloc(attr_float3_size); dscene->attributes_uchar4.alloc(attr_uchar4_size); - const bool copy_all_data = dscene->attributes_float.need_realloc() || - dscene->attributes_float2.need_realloc() || - dscene->attributes_float3.need_realloc() || - dscene->attributes_uchar4.need_realloc(); + /* The order of those flags needs to match that of AttrKernelDataType. */ + const bool attributes_need_realloc[4] = { + dscene->attributes_float.need_realloc(), + dscene->attributes_float2.need_realloc(), + dscene->attributes_float3.need_realloc(), + dscene->attributes_uchar4.need_realloc(), + }; size_t attr_float_offset = 0; size_t attr_float2_offset = 0; @@ -852,7 +855,7 @@ void GeometryManager::device_update_attributes(Device *device, if (attr) { /* force a copy if we need to reallocate all the data */ - attr->modified |= copy_all_data; + attr->modified |= attributes_need_realloc[Attribute::kernel_type(*attr)]; } update_attribute_element_offset(geom, @@ -875,7 +878,7 @@ void GeometryManager::device_update_attributes(Device *device, if (subd_attr) { /* force a copy if we need to reallocate all the data */ - subd_attr->modified |= copy_all_data; + subd_attr->modified |= attributes_need_realloc[Attribute::kernel_type(*subd_attr)]; } update_attribute_element_offset(mesh, @@ -906,6 +909,10 @@ void GeometryManager::device_update_attributes(Device *device, foreach (AttributeRequest &req, attributes.requests) { Attribute *attr = values.find(req); + if (attr) { + attr->modified |= attributes_need_realloc[Attribute::kernel_type(*attr)]; + } + update_attribute_element_offset(object->geometry, dscene->attributes_float, attr_float_offset, @@ -941,10 +948,10 @@ void GeometryManager::device_update_attributes(Device *device, /* copy to device */ progress.set_status("Updating Mesh", "Copying Attributes to device"); - dscene->attributes_float.copy_to_device(); - dscene->attributes_float2.copy_to_device(); - dscene->attributes_float3.copy_to_device(); - dscene->attributes_uchar4.copy_to_device(); + dscene->attributes_float.copy_to_device_if_modified(); + dscene->attributes_float2.copy_to_device_if_modified(); + dscene->attributes_float3.copy_to_device_if_modified(); + dscene->attributes_uchar4.copy_to_device_if_modified(); if (progress.get_cancel()) return; @@ -1431,24 +1438,46 @@ static void update_device_flags_attribute(uint32_t &device_update_flags, continue; } - if (attr.element == ATTR_ELEMENT_CORNER) { - device_update_flags |= ATTR_UCHAR4_MODIFIED; - } - else if (attr.type == TypeDesc::TypeFloat) { - device_update_flags |= ATTR_FLOAT_MODIFIED; - } - else if (attr.type == TypeFloat2) { - device_update_flags |= ATTR_FLOAT2_MODIFIED; - } - else if (attr.type == TypeDesc::TypeMatrix) { - device_update_flags |= ATTR_FLOAT3_MODIFIED; - } - else if (attr.element != ATTR_ELEMENT_VOXEL) { - device_update_flags |= ATTR_FLOAT3_MODIFIED; + AttrKernelDataType kernel_type = Attribute::kernel_type(attr); + + switch (kernel_type) { + case AttrKernelDataType::FLOAT: { + device_update_flags |= ATTR_FLOAT_MODIFIED; + break; + } + case AttrKernelDataType::FLOAT2: { + device_update_flags |= ATTR_FLOAT2_MODIFIED; + break; + } + case AttrKernelDataType::FLOAT3: { + device_update_flags |= ATTR_FLOAT3_MODIFIED; + break; + } + case AttrKernelDataType::UCHAR4: { + device_update_flags |= ATTR_UCHAR4_MODIFIED; + break; + } } } } +static void update_attribute_realloc_flags(uint32_t &device_update_flags, + const AttributeSet &attributes) +{ + if (attributes.modified(AttrKernelDataType::FLOAT)) { + device_update_flags |= ATTR_FLOAT_NEEDS_REALLOC; + } + if (attributes.modified(AttrKernelDataType::FLOAT2)) { + device_update_flags |= ATTR_FLOAT2_NEEDS_REALLOC; + } + if (attributes.modified(AttrKernelDataType::FLOAT3)) { + device_update_flags |= ATTR_FLOAT3_NEEDS_REALLOC; + } + if (attributes.modified(AttrKernelDataType::UCHAR4)) { + device_update_flags |= ATTR_UCHAR4_NEEDS_REALLOC; + } +} + void GeometryManager::device_update_preprocess(Device *device, Scene *scene, Progress &progress) { if (!need_update() && !need_flags_update) { @@ -1471,16 +1500,11 @@ void GeometryManager::device_update_preprocess(Device *device, Scene *scene, Pro foreach (Geometry *geom, scene->geometry) { geom->has_volume = false; - if (geom->attributes.modified) { - device_update_flags |= ATTRS_NEED_REALLOC; - } + update_attribute_realloc_flags(device_update_flags, geom->attributes); if (geom->is_mesh()) { Mesh *mesh = static_cast<Mesh *>(geom); - - if (mesh->subd_attributes.modified) { - device_update_flags |= ATTRS_NEED_REALLOC; - } + update_attribute_realloc_flags(device_update_flags, mesh->subd_attributes); } foreach (Node *node, geom->get_used_shaders()) { @@ -2039,7 +2063,7 @@ void GeometryManager::device_update(Device *device, * for meshes with correct bounding boxes. * * This wouldn't cause wrong results, just true - * displacement might be less optimal ot calculate. + * displacement might be less optimal to calculate. */ scene->object_manager->need_flags_update = old_need_object_flags_update; } diff --git a/intern/cycles/render/nodes.cpp b/intern/cycles/render/nodes.cpp index f3d420c6fcb..5792d2458d1 100644 --- a/intern/cycles/render/nodes.cpp +++ b/intern/cycles/render/nodes.cpp @@ -6093,6 +6093,7 @@ NODE_DEFINE(VectorMathNode) type_enum.insert("reflect", NODE_VECTOR_MATH_REFLECT); type_enum.insert("refract", NODE_VECTOR_MATH_REFRACT); type_enum.insert("faceforward", NODE_VECTOR_MATH_FACEFORWARD); + type_enum.insert("multiply_add", NODE_VECTOR_MATH_MULTIPLY_ADD); type_enum.insert("dot_product", NODE_VECTOR_MATH_DOT_PRODUCT); @@ -6165,7 +6166,8 @@ void VectorMathNode::compile(SVMCompiler &compiler) int vector_stack_offset = compiler.stack_assign_if_linked(vector_out); /* 3 Vector Operators */ - if (math_type == NODE_VECTOR_MATH_WRAP || math_type == NODE_VECTOR_MATH_FACEFORWARD) { + if (math_type == NODE_VECTOR_MATH_WRAP || math_type == NODE_VECTOR_MATH_FACEFORWARD || + math_type == NODE_VECTOR_MATH_MULTIPLY_ADD) { ShaderInput *vector3_in = input("Vector3"); int vector3_stack_offset = compiler.stack_assign(vector3_in); compiler.add_node( diff --git a/intern/cycles/util/util_task.h b/intern/cycles/util/util_task.h index 7c39ed675b5..906bf420756 100644 --- a/intern/cycles/util/util_task.h +++ b/intern/cycles/util/util_task.h @@ -32,7 +32,7 @@ typedef function<void(void)> TaskRunFunction; /* Task Pool * - * Pool of tasks that will be executed by the central TaskScheduler.For each + * Pool of tasks that will be executed by the central TaskScheduler. For each * pool, we can wait for all tasks to be done, or cancel them before they are * done. * @@ -77,7 +77,7 @@ class TaskPool { /* Task Scheduler * - * Central scheduler that holds running threads ready to execute tasks. A singe + * Central scheduler that holds running threads ready to execute tasks. A single * queue holds the task from all pools. */ class TaskScheduler { |