SceneKit场景背景纹理动画实现失败,求正确方案
SceneKit场景背景循环动画实现问题
我需要在SceneKit中实现场景背景(scene.background.contents)的循环动画,而非物体上的纹理动画。当前项目使用单个EXR文件作为背景,尝试用3张测试用EXR序列图(background_0、1、2)创建CAKeyframeAnimation,再通过SCNMaterialProperty、SCNMaterial赋值给场景背景,但应用加载后仅显示半秒背景就变黑,单帧单独使用正常。
原实现代码
// load the image sequences: .exr var imageArray: [UIImage] = [] for i in 0...2 { // afor now 3 frames long, 0, 1 and 2 if let image = UIImage(named: "background_\(i).exr") { // we assume their names: "background_0.exr", "background_1.exr", etc. imageArray.append(image) print(image) } } print(imageArray) // Create texture animation let bg_animation = CAKeyframeAnimation(keyPath: "contents") bg_animation.calculationMode = .discrete bg_animation.duration = 5 // animation's duration bg_animation.values = imageArray.map { $0.cgImage as Any } bg_animation.repeatCount = .infinity // animation repeats forever. //bg_animation.autoreverses = true // the animation reverses print("hi, this is the bg animation: ") print(bg_animation) // Create the animation using the array backgroundAnimation = SCNMaterialProperty(contents: bg_animation) print("hi, this is the SCNMaterial property animation: ") print(backgroundAnimation) // Create the material and asign the animation as its content let backgroundMaterial = SCNMaterial() backgroundMaterial.setValue(backgroundAnimation, forKey: "contents") backgroundMaterial.locksAmbientWithDiffuse = true print("This is background Material") print(backgroundMaterial) print(backgroundMaterial.animationKeys) print(backgroundMaterial.diffuse) // Assign the material to the scene background content scene.background.contents = backgroundMaterial print("Asign material as background") print(scene.background.contents)
问题原因
原实现的核心错误是错误地通过SCNMaterial中转动画。SceneKit的scene.background本身就是一个SCNMaterialProperty,不需要将动画封装到SCNMaterial里再赋值,这种方式会导致动画的生命周期管理异常,进而出现黑屏。
正确实现方式
方式一:直接给背景属性添加关键帧动画
这是最贴合需求的轻量方案,直接对scene.background这个SCNMaterialProperty添加动画:
// 1. 加载序列图为CGImage数组(确保图片有效) var cgImageSequence: [CGImage] = [] for index in 0...2 { guard let uiImage = UIImage(named: "background_\(index).exr"), let cgImage = uiImage.cgImage else { continue } cgImageSequence.append(cgImage) } // 2. 创建关键帧动画 let bgAnimation = CAKeyframeAnimation(keyPath: "contents") bgAnimation.values = cgImageSequence bgAnimation.duration = 5 bgAnimation.calculationMode = .discrete // 离散切换,无插值 bgAnimation.repeatCount = .infinity // 3. 设置初始帧(避免动画启动前黑屏) scene.background.contents = cgImageSequence.first // 4. 给背景属性添加动画 scene.background.addAnimation(bgAnimation, forKey: "backgroundLoop")
方式二:用AVPlayer播放序列帧视频(适合大量帧的场景)
如果后续序列帧数量较多,建议将EXR序列转成循环视频(如MOV格式),用AVPlayer实现更高效的循环播放:
// 加载本地循环视频 guard let videoURL = Bundle.main.url(forResource: "background_loop", withExtension: "mov") else { return } let player = AVPlayer(url: videoURL) player.actionAtItemEnd = .none // 播放结束后不暂停 // 设置循环播放逻辑 NotificationCenter.default.addObserver(forName: .AVPlayerItemDidPlayToEndTime, object: player.currentItem, queue: .main) { [weak player] _ in player?.seek(to: .zero) player?.play() } // 启动播放并赋值给背景 player.play() scene.background.contents = player
内容的提问来源于stack exchange,提问作者sar
相关产品推荐
相关产品推荐

