SceneKit SCNSceneRendererDelegate - 未调用渲染器函数

SceneKit SCNSceneRendererDelegate - renderer function not called

我最近问了一个 question,答案很明显。我仍在处理同一个项目,运行 遇到另一个问题。我需要实现每帧逻辑,SCNSceneRendererDelegate 协议在 iOS 上工作得很好,但在 OSX 上,renderer 函数没有触发。我创建了一个小示例项目来说明我的问题。它由故事板中的 Scene Kit View 和 ViewController class:

中的以下代码组成
import Cocoa
import SceneKit

class ViewController: NSViewController, SCNSceneRendererDelegate {

    @IBOutlet weak var sceneView: SCNView!

    let cubeNode = SCNNode()

    override func viewDidLoad() {

        super.viewDidLoad()

        let scene = SCNScene()

        let sphere = SCNSphere(radius: 0.1)
        sphere.firstMaterial!.diffuse.contents = NSColor.yellowColor()
        let sphereNode = SCNNode(geometry: sphere)
        scene.rootNode.addChildNode(sphereNode)

        let cube = SCNBox(width: 0.2, height: 0.2, length: 0.2, chamferRadius: 0)
        cube.firstMaterial!.diffuse.contents = NSColor.greenColor()
        cubeNode.geometry = cube
        cubeNode.position = SCNVector3(1,0,0)
        scene.rootNode.addChildNode(cubeNode)

        let cameraNode = SCNNode()
        cameraNode.camera = SCNCamera()
        cameraNode.position = SCNVector3(2, 1, 2)
        let constraint = SCNLookAtConstraint(target: cubeNode)
        cameraNode.constraints = [constraint]
        scene.rootNode.addChildNode(cameraNode)

        sceneView.scene = scene
        sceneView.backgroundColor = NSColor(red: 0.5, green: 0, blue: 0.3, alpha: 1)
        sceneView.allowsCameraControl = true

        sceneView.delegate = self
        sceneView.playing = true

    }

    func renderer(renderer: SCNSceneRenderer, updateAtTime time: NSTimeInterval) {
        cubeNode.position.x += 0.1
    }
}

我只想在每一帧移动立方体。但是没有任何反应。奇怪的是,当我将 sceneView.allowsCameraControl 设置为 true 时,每当我在屏幕上单击或拖动时都会调用 renderer 函数(这是有道理的,因为它需要根据更新视图摄像机角度)。但我希望每一帧都调用它。

是否有我没有看到的错误,或者这是我的 Xcode 中的错误?

编辑: 我已尝试按照下面答案中的说明进行操作,现在 ViewController:

的代码如下
import Cocoa
import SceneKit

class ViewController: NSViewController {

    @IBOutlet weak var sceneView: SCNView!
    let scene = MyScene(create: true)

    override func viewDidLoad() {

        super.viewDidLoad()

        sceneView.scene = scene
        sceneView.backgroundColor = NSColor(red: 0.5, green: 0, blue: 0.3, alpha: 1)
        sceneView.allowsCameraControl = true

        sceneView.delegate = scene
        sceneView.playing = true

    }

}

还有一个 MyScene class:

import Foundation
import SceneKit

final class MyScene: SCNScene, SCNSceneRendererDelegate {

    let cubeNode = SCNNode()

    convenience init(create: Bool) {
        self.init()

        let sphere = SCNSphere(radius: 0.1)
        sphere.firstMaterial!.diffuse.contents = NSColor.yellowColor()
        let sphereNode = SCNNode(geometry: sphere)
        rootNode.addChildNode(sphereNode)

        let cube = SCNBox(width: 0.2, height: 0.2, length: 0.2, chamferRadius: 0)
        cube.firstMaterial!.diffuse.contents = NSColor.greenColor()
        cubeNode.geometry = cube
        cubeNode.position = SCNVector3(1,0,0)
        rootNode.addChildNode(cubeNode)

        let cameraNode = SCNNode()
        cameraNode.camera = SCNCamera()
        cameraNode.position = SCNVector3(2, 1, 2)
        let constraint = SCNLookAtConstraint(target: cubeNode)
        cameraNode.constraints = [constraint]

        rootNode.addChildNode(cameraNode)
    }

    @objc func renderer(aRenderer: SCNSceneRenderer, updateAtTime time: NSTimeInterval) {
        cubeNode.position.x += 0.01
    }
}

但是,还是不行。我做错了什么?

编辑:设置 sceneView.loops = true 修复了所描述的问题

试试这个…将您的代码放在场景中 class – 保持视图控制器干净。

final class MySCNScene:SCNScene, SCNSceneRendererDelegate
{
    @objc func renderer(aRenderer:SCNSceneRenderer, updateAtTime time:NSTimeInterval)
    {
    }
}

同时将视图的代理设置为您的场景:

mySCNView!.delegate = mySCNScene

我不明白是什么导致了这个问题,但我能够复制它。不过,我通过添加一个无意义的 SCNAction:

来让它工作
let dummyAction = SCNAction.scaleBy(1.0, duration: 1.0)
let repeatAction = SCNAction.repeatActionForever(dummyAction)
cubeNode.runAction(repeatAction)

渲染循环仅在场景为 "playing" 时触发(参见 )。我希望设置

    sceneView.isPlaying = true

(正如您已经在做的那样)足以触发渲染回调。

我上面的代码不是解决方案。解决您的问题并让您继续生活是一种令人讨厌的技巧。

对于仍然有问题的任何人,设置委托和播放变量都可以。

sceneView.delegate = self
sceneView.isPlaying = true

我怀疑答案取决于 Querent "every frame" 的意思。 Querent 可能应该澄清这一点,但无论如何我都会尝试回答,因为我就是那样。

最简单的解释可能是 "every frame that would render anyway",但这似乎不太可能是我们想要的,除非立方体旨在作为渲染器的一种 activity 监视器,这似乎不太可能任何一个;有很多更好的方法。

Querent 可能想要的是在视图 playing property is YES. If that's the case, then perhaps the answer is as simple as setting the view's loops 属性 为 YES 时重复呈现。这最近为我解决了一个问题,我希望在按住键盘键时进行渲染。 (我注意到将播放设置为“是”会引发对我的委托的一次调用。)

除了此链中的几个有用提示外,我调用委托的最后一个提示如下:如果您对 SCNSceneRendererDelegate [=25] 使用前 Swift 4 种方法=],它编译得很好,没有错误或警告,但永远不会调用委托。

因此过时的前Swift 4定义:

func renderer(aRenderer:SCNSceneRenderer, updateAtTime time:TimeInterval) {...}

(我从网上的一个片段中得到的)编译得很好并且从未被调用,而正确的定义

func renderer(_ renderer:SCNSceneRenderer, updateAtTimet time:TimeInterval) {...}

编译并被调用!

由于 SCNSceneRendererDelegate 是一个协议,覆盖提供的正常 Swift 保护是不合适的。由于 SCNSceneRendererDelegate 将它的方法定义为可选的(我喜欢),所以它也不会被那样捕获。

我一直在使用 SCNRenderer 而不是 SCNView 来解决一些类似的错误,这是 Google 上的第一次点击,所以我只想添加一个注释我的解决方案以防对任何人有帮助。

我正在使用

.render(withViewport:commandBuffer:passDescriptor:)

但这并没有调用委托渲染方法。相反,使用

.render(atTime:viewport:commandBuffer:passDescriptor:)

即使您没有使用时间间隔参数。那么委托方法renderer(_:updateAtTime:)就会被正确调用。