在SceneKit中向球体表面添加形状

时间:2016-01-26 20:20:14

标签: ios swift scenekit

我希望能够使用SceneKit向球体表面添加形状。我从一个简单的例子开始,我只是试图为球体表面的一部分着色另一种颜色。我希望这是一个可以点击,选择等的对象......所以我的想法是使用自定义SCNNodes对象为几何体添加SCNShape形状。

我现在拥有的是一个蓝色正方形,我从一系列点中绘制并添加到包含红色球体的场景中。它基本上与球体上的一个点相切,但真正的目标是在表面上绘制它。 SceneKit中有什么东西可以让我这样做吗?我是否需要进行一些数学/几何以使其与球体形状相同或映射到球体的坐标?我正在尝试在SceneKit的范围之外做什么?

如果这个问题过于宽泛,我会很高兴有人能指出我的书籍或资源来了解我所缺少的东西。我对SceneKit和3D一般都很陌生,只是玩得很​​开心。

以下是我现在拥有的游乐场代码:

import UIKit
import SceneKit
import XCPlayground

class SceneViewController: UIViewController {

    let sceneView = SCNView()

    private lazy var sphere: SCNSphere = {
        let sphere = SCNSphere(radius: 100.0)
        sphere.materials = [self.surfaceMaterial]
        return sphere
    }()

    private lazy var testScene: SCNScene = {
        let scene = SCNScene()
        let sphereNode: SCNNode = SCNNode(geometry: self.sphere)
        sphereNode.addChildNode(self.blueChildNode)

        scene.rootNode.addChildNode(sphereNode)
        //scene.rootNode.addChildNode(self.blueChildNode)
        return scene
    }()

    private lazy var surfaceMaterial: SCNMaterial = {
        let material = SCNMaterial()
        material.diffuse.contents = UIColor.redColor()
        material.specular.contents = UIColor(white: 0.6, alpha: 1.0)
        material.shininess = 0.3
        return material
    }()

    private lazy var blueChildNode: SCNNode = {
        let node: SCNNode = SCNNode(geometry: self.blueGeometry)
        node.position = SCNVector3(0, 0, 100)
        return node
    }()

    private lazy var blueGeometry: SCNShape = {
        let points: [CGPoint] = [
            CGPointMake(0, 0),
            CGPointMake(50, 0),
            CGPointMake(50, 50),
            CGPointMake(0, 50),
            CGPointMake(0, 0)]

        var pathRef: CGMutablePathRef = CGPathCreateMutable()
        CGPathAddLines(pathRef, nil, points, points.count)

        let bezierPath: UIBezierPath = UIBezierPath(CGPath: pathRef)
        let shape = SCNShape(path: bezierPath, extrusionDepth: 1)
        shape.materials = [self.blueNodeMaterial]
        return shape
    }()

    private lazy var blueNodeMaterial: SCNMaterial = {
        let material = SCNMaterial()
        material.diffuse.contents = UIColor.blueColor()
        return material
    }()

    override func viewDidLoad() {
        super.viewDidLoad()

        sceneView.frame = self.view.bounds
        sceneView.backgroundColor = UIColor.blackColor()
        self.view.addSubview(sceneView)

        sceneView.autoenablesDefaultLighting = true
        sceneView.allowsCameraControl = true

        sceneView.scene = testScene
    }
}

XCPShowView("SceneKit", view: SceneViewController().view)

2 个答案:

答案 0 :(得分:6)

如果要将2D内容映射到3D SceneKit对象的表面,并使2D内容成为动态/交互式,最简单的解决方案之一是将SpriteKit用于2D内容。您可以将球体的漫反射内容设置为SKScene,并在该场景中创建/定位/修饰SpriteKit节点,以将它们排列在球体的面上。

如果您希望此内容响应点击事件...在SceneKit视图中使用hitTest会获得SCNHitTestResult,然后您可以获取该点的纹理坐标球。从纹理坐标,您可以转换为SKScene坐标并生成节点,运行操作或其他任何内容。

有关详细信息,您最好的选择可能是Apple的SceneKitReel示例代码项目。这是在WWDC14上为iOS引入SceneKit的演示。在那个演示中有一个“幻灯片”,其中油漆球在旋转的圆环上从相机飞出并留下油漆溅到它们的位置 - 圆环有一个SpriteKit场景作为其材料,并且在碰撞时留下飞溅的技巧基本相同命中测试 - >纹理坐标 - >上面概述的SpriteKit坐标方法。

答案 1 :(得分:3)

David Ronnqvist的SceneKit书中有一个值得关注的例子(EarthView示例,一个说话的地球,第5章)。该示例构建了一个3D图钉,然后将其附加到水龙头位置的地球表面。

你的问题更复杂,因为你正在构建一个覆盖球体一部分的形状。你的" square"实际上是一个球形梯形,是由四个大圆弧限定的球体的一部分。我可以看到三种可能的方法,具体取决于您最终寻找的内容。

最简单的方法是使用图像作为球体表面的材质。这种方法在Ronnqvist EarthView示例中得到了很好的说明,该示例使用多个图像来显示地球表面。你不是画大陆,而是画你的广场。不过,这种方法并不适合交互性。看看SCNMaterial

另一种方法是使用命中测试结果。这已记录在SCNSceneRendererSCNView符合)和SCNHitTest上。使用命中测试结果,您可以拉出被轻击的面,然后拉出其几何元素。但是,这不会让你一直回家,因为SceneKit使用三角形SCNSphere,你正在寻找四边形。您还将被限制为与SceneKit的基础线框表示对齐的正方形。

如果你想完全控制" square"绘制,包括改变其相对于赤道的角度,我认为你必须从头开始构建自己的几何。这意味着计算每个角点的纬度/经度,然后在这些点之间生成弧,然后计算沿弧的一堆中间点。您必须添加一个软糖因子,将中间点提升到略高于球体表面的位置,并建立自己的四边形或三角形条带。这里的课程是SCNGeometrySCNGeometryElementSCNGeometrySource