一、概述
SceneKit,是用来构建3D场景的框架,且可以与Core Animation和SpriteKit无缝交互。在SceneKit中可以直接引入COLLADA行业标准文件制作好的3D模型或场景。
与SpriteKit一样,SceneKit通过场景(SCNScene)来显示物体,场景包涵在SCNView。场景内同样是以节点的结构来呈现物体。场景里可以包含这些类型的项目:
* 几何体。 代码建立的3D对象或从文件加载的3D模型,在它们上可以附加不同的材料来达成控制颜色,纹理,反光等效果
* 照相机。 这是你观察这个场景的入口,可以设置位置和视角
* 光源。 有各种光源后面会提到
* 物理实体。 受物理特效控制的各种有实体的物体
使用SceneKit的方法不止有在SCNView内部来使用。还可以在Core Animation层的层次结构中使用SCNLayer。使用SCNRender来渲染你自己的OpenGL渲染器。
SceneKit可以同时在IOS和OS X下工作
二、基本概念
- SCNVector3
三维向量 - SCNCamera
三维摄像头 - SCNGeometry
三维几何图形
三、几何图形
- SCNPlane
平面矩形 - SCNBox
长方体 - SCNSphere
球形 - SCNPyramid
四棱锥 - SCNTorus
圆环 - SCNCapsule
胶囊体 - SCNCylinder
圆柱 - SCNCone
圆台 - SCNTube
管道体
四、实战
import UIKit
import SceneKit
import SpriteKit
class TestViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
// 创建游戏专用视图
let scnView = SCNView(frame: self.view.frame)
scnView.scene = SCNScene()
self.view.addSubview(scnView)
// 创建一个摄像机
let cameraNode = SCNNode()
cameraNode.camera = SCNCamera()
// 自动调节可视范围
cameraNode.camera?.automaticallyAdjustsZRange = true
cameraNode.position = SCNVector3(0, 0, 10)
scnView.scene?.rootNode.addChildNode(cameraNode)
// 创建一个节点并绑定一个平面几何对象
let boxNode = SCNNode()
let plane = SCNPlane(width: 16, height: 9)
boxNode.geometry = plane
boxNode.geometry?.firstMaterial?.isDoubleSided = true
boxNode.position = SCNVector3(0, 0, -30)
scnView.scene?.rootNode.addChildNode(boxNode)
guard let url = Bundle.main.url(forResource: "1", withExtension: "mp4") else {
return
}
let videoNode = SKVideoNode(url: url)
videoNode.size = CGSize(width: 1600, height: 900)
videoNode.position = CGPoint(x: videoNode.size.width/2, y: videoNode.size.height/2)
videoNode.zRotation = CGFloat(M_PI)
let skScene = SKScene()
skScene.addChild(videoNode)
skScene.size = videoNode.size
// 给平面体设置渲染内容
plane.firstMaterial?.diffuse.contents = skScene
// 播放视频
videoNode.play()
// 打开摄像头控制查看效果
scnView.allowsCameraControl = true
}
}
五、粒子系统
六、光源
- 环境光源
它在整个场景内投射均匀光 - 泛光源
这是用的最多的,就是点光源,向各个方向投射光 - 平行光源
在单个方向投射光 - 聚光源
在给定方向从单个位置投射光
// 添加环境光源
let ambientLight = SCNLight()
// 光源的类型,这里是环境光源
ambientLight.type = .ambient
ambientLight.color = UIColor.red
let myAmbientLightNode = SCNNode()
myAmbientLightNode.light = ambientLight
myScene.rootNode.addChildNode(myAmbientLightNode)
// 添加泛光源
let omniLight = SCNLight()
omniLight.type = .omni
omniLight.color = UIColor.black
let omniLightNode = SCNNode()
omniLightNode.light = omniLight
omniLightNode.position = SCNVector3(-10, 8, 5)
myScene.rootNode.addChildNode(omniLightNode)
七、材料
- diffuse
材料的基本颜色,纹理等 - specular
材料的亮度以及该如何反射光 - emissive
材料发光时的样子 - normal
又称为法向,设置材料表面更多的细节
//使用材料
let redMetallicMateril = SCNMaterial()
//contents设置为颜色
redMetallicMateril.diffuse.contents = UIColor.blue
redMetallicMateril.specular.contents = UIColor.white
redMetallicMateril.shininess = 1.0
//一个物体的材料是不唯一的,故传进去一个数组
plane.materials = [redMetallicMateril]
let noiseTexture = SKTexture(noiseWithSmoothness: 0.25, size: CGSize(width: 512, height: 512), grayscale: true)
let noiseMaterial = SCNMaterial()
//contents设置为SpriteKit纹理
noiseMaterial.diffuse.contents = noiseTexture
text.materials = [noiseMaterial]
//法线贴图
let noiseNormalMapTexture = noiseTexture.generatingNormalMap(withSmoothness: 1, contrast: 1)
redMetallicMateril.normal.contents = noiseNormalMapTexture
八、动画
// 向胶囊节点添加动画
let moveUpDownAnimation = CABasicAnimation(keyPath: "position")
// 移动的坐标
moveUpDownAnimation.byValue = NSValue(scnVector3: SCNVector3(30, 0, 0))
moveUpDownAnimation.timingFunction = CAMediaTimingFunction(name: kCAMediaTimingFunctionEaseInEaseOut)//移动速度的曲线
moveUpDownAnimation.autoreverses = true//是否自动返回
moveUpDownAnimation.repeatCount = Float.infinity//重复次数
moveUpDownAnimation.duration = 10.0//持续时间
plane.addAnimation(moveUpDownAnimation, forKey: "updown")//这里的这个keyPath貌似随便写就可以
//胶囊节点的子节点,一个文本
let text = SCNText(string: "BaLaLaLa", extrusionDepth: 1)
text.font = UIFont.systemFont(ofSize: 2)
let textNode = SCNNode(geometry: text)
textNode.position = SCNVector3(-5, 6, 0)
boxNode.addChildNode(textNode)
//在一个节点上添加多个动画结果会复合,即便是继承自父节点的动画也同样一起复合
let rotate = CABasicAnimation(keyPath: "eulerAngles")
rotate.byValue = NSValue(scnVector3: SCNVector3(0, M_PI * 2, 0))
rotate.repeatCount = Float.infinity
rotate.duration = 5.0
textNode.addAnimation(rotate, forKey: "rotation")
九、从COLLADA文件中加载文件
//加载一个已经建好的3D模型或场景,会是一个COLLADA文件,后缀名为.dae
guard let critterURL = Bundle.main.url(forResource: "", withExtension: "dae") else {
return
}
let critterData = SCNSceneSource(url: critterURL, options: nil)
let critterNode = critterData?.entryWithIdentifier("Critter", withClass: SCNNode.self)
if (critterNode != nil) {
critterNode!.position = SCNVector3(0, 0, -10)
critterNode?.name = "Critter"
myScene.rootNode.addChildNode(critterNode!)
}
十、添加物理仿真
//像节点添加物理特性
var critterPhysicsShape: SCNPhysicsShape?
if let geometry = critterNode?.geometry {
critterPhysicsShape = SCNPhysicsShape(geometry: geometry, options: nil)
}
let critterPhysicsBody = SCNPhysicsBody(type: .dynamic, shape: critterPhysicsShape)
critterPhysicsBody.mass = 1
critterNode?.physicsBody = critterPhysicsBody
//添加一个地板
let floor = SCNFloor()
let floorNode = SCNNode(geometry: floor)
floorNode.position = SCNVector3(0, -10, 0)
myScene.rootNode.addChildNode(floorNode)
let floorPhysicsBody = SCNPhysicsBody(type: .static, shape: SCNPhysicsShape(geometry: floor, options: nil))
floorNode.physicsBody = floorPhysicsBody
网友评论