SceneKit简介:基础知识

最终产品图片
您将要创造的

在本教程中,您将学习如何在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坐标系提供基础。

将节点添加到场景时,其位置由一组三个数字指定,这三个分量由代码中的SCNVector3结构表示。 这三个组件中的每一个都定义了节点在x,y和z轴上的位置,如下图所示。

3D坐标图
图片来源:Apple SceneKit 框架参考

场景的根节点位置定义为(0,0,0) 。 在上图中,这是三个轴相交的位置。 图像中包含的相机代表将相机添加到场景时相机指向的默认方向。

既然您已经了解了SceneKit如何表示对象的一些基础知识,那么您就可以开始编写一些代码了。

2.项目设置

打开Xcode并基于Single View Application模板创建一个新的iOS应用 程序 。 尽管您可以使用SceneKit从Game模板轻松创建应用程序,但在本教程中,我将向您展示如何从头开始使用SceneKit。

选择应用程序模板

输入产品名称 ,将Language设置为Swift ,将Devices设置Universal 。 单击下一步继续。

应用程式详细资料

创建项目后,导航至ViewController.swift并在顶部添加以下import语句以导入SceneKit框架:

import SceneKit

接下来,在ViewController类中添加以下viewDidLoad方法的实现:

override func viewDidLoad() {
    super.viewDidLoad()
    
    let sceneView = SCNView(frame: self.view.frame)
    self.view.addSubview(sceneView)
}

viewDidLoad方法中,我们创建一个SCNView对象,并传入视图控制器视图的框架。 我们将SCNView实例分配给常量sceneView ,并将其添加为视图控制器视图的子视图。

SCNView类是UIView的子类,并提供SceneKit内容的出口。 除了具有常规视图的功能之外, SCNView还具有与SceneKit内容相关的几种属性和方法。

要检查所有功能是否正常运行,请构建并运行您的应用程序。 您会看到您只有一个空白的白色视图。

初始应用程序视图

3.场景设置

要在SCNView呈现内容,首先需要创建一个SCNScene并将其分配给视图。 在此场景中,您需要添加一个 摄像头和至少一盏灯。 对于此示例,您还将添加一个多维数据集以用于SceneKit进行渲染。 将以下代码添加到viewDidLoad方法:

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)
}

让我们逐步查看viewDidLoad方法:

  • 首先通过调用init方法为您的视图创建场景。 除非要从外部文件加载准备好的场景,否则这将是您将始终使用的初始化程序。
  • 接下来,为摄像机创建一个SCNCamera对象和一个SCNNode实例。 然后,指定SCNCamera对象到camera的性能cameraNode和沿Z轴去看了一下以后你会创建多维数据集移动这个节点。
  • 在接下来的步骤中,您创建一个SCNLight对象和SCNNode命名lightNode 。 将SCNLight实例分配给光源节点的light属性。 SCNLighttype属性设置为SCNLightTypeOmni 。 此光源类型从3D空间中的一个点向各个方向均匀地分配光线。 您可以将此灯类型视为普通灯泡。
  • 最后,使用SCNBox类创建一个多维数据集,使宽度,高度和长度都相同。 该SCNBox类的子类SCNGeometry ,是你可以创建原始形状之一。 其他形状包括球体,金字塔和圆环。 您还可以创建一个传入geometry参数的多维数据集的节点。
  • 要设置场景,请将三个节点(相机,光源和立方体)添加到场景的场景图中。 不需要额外的设置,因为SCNScene对象会自动检测节点何时包含相机或灯光对象,从而相应地渲染场景。

生成并运行您的应用程序,您将看到从右上角的灯光现在可以看到一个黑色的立方体。

第一个SceneKit渲染

不幸的是,该多维数据集目前看起来不是三维的。 这是因为相机直接位于其前面。 现在要做的就是更改摄像机的位置,以便更好地查看立方体。

但是,要使相机直接指向立方体,您还需要向相机添加SCNLookAtConstraint 。 首先,如下所示更新相机的位置。

cameraNode.position = SCNVector3(x: -3.0, y: 3.0, z: 3.0)

接下来,在实例化多维数据集的节点之后,将以下代码片段添加到viewDidLoad方法:

let constraint = SCNLookAtConstraint(target: cubeNode)
constraint.gimbalLockEnabled = true
cameraNode.constraints = [constraint]

位置更改会将摄像机左右移动。 通过添加约束,将多维数据集作为目标,并将gimbalLockEnabled设置为true ,可以确保相机与水平和视口(在这种情况下为屏幕)保持平行。 这是通过禁用沿滚动轴(从摄像机指向约束目标的轴)的旋转来完成的。

再次构建并运行您的应用程序,您将在其所有3D荣耀中看到您的多维数据集。

3D立方体

4.材质和阴影

现在是时候使用材质和阴影为场景添加更多真实感了。 首先,您需要另一个对象才能在其上投射阴影。 使用以下代码段创建一个平面,一个扁平矩形并将其放置在多维数据集下方。 不要忘记将新节点作为子节点添加到场景的根节点。

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)
}

通过更改平面节点的eulerAngles属性,可以将平面沿x轴向后旋转90度。 我们需要这样做,因为默认情况下会垂直创建平面。 在SceneKit中,旋转角度以弧度而不是度来计算,但是可以使用GLKMathDegreesToRadians(_:)GLKMathsRadiansToDegrees(_:)函数轻松地转换这些值。 GLK代表Apple的OpenGL框架GLKit。

接下来,向多维数据集和平面添加材料。 在此示例中,您将为立方体和平面分别赋予纯色,分别为红色和绿色。 viewDidLoad添加到viewDidLoad方法以创建这些材料。

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)
    ...
}

对于每个SCNMaterial对象,您可以为其漫反射内容分配一个UIColor值。 材料的漫射特性决定了它在直射光下的外观。 请注意,分配的值不必是UIColor对象。 还有许多其他可接受的对象类型可以分配给此属性,例如UIImageCALayer甚至SpriteKit纹理( SKTexture )。

再次构建并运行您的应用程序,不仅可以第一次看到飞机,还可以看到您创建的材料。

红色立方体和绿色飞机

现在是时候为场景添加一些阴影了。 SceneKit中可用的四种光源类型中,只有聚光灯可以创建阴影。 在此示例中,您将把现有的全向光变成针对立方体的聚光灯。 将以下代码添加到viewDidLoad方法:

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]
    ...
}

要创建聚光灯,请首先将灯的类型设置为SCNLightTypeSpot 。 然后,您可以指定聚光灯的内角和外角(以度为单位)。 默认值分别为045 。 内角确定光线在直射光中覆盖的面积,而外角确定部分照明的面积。 一旦看到结果场景,这些角度之间的差异将变得清晰。 然后,您可以明确地告诉灯光投射阴影,并添加与SCNLookAtConstraint为相机创建的相同的SCNLookAtConstraint

生成并运行您的应用程序以查看生成的场景。 将显示您在代码中指定的内角,其中平面为纯绿色,正好位于立方体下方。 外角由光的梯度表示,当光从光目标移开时,它逐渐变为黑色。

多维数据集和平面与阴影

您会看到您现在已使多维数据集正确投射阴影。 但是,聚光灯仅照亮飞机的一部分。 这是因为场景中没有环境光。

环境光是一种以均匀的光分布照亮一切的光源。 因为环境光照亮了整个场景,所以它的位置无关紧要,您可以将其添加到所需的任何节点,甚至是与相机相同的节点。 使用以下代码片段为场景创建环境光。

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
    ...
}

就像您之前所做的SCNLight ,该代码段创建了一个SCNLight 。 主要区别在于光源的type属性,该属性设置为SCNLightTypeAmbient 。 您还可以将其颜色设置为深灰色,以免影响场景。 光源的默认颜色是纯白色(RGB值为1、1、1),并且在环境光下使用该颜色会使整个场景完全照亮,如下面的屏幕快照所示。

充分照明的场景

最后一次构建并运行您的应用程序以查看最终结果。

最终产品

结论

如果您已完成本教程的结尾,则现在应该熟悉以下主题:

在本系列的下一个教程中,您将学习SceneKit框架的一些更高级的概念,包括动画,用户交互,粒子系统和模拟物理。

翻译自: https://code.tutsplus.com/tutorials/an-introduction-to-scenekit-fundamentals--cms-23847

  • 3
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值