Unsuccessful attempt to use Perspective Transformations in Core Animation

I'm trying to create a simple iPhone application that displays an image with a reflection at the bottom, and I want to rotate the image around the X axis using Core Animation.

I started by creating a new iPhone application using the App-Based View template. I added the image file "Picture.jpg" and then added it to the view controller:

- (void)awakeFromNib { CGFloat zDistance = 1500.0f; // Create perspective transformation CATransform3D transform = CATransform3DIdentity; transform.m34 = 1.0f / -zDistance; // Create perspective transform for reflected layer CATransform3D reflectedTransform = CATransform3DMakeRotation(M_PI, 1.0f, 0.0f, 0.0f); reflectedTransform.m34 = 1.0f / -zDistance; // Create spinning picture CALayer *spinningLayer = [CALayer layer]; spinningLayer.frame = CGRectMake(60.0f, 60.0f, 200.0f, 300.0f); spinningLayer.contents = (id)[UIImage imageNamed:@"Picture.jpg"].CGImage; spinningLayer.transform = transform; // Create reflection of spinning picture CALayer *reflectionLayer = [CALayer layer]; reflectionLayer.frame = CGRectMake(60.0f, 360.0f, 200.0f, 300.0f); reflectionLayer.contents = (id)[UIImage imageNamed:@"Picture.jpg"].CGImage; reflectionLayer.opacity = 0.4f; reflectionLayer.transform = reflectedTransform; // Add layers to the root layer [self.view.layer addSublayer:spinningLayer]; [self.view.layer insertSublayer:reflectionLayer below:spinningLayer]; // Spin the layers CABasicAnimation *anim = [CABasicAnimation animationWithKeyPath:@"transform.rotation.y"]; anim.fromValue = [NSNumber numberWithFloat:0.0f]; anim.toValue = [NSNumber numberWithFloat:(2 * M_PI)]; anim.duration = 3.0f; anim.repeatCount = 1e100f; [spinningLayer addAnimation:anim forKey:@"anim"]; [reflectionLayer addAnimation:anim forKey:@"anim"]; } 

It almost works. The problem is that the rotation of the main picture and reflection are not perfectly synchronized. It is very close to perfection, but the edges of the lower part of the image and the peaks of reflection are at a distance of several pixels from each other. It seems that they differ by several degrees.

On the left side of the screen, the reflection appears โ€œin frontโ€ of the image, and on the right side it is reflected behind the picture. In other words, it seems that the lower corners of the upper image are pushed toward the screen, and the upper corners of the reflection are pulled towards the viewer. This is true both when viewed from the front and the back of the images as they rotate.

If I increase zDistance , the effect will be less noticeable. If I completely exclude perspective transformations, leaving transform.m34 equal to zero for both layers, then the image and reflection look perfectly synchronized. Therefore, I do not think that the problem is related to time synchronization problems.

I suspect that my prospective transformations are missing something, but I'm not sure how to determine what is wrong. I think I'm basically doing the same transformation described in in this related question .

Can anyone help?

+4
source share
2 answers

I think I might have found the answer to my question. In my source code, I created a reflection by placing it under the image and applying flip and perspective. The right way to do this seems to be to put the reflection in the same position as the image, apply the translation and rotation transformations to bring them to the right place, and then apply the perspective transformation.

When animating rotation, you must rotate the reflection in the opposite direction of the image animation.

So here is the code that works:

 - (void)awakeFromNib { CGFloat zDistance = 1500.0f; // Create perspective transformation CATransform3D transform = CATransform3DIdentity; transform.m34 = 1.0f / -zDistance; // Create perspective transform for reflected layer CATransform3D reflectedTransform = CATransform3DMakeTranslation(0.0f, 300.0f, 0.0f); reflectedTransform = CATransform3DRotate(reflectedTransform, M_PI, 1.0f, 0.0f, 0.0f); reflectedTransform.m34 = 1.0f / -zDistance; // Create spinning picture CALayer *spinningLayer = [CALayer layer]; spinningLayer.frame = CGRectMake(60.0f, 60.0f, 200.0f, 300.0f); spinningLayer.contents = (id)[UIImage imageNamed:@"Picture.jpg"].CGImage; spinningLayer.transform = transform; // Create reflection of spinning picture CALayer *reflectionLayer = [CALayer layer]; reflectionLayer.frame = CGRectMake(60.0f, 60.0f, 200.0f, 300.0f); reflectionLayer.contents = (id)[UIImage imageNamed:@"Picture.jpg"].CGImage; reflectionLayer.opacity = 0.4f; reflectionLayer.transform = reflectedTransform; // Add layers to the root layer [self.view.layer addSublayer:spinningLayer]; [self.view.layer insertSublayer:reflectionLayer below:spinningLayer]; // Spin the layers CABasicAnimation *anim = [CABasicAnimation animationWithKeyPath:@"transform.rotation.y"]; anim.fromValue = [NSNumber numberWithFloat:0.0f]; anim.toValue = [NSNumber numberWithFloat:(2 * M_PI)]; anim.duration = 3.0f; anim.repeatCount = 1e100f; [spinningLayer addAnimation:anim forKey:@"anim"]; CABasicAnimation *reflectAnim = [CABasicAnimation animationWithKeyPath:@"transform.rotation.y"]; reflectAnim.fromValue = [NSNumber numberWithFloat:0.0f]; reflectAnim.toValue = [NSNumber numberWithFloat:(-2 * M_PI)]; reflectAnim.duration = 3.0f; reflectAnim.repeatCount = 1e100f; [reflectionLayer addAnimation:reflectAnim forKey:@"reflectAnim"]; } 

I donโ€™t know exactly why this works. It seems intuitive to me that placing the reflection in the same position as the original and then converting it somehow puts the two images in the same โ€œtransformation space", but I would appreciate it if someone could explain the math behind this.

The full project is available on GitHub: https://github.com/kristopherjohnson/perspectivetest

+3
source

You can use the CAAnimationGroup object to run two animations at the same time. This should take care of any synchronization issues.

EDIT: deleted dumb code.

+1
source

Source: https://habr.com/ru/post/1301287/


All Articles