Compositor: Make transformed realized domains symmetric
This patch changes how transformations are realized by adjusting the computed size of the new domain after transformation. Previously, this was computed with the lower left corner of the domain as the origin of transformation, while now, the center of the domain is used as the origin. Consequently, domains shrinks/grows around their center, which results in a more stable output as transforms are animated. A consequence of this change is that we can no longer scale odd sized domains to even sized domains or vice versa, since it grows/shrinks by the same amount on both sides. Supporting this case requires further investigation and will probably require passing down information to the realization functions themselves.
This commit is contained in:
@@ -24,32 +24,57 @@ namespace blender::compositor {
|
||||
* identity and the size of the domain is increased/reduced to adapt to the new transformation. For
|
||||
* instance, if the domain is rotated, the returned domain will have zero rotation but expanded
|
||||
* size to account for the bounding box of the domain after rotation. The size of the returned
|
||||
* domain is bound and clipped by the maximum possible GPU texture size to avoid allocations that
|
||||
* surpass hardware limits, which is typically 16k. */
|
||||
* domain is bound and clipped by the maximum possible size to avoid allocations that surpass
|
||||
* hardware limits. */
|
||||
static Domain compute_realized_transformation_domain(Context &context, const Domain &domain)
|
||||
{
|
||||
math::AngleRadian rotation;
|
||||
float2 translation, scale;
|
||||
float2 size = float2(domain.size);
|
||||
math::to_loc_rot_scale(domain.transformation, translation, rotation, scale);
|
||||
const int2 size = domain.size;
|
||||
|
||||
/* Set the rotation to zero and expand the domain size to fit the bounding box of the rotated
|
||||
* result. */
|
||||
const float sine = math::abs(math::sin(rotation));
|
||||
const float cosine = math::abs(math::cos(rotation));
|
||||
size = float2(size.x * cosine + size.y * sine, size.x * sine + size.y * cosine);
|
||||
rotation = 0.0f;
|
||||
/* Compute the 4 corners of the domain. */
|
||||
const float2 lower_left_corner = float2(0.0f);
|
||||
const float2 lower_right_corner = float2(size.x, 0.0f);
|
||||
const float2 upper_left_corner = float2(0.0f, size.y);
|
||||
const float2 upper_right_corner = float2(size);
|
||||
|
||||
/* Set the scale to 1 and scale the domain size to adapt to the new domain. */
|
||||
size *= scale;
|
||||
scale = float2(1.0f);
|
||||
/* Eliminate the translation component of the transformation and create a centered
|
||||
* transformation with the image center as the origin. Translation is ignored since it has no
|
||||
* effect on the size of the domain and will be restored later. */
|
||||
const float2 center = float2(float2(size) / 2.0f);
|
||||
const float3x3 transformation = float3x3(float2x2(domain.transformation));
|
||||
const float3x3 centered_transformation = math::from_origin_transform(transformation, center);
|
||||
|
||||
const float3x3 transformation = math::from_loc_rot_scale<float3x3>(translation, rotation, scale);
|
||||
/* Transform each of the 4 corners of the image by the centered transformation. */
|
||||
const float2 transformed_lower_left_corner = math::transform_point(centered_transformation,
|
||||
lower_left_corner);
|
||||
const float2 transformed_lower_right_corner = math::transform_point(centered_transformation,
|
||||
lower_right_corner);
|
||||
const float2 transformed_upper_left_corner = math::transform_point(centered_transformation,
|
||||
upper_left_corner);
|
||||
const float2 transformed_upper_right_corner = math::transform_point(centered_transformation,
|
||||
upper_right_corner);
|
||||
|
||||
/* Compute the lower and upper bounds of the bounding box of the transformed corners. */
|
||||
const float2 lower_bound = math::min(
|
||||
math::min(transformed_lower_left_corner, transformed_lower_right_corner),
|
||||
math::min(transformed_upper_left_corner, transformed_upper_right_corner));
|
||||
const float2 upper_bound = math::max(
|
||||
math::max(transformed_lower_left_corner, transformed_lower_right_corner),
|
||||
math::max(transformed_upper_left_corner, transformed_upper_right_corner));
|
||||
|
||||
/* Round the bounds such that they cover the entire transformed domain, which means flooring for
|
||||
* the lower bound and ceiling for the upper bound. */
|
||||
const int2 integer_lower_bound = int2(math::floor(lower_bound));
|
||||
const int2 integer_upper_bound = int2(math::ceil(upper_bound));
|
||||
|
||||
const int2 new_size = integer_upper_bound - integer_lower_bound;
|
||||
|
||||
/* Make sure the new size is safe by clamping to the hardware limits and an upper bound. */
|
||||
const int max_size = context.use_gpu() ? GPU_max_texture_size() : 65536;
|
||||
const int2 domain_size = math::clamp(int2(math::round(size)), int2(1), int2(max_size));
|
||||
const int2 safe_size = math::clamp(new_size, int2(1), int2(max_size));
|
||||
|
||||
return Domain(domain_size, transformation);
|
||||
/* Create a domain from the new safe size and just the translation component of the
|
||||
* transformation, */
|
||||
return Domain(safe_size, math::from_location<float3x3>(domain.transformation.location()));
|
||||
}
|
||||
|
||||
void transform(Context &context,
|
||||
|
||||
Submodule tests/data updated: b64ba9eb55...a965544f03
Reference in New Issue
Block a user