在本教程中,您将学习如何在SceneKit中创建基本的3D场景,而无需复杂的OpenGL。 这包括基本几何图形,相机,灯光,材质和阴影。
介绍
SceneKit框架最初由Apple与OS X 10.8 Mountain Lion一起启动,后来随着iOS 8的发布而在iOS上可用。此框架的目的是使开发人员能够轻松地将3D图形集成到游戏和应用程序中,而无需复杂性图形API,例如OpenGL和Metal。
SceneKit允许您简单地提供场景中所需资产的描述,而框架本身可以为您处理所有OpenGL渲染代码。 在第一个教程中,我将教您一些使用3D资源的基础知识和SceneKit框架的基础知识。
本教程要求您以Xcode 6或更高版本运行。 虽然不是必需的,但我建议使用运行iOS 8的物理设备来测试SceneKit代码。 您可以使用iOS模拟器,但是如果您的场景变得更加复杂,则性能将不佳。 请注意,在物理iOS设备上进行测试要求您具有注册的iOS开发人员帐户。
1.基本原理
关于SceneKit,您需要了解的第一件事是,由节点表示的资产被安排在称为场景图的层次树中。 如果您熟悉iOS开发,则此树的工作原理类似于常规视图层次结构 在UIKit中。 您创建的每个场景都具有一个根节点,您可以在该根节点上添加后续节点,并为该场景的3D坐标系提供基础。
将节点添加到场景时,其位置由一组三个数字指定,这三个分量由代码中的
场景的根节点位置定义为(0,0,0) 。 在上图中,这是三个轴相交的位置。 图像中包含的相机代表将相机添加到场景时相机指向的默认方向。
既然您已经了解了SceneKit如何表示对象的一些基础知识,那么您就可以开始编写一些代码了。
2.项目设置
打开Xcode并基于Single View Application模板创建一个新的iOS应用 程序 。 尽管您可以使用SceneKit从Game模板轻松创建应用程序,但在本教程中,我将向您展示如何从头开始使用SceneKit。
输入产品名称 ,将Language设置为Swift ,将Devices设置为Universal 。 单击下一步继续。
创建项目后,导航至ViewController.swift并在顶部添加以下import语句以导入SceneKit框架:
1 | import SceneKit |
接下来,在
1 2 3 4 5 6 | override func viewDidLoad() { super.viewDidLoad() let sceneView = SCNView(frame: self.view.frame) self.view.addSubview(sceneView) } |
在
要检查所有功能是否正常运行,请构建并运行您的应用程序。 您会看到您只有一个空白的白色视图。
3.场景设置
要在
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 | override func viewDidLoad() { super.viewDidLoad() let sceneView = SCNView(frame: self.view.frame) self.view.addSubview(sceneView) let scene = SCNScene() sceneView.scene = scene let camera = SCNCamera() let cameraNode = SCNNode() cameraNode.camera = camera cameraNode.position = SCNVector3(x: 0.0, y: 0.0, z: 3.0) let light = SCNLight() light.type = SCNLightTypeOmni let lightNode = SCNNode() lightNode.light = light lightNode.position = SCNVector3(x: 1.5, y: 1.5, z: 1.5) let cubeGeometry = SCNBox(width: 1.0, height: 1.0, length: 1.0, chamferRadius: 0.0) let cubeNode = SCNNode(geometry: cubeGeometry) scene.rootNode.addChildNode(lightNode) scene.rootNode.addChildNode(cameraNode) scene.rootNode.addChildNode(cubeNode) } |
让我们逐步查看
- 首先通过调用
init 方法为您的视图创建场景。 除非要从外部文件加载准备好的场景,否则这将是您将始终使用的初始化程序。 - 接下来,为摄像机创建一个
SCNCamera 对象和一个SCNNode 实例。 然后,指定SCNCamera 对象到camera 的性能cameraNode 和沿Z轴去看了一下以后你会创建多维数据集移动这个节点。 - 在接下来的步骤中,您创建一个
SCNLight 对象和SCNNode 命名lightNode 。 将SCNLight 实例分配给光源节点的light 属性。SCNLight 的type 属性设置为SCNLightTypeOmni 。 此光源类型从3D空间中的一个点向各个方向均匀地分配光线。 您可以将此灯类型视为普通灯泡。 - 最后,使用
SCNBox 类创建一个多维数据集,使宽度,高度和长度都相同。 该SCNBox 类的子类SCNGeometry ,是你可以创建原始形状之一。 其他形状包括球体,金字塔和圆环。 您还可以创建一个传入geometry 参数的多维数据集的节点。 - 要设置场景,请将三个节点(相机,光源和立方体)添加到场景的场景图中。 不需要额外的设置,因为
SCNScene 对象会自动检测节点何时包含相机或灯光对象,从而相应地渲染场景。
生成并运行您的应用程序,您将看到从右上角的灯光现在可以看到一个黑色的立方体。
不幸的是,该多维数据集目前看起来不是三维的。 这是因为相机直接位于其前面。 现在要做的就是更改摄像机的位置,以便更好地查看立方体。
但是,要使相机直接指向立方体,您还需要向相机添加
1 | cameraNode.position = SCNVector3(x: -3.0, y: 3.0, z: 3.0) |
接下来,在实例化多维数据集的节点之后,将以下代码片段添加到viewDidLoad方法:
1 2 3 | let constraint = SCNLookAtConstraint(target: cubeNode) constraint.gimbalLockEnabled = true cameraNode.constraints = [constraint] |
位置更改会将摄像机左右移动。 通过添加约束,将多维数据集作为目标,并将
再次构建并运行您的应用程序,您将在其所有3D荣耀中看到您的多维数据集。
4.材质和阴影
现在是时候使用材质和阴影为场景添加更多真实感了。 首先,您需要另一个对象才能在其上投射阴影。 使用以下代码段创建一个平面,一个扁平矩形并将其放置在多维数据集下方。 不要忘记将新节点作为子节点添加到场景的根节点。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | override func viewDidLoad() { ... let cubeGeometry = SCNBox(width: 1.0, height: 1.0, length: 1.0, chamferRadius: 0.0) let cubeNode = SCNNode(geometry: cubeGeometry) let planeGeometry = SCNPlane(width: 50.0, height: 50.0) let planeNode = SCNNode(geometry: planeGeometry) planeNode.eulerAngles = SCNVector3(x: GLKMathDegreesToRadians(-90), y: 0, z: 0) planeNode.position = SCNVector3(x: 0, y: -0.5, z: 0) ... scene.rootNode.addChildNode(lightNode) scene.rootNode.addChildNode(cameraNode) scene.rootNode.addChildNode(cubeNode) scene.rootNode.addChildNode(planeNode) } |
通过更改平面节点的
接下来,向多维数据集和平面添加材料。 在此示例中,您将为立方体和平面分别赋予纯色,分别为红色和绿色。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | override func viewDidLoad() { ... planeNode.position = SCNVector3(x: 0, y: -0.5, z: 0) let redMaterial = SCNMaterial() redMaterial.diffuse.contents = UIColor.redColor() cubeGeometry.materials = [redMaterial] let greenMaterial = SCNMaterial() greenMaterial.diffuse.contents = UIColor.greenColor() planeGeometry.materials = [greenMaterial] let constraint = SCNLookAtConstraint(target: cubeNode) ... } |
对于每个
再次构建并运行您的应用程序,不仅可以第一次看到飞机,还可以看到您创建的材料。
现在是时候为场景添加一些阴影了。 SceneKit中可用的四种光源类型中,只有聚光灯可以创建阴影。 在此示例中,您将把现有的全向光变成针对立方体的聚光灯。 将以下代码添加到
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | override func viewDidLoad() { ... let light = SCNLight() light.type = SCNLightTypeSpot light.spotInnerAngle = 30.0 light.spotOuterAngle = 80.0 light.castsShadow = true let lightNode = SCNNode() lightNode.light = light lightNode.position = SCNVector3(x: 1.5, y: 1.5, z: 1.5) ... let constraint = SCNLookAtConstraint(target: cubeNode) constraint.gimbalLockEnabled = true cameraNode.constraints = [constraint] lightNode.constraints = [constraint] ... } |
要创建聚光灯,请首先将灯的类型设置为
生成并运行您的应用程序以查看生成的场景。 将显示您在代码中指定的内角,其中平面为纯绿色,正好位于立方体下方。 外角由光的梯度表示,当光从光目标移开时,它逐渐变为黑色。
您会看到您现在已使多维数据集正确投射阴影。 但是,聚光灯仅照亮飞机的一部分。 这是因为场景中没有环境光。
环境光是一种以均匀的光分布照亮一切的光源。 因为环境光照亮了整个场景,所以它的位置无关紧要,您可以将其添加到所需的任何节点,甚至是与相机相同的节点。 使用以下代码片段为场景创建环境光。
1 2 3 4 5 6 7 8 9 10 11 12 13 | override func viewDidLoad() { ... let camera = SCNCamera() let cameraNode = SCNNode() cameraNode.camera = camera cameraNode.position = SCNVector3(x: -3.0, y: 3.0, z: 3.0) let ambientLight = SCNLight() ambientLight.type = SCNLightTypeAmbient ambientLight.color = UIColor(red: 0.2, green: 0.2, blue: 0.2, alpha: 1.0) cameraNode.light = ambientLight ... } |
就像您之前所做的
最后一次构建并运行您的应用程序以查看最终结果。
结论
如果您已完成本教程的结尾,则现在应该熟悉以下主题:
在本系列的下一个教程中,您将学习SceneKit框架的一些更高级的概念,包括动画,用户交互,粒子系统和模拟物理。
翻译自: https://code.tutsplus.com/tutorials/an-introduction-to-scenekit-fundamentals--cms-23847