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

3
我想使用SceneKit在球体表面添加形状。我从一个简单的例子开始,只是试图将球体表面的一部分着色为另一种颜色。我希望这是一个可以被点击、选择等操作的对象,所以我的想法是使用自定义的SCNShape对象作为几何体来添加SCNNodes形状。
现在我有一个蓝色的正方形,我正在从一系列点绘制它,并将其添加到包含红色球体的场景中。它基本上与球体上的一个点相切,但真正的目标是在表面上绘制它。SceneKit中是否有任何可以让我做到这一点的方法?我需要进行一些数学/几何计算,使它与球体的形状相同或映射到球体的坐标吗?我正在尝试做的事情是否超出了SceneKit的范围?
如果这个问题太过宽泛,我会很高兴,如果有人能指向一些书籍或资源来学习我所缺少的知识。我完全是新手,对SceneKit和3D一窍不通,只是玩弄一些想法而已。
以下是我现在拥有的一些playground代码:
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个回答

6
如果您想将2D内容映射到3D SceneKit对象的表面,并使2D内容具有动态/交互性,其中一种最简单的解决方案是使用SpriteKit处理2D内容。您可以将球体的漫反射内容设置为一个SKScene,并在该场景中创建/定位/装饰SpriteKit节点以将它们排列在球体的表面上。
如果您希望此内容响应点击事件...在您的SceneKit视图中使用hitTest可以获得一个SCNHitTestResult,从而可以获取球体上击中点的纹理坐标。从纹理坐标可以转换为SKScene坐标,并生成节点、运行操作或其他操作。
如需更多详细信息,Apple的SceneKitReel示例代码项目可能是您最好的选择。这是介绍iOS SceneKit的WWDC14演示文稿。在该演示文稿中有一个“幻灯片”,颜料团从相机飞向旋转的环面并在其击中位置留下颜料斑点——环面作为其材质具有一个 SpriteKit 场景,留下斑点的技巧基本上与上述命中测试->纹理坐标->SpriteKit坐标方法相同。

在查看了苹果的场景工具包演示和源代码后,我发现他们实际上将漫反射内容作为“SKScene”使用,但我无法重新创建这个过程。你有任何想法如何将漫反射内容设置为skscene吗? - Will Von Ullrich

4
David Rönnqvist的SceneKit书籍(可作为iBook下载)有一个例子(第5章的EarthView example,即一个会说话的地球),值得一看。该示例构建了一个3D图钉,然后将其附加到球面上在点击位置处。
你的问题更复杂,因为你正在构建一个覆盖球面部分的形状。你的“正方形”实际上是一个球形梯形,由四个大圆弧围成的球面段。根据你最终想要的结果,我可以看到三种可能的方法。
最简单的方法是使用图像作为球面表面的材质。这种方法在Ronnqvist EarthView示例中有很好的说明,它使用几个图像显示地球表面。你可以画出你的正方形,而不是大陆。但是,这种方法不适用于交互性。请查看SCNMaterial
另一种方法是使用命中测试结果。这在SCNSceneRenderer(与SCNView兼容)和SCNHitTest上有文档记录。使用命中测试结果,您可以提取被点击的面,然后提取其几何元素。但这并不能完全解决问题,因为SceneKit对于SCNSphere使用三角形,而您要寻找的是四边形。您还将受到仅限于与SceneKit底层线框架表示对齐的正方形的限制。
如果您想完全控制“正方形”绘制的位置,包括相对于赤道的角度变化,那么我认为您将不得不从头开始构建自己的几何体。这意味着计算每个角点的纬度/经度,然后在这些点之间生成弧,接着计算沿弧的许多中间点。您必须添加一个调整因子,稍微将中间点抬高到球面上,并构建自己的四边形或三角形带。这里的类是SCNGeometrySCNGeometryElementSCNGeometrySource

这里有一些好的观点,但是仅仅为了在球面上标记区域,像这样使用自定义几何方法可能比它值得的更加复杂。至少对于某些效果来说,按照球体的纹理图工作会更简单(参见我的答案)。然而,如果你想用这些区域做“三维事物”(比如让国家离开地球并飞走),自定义几何将是唯一的选择。 - rickster

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接