SceneKit

作者: rectinajh | 来源:发表于2017-07-11 22:07 被阅读213次
                    https://github.com/rectinajh/ScenkitDemo  
    

    Scene Kit是一个苹果Cocoa风格的3D渲染框架,该框架被引入OS X是在WWDC 2012 (那时 OS X 系统还在用喵系命名)。在第一版通用 3D 渲染器发布后,一年内又陆续增加了像 shader (着色器) 修改器、节点约束、骨骼动画等几个强大的特性 (随 Mavericks 发布)。 今年(2014年),Scene Kit 变的更加强大,支持了粒子效果、物理引擎、脚本事件以及多通道分层渲染等多种技术,以及(也许对许多人来说最重要的)它被引入iOS,而苹果现在指的是把它当成“休闲游戏”的套件。

    从一开始,我发现Scene Kit的最大优势和差异在于与其他图形框架,如Core Image,Core Animation,Sprite Kit的集成。 这在其他游戏引擎中可不常见,但是如果你是一个业余爱好者,或者主要是Cocoa 或 Cocoa Touch 框架下的开发者,那么这意味着很多东西应该很亲切了。

    Scene Kit 概要

    Scene Kit 构建在OpenGL之上,其中灯光,几何图形,材料和相机等高级引擎特性,这些组件都是面向对象的,你可以用熟悉的 Objective-C 或 Swift 语言来编写代码。 如果用过最早的版本的OpenGL,那时还没有 shader,只能苦逼的使用各种底层受限制的 API 开发。 幸运的是 Scene Kit 就好了很多,高级配置对于大多数常见任务是足够的 - 甚至更先进的功能,如动态阴影和景深效果,使用它提供的上层 API 来配置,就已经足够了。 不仅如此,Scene Kit 还允许你直接调用底层 API,或自己写 shader 进行手动渲染 (GLSL)。

    节点 (Nodes)

    除了灯光,几何图形,材料和相机这几个具体的对象之外,Scene Kit还使用节点层次来组织其内容。 每个节点相对于其父节点具有位置,旋转和缩放,而父节点又相对于其父节点,一直向上,直到根节点。 假如要给一个节点确定一个位置,就必须将它挂载到节点树中的某个节点上。 使用以下方法管理节点层次结构:

            addChildNode(_:)
            insertChildNode(_: atIndex:)
            removeFromParentNode()
    

    这些方法与 iOS 和 OS X 中管理 view 和 layer 层级方法如出一辙。

    几何模型对象
    Scene Kit 内建了几种简单的几何模型,如盒子、球体、平面、圆锥体等,对于游戏,一般都会从文件中加载3D模型。你可以通过制定文件名来导入 (或导出) COLLADA 格式的模型文件:

            let chessPieces = SCNScene(named: "chess pieces") // SCNScene?
    

    如果一个从文件里加载的场景可以全部显示时,将其设置成SCNView 的 scene 就好了。 如果场景包含多个对象,但是屏幕上只能显示一些对象,则可以它们的名称找到它,并将其添加到SCNView中呈现的Scene中:

          if let knight =           chessPieces.rootNode.childNodeWithName("Knight", recursively: true) {
    sceneView.scene?.rootNode.addChildNode(knight)
    

    }

    这是一个对导入文件原始节点的引用,其中包含了任一和每一个子节点,也包括了模型对象 (包括其材质),光照,以及绑定在这些节点上的摄像机。只要传入的名字一样,不论调用多少次,返回的都是对同一个对象的引用。

    textures.png

    若需要在场景中拥有一个节点的多个拷贝,如在一个国际象棋棋盘上显示两个马,你可以对马这个节点进行 copy 或 clone (递归的copy)。这将会拷贝一份节点的引用,但两份引用所指向的材质对象和模型对象仍然是原来那个。所以,想要单独改变副本材质的话,需要再copy一份模型对象,并对这个新的模型对象设置新材质。copy一个模型对象的速度仍然很快,开销也不高,因为副本引用的顶点数据还是同一份。

    带有骨骼动画的模型对象也会拥有一个皮肤对象,它提供了对骨骼中各个节点的访问接口,以及管理骨骼和模型间连接的功能。每个单独的骨骼都可以被移动和旋转,而复杂的动画需要同时对多块骨骼进行操作,如一个角色走路的动画,很可能就是从文件读取并加到对象上的 (而不是用代码一根骨头一根骨头的写)。

    光照

    Scene Kit 中完全都是动态光照,使用起来一般会很简单,但也意味着与完整的游戏引擎相比,光照这块进步并不明显。Scene Kit 提供四种类型的光照:环境光、定向光源、点光源和聚光灯。

    通常来说,旋转坐标轴和变换角度并不是设定光照的最佳方法。下面的例子表示一个光照对象通过一个节点对象来设置空间坐标,再通过 "look at" 约束,将光照对象约束到了目标对象上,即使它移动,光照也会一直朝向目标对象。

        let spot = SCNLight()
        spot.type = SCNLightTypeSpot
        spot.castsShadow = true
    
        let spotNode = SCNNode()
        spotNode.light = spot
        spotNode.position = SCNVector3(x: 4, y: 7, z: 6)
    
        let lookAt = SCNLookAtConstraint(target: knight)
        spotNode.constraints = [lookAt]
    
    spinning.gif

    动画

    Scene Kit 的对象中绝大多数属性都是可以进行动画的,就像 Cocoa (或 Cocoa Touch) 框架一样,你可以创建一个 CAAnimation 对象,并指定一个 key path (甚至可以 "position.x") ,然后向一个对象施加这个动画。同样的,你可以在 SCNTransaction 的 "begin" 和 "commit" 调用间去改变值,和刚才的 CAAnimation 非常相似:

        let move = CABasicAnimation(keyPath: "position.x")
        move.byValue  = 10
        move.duration = 1.0
        knight.addAnimation(move, forKey: "slide right")
    

    Scene Kit 也提供了像 Sprite Kit 那样的 action 形式的动画 API,你可以创建串行的动画组,也支持自定义 action 来协同使用。与 Core Animation 不同的是,这些 action 作为游戏循环的一部分执行,在每一帧都更新模型对象的值,而不只是更新表现层的节点。

    假如你之前用过 Sprite Kit,会发现 Scene Kit 除了变成了 3D 之外,没有太多陌生的东西。目前,在 iOS8 (首次支持 Scene Kit) 和 OS X 10.10 下,Scene Kit 和 Sprite Kit 可以协同工作:对 Sprite Kit 来说,3D 模型可以与 2D 精灵混合使用;对 Scene Kit 来说,Sprite Kit 中的场景和纹理可以作为 Scene Kit 的纹理贴图,而且 Sprite Kit 的场景可以作为 Scene Kit 场景的蒙层 (如3D游戏中的2D菜单面板,译者注。两套非常像的API和概念 (像场景啊,节点啊,约束啊两边都有), 让人容易混淆。

    开始用 Scene Kit 写游戏

    不仅是动作和纹理,Scene Kit 和 Sprite Kit 还有很多相同之处。当开始写游戏的时候,Scene Kit 和它 2D 版本的小伙伴非常相似,它们的游戏循环步骤完全一致,使用下面几个代理回调:

    1,更新场景
    2,应用动画/动作
    3,模拟物理效果
    4,应用约束
    5,渲染
    
    gameloop.png

    这些回调在每帧被调用,并用来执行游戏相关的逻辑,如用户输入,AI (人工智能) 和游戏脚本。

    处理用户输入
    Scene Kit 与普通 Cocoa 或 Cocoa Touch 应用使用一样的机制来处理用户输入,如键盘事件、鼠标事件、触摸事件和手势识别,而主要区别在于 Scene Kit 中只有一个视图,场景视图 (scene view) 。像键盘事件或如捏取、滑动、旋转的手势,只要知道事件的发生就好了,但像鼠标点击,或触碰、拖动手势等就需要知道具体的事件信息了。

    这些情况下,scene view 可以使用 -hitTest(_: options:) 来做点击测试。与通常的视图只返回被点击的子 view 或子 layer 不同,Scene Kit 返回一个数组,里面存有每个相交的模型对象以及从摄像机投向这个测试点的射线。每个 hit test 的结果包含被击中模型的节点对象,也包含了交点的详细信息 (交点坐标、交点表面法线,交点的纹理坐标)。多数情况下,知道第一个被击中的节点就足够了:

          if let firstHit = sceneView.hitTest(tapLocation, options: nil)?.first as? SCNHitTestResult     {
    let hitNode = firstHit.node
    // do something with the node that was hit...
    

    }

    扩展默认渲染流程

    光照和材质的配置方法很易用,但有局限性。假如你有写好的 OpenGL 着色器 (shader),可以用于完全自定制的进行材质渲染;如果你只想修改下默认的渲染,Scene Kit 暴露了 4 个入口用于插入 shader代码 (GLSL) 来改变默认渲染。Scene Kit 在不同入口点分别提供了对旋转矩阵、模型数据、样本贴图及渲染后输出的色值的访问。
    比如,下面的 GLSL 代码被用在模型数据的入口点中,可以将模型对象上所有点沿 x 轴扭曲。这是通过定义一个函数来创建一个旋转变换,并将其应用在模型的位置和法线上。同时,也自定义了一个 "uniform" 变量来决定对象该如何被扭曲。

    // a function that creates a rotation transform matrix around X
    mat4 rotationAroundX(float angle)
    {
    return mat4(1.0, 0.0, 0.0, 0.0,
    0.0, cos(angle), -sin(angle), 0.0,
    0.0, sin(angle), cos(angle), 0.0,
    0.0, 0.0, 0.0, 1.0);
    }

        #pragma body
    
        uniform float twistFactor = 1.0;
        float rotationAngle = _geometry.position.x * twistFactor;
        mat4 rotationMatrix = rotationAroundX(rotationAngle);
    
        // position is a vec4
        _geometry.position *= rotationMatrix;
    
        // normal is a vec3
        vec4 twistedNormal = vec4(_geometry.normal, 1.0) *             rotationMatrix;
      _geometry.normal   = twistedNormal.xyz;
    

    着色修改器 (Shader modifier) 既可以绑定在模型对象上,也可以绑定在它的材质对象上。这两个类都完全支持 key-value coding (KVC),你可以指定任意 key 进行赋值。在 shader 中声明的 "twistFactor" uniform 变量使得 Scene Kit 在这个值改变时自动重新绑定 uniform,这使得你也可以用 KVC 来实现:

        torus.setValue(5.0, forKey: "twistFactor")
    

    使用这个 key path 的 CAAnimation 也 ok:

        let twist = CABasicAnimation(keyPath: "twistFactor")
        twist.fromValue = 5
        twist.toValue   = 0
        twist.duration  = 2.0
    
        torus.addAnimation(twist, forKey: "Twist the torus")
    
    twist.gif

    延时着色

    即使在纯 OpenGL 环境下,有些图像效果也无法通过一次渲染 pass 完成,我们可以将不同 shader 进行序列操作,以达到后续处理的目的,称为延时着色。Scene Kit 使用 SCNTechnique 类来表示这种技术。它使用字典来创建,字典中定义了绘图步骤、输入输出、shader 文件、符号等等。
    第一个渲染 pass 永远是 Scene Kit 的默认渲染,它输出场景的颜色和景深。如果你不想这时计算色值,可以将材质设置成"恒定"的光照模型,或者将场景里所有光照都设置成环境光。
    比如,从 Scene Kit 渲染流程的第一个 pass 获取景深,第二个获取法线,第三个对其执行边界检测,你即可以沿轮廓也可以沿边缘画粗线:

    参考资料:
    https://developer.apple.com/documentation/scenekit
    https://developer.apple.com/videos/play/wwdc2014/609/
    https://developer.apple.com/videos/play/wwdc2013/500/
    https://www.objc.io/issues/18-games/scenekit/

    相关文章

      网友评论

          本文标题:SceneKit

          本文链接:https://www.haomeiwen.com/subject/dqomhxtx.html