我的意思是动画场景背景(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)
我尝试过:
创建图像数组,将其转换为动画,该动画使用它作为纹理并将其指定为背景内容。
每个框架都独立工作,但看起来不像数组。
尝试以下方法之一(UIKit 与 SwiftUI):
基于
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 版本将额外要求您实现协调器。
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
}
}
一种可能的方法是使用
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)