From 56bb8b2b3c8e5c3421ed69b71bf5cc420b802c72 Mon Sep 17 00:00:00 2001 From: Alaska Date: Tue, 18 Jun 2024 17:35:16 +0200 Subject: [PATCH 01/11] Fix #123324: Improve Cycles camera bounding box size calculation Cycles runs a check to see if the camera is possibly inside a volumetric object by seeing if the bounding box of the camera and volumetric object intersect. If the calculation is wrong (Cycles says the camera is outside the volume when it's inside it), then the volume will not render properly. This commit resolves most of these issues by making the camera bounding box larger than before, taking into consideration features like: 1. The impact DOF could have on the camera ray start position and how that should impact the bounding box size. 2. Taking into consideration near clipping, which was missed from the orthographic camera due to a oversight in a previous commit (08cc73a9bb). Pull Request: https://projects.blender.org/blender/blender/pulls/123341 --- intern/cycles/scene/camera.cpp | 50 +++++++++++++++++++++++++++------- 1 file changed, 40 insertions(+), 10 deletions(-) diff --git a/intern/cycles/scene/camera.cpp b/intern/cycles/scene/camera.cpp index 7ef7bd6729c..ffacdfe0292 100644 --- a/intern/cycles/scene/camera.cpp +++ b/intern/cycles/scene/camera.cpp @@ -593,40 +593,70 @@ BoundBox Camera::viewplane_bounds_get() * checks we need in a more clear and smart fashion? */ BoundBox bounds = BoundBox::empty; + const float max_aperture_size = aperture_ratio < 1.0f ? aperturesize / aperture_ratio : + aperturesize; + if (camera_type == CAMERA_PANORAMA) { + const float extend = max_aperture_size + nearclip; if (use_spherical_stereo == false) { - bounds.grow(make_float3(cameratoworld.x.w, cameratoworld.y.w, cameratoworld.z.w), nearclip); + bounds.grow(make_float3(cameratoworld.x.w, cameratoworld.y.w, cameratoworld.z.w), extend); } else { float half_eye_distance = interocular_distance * 0.5f; bounds.grow( make_float3(cameratoworld.x.w + half_eye_distance, cameratoworld.y.w, cameratoworld.z.w), - nearclip); + extend); bounds.grow( make_float3(cameratoworld.z.w, cameratoworld.y.w + half_eye_distance, cameratoworld.z.w), - nearclip); + extend); bounds.grow( make_float3(cameratoworld.x.w - half_eye_distance, cameratoworld.y.w, cameratoworld.z.w), - nearclip); + extend); bounds.grow( make_float3(cameratoworld.x.w, cameratoworld.y.w - half_eye_distance, cameratoworld.z.w), - nearclip); + extend); } } else { - bounds.grow(transform_raster_to_world(0.0f, 0.0f)); - bounds.grow(transform_raster_to_world(0.0f, (float)height)); - bounds.grow(transform_raster_to_world((float)width, (float)height)); - bounds.grow(transform_raster_to_world((float)width, 0.0f)); + /* max_aperture_size = Max horizontal distance a ray travels from aperture edge to focus point. + * Scale that value based on the ratio between focaldistance and nearclip to figure out the + * horizontal distance the DOF ray will travel before reaching the nearclip plane, where it + * will start rendering from. + * In some cases (focus distance is close to camera, and nearclip plane is far from camera), + * this scaled value is larger than nearclip, in which case we add it to `extend` to extend the + * bounding box to account for these rays. + * + * ----------------- nearclip plane + * / scaled_horz_dof_ray, nearclip + * / + * / + * / horz_dof_ray, focaldistance + * /| + * / | + * / | + * / | + * ------ max_aperture_size, 0 + * 0, 0 + */ + + const float scaled_horz_dof_ray = (max_aperture_size > 0.0f) ? + max_aperture_size * (nearclip / focaldistance) : + 0.0f; + const float extend = max_aperture_size + max(nearclip, scaled_horz_dof_ray); + + bounds.grow(transform_raster_to_world(0.0f, 0.0f), extend); + bounds.grow(transform_raster_to_world(0.0f, (float)height), extend); + bounds.grow(transform_raster_to_world((float)width, (float)height), extend); + bounds.grow(transform_raster_to_world((float)width, 0.0f), extend); if (camera_type == CAMERA_PERSPECTIVE) { /* Center point has the most distance in local Z axis, * use it to construct bounding box/ */ - bounds.grow(transform_raster_to_world(0.5f * width, 0.5f * height)); + bounds.grow(transform_raster_to_world(0.5f * width, 0.5f * height), extend); } } return bounds; From d41cd2095a83af3f2003979ae4e3111debb8fab1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Foucault?= Date: Tue, 18 Jun 2024 16:25:10 +0200 Subject: [PATCH 02/11] EEVEE: Make render tests use volume indirect lighting --- tests/python/eevee_next_render_tests.py | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/python/eevee_next_render_tests.py b/tests/python/eevee_next_render_tests.py index b17dd0f71b4..19441058031 100644 --- a/tests/python/eevee_next_render_tests.py +++ b/tests/python/eevee_next_render_tests.py @@ -42,6 +42,7 @@ def setup(): eevee.volumetric_end = 50.0 eevee.volumetric_samples = 128 eevee.use_volumetric_shadows = True + eevee.clamp_volume_indirect = 0.0 # Motion Blur if scene.render.use_motion_blur: From 3112799804de066a6c22e9d816fcc83716343f80 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Foucault?= Date: Tue, 18 Jun 2024 17:24:22 +0200 Subject: [PATCH 03/11] Fix: EEVEE: AO: LOD transition too visible at high sample count This add dithering to the lod steping allowing for a smoother transition. --- .../shaders/eevee_ambient_occlusion_pass_comp.glsl | 6 ++---- .../engines/eevee_next/shaders/eevee_horizon_scan_comp.glsl | 4 ++-- .../eevee_next/shaders/eevee_horizon_scan_eval_lib.glsl | 4 ++-- 3 files changed, 6 insertions(+), 8 deletions(-) diff --git a/source/blender/draw/engines/eevee_next/shaders/eevee_ambient_occlusion_pass_comp.glsl b/source/blender/draw/engines/eevee_next/shaders/eevee_ambient_occlusion_pass_comp.glsl index 57ce49042bc..98969991f79 100644 --- a/source/blender/draw/engines/eevee_next/shaders/eevee_ambient_occlusion_pass_comp.glsl +++ b/source/blender/draw/engines/eevee_next/shaders/eevee_ambient_occlusion_pass_comp.glsl @@ -26,10 +26,8 @@ void main() vec3 N = imageLoad(in_normal_img, ivec3(texel, in_normal_img_layer_index)).xyz; vec3 vN = drw_normal_world_to_view(N); - vec3 noise; - noise.x = interlieved_gradient_noise(vec2(texel), 3.0, 0.0); - noise.yz = utility_tx_fetch(utility_tx, vec2(texel), UTIL_BLUE_NOISE_LAYER).rg; - noise = fract(noise + sampling_rng_3D_get(SAMPLING_AO_U)); + vec4 noise = utility_tx_fetch(utility_tx, vec2(texel), UTIL_BLUE_NOISE_LAYER); + noise = fract(noise + sampling_rng_3D_get(SAMPLING_AO_U).xyzx); HorizonScanResult scan = horizon_scan_eval(vP, vN, diff --git a/source/blender/draw/engines/eevee_next/shaders/eevee_horizon_scan_comp.glsl b/source/blender/draw/engines/eevee_next/shaders/eevee_horizon_scan_comp.glsl index 49039418736..5f8830cd735 100644 --- a/source/blender/draw/engines/eevee_next/shaders/eevee_horizon_scan_comp.glsl +++ b/source/blender/draw/engines/eevee_next/shaders/eevee_horizon_scan_comp.glsl @@ -43,8 +43,8 @@ void main() vec3 vP = drw_point_screen_to_view(vec3(uv, depth)); vec3 vN = horizon_scan_sample_normal(uv); - vec3 noise = utility_tx_fetch(utility_tx, vec2(texel), UTIL_BLUE_NOISE_LAYER).rgb; - noise = fract(noise + sampling_rng_3D_get(SAMPLING_AO_U)); + vec4 noise = utility_tx_fetch(utility_tx, vec2(texel), UTIL_BLUE_NOISE_LAYER); + noise = fract(noise + sampling_rng_3D_get(SAMPLING_AO_U).xyzx); HorizonScanResult scan = horizon_scan_eval(vP, vN, diff --git a/source/blender/draw/engines/eevee_next/shaders/eevee_horizon_scan_eval_lib.glsl b/source/blender/draw/engines/eevee_next/shaders/eevee_horizon_scan_eval_lib.glsl index ba47f3173d5..9d0c9809d9a 100644 --- a/source/blender/draw/engines/eevee_next/shaders/eevee_horizon_scan_eval_lib.glsl +++ b/source/blender/draw/engines/eevee_next/shaders/eevee_horizon_scan_eval_lib.glsl @@ -65,7 +65,7 @@ struct HorizonScanResult { */ HorizonScanResult horizon_scan_eval(vec3 vP, vec3 vN, - vec3 noise, + vec4 noise, vec2 pixel_size, float search_distance, float thickness_near, @@ -142,7 +142,7 @@ HorizonScanResult horizon_scan_eval(vec3 vP, time += 1.0; } - float lod = 1.0 + float(j) * uniform_buf.ao.lod_factor; + float lod = 1.0 + saturate(float(j) - noise.w) * uniform_buf.ao.lod_factor; vec2 sample_uv = ssray.origin.xy + ssray.direction.xy * time; float sample_depth = textureLod(hiz_tx, sample_uv * uniform_buf.hiz.uv_scale, lod).r; From 90c1d5832e40f4f58dfc4cf8e0265edca7bf62ac Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Foucault?= Date: Tue, 18 Jun 2024 17:27:11 +0200 Subject: [PATCH 04/11] Fix: EEVEE: AO: Only clip occluders based on front sample This is cheaper but avoid loosing the sample influence when thickness is large. --- .../shaders/eevee_horizon_scan_eval_lib.glsl | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/source/blender/draw/engines/eevee_next/shaders/eevee_horizon_scan_eval_lib.glsl b/source/blender/draw/engines/eevee_next/shaders/eevee_horizon_scan_eval_lib.glsl index 9d0c9809d9a..4199fb7c46c 100644 --- a/source/blender/draw/engines/eevee_next/shaders/eevee_horizon_scan_eval_lib.glsl +++ b/source/blender/draw/engines/eevee_next/shaders/eevee_horizon_scan_eval_lib.glsl @@ -156,16 +156,12 @@ HorizonScanResult horizon_scan_eval(vec3 vP, const float bias = 2.0 * 2.4e-7; sample_depth += reversed ? -bias : bias; - vec3 vP_sample = drw_point_screen_to_view(vec3(sample_uv, sample_depth)); + vec3 vP_sample_front = drw_point_screen_to_view(vec3(sample_uv, sample_depth)); + vec3 vP_sample_back = vP_sample_front - vV * thickness_near; float sample_distance; - vec3 vL_front = normalize_and_get_length(vP_sample - vP, sample_distance); - if (sample_distance > search_distance) { - continue; - } - - vec3 vL_back = normalize_and_get_length((vP_sample - vV * thickness_near) - vP, - sample_distance); + vec3 vL_front = normalize_and_get_length(vP_sample_front - vP, sample_distance); + vec3 vL_back = normalize(vP_sample_back - vP); if (sample_distance > search_distance) { continue; } From e5f514c60caffdf145fd7039f0585106c6847ecf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Foucault?= Date: Tue, 18 Jun 2024 17:53:16 +0200 Subject: [PATCH 05/11] Fix: EEVEE: AO Pass: Regression in quality caused by sample count Sample count was mismatching the LOD offset leading to quality regression. Using the same specialization constant as the fast GI fixes the issue. --- .../draw/engines/eevee_next/eevee_ambient_occlusion.cc | 8 +++++++- .../draw/engines/eevee_next/eevee_ambient_occlusion.hh | 2 ++ .../shaders/eevee_ambient_occlusion_pass_comp.glsl | 4 ++-- .../shaders/infos/eevee_ambient_occlusion_info.hh | 2 ++ 4 files changed, 13 insertions(+), 3 deletions(-) diff --git a/source/blender/draw/engines/eevee_next/eevee_ambient_occlusion.cc b/source/blender/draw/engines/eevee_next/eevee_ambient_occlusion.cc index 09b0a741793..5da3f3bb8a0 100644 --- a/source/blender/draw/engines/eevee_next/eevee_ambient_occlusion.cc +++ b/source/blender/draw/engines/eevee_next/eevee_ambient_occlusion.cc @@ -51,6 +51,9 @@ void AmbientOcclusion::init() data_.thickness_far = sce_eevee.fast_gi_thickness_far; /* Size is multiplied by 2 because it is applied in NDC [-1..1] range. */ data_.pixel_size = float2(2.0f) / float2(inst_.film.render_extent_get()); + + ray_count_ = sce_eevee.fast_gi_ray_count; + step_count_ = sce_eevee.fast_gi_step_count; } void AmbientOcclusion::sync() @@ -60,7 +63,10 @@ void AmbientOcclusion::sync() } render_pass_ps_.init(); - render_pass_ps_.shader_set(inst_.shaders.static_shader_get(AMBIENT_OCCLUSION_PASS)); + GPUShader *sh = inst_.shaders.static_shader_get(AMBIENT_OCCLUSION_PASS); + render_pass_ps_.specialize_constant(sh, "ao_slice_count", ray_count_); + render_pass_ps_.specialize_constant(sh, "ao_step_count", step_count_); + render_pass_ps_.shader_set(sh); render_pass_ps_.bind_texture(RBUFS_UTILITY_TEX_SLOT, &inst_.pipelines.utility_tx); render_pass_ps_.bind_resources(inst_.uniform_data); diff --git a/source/blender/draw/engines/eevee_next/eevee_ambient_occlusion.hh b/source/blender/draw/engines/eevee_next/eevee_ambient_occlusion.hh index 5526194790b..d1e2a44b419 100644 --- a/source/blender/draw/engines/eevee_next/eevee_ambient_occlusion.hh +++ b/source/blender/draw/engines/eevee_next/eevee_ambient_occlusion.hh @@ -29,6 +29,8 @@ class AmbientOcclusion { class Instance &inst_; bool render_pass_enabled_; + int ray_count_ = 0; + int step_count_ = 0; AOData &data_; PassSimple render_pass_ps_ = {"AO Render Pass"}; diff --git a/source/blender/draw/engines/eevee_next/shaders/eevee_ambient_occlusion_pass_comp.glsl b/source/blender/draw/engines/eevee_next/shaders/eevee_ambient_occlusion_pass_comp.glsl index 98969991f79..f9f66054bfc 100644 --- a/source/blender/draw/engines/eevee_next/shaders/eevee_ambient_occlusion_pass_comp.glsl +++ b/source/blender/draw/engines/eevee_next/shaders/eevee_ambient_occlusion_pass_comp.glsl @@ -37,8 +37,8 @@ void main() uniform_buf.ao.thickness_near, uniform_buf.ao.thickness_far, uniform_buf.ao.angle_bias, - 2, - 10, + ao_slice_count, + ao_step_count, false, true); diff --git a/source/blender/draw/engines/eevee_next/shaders/infos/eevee_ambient_occlusion_info.hh b/source/blender/draw/engines/eevee_next/shaders/infos/eevee_ambient_occlusion_info.hh index 70470323105..c66da0db940 100644 --- a/source/blender/draw/engines/eevee_next/shaders/infos/eevee_ambient_occlusion_info.hh +++ b/source/blender/draw/engines/eevee_next/shaders/infos/eevee_ambient_occlusion_info.hh @@ -13,6 +13,8 @@ GPU_SHADER_CREATE_INFO(eevee_ambient_occlusion_pass) .push_constant(Type::INT, "in_normal_img_layer_index") .image(1, GPU_RG16F, Qualifier::WRITE, ImageType::FLOAT_2D_ARRAY, "out_ao_img") .push_constant(Type::INT, "out_ao_img_layer_index") + .specialization_constant(Type::INT, "ao_slice_count", 2) + .specialization_constant(Type::INT, "ao_step_count", 8) .additional_info("draw_view", "eevee_shared", "eevee_hiz_data", From 355c7b788ec5cadbf4b25c7771653d8c6faa00f8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Foucault?= Date: Tue, 18 Jun 2024 18:17:34 +0200 Subject: [PATCH 06/11] Fix: EEVEE: Remaining references to EEVEE-Legacy in codebase Fix #123387 --- source/blender/blenkernel/intern/scene.cc | 2 +- source/blender/editors/render/render_preview.cc | 4 ++-- source/blender/editors/space_view3d/view3d_draw.cc | 5 ++--- source/blender/render/intern/pipeline.cc | 2 +- 4 files changed, 6 insertions(+), 7 deletions(-) diff --git a/source/blender/blenkernel/intern/scene.cc b/source/blender/blenkernel/intern/scene.cc index 3032e076591..0cd93624d07 100644 --- a/source/blender/blenkernel/intern/scene.cc +++ b/source/blender/blenkernel/intern/scene.cc @@ -203,7 +203,7 @@ static void scene_init_data(ID *id) pset->brush[PE_BRUSH_CUT].strength = 1.0f; } - STRNCPY(scene->r.engine, RE_engine_id_BLENDER_EEVEE); + STRNCPY(scene->r.engine, RE_engine_id_BLENDER_EEVEE_NEXT); STRNCPY(scene->r.pic, U.renderdir); diff --git a/source/blender/editors/render/render_preview.cc b/source/blender/editors/render/render_preview.cc index 0effb91b2c8..6beb5f74138 100644 --- a/source/blender/editors/render/render_preview.cc +++ b/source/blender/editors/render/render_preview.cc @@ -275,7 +275,7 @@ const char *ED_preview_collection_name(const ePreviewType pr_type) static bool render_engine_supports_ray_visibility(const Scene *sce) { - return !STREQ(sce->r.engine, RE_engine_id_BLENDER_EEVEE); + return !STREQ(sce->r.engine, RE_engine_id_BLENDER_EEVEE_NEXT); } static void switch_preview_collection_visibility(ViewLayer *view_layer, const ePreviewType pr_type) @@ -517,7 +517,7 @@ static Scene *preview_prepare_scene( if (id_type == ID_TE) { /* Texture is not actually rendered with engine, just set dummy value. */ - STRNCPY(sce->r.engine, RE_engine_id_BLENDER_EEVEE); + STRNCPY(sce->r.engine, RE_engine_id_BLENDER_EEVEE_NEXT); } if (id_type == ID_MA) { diff --git a/source/blender/editors/space_view3d/view3d_draw.cc b/source/blender/editors/space_view3d/view3d_draw.cc index 009d4dfdd2c..4091a380aba 100644 --- a/source/blender/editors/space_view3d/view3d_draw.cc +++ b/source/blender/editors/space_view3d/view3d_draw.cc @@ -1580,7 +1580,7 @@ RenderEngineType *ED_view3d_engine_type(const Scene *scene, int drawtype) */ RenderEngineType *type = RE_engines_find(scene->r.engine); if (drawtype == OB_MATERIAL && (type->flag & RE_USE_EEVEE_VIEWPORT)) { - return RE_engines_find(RE_engine_id_BLENDER_EEVEE); + return RE_engines_find(RE_engine_id_BLENDER_EEVEE_NEXT); } return type; } @@ -2477,8 +2477,7 @@ bool ED_view3d_has_depth_buffer_updated(const Depsgraph *depsgraph, const View3D bool is_viewport_preview_solid = v3d->shading.type == OB_SOLID; bool is_viewport_preview_material = v3d->shading.type == OB_MATERIAL; bool is_viewport_render_eevee = v3d->shading.type == OB_RENDER && - (STREQ(engine_name, RE_engine_id_BLENDER_EEVEE) || - STREQ(engine_name, RE_engine_id_BLENDER_EEVEE_NEXT)); + (STREQ(engine_name, RE_engine_id_BLENDER_EEVEE_NEXT)); bool is_viewport_render_workbench = v3d->shading.type == OB_RENDER && STREQ(engine_name, RE_engine_id_BLENDER_WORKBENCH); bool is_viewport_render_external_with_overlay = v3d->shading.type == OB_RENDER && diff --git a/source/blender/render/intern/pipeline.cc b/source/blender/render/intern/pipeline.cc index fd9f965c280..632cecf5922 100644 --- a/source/blender/render/intern/pipeline.cc +++ b/source/blender/render/intern/pipeline.cc @@ -2074,7 +2074,7 @@ void RE_RenderFreestyleStrokes(Render *re, Main *bmain, Scene *scene, const bool char scene_engine[32]; STRNCPY(scene_engine, re->r.engine); if (use_eevee_for_freestyle_render(re)) { - change_renderdata_engine(re, RE_engine_id_BLENDER_EEVEE); + change_renderdata_engine(re, RE_engine_id_BLENDER_EEVEE_NEXT); } RE_engine_render(re, false); From 44309ac7e1727de2e334338875f51d9df15463e2 Mon Sep 17 00:00:00 2001 From: Brecht Van Lommel Date: Tue, 18 Jun 2024 18:20:54 +0200 Subject: [PATCH 07/11] Build: Change make deps HARVEST_TARGET to new Git LFS location --- GNUmakefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/GNUmakefile b/GNUmakefile index 92a603c5657..90cbc76f738 100644 --- a/GNUmakefile +++ b/GNUmakefile @@ -196,7 +196,7 @@ ifndef DEPS_BUILD_DIR endif ifndef DEPS_INSTALL_DIR - DEPS_INSTALL_DIR:=$(shell dirname "$(BLENDER_DIR)")/lib/$(OS_LIBDIR)_$(CPU) + DEPS_INSTALL_DIR:=$(BLENDER_DIR)/lib/$(OS_LIBDIR)_$(CPU) endif # Set the LIBDIR, an empty string when not found. From 295df944786b6d0050d3dce8ea22d430edf4789a Mon Sep 17 00:00:00 2001 From: Nathan Vegdahl Date: Tue, 18 Jun 2024 18:29:25 +0200 Subject: [PATCH 08/11] Anim: add "legacy behavior" option to Limit Rotation constraint This adds a "Legacy Behavior" option to the Limit Rotation constraint that makes it behave how Limit Rotation constraints did prior to ed2408400d53e943cb6c97db10f12cf6729ef089. Newly created constraints have this option disabled, but versioning code enables the option on constraints from older files to ensure that the behavior of e.g. existing rigs is not altered. This is one part of a two-part fix for #123105. The other part is in PR extensions/rigify#4. Pull Request: https://projects.blender.org/blender/blender/pulls/123361 --- .../startup/bl_ui/properties_constraint.py | 1 + .../blender/blenkernel/BKE_blender_version.h | 2 +- .../blender/blenkernel/intern/constraint.cc | 46 +++++++++++++++---- .../blenloader/intern/versioning_400.cc | 26 +++++++++++ .../blender/makesdna/DNA_constraint_types.h | 5 ++ .../blender/makesrna/intern/rna_constraint.cc | 8 ++++ 6 files changed, 77 insertions(+), 11 deletions(-) diff --git a/scripts/startup/bl_ui/properties_constraint.py b/scripts/startup/bl_ui/properties_constraint.py index 66f2195d348..1a01714b525 100644 --- a/scripts/startup/bl_ui/properties_constraint.py +++ b/scripts/startup/bl_ui/properties_constraint.py @@ -233,6 +233,7 @@ class ConstraintButtonsPanel: layout.prop(con, "euler_order", text="Order") layout.prop(con, "use_transform_limit") + layout.prop(con, "use_legacy_behavior") self.space_template(layout, con, target=False, owner=True) self.draw_influence(layout, con) diff --git a/source/blender/blenkernel/BKE_blender_version.h b/source/blender/blenkernel/BKE_blender_version.h index 30c6f3f823f..678124d8a87 100644 --- a/source/blender/blenkernel/BKE_blender_version.h +++ b/source/blender/blenkernel/BKE_blender_version.h @@ -29,7 +29,7 @@ extern "C" { /* Blender file format version. */ #define BLENDER_FILE_VERSION BLENDER_VERSION -#define BLENDER_FILE_SUBVERSION 59 +#define BLENDER_FILE_SUBVERSION 60 /* Minimum Blender version that supports reading file written with the current * version. Older Blender versions will test this and cancel loading the file, showing a warning to diff --git a/source/blender/blenkernel/intern/constraint.cc b/source/blender/blenkernel/intern/constraint.cc index 7e575b5111c..a5a8cfa5ade 100644 --- a/source/blender/blenkernel/intern/constraint.cc +++ b/source/blender/blenkernel/intern/constraint.cc @@ -1758,17 +1758,43 @@ static void rotlimit_evaluate(bConstraint *con, bConstraintOb *cob, ListBase * / mat4_to_eulO(eul, rot_order, cob->matrix); - /* constraint data uses radians internally */ - - /* limiting of euler values... */ - if (data->flag & LIMIT_XROT) { - eul[0] = clamp_angle(eul[0], data->xmin, data->xmax); + /* Limit the euler values. */ + if (data->flag & LIMIT_ROT_LEGACY_BEHAVIOR) { + /* The legacy behavior, which just does a naive clamping of the angles as + * simple numbers. Since the input angles are always in the range [-180, + * 180] degrees due to being derived from matrix decomposition, this naive + * approach causes problems when rotations cross 180 degrees. Specifically, + * it results in unpredictable and unwanted rotation flips of the + * constrained objects/bones, especially when the constraint isn't in local + * space. + * + * The correct thing to do is a more sophisticated form of clamping that + * treats the angles as existing on a continuous loop, which is what the + * non-legacy behavior further below does. However, for backwards + * compatibility we are preserving this old behavior behind an option. + * + * See issues #117927 and #123105 for additional background. */ + if (data->flag & LIMIT_XROT) { + eul[0] = clamp_f(eul[0], data->xmin, data->xmax); + } + if (data->flag & LIMIT_YROT) { + eul[1] = clamp_f(eul[1], data->ymin, data->ymax); + } + if (data->flag & LIMIT_ZROT) { + eul[2] = clamp_f(eul[2], data->zmin, data->zmax); + } } - if (data->flag & LIMIT_YROT) { - eul[1] = clamp_angle(eul[1], data->ymin, data->ymax); - } - if (data->flag & LIMIT_ZROT) { - eul[2] = clamp_angle(eul[2], data->zmin, data->zmax); + else { + /* The correct, non-legacy behavior. */ + if (data->flag & LIMIT_XROT) { + eul[0] = clamp_angle(eul[0], data->xmin, data->xmax); + } + if (data->flag & LIMIT_YROT) { + eul[1] = clamp_angle(eul[1], data->ymin, data->ymax); + } + if (data->flag & LIMIT_ZROT) { + eul[2] = clamp_angle(eul[2], data->zmin, data->zmax); + } } loc_eulO_size_to_mat4(cob->matrix, loc, eul, size, rot_order); diff --git a/source/blender/blenloader/intern/versioning_400.cc b/source/blender/blenloader/intern/versioning_400.cc index 5b1a89aee52..478f376d073 100644 --- a/source/blender/blenloader/intern/versioning_400.cc +++ b/source/blender/blenloader/intern/versioning_400.cc @@ -17,6 +17,7 @@ #include "DNA_anim_types.h" #include "DNA_brush_types.h" #include "DNA_camera_types.h" +#include "DNA_constraint_types.h" #include "DNA_curve_types.h" #include "DNA_defaults.h" #include "DNA_light_types.h" @@ -4165,6 +4166,31 @@ void blo_do_versions_400(FileData *fd, Library * /*lib*/, Main *bmain) FOREACH_NODETREE_END; } + if (!MAIN_VERSION_FILE_ATLEAST(bmain, 402, 60)) { + /* Limit Rotation constraints from old files should use the legacy Limit + * Rotation behavior. */ + LISTBASE_FOREACH (Object *, obj, &bmain->objects) { + LISTBASE_FOREACH (bConstraint *, constraint, &obj->constraints) { + if (constraint->type != CONSTRAINT_TYPE_ROTLIMIT) { + continue; + } + static_cast(constraint->data)->flag |= LIMIT_ROT_LEGACY_BEHAVIOR; + } + + if (!obj->pose) { + continue; + } + LISTBASE_FOREACH (bPoseChannel *, pbone, &obj->pose->chanbase) { + LISTBASE_FOREACH (bConstraint *, constraint, &pbone->constraints) { + if (constraint->type != CONSTRAINT_TYPE_ROTLIMIT) { + continue; + } + static_cast(constraint->data)->flag |= LIMIT_ROT_LEGACY_BEHAVIOR; + } + } + } + } + /** * Always bump subversion in BKE_blender_version.h when adding versioning * code here, and wrap it inside a MAIN_VERSION_FILE_ATLEAST check. diff --git a/source/blender/makesdna/DNA_constraint_types.h b/source/blender/makesdna/DNA_constraint_types.h index 5a36d2f0b66..e4b0e94b408 100644 --- a/source/blender/makesdna/DNA_constraint_types.h +++ b/source/blender/makesdna/DNA_constraint_types.h @@ -1067,6 +1067,11 @@ typedef enum eRotLimit_Flags { LIMIT_XROT = (1 << 0), LIMIT_YROT = (1 << 1), LIMIT_ZROT = (1 << 2), + + /* Use the legacy behavior of the Limit Rotation constraint. See the + * implementation of `rotlimit_evaluate()` in constraint.cc for more + * details. */ + LIMIT_ROT_LEGACY_BEHAVIOR = (1 << 3), } eRotLimit_Flags; /* distance limit constraint */ diff --git a/source/blender/makesrna/intern/rna_constraint.cc b/source/blender/makesrna/intern/rna_constraint.cc index 18b54ef216a..45eb79e5c30 100644 --- a/source/blender/makesrna/intern/rna_constraint.cc +++ b/source/blender/makesrna/intern/rna_constraint.cc @@ -2690,6 +2690,14 @@ static void rna_def_constraint_rotation_limit(BlenderRNA *brna) prop, "Affect Transform", "Transform tools are affected by this constraint as well"); RNA_def_property_update(prop, NC_OBJECT | ND_CONSTRAINT, "rna_Constraint_update"); + prop = RNA_def_property(srna, "use_legacy_behavior", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, nullptr, "flag", LIMIT_ROT_LEGACY_BEHAVIOR); + RNA_def_property_ui_text( + prop, + "Legacy Behavior", + "Use the old semi-broken behavior that doesn't understand that rotations loop around"); + RNA_def_property_update(prop, NC_OBJECT | ND_CONSTRAINT, "rna_Constraint_update"); + RNA_define_lib_overridable(false); } From feed10a4c22c02e5f433667035eb53e4e90d81de Mon Sep 17 00:00:00 2001 From: Miguel Pozo Date: Tue, 18 Jun 2024 18:36:09 +0200 Subject: [PATCH 09/11] Fix: GPU: Workaround for validation errors on replaced passes --- source/blender/gpu/intern/gpu_material.cc | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/source/blender/gpu/intern/gpu_material.cc b/source/blender/gpu/intern/gpu_material.cc index 06948a27cc9..231584d6443 100644 --- a/source/blender/gpu/intern/gpu_material.cc +++ b/source/blender/gpu/intern/gpu_material.cc @@ -875,6 +875,12 @@ GPUMaterial *GPU_material_from_nodetree(Scene *scene, if (GPUPass *default_pass = pass_replacement_cb ? pass_replacement_cb(thunk, mat) : nullptr) { mat->pass = default_pass; GPU_pass_acquire(mat->pass); + /** WORKAROUND: + * The node tree code is never executed in default replaced passes, + * but the GPU validation will still complain if the node tree UBO is not bound. + * So we create a dummy UBO with (at least) the size of the default material one (192 bytes). + * We allocate 256 bytes to leave some room for future changes. */ + mat->ubo = GPU_uniformbuf_create_ex(256, nullptr, "Dummy UBO"); } else { /* Create source code and search pass cache for an already compiled version. */ From 10b1e45ca8aa44bf7abe2631e695aee99665a865 Mon Sep 17 00:00:00 2001 From: Falk David Date: Tue, 18 Jun 2024 19:08:11 +0200 Subject: [PATCH 10/11] =?UTF-8?q?Fix:=20Curves:=20Crash=20in=20transform?= =?UTF-8?q?=20code=20for=20non=20b=C3=A9zier=20curves?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This caused a crash in grease pencil when trying to transform strokes (poly curves). The code assumed that the left and right handle indices existed. Pull Request: https://projects.blender.org/blender/blender/pulls/123311 --- .../transform/transform_convert_curves.cc | 37 +++++++++---------- 1 file changed, 18 insertions(+), 19 deletions(-) diff --git a/source/blender/editors/transform/transform_convert_curves.cc b/source/blender/editors/transform/transform_convert_curves.cc index a05839d2a94..ef2c3e44823 100644 --- a/source/blender/editors/transform/transform_convert_curves.cc +++ b/source/blender/editors/transform/transform_convert_curves.cc @@ -262,9 +262,10 @@ static OffsetIndices recent_position_offsets(TransCustomData &custom_data, } /** - * Creates map of indices to `tc.data` representing curve in layout - * [L0, P0, R0, L1, P1, R1, L2,P2, R2], where [P0, P1, P2], [L0, L1, L2] and [R0, R1, R2] are - * positions, left handles and right handles respectively. + * Creates map of indices to `tc.data` representing the curves. + * For bezier curves it uses the layout [L0, P0, R0, L1, P1, R1, L2, P2, R2], where [P0, P1, P2], + * [L0, L1, L2] and [R0, R1, R2] are positions, left handles and right handles respectively. + * Other curve types just use the positions [P0, P1, ..., Pn] of the control points directly. */ static void fill_map(const CurveType curve_type, const IndexRange curve_points, @@ -272,24 +273,22 @@ static void fill_map(const CurveType curve_type, const int handles_offset, MutableSpan map) { - const int attr_num = (curve_type == CURVE_TYPE_BEZIER) ? 3 : 1; - const int left_handle_index = handles_offset + position_offsets_in_td[1].start(); const int position_index = curve_points.start() + position_offsets_in_td[0].start(); - const int right_handle_index = handles_offset + position_offsets_in_td[2].start(); - - std::array first_per_attr = {curve_type == CURVE_TYPE_BEZIER ? left_handle_index : - position_index, - /* Next two unused for non Bezier curves. */ - position_index, - right_handle_index}; - - threading::parallel_for(curve_points.index_range(), 4096, [&](const IndexRange range) { - for (const int i : range) { - for (const int attr : IndexRange(attr_num)) { - map[i * attr_num + attr] = first_per_attr[attr] + i; + if (curve_type == CURVE_TYPE_BEZIER) { + const int left_handle_index = handles_offset + position_offsets_in_td[1].start(); + const int right_handle_index = handles_offset + position_offsets_in_td[2].start(); + std::array first_per_attr = {left_handle_index, position_index, right_handle_index}; + threading::parallel_for(curve_points.index_range(), 4096, [&](const IndexRange range) { + for (const int i : range) { + for (const int attr : IndexRange(3)) { + map[i * 3 + attr] = first_per_attr[attr] + i; + } } - } - }); + }); + } + else { + array_utils::fill_index_range(map, position_index); + } } } // namespace blender::ed::transform::curves From ff03ab4d08a636125bc3befd500e797a955a4077 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Foucault?= Date: Tue, 18 Jun 2024 19:31:39 +0200 Subject: [PATCH 11/11] Fix: EEVEE: Overblur of textures because of TAA This was a missing block of the TAA implementation. TAA jitter and reprojection have a tedency to soften the texture. Add a 1.5 bias to make them a bit sharper. Note that this is a bit different than the usual TAA blurring. In final render we don't do reprojection so it is only because the texture filter (box filter from the LOD) is applied at the same time than our pixel filter (blackmann-harris). It is less noticeable than the normal TAA blur, but still blurs ~2px instead of 1.5px. --- source/blender/draw/engines/eevee_next/eevee_film.cc | 2 ++ .../blender/draw/engines/eevee_next/eevee_shader_shared.hh | 3 ++- .../draw/engines/eevee_next/shaders/eevee_nodetree_lib.glsl | 6 ++---- .../gpu/shaders/material/gpu_shader_material_tex_image.glsl | 5 ++--- 4 files changed, 8 insertions(+), 8 deletions(-) diff --git a/source/blender/draw/engines/eevee_next/eevee_film.cc b/source/blender/draw/engines/eevee_next/eevee_film.cc index 5dccb816f8b..2683648a4f7 100644 --- a/source/blender/draw/engines/eevee_next/eevee_film.cc +++ b/source/blender/draw/engines/eevee_next/eevee_film.cc @@ -253,6 +253,8 @@ void Film::init(const int2 &extent, const rcti *output_rect) data_.scaling_factor = BKE_render_preview_pixel_size(&inst_.scene->r); } } + /* Sharpen the LODs (1.5x) to avoid TAA filtering causing over-blur (see #122941). */ + data_.texture_lod_bias = 1.0f / (data_.scaling_factor * 1.5f); } { rcti fallback_rect; diff --git a/source/blender/draw/engines/eevee_next/eevee_shader_shared.hh b/source/blender/draw/engines/eevee_next/eevee_shader_shared.hh index 7661ca9f5e8..83fe168e679 100644 --- a/source/blender/draw/engines/eevee_next/eevee_shader_shared.hh +++ b/source/blender/draw/engines/eevee_next/eevee_shader_shared.hh @@ -449,13 +449,14 @@ struct FilmData { float exposure_scale; /** Scaling factor for scaled resolution rendering. */ int scaling_factor; + /** Software LOD bias to apply to when sampling texture inside the node-tree evaluation. */ + float texture_lod_bias; /** Film pixel filter radius. */ float filter_radius; /** Precomputed samples. First in the table is the closest one. The rest is unordered. */ int samples_len; /** Sum of the weights of all samples in the sample table. */ float samples_weight_total; - int _pad1; int _pad2; FilmSample samples[FILM_PRECOMP_SAMPLE_MAX]; }; diff --git a/source/blender/draw/engines/eevee_next/shaders/eevee_nodetree_lib.glsl b/source/blender/draw/engines/eevee_next/shaders/eevee_nodetree_lib.glsl index 9a9f048b57a..9b1d7be079b 100644 --- a/source/blender/draw/engines/eevee_next/shaders/eevee_nodetree_lib.glsl +++ b/source/blender/draw/engines/eevee_next/shaders/eevee_nodetree_lib.glsl @@ -9,8 +9,6 @@ #pragma BLENDER_REQUIRE(gpu_shader_codegen_lib.glsl) #pragma BLENDER_REQUIRE(eevee_renderpass_lib.glsl) -#define filmScalingFactor float(uniform_buf.film.scaling_factor) - vec3 g_emission; vec3 g_transmittance; float g_holdout; @@ -713,9 +711,9 @@ vec3 coordinate_incoming(vec3 P) * * \{ */ -float film_scaling_factor_get() +float texture_lod_bias_get() { - return float(uniform_buf.film.scaling_factor); + return uniform_buf.film.texture_lod_bias; } /** \} */ diff --git a/source/blender/gpu/shaders/material/gpu_shader_material_tex_image.glsl b/source/blender/gpu/shaders/material/gpu_shader_material_tex_image.glsl index b956029954e..31145bbe8d9 100644 --- a/source/blender/gpu/shaders/material/gpu_shader_material_tex_image.glsl +++ b/source/blender/gpu/shaders/material/gpu_shader_material_tex_image.glsl @@ -57,9 +57,8 @@ void point_map_to_tube(vec3 vin, out vec3 vout) void node_tex_image_linear(vec3 co, sampler2D ima, out vec4 color, out float alpha) { #ifdef GPU_FRAGMENT_SHADER - vec2 scaling_factor = vec2(film_scaling_factor_get()); - vec2 dx = dFdx(co.xy) / scaling_factor; - vec2 dy = dFdy(co.xy) / scaling_factor; + vec2 dx = dFdx(co.xy) * texture_lod_bias_get(); + vec2 dy = dFdy(co.xy) * texture_lod_bias_get(); color = safe_color(textureGrad(ima, co.xy, dx, dy)); #else