SceneKit – 如何使用材质访问节点的变换?
SceneKit – How can I use materials to access node's transform?
我有一个免费的 3d 头骨,就像这张照片和下面的代码:
import UIKit
import SceneKit
class GameViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
let scene = SCNScene(named: "skull.scn")!
let cameraNode = SCNNode()
cameraNode.camera = SCNCamera()
scene.rootNode.addChildNode(cameraNode)
cameraNode.position = SCNVector3(x: 0, y: 0, z: 15)
let lightNode = SCNNode()
lightNode.light = SCNLight()
lightNode.light!.type = .omni
lightNode.position = SCNVector3(x: 0, y: 10, z: 10)
scene.rootNode.addChildNode(lightNode)
let ambientLightNode = SCNNode()
ambientLightNode.light = SCNLight()
ambientLightNode.light!.type = .ambient
ambientLightNode.light!.color = UIColor.darkGray
scene.rootNode.addChildNode(ambientLightNode)
let scnView = self.view as! SCNView
scnView.scene = scene
scnView.allowsCameraControl = true
scnView.backgroundColor = UIColor.black
let button = UIButton(frame: CGRect(x: 100, y: 600, width: 200, height: 60))
button.setTitle("Open Jaw", for: .normal)
button.setTitleColor(.systemBlue, for: .normal)
button.addTarget(self, action: #selector(buttonAction), for: .touchUpInside)
self.view.addSubview(button)
}
@objc func buttonAction() {
print("Button pressed")
// How can I open jaw here?
}
}
正如你在代码中看到的,我想通过按钮打开下巴,因为我有所有的材料在文件中,我怎么能用 Dflt2 来做到这一点,我想知道如何访问 Dflt2 以进行旋转,因为打开 Jaw 时动画流畅甚至更改颜色,这可能吗?
Node-centric 行为
与 Autodesk Maya 等专业应用程序不同,通过 material 您可以 select 分配给 material 的几何体,SceneKit 不允许您这样做.然而,SceneKit 和 Maya 一样,具有节点层次场景结构...
SCNView –> SCNScene -> RootNode
| -> Node_01
| | -> Subnode_A -> Geometry -> Materials
| | -> Subnode_B -> Geometry -> Materials
|
| -> Node_02 -> Geometry -> Materials
|
| -> Node_03 -> Geometry -> Materials
|
| -> Node_04
| | -> Subnode_A
| | -> Subnode_B -> Camera
|
| -> Node_05 -> SpotLight
|
| -> Node_06 -> ParticleSystem
|
| -> Node_07 -> AudioPlayer
...您只能通过 select 特定节点内的特定几何形状才能获得所需的 material。这种方法称为 node-centric,默认情况下没有 material-centric 方法。上面的抽象图向我们展示了场景中节点的连接,其中每个节点都有自己的变换参数(平移、旋转和缩放)。
因此,您可以只在检索节点时做您想做的事情:
import SceneKit
class GameViewController: UIViewController {
@IBOutlet var sceneView: SCNView!
let scene = SCNScene(named: "art.scnassets/Skull.usdz")!
var lowerJaw: SCNNode? = nil
var counter: Int = 0
override func viewDidLoad() {
super.viewDidLoad()
sceneView.scene = self.scene
sceneView.allowsCameraControl = true
sceneView.autoenablesDefaultLighting = true
sceneView.backgroundColor = .black
guard let skullNode = self.scene.rootNode.childNode(
withName: "Geom", recursively: true)
else { return }
self.lowerJaw = skullNode.childNodes[0].childNode(
withName: "lower_part", recursively: true)
lowerJaw?.childNodes[0].geometry?.firstMaterial?.diffuse.contents =
UIColor.systemBlue
}
@IBAction func pressed(_ sender: UIButton) {
self.counter += 1
let opened = SCNAction.rotateTo(x: .pi/20, y: 0, z: 0, duration: 0.3)
let closed = SCNAction.rotateTo(x: 0, y: 0, z: 0, duration: 0.3)
if (counter % 2 == 0) {
self.lowerJaw?.runAction(closed)
} else {
self.lowerJaw?.runAction(opened)
}
}
}
如您所见,在 Xcode Scene graph
中,"Geom"
节点包含头骨的所有部分,以及 "lower_part"
子节点,我们'重新旋转,包含头骨的下颌和下牙。因此,关于 3D 对象最重要的事情 – 您需要在场景中具有单独的 3D 几何部件(节点)以便为它们设置动画(不是 mono-model)。 "lower_part"
节点围绕其枢轴点旋转。每个 SceneKit 的节点都有自己的个人枢轴。如果您需要将枢轴点移动到另一个位置,请使用 .
在 SceneKit 中,您可以使用 .usdz
、.scn
、.dae
或 .obj
场景。 Xcode 的 Scene graph
允许您创建、删除、重新定位、嵌套和重命名节点。然而,构建节点层次结构的最稳健的地方是 3D 创作应用程序(如 Maya 或 Blender)。
Material-centric接近
但是,没有什么能阻止您创建像 [SCNMaterial: SCNNode] 这样的字典集合。
抽象示例(2 个球体,2 materials):
import SceneKit
let sceneView = SCNView(frame: .zero)
sceneView.scene = SCNScene()
let redMaterial = SCNMaterial()
redMaterial.diffuse.contents = UIColor.red
let greenMaterial = SCNMaterial()
redMaterial.diffuse.contents = UIColor.green
let nodeA = SCNNode(geometry: SCNSphere(radius: 0.1))
nodeA.position.z = -2.5
nodeA.geometry?.materials[0] = redMaterial
let nodeB = SCNNode(geometry: SCNSphere(radius: 0.1))
nodeB.position.y = 1.73
nodeB.geometry?.materials[0] = greenMaterial
sceneView.scene?.rootNode.addChildNode(nodeA)
sceneView.scene?.rootNode.addChildNode(nodeB)
// Dictionary
var getNode = [redMaterial: nodeA, greenMaterial: nodeB]
getNode[redMaterial]?.position.z // -2.5
getNode[greenMaterial]?.position.y // 1.73
我有一个免费的 3d 头骨,就像这张照片和下面的代码:
import UIKit
import SceneKit
class GameViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
let scene = SCNScene(named: "skull.scn")!
let cameraNode = SCNNode()
cameraNode.camera = SCNCamera()
scene.rootNode.addChildNode(cameraNode)
cameraNode.position = SCNVector3(x: 0, y: 0, z: 15)
let lightNode = SCNNode()
lightNode.light = SCNLight()
lightNode.light!.type = .omni
lightNode.position = SCNVector3(x: 0, y: 10, z: 10)
scene.rootNode.addChildNode(lightNode)
let ambientLightNode = SCNNode()
ambientLightNode.light = SCNLight()
ambientLightNode.light!.type = .ambient
ambientLightNode.light!.color = UIColor.darkGray
scene.rootNode.addChildNode(ambientLightNode)
let scnView = self.view as! SCNView
scnView.scene = scene
scnView.allowsCameraControl = true
scnView.backgroundColor = UIColor.black
let button = UIButton(frame: CGRect(x: 100, y: 600, width: 200, height: 60))
button.setTitle("Open Jaw", for: .normal)
button.setTitleColor(.systemBlue, for: .normal)
button.addTarget(self, action: #selector(buttonAction), for: .touchUpInside)
self.view.addSubview(button)
}
@objc func buttonAction() {
print("Button pressed")
// How can I open jaw here?
}
}
正如你在代码中看到的,我想通过按钮打开下巴,因为我有所有的材料在文件中,我怎么能用 Dflt2 来做到这一点,我想知道如何访问 Dflt2 以进行旋转,因为打开 Jaw 时动画流畅甚至更改颜色,这可能吗?
Node-centric 行为
与 Autodesk Maya 等专业应用程序不同,通过 material 您可以 select 分配给 material 的几何体,SceneKit 不允许您这样做.然而,SceneKit 和 Maya 一样,具有节点层次场景结构...
SCNView –> SCNScene -> RootNode
| -> Node_01
| | -> Subnode_A -> Geometry -> Materials
| | -> Subnode_B -> Geometry -> Materials
|
| -> Node_02 -> Geometry -> Materials
|
| -> Node_03 -> Geometry -> Materials
|
| -> Node_04
| | -> Subnode_A
| | -> Subnode_B -> Camera
|
| -> Node_05 -> SpotLight
|
| -> Node_06 -> ParticleSystem
|
| -> Node_07 -> AudioPlayer
...您只能通过 select 特定节点内的特定几何形状才能获得所需的 material。这种方法称为 node-centric,默认情况下没有 material-centric 方法。上面的抽象图向我们展示了场景中节点的连接,其中每个节点都有自己的变换参数(平移、旋转和缩放)。
因此,您可以只在检索节点时做您想做的事情:
import SceneKit
class GameViewController: UIViewController {
@IBOutlet var sceneView: SCNView!
let scene = SCNScene(named: "art.scnassets/Skull.usdz")!
var lowerJaw: SCNNode? = nil
var counter: Int = 0
override func viewDidLoad() {
super.viewDidLoad()
sceneView.scene = self.scene
sceneView.allowsCameraControl = true
sceneView.autoenablesDefaultLighting = true
sceneView.backgroundColor = .black
guard let skullNode = self.scene.rootNode.childNode(
withName: "Geom", recursively: true)
else { return }
self.lowerJaw = skullNode.childNodes[0].childNode(
withName: "lower_part", recursively: true)
lowerJaw?.childNodes[0].geometry?.firstMaterial?.diffuse.contents =
UIColor.systemBlue
}
@IBAction func pressed(_ sender: UIButton) {
self.counter += 1
let opened = SCNAction.rotateTo(x: .pi/20, y: 0, z: 0, duration: 0.3)
let closed = SCNAction.rotateTo(x: 0, y: 0, z: 0, duration: 0.3)
if (counter % 2 == 0) {
self.lowerJaw?.runAction(closed)
} else {
self.lowerJaw?.runAction(opened)
}
}
}
如您所见,在 Xcode Scene graph
中,"Geom"
节点包含头骨的所有部分,以及 "lower_part"
子节点,我们'重新旋转,包含头骨的下颌和下牙。因此,关于 3D 对象最重要的事情 – 您需要在场景中具有单独的 3D 几何部件(节点)以便为它们设置动画(不是 mono-model)。 "lower_part"
节点围绕其枢轴点旋转。每个 SceneKit 的节点都有自己的个人枢轴。如果您需要将枢轴点移动到另一个位置,请使用
在 SceneKit 中,您可以使用 .usdz
、.scn
、.dae
或 .obj
场景。 Xcode 的 Scene graph
允许您创建、删除、重新定位、嵌套和重命名节点。然而,构建节点层次结构的最稳健的地方是 3D 创作应用程序(如 Maya 或 Blender)。
Material-centric接近
但是,没有什么能阻止您创建像 [SCNMaterial: SCNNode] 这样的字典集合。
抽象示例(2 个球体,2 materials):
import SceneKit
let sceneView = SCNView(frame: .zero)
sceneView.scene = SCNScene()
let redMaterial = SCNMaterial()
redMaterial.diffuse.contents = UIColor.red
let greenMaterial = SCNMaterial()
redMaterial.diffuse.contents = UIColor.green
let nodeA = SCNNode(geometry: SCNSphere(radius: 0.1))
nodeA.position.z = -2.5
nodeA.geometry?.materials[0] = redMaterial
let nodeB = SCNNode(geometry: SCNSphere(radius: 0.1))
nodeB.position.y = 1.73
nodeB.geometry?.materials[0] = greenMaterial
sceneView.scene?.rootNode.addChildNode(nodeA)
sceneView.scene?.rootNode.addChildNode(nodeB)
// Dictionary
var getNode = [redMaterial: nodeA, greenMaterial: nodeB]
getNode[redMaterial]?.position.z // -2.5
getNode[greenMaterial]?.position.y // 1.73