在 SceneKit 中将形状添加到球体表面
Add shape to sphere surface in SceneKit
我希望能够使用 SceneKit
向球体表面添加形状。我从一个简单的例子开始,我只是想给球体表面的一部分涂上另一种颜色。我希望这是一个可以点击、选择等的对象...所以我的想法是使用自定义 SCNShape
几何对象添加形状 SCNNodes
。
我现在拥有的是一个蓝色正方形,我从一系列点绘制并添加到包含红色球体的场景中。它基本上最终与球体上的一个点相切,但真正的目标是将它绘制在表面上。 SceneKit 中有什么可以让我这样做的吗?我是否需要做一些 math/geometry 以使其与球体形状相同或映射到球体的坐标?我想做的是在 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)
David Rönnqvist 的 SceneKit 书籍(作为 iBook 提供)有一个值得一看的示例(EarthView example,一个会说话的地球仪,第 5 章)。该示例构建了一个 3D 图钉,然后将其附加到地球表面的水龙头位置。
你的问题更复杂,因为你正在构建一个覆盖球体一部分的形状。您的 "square" 实际上是一个球形梯形,由四个大圆弧围成的球体的一部分。我可以看到三种可能的方法,具体取决于您最终要寻找的内容。
最简单的方法是使用图像作为球体表面的 material。这种方法在 Ronnqvist EarthView 示例中得到了很好的说明,该示例使用多个图像来显示地球表面。与其绘制大陆,不如绘制正方形。但是,这种方法不适合交互。看看SCNMaterial
.
另一种方法是使用命中测试结果。这记录在 SCNSceneRenderer
(SCNView
符合)和 SCNHitTest
上。使用命中测试结果,您可以拉出被敲击的面,然后拉出它的几何元素。但是,这不会让您一路回家,因为 SceneKit 使用三角形表示 SCNSphere
,而您正在寻找四边形。您还将仅限于与 SceneKit 的底层线框表示对齐的正方形。
如果您想完全控制 "square" 的绘制位置,包括改变其相对于赤道的角度,我认为您必须从头开始构建自己的几何图形。这意味着计算每个角点的 latitude/longitude,然后在这些点之间生成圆弧,然后沿着圆弧计算一堆中间点。您必须添加一个软糖因子,将中间点升高到略高于球体表面,并构建您自己的四边形或三角形条带。 类 这里是 SCNGeometry
、SCNGeometryElement
和 SCNGeometrySource
。
如果您想将 2D 内容映射到 3D SceneKit 对象的表面,并使 2D 内容成为 dynamic/interactive,最简单的解决方案之一是对 2D 内容使用 SpriteKit。您可以将球体的漫反射内容设置为该场景中的 SKScene
和 create/position/decorate SpriteKit 节点,以将它们排列在球体的表面上。
如果你想让这个内容响应点击事件...在你的 SceneKit 视图中使用 hitTest
可以得到一个 SCNHitTestResult
,你可以从中得到点击点的纹理坐标在球体上。您可以从纹理坐标转换为 SKScene
坐标和生成节点、运行 动作等。
有关更多详细信息,最好的选择可能是 Apple 的 SceneKitReel 示例代码项目。这是在 WWDC14 上为 iOS 引入 SceneKit 的演示。在该演示中有一个 "slide",其中油漆球从旋转的圆环上的相机飞出并在它们击中它的地方留下油漆飞溅 - 圆环有一个 SpriteKit 场景作为其 material,以及留下飞溅的技巧碰撞基本上与上面概述的命中测试 -> 纹理坐标 -> SpriteKit 坐标方法相同。
我希望能够使用 SceneKit
向球体表面添加形状。我从一个简单的例子开始,我只是想给球体表面的一部分涂上另一种颜色。我希望这是一个可以点击、选择等的对象...所以我的想法是使用自定义 SCNShape
几何对象添加形状 SCNNodes
。
我现在拥有的是一个蓝色正方形,我从一系列点绘制并添加到包含红色球体的场景中。它基本上最终与球体上的一个点相切,但真正的目标是将它绘制在表面上。 SceneKit 中有什么可以让我这样做的吗?我是否需要做一些 math/geometry 以使其与球体形状相同或映射到球体的坐标?我想做的是在 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)
David Rönnqvist 的 SceneKit 书籍(作为 iBook 提供)有一个值得一看的示例(EarthView example,一个会说话的地球仪,第 5 章)。该示例构建了一个 3D 图钉,然后将其附加到地球表面的水龙头位置。
你的问题更复杂,因为你正在构建一个覆盖球体一部分的形状。您的 "square" 实际上是一个球形梯形,由四个大圆弧围成的球体的一部分。我可以看到三种可能的方法,具体取决于您最终要寻找的内容。
最简单的方法是使用图像作为球体表面的 material。这种方法在 Ronnqvist EarthView 示例中得到了很好的说明,该示例使用多个图像来显示地球表面。与其绘制大陆,不如绘制正方形。但是,这种方法不适合交互。看看SCNMaterial
.
另一种方法是使用命中测试结果。这记录在 SCNSceneRenderer
(SCNView
符合)和 SCNHitTest
上。使用命中测试结果,您可以拉出被敲击的面,然后拉出它的几何元素。但是,这不会让您一路回家,因为 SceneKit 使用三角形表示 SCNSphere
,而您正在寻找四边形。您还将仅限于与 SceneKit 的底层线框表示对齐的正方形。
如果您想完全控制 "square" 的绘制位置,包括改变其相对于赤道的角度,我认为您必须从头开始构建自己的几何图形。这意味着计算每个角点的 latitude/longitude,然后在这些点之间生成圆弧,然后沿着圆弧计算一堆中间点。您必须添加一个软糖因子,将中间点升高到略高于球体表面,并构建您自己的四边形或三角形条带。 类 这里是 SCNGeometry
、SCNGeometryElement
和 SCNGeometrySource
。
如果您想将 2D 内容映射到 3D SceneKit 对象的表面,并使 2D 内容成为 dynamic/interactive,最简单的解决方案之一是对 2D 内容使用 SpriteKit。您可以将球体的漫反射内容设置为该场景中的 SKScene
和 create/position/decorate SpriteKit 节点,以将它们排列在球体的表面上。
如果你想让这个内容响应点击事件...在你的 SceneKit 视图中使用 hitTest
可以得到一个 SCNHitTestResult
,你可以从中得到点击点的纹理坐标在球体上。您可以从纹理坐标转换为 SKScene
坐标和生成节点、运行 动作等。
有关更多详细信息,最好的选择可能是 Apple 的 SceneKitReel 示例代码项目。这是在 WWDC14 上为 iOS 引入 SceneKit 的演示。在该演示中有一个 "slide",其中油漆球从旋转的圆环上的相机飞出并在它们击中它的地方留下油漆飞溅 - 圆环有一个 SpriteKit 场景作为其 material,以及留下飞溅的技巧碰撞基本上与上面概述的命中测试 -> 纹理坐标 -> SpriteKit 坐标方法相同。