Can I shade an animated object?

My app shows an animation of rotating fan blades in front of the fan body and background The image of the blades are in a UIImageView and I have no problem making that view rotate nicely. But I'd like to add some detail by applying a drop shadow to the spinning blades.


The code below works fine, but the shadow rotates along with blades and of course that doesn't look realistic. How can I make the light source hold still and recalculate the shadow for each frame of animation?


    func startRotating(_ duration: Double = 1) {
        let kAnimationKey = "rotation"
        if self.layer.animation(forKey: kAnimationKey) == nil {
            self.layer.shadowColor = UIColor.black.cgColor
            self.layer.shadowOpacity = 0.8
            self.layer.shadowOffset = CGSize(width: 1, height: 1)
            self.layer.shadowRadius = 2
            let animate = CABasicAnimation(keyPath: "transform.rotation")
            animate.duration = duration
            animate.repeatCount = Float.infinity
            animate.fromValue = 0.0
            animate.toValue = Float(.pi * 2.0)
            self.layer.add(animate, forKey: kAnimationKey)
        }
    }

Replies

So you want to see the shadow on the blade or on the background ?


If on background, it is normal that the shadow rotates with the blades. So I understand you want to see the shadow on the blades themselves ?

The shadow needs to move as the profile of a blade exposed to the light source changes, but the apparent source of light should not. That means the shadow needs to change relative to the blades, as if the blades were stationary with the light source rotating around them. The code I posted above creates a shadow that is static relative to the fan blades image, and then rotates along with the blades. It's exactly the result you would get from including the shadow in the fan blade image itself.


I should add that the rotation is lsow and your eye can easily follow each blade. It's not just a blur.

To make sure I understand, you want the form of the shadow change because its position relative to light source is changing ?


That's waht software like ray tracing are doing

Yes I suppose so. I want it to look realistic and as you say, that's what fancy rendering software is for. I'm just wondering if I can somehow insert the shadow-drawing into the animation loop, so that it recalculates the shadow for every frame of automation.

I've never played with such animations, so I'm in risky waters for me.


In the present code, evrything is done by the transform.rotation, which apply to blades and shadow and is causing the wrong effect.


I would try to apply independantly 2 animations in paralle (don't ask me to write the code !):

- have 2 layers, for blades and one for shadow

- the rotation to the blades

- a more complex, composed transform to the shadow, on another layer

Ithis could give a hint on how to do it (answer 51):

h ttps://stackoverflow.com/questions/30929986/how-to-apply-multiple-transforms-in-swift

- this transform must compose a rotation and a projection of the blade on a plane seen from the light source (hope it's clear)


    func startRotating(_ duration: Double = 1) { 
        let kAnimationKey = "rotation" 
          // self should have 2 layers, blades and shadow
        if self.layer.animation(forKey: kAnimationKey) == nil { 
     // handle layers independantly
            self.layer.shadowColor = UIColor.black.cgColor 
            self.layer.shadowOpacity = 0.8 
            self.layer.shadowOffset = CGSize(width: 1, height: 1) 
            self.layer.shadowRadius = 2 
            let animate = CABasicAnimation(keyPath: "transform.rotation") 
            animate.duration = duration 
            animate.repeatCount = Float.infinity 
            animate.fromValue = 0.0 
            animate.toValue = Float(.pi * 2.0) 
          // Create a second animation for shadow, with a transform of your own
            self.layer.add(animate, forKey: kAnimationKey) 
     // add the second animation to the shadow layer
        } 
    }


Hope I'm not totally off ground

I think that might be a solution but it would be tough for me to implement.


Unless maybe I can animate the shadow offset values at the same time the blades are rotatiing. How would I try that?


The math to calculate the proper offset, I think, is given here:

h ttps://stackoverflow.com/questions/5777917/how-can-i-draw-a-rotated-uiview-with-a-shadow-but-without-the-shadow-being-rotat

Well found.


yes, the trick would be to have 2 animations running. But I've not looked at how to do it. And I can be very little help further.


Another way could be to compute each frame (evry 15° rotation for instance)both for blades and shadow. If you have 4 blades, you need to to it only for 90°, so just 6 frames would be enogh.

But probably you should not use animate anymore.

Looks like you have two of these going for some reason...


How to cast shadow of rotating fan blades?