SceneKit

作者: exialym | 来源:发表于2016-05-13 20:10 被阅读5363次

    概述

    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下工作

    添加场景

    首先要做的当然是添加场景

    //获取SCNView
    let scnView = self.view as! SCNView
    //我自己的场景
    let myScenes = myScene()
    //将SCNView的场景设置为我的场景
    scnView.scene = myScenes
    

    添加照相机

    照相机是你观察你构建的3D场景的眼睛

    //创建视角,可以说是整个场景的入口,没有视角没法观察一个3D场景
    let myCamera = SCNCamera()
    myCamera.xFov = 45
    myCamera.yFov = 45
    let myCameraNode = SCNNode()
    myCameraNode.camera = myCamera
    myCameraNode.position = SCNVector3(0, 0, 20)
    //向场景添加节点与SpriteKit有些不一样
    myScenes.rootNode.addChildNode(myCameraNode)
    

    添加3D对象

    //创建胶囊
    let capsule = SCNCapsule(capRadius: 2.5, height: 10)
    //SCNCapsule是SCNGeomery的一个子类,通过这个类可以创建更多的形状
    let capsuleNode = SCNNode(geometry: capsule)
    capsuleNode.position = SCNVector3(-15, -2.8, 0)//节点的默认位置是0,0,0
    capsuleNode.name = "myCapsule"
    myScenes.rootNode.addChildNode(capsuleNode)
    

    添加光源

    光源有许多种:

    • 环境光源,它在整个场景内投射均匀光
    • 泛光源,这是用的最多的,就是点光源,向各个方向投射光
    • 平行光源,在单个方向投射光
    • 聚光源,在给定方向从单个位置投射光
    //添加环境光源
    let ambientLight = SCNLight()
    //光源的类型,这里是环境光源
    ambientLight.type = SCNLightTypeAmbient
    ambientLight.color = UIColor(white: 0.25, alpha: 1.0)
    let myAmbientLightNode = SCNNode()
    myAmbientLightNode.light = ambientLight
    myScenes.rootNode.addChildNode(myAmbientLightNode)
            
    //添加泛光源
    let omniLight = SCNLight()
    omniLight.type = SCNLightTypeOmni
    omniLight.color = UIColor(red: 0, green: 0, blue: 1, alpha: 1)
    let omniLightNode = SCNNode()
    omniLightNode.light = omniLight
    omniLightNode.position = SCNVector3(-10, 8, 5)
    myScenes.rootNode.addChildNode(omniLightNode)
    

    添加动画

    //向胶囊节点添加动画
    let moveUpDownAnimation = CABasicAnimation(keyPath: "position")//这里的这个keyPath很重要,不是随便写一个就好的
    moveUpDownAnimation.byValue = NSValue(SCNVector3: SCNVector3(30, 0, 0))//移动的坐标
    moveUpDownAnimation.timingFunction = CAMediaTimingFunction(name: kCAMediaTimingFunctionEaseInEaseOut)//移动速度的曲线
    moveUpDownAnimation.autoreverses = true//是否自动返回
    moveUpDownAnimation.repeatCount = Float.infinity//重复次数
    moveUpDownAnimation.duration = 10.0//持续时间
    capsuleNode.addAnimation(moveUpDownAnimation, forKey: "updown")//这里的这个keyPath貌似随便写就可以
            
    //胶囊节点的子节点,一个文本
    let text = SCNText(string: "BaLaLaLa", extrusionDepth: 1)
    text.font = UIFont.systemFontOfSize(2)
    let textNode = SCNNode(geometry: text)
    textNode.position = SCNVector3(-5, 6, 0)
    capsuleNode.addChildNode(textNode)
            
    //在一个节点上添加多个动画结果会复合,即便是继承自父节点的动画也同样一起复合
    let rotate = CABasicAnimation(keyPath: "eulerAngles")
    rotate.byValue = NSValue(SCNVector3: SCNVector3(Float(0), Float(M_PI * 2), Float(0)))
    rotate.repeatCount = Float.infinity
    rotate.duration = 5.0
    textNode.addAnimation(rotate, forKey: "rotation")
    

    使用材料

    材料可以用来改变物体表面的样子,材料使用SCNMaterial类来表示,材料对象拥有许多许多属性,比如:

    • diffuse(材料的基本颜色,纹理等)
    • specular(材料的亮度以及该如何反射光)
    • emissive(材料发光时的样子)
    • normal(又称为法向,设置材料表面更多的细节)

    每个属性都拥有contents属性,给这个属性设置不同的内容来设置这些属性的样子:

    • 颜色
    • 图像(NSImage等)
    • SpriteKit场景
    • SpriteKit纹理
    //使用材料
    let redMetallicMateril = SCNMaterial()
    //contents设置为颜色
    redMetallicMateril.diffuse.contents = UIColor.blueColor()
    redMetallicMateril.specular.contents = UIColor.whiteColor()
    redMetallicMateril.shininess = 1.0
    //一个物体的材料是不唯一的,故传进去一个数组
    capsule.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.textureByGeneratingNormalMapWithSmoothness(1, contrast: 1.0)
    redMetallicMateril.normal.contents = noiseNormalMapTexture
    

    命中检测

    这个说白了就是监测你点击了哪个物体,首先当然要添加手势。

    //命中检测
    let tapRecognizer = UITapGestureRecognizer(target: self, action: "tapped:")
    let longTapRecoginizer = UILongPressGestureRecognizer(target: self, action: "longTapped:")
    scnView.addGestureRecognizer(tapRecognizer)
    scnView.addGestureRecognizer(longTapRecoginizer)
    scnView.userInteractionEnabled = true
    

    接下来添加手势对应的执行函数

        func tapped(tapRecognize: UIGestureRecognizer){
            if tapRecognize.state == UIGestureRecognizerState.Ended {
                let scnView = self.view as! SCNView
                //检测点击到哪个,返回被点到的物体,这里返回的数组里包含了你点击的点顺着屏幕法线穿过的所有的物体
                let hits = scnView.hitTest(tapRecognize.locationInView(tapRecognize.view), options: nil) as [SCNHitTestResult]
                for hit in hits {
                    if let theMaterial = hit.node.geometry?.materials[0] {
                        let hightLightAnimation = CABasicAnimation(keyPath: "contents")
                        hightLightAnimation.fromValue = UIColor.blackColor()
                        hightLightAnimation.toValue = UIColor.yellowColor()
                        hightLightAnimation.autoreverses = true
                        hightLightAnimation.repeatCount = 2
                        hightLightAnimation.duration = 1
                        theMaterial.emission.addAnimation(hightLightAnimation, forKey: "heightLight")
                    }
                }
            }
        }
    

    为节点添加约束

    //添加指向胶囊节点这个约束
    let lookAtConstraint = SCNLookAtConstraint(target: capsuleNode)
    //使其只围绕一个轴转动
    lookAtConstraint.gimbalLockEnabled = true
    pointerNode.constraints = [lookAtConstraint]
    

    从COLLADA文件中加载文件

    //加载一个已经建好的3D模型或场景,会是一个COLLADA文件,后缀名为.dae
    let critterURL = NSBundle.mainBundle().URLForResource("Critter", withExtension: "dae")
    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"
        myScenes.rootNode.addChildNode(critterNode!)
    }
    

    添加物理仿真

    物理仿真和SpriteKit中的很像,物理实体的类别有一些不同:

    • 静态实体,从不移动,不受重力影响,但会碰撞
    • 运动学实体不受物理力的作用,但是它是有实体的,在动画中碰到其他物体会将它们推开。
    • 动态实体受物理力及碰撞影响

    神奇的是SceneKit为我们提供了地板类,它的几何形状就是一个地板

    //像节点添加物理特性
    var critterPhysicsShape: SCNPhysicsShape?
    if let geometry = critterNode?.geometry {
        critterPhysicsShape = SCNPhysicsShape(geometry: geometry, options: nil)
    }
    let critterPhysicsBody = SCNPhysicsBody(type: SCNPhysicsBodyType.Dynamic, shape: critterPhysicsShape)
    critterPhysicsBody.mass = 1
    critterNode?.physicsBody = critterPhysicsBody
            
    //添加一个地板
    let floor = SCNFloor()
    let floorNode = SCNNode(geometry: floor)
    floorNode.position = SCNVector3(0, -10, 0)
    myScenes.rootNode.addChildNode(floorNode)
    let floorPhysicsBody = SCNPhysicsBody(type: SCNPhysicsBodyType.Static, shape: SCNPhysicsShape(geometry: floor, options: nil))
    floorNode.physicsBody = floorPhysicsBody
    

    相关文章

      网友评论

      • 郝嗨森:怎么判断点击的是不是添加的模型呢?
        凌默默:同问, 你现在解决了么?
      • 鬼晓晓:不错 还是有部分参考意义
      • 酷走天涯:appstory 搜索SceneKit,可下载完整中文教程
      • e7f9a507e49e:大神你好,我想知道怎么从外部导入DAE文件到xcode里,我用3DMax做了个模型,最开始导出选的AutoDesk的DAE格式,拖到xcode里后什么都没有,就只有个坐标。然后又下载了opencollada的dle文件放到3DMax的plugins文件夹里试着导出,但是却出现错误,无法导出,我用的3DMax2014
        小点草:需要在项目创建一个模型文件夹,可以看看这里http://www.jianshu.com/p/903b3b14b9eb
        a28bec5b63af:用blender制作
        exialym:@三千5945 额,不好意思哦,这个我也不知道,我用的是现成的模型呢
      • 4b35c2846a14:请问 你大概知道缩放手势的内部实现大概是什么样子的吗 我重写了缩放手势 感觉效果不是太好 有bug
      • Aaron丶丶:请问有没有 demo 可以参考的

      本文标题:SceneKit

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