前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >SceneKitScene Kit 概要节点 (Nodes)光照动画开始用 Scene Kit 写游戏扩展默认渲染流程延时着色

SceneKitScene Kit 概要节点 (Nodes)光照动画开始用 Scene Kit 写游戏扩展默认渲染流程延时着色

作者头像
rectinajh
发布2018-05-17 16:16:11
1.4K0
发布2018-05-17 16:16:11
举报

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还使用节点层次来组织其内容。 每个节点相对于其父节点具有位置,旋转和缩放,而父节点又相对于其父节点,一直向上,直到根节点。 假如要给一个节点确定一个位置,就必须将它挂载到节点树中的某个节点上。 使用以下方法管理节点层次结构:

代码语言:javascript
复制
        addChildNode(_:)
        insertChildNode(_: atIndex:)
        removeFromParentNode()

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

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

代码语言:javascript
复制
        let chessPieces = SCNScene(named: "chess pieces") // SCNScene?

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

代码语言:javascript
复制
      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" 约束,将光照对象约束到了目标对象上,即使它移动,光照也会一直朝向目标对象。

代码语言:javascript
复制
    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 非常相似:

代码语言:javascript
复制
    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 版本的小伙伴非常相似,它们的游戏循环步骤完全一致,使用下面几个代理回调:

代码语言:javascript
复制
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 的结果包含被击中模型的节点对象,也包含了交点的详细信息 (交点坐标、交点表面法线,交点的纹理坐标)。多数情况下,知道第一个被击中的节点就足够了:

代码语言:javascript
复制
      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); }

代码语言:javascript
复制
    #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 来实现:

代码语言:javascript
复制
    torus.setValue(5.0, forKey: "twistFactor")

使用这个 key path 的 CAAnimation 也 ok:

代码语言:javascript
复制
    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/

本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2017.07.11 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • Scene Kit 概要
  • 节点 (Nodes)
  • 光照
  • 动画
  • 开始用 Scene Kit 写游戏
  • 扩展默认渲染流程
  • 延时着色
相关产品与服务
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档