SceneKit 中可以有动画背景纹理吗?

问题描述 投票:0回答:2

我的意思是动画场景背景(scene.background.contents)。不是放置在物体上的纹理。

我目前有一个 .exr 文件作为我的项目中的背景,但我希望有一个循环运行的动画背景。我的第一个想法是创建一系列 exr 图像,将其转换为动画,然后使用该动画作为材质的漫反射纹理。但是,它不起作用。

我只创建了 3 个起始帧来测试(background_0、1 和 2)
但是,当应用程序加载时,我可以看到背景半秒钟,然后它就会变黑。

我做错了什么吗?或者也许我以错误的方式处理这个问题,并且有一种更简单的方法来实现这种效果??

这是我目前的背景代码,非常感谢任何帮助:

// 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)

我尝试过:
创建图像数组,将其转换为动画,该动画使用它作为纹理并将其指定为背景内容。
每个框架都独立工作,但看起来不像数组。

swift scenekit openexr
2个回答
1
投票

尝试以下方法之一(UIKit 与 SwiftUI):

UIKit版本

基于

SCNScene
图像序列创建动画
OpenEXR
背景的最简单解决方案是实现
renderer(_:updateAtTime:)
委托方法。

import UIKit
import SceneKit

extension ViewController: SCNSceneRendererDelegate {
    
    func renderer(_ renderer: SCNSceneRenderer,
           updateAtTime time: TimeInterval) {
        scene.background.contents = imageArray[Int(time / 5) % 3]
    }
}

class ViewController: UIViewController {
    
    @IBOutlet var sceneView: SCNView!
    var imageArray: [UIImage] = []
    let scene = SCNScene()

    override func viewDidLoad() {
        super.viewDidLoad()
        
        sceneView.delegate = self
        sceneView.scene = scene
        sceneView.isPlaying = true
        sceneView.allowsCameraControl = true
        
        for frame in 0...2 {
            DispatchQueue.main.async { [self] in
                if let image = UIImage(named: "bg_\(frame).exr") {
                    self.imageArray.append(image)
                }
            }
        }
    }
}

SwiftUI 版本

SwiftUI 版本将额外要求您实现协调器。

import SwiftUI
import SceneKit

struct ContentView : View {
    var body: some View {
        SceneKitView().ignoresSafeArea()
    }
}

struct SceneKitView : UIViewRepresentable {        
    @State var imageArray: [UIImage] = []
    let sceneView = SCNView(frame: .zero)
    let scene = SCNScene()
    
    func makeCoordinator() -> Coordinator { Coordinator(self) }
    
    final class Coordinator: NSObject, SCNSceneRendererDelegate {
        var control: SceneKitView
        init(_ control: SceneKitView) { self.control = control }
        
        func renderer(_ renderer: SCNSceneRenderer, 
               updateAtTime time: TimeInterval) {
            control.scene.background.contents = 
                                     control.imageArray[Int(time / 5) % 3]
        }
    }
    func updateUIView(_ view: SCNView, context: Context) {
        for frame in 0...2 {
            DispatchQueue.main.async {
                if let image = UIImage(named: "bg_\(frame).exr") {
                    imageArray.append(image)
                }
            }
        }
    }
    func makeUIView(context: Context) -> SCNView {
        sceneView.delegate = context.coordinator
        sceneView.scene = scene
        sceneView.isPlaying = true
        sceneView.allowsCameraControl = true
        return sceneView
    }
}

0
投票

一种可能的方法是使用

SCNAction

以下代码将背景的

contents
设置为三幅图像之一,定期、无限。

var ind: Int = 0 
let images: [UIImage] = [
    UIImage(named: "background_0.exr")!,
    UIImage(named: "background_1.exr")!,
    UIImage(named: "background_2.exr")!
]
let action = SCNAction.run { [weak self] node in
    if let s = self {
        s.scene?.background.contents = s.images[s.ind]

        s.ind += 1

        if s.ind > s.images.count-1 {
            //We've reached the end of the image sequence. Go back to the beginning:
            s.ind = 0
        }
    }
}

//By using `SCNAction.wait` in an `SCNAction.sequence`, we can control the frame rate. Simply adjust the `duration` parameter to fine-tune. 
let seq = SCNAction.sequence([
    action,
    SCNAction.wait(duration: 0.055)
])
//Repeat the sequence infinitely:
let infiniteAnim = SCNAction.repeatForever(seq)

//Run the action on a node in the scene. In this case, the rootNode:
self.scene?.rootNode.runAction(infiniteAnim)
© www.soinside.com 2019 - 2024. All rights reserved.