如何在Scenekit中创建3D网格

3

我该如何制作一个由3D物体(盒子)组成的网格。我已经知道如何设置SCNScene并创建对象,但我不知道如何制作布局。这个网格应该看起来像这样,在3D空间中放置3D物体。

这是我尝试过的:

           convenience init(create: Bool) {
        self.init()

        let geometry = SCNBox(width: 0.8 , height: 0.8,
                              length: 0.1, chamferRadius: 0.005)
        geometry.firstMaterial?.diffuse.contents = UIColor.red
        geometry.firstMaterial?.specular.contents = UIColor.white
        geometry.firstMaterial?.emission.contents = UIColor.blue
        let offset: Int = 10

        for xIndex:Int in 0...2 {
            for yIndex:Int in 0...2 {
                // create a geometry copy
                let geoCopy = geometry.copy() as! SCNGeometry

                var images:[UIImage]=[]
                for i in 1...5 {
                    if let img = UIImage(named: "\(i)"){
                        images.append(img)
                        let material = SCNMaterial()
                        material.diffuse.contents = img
                        geoCopy.firstMaterial = material

                    }
                }

                let boxnode = SCNNode(geometry: geoCopy)
                let boxCopy = boxnode.copy() as! SCNNode
                boxCopy.position.x = Float(xIndex - offset)
                boxCopy.position.y = Float(yIndex - offset)
                self.rootNode.addChildNode(boxCopy)
            }
        }
    }

但是我只看到一个盒子。

谢谢!

我的图像:

enter image description here

2个回答

2
你需要创建一个几何体,一个盒子节点,然后复制该盒子节点。当你有带有子节点的节点时,使用clone;当你想合并整个子树中的几何体/材料时,请使用flattenedClone。在你的情况下,copy应该足够了。只需更改所复制节点的位置即可。
游戏场景
import Foundation
import SceneKit

class GameScene: SCNScene {

    override init() {
        super.init()

        let geometry = SCNBox(width: 0.6 , height: 0.6,
                               length: 0.1, chamferRadius: 0.005)
        geometry.firstMaterial?.diffuse.contents = UIColor.red
        geometry.firstMaterial?.specular.contents = UIColor.white
        geometry.firstMaterial?.emission.contents = UIColor.blue
        let boxnode = SCNNode(geometry: geometry)
        let offset: Int = 16

        for xIndex:Int in 0...32 {
            for yIndex:Int in 0...32 {
                let boxCopy = boxnode.copy() as! SCNNode
                boxCopy.position.x = Float(xIndex - offset)
                boxCopy.position.y = Float(yIndex - offset)
                self.rootNode.addChildNode(boxCopy)
            }
        }
    }

    required init?(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)
    }
}

在您的视图控制器中,viewDidLoad方法:
override func viewDidLoad() {
        super.viewDidLoad()

        // create a new scene
        let scene = GameScene()

        // retrieve the SCNView
        let scnView = self.view as! SCNView

        // set the scene to the view
        scnView.scene = scene
        scnView.pointOfView?.position = SCNVector3Make(0, 0, 100)

        // allows the user to manipulate the camera
        scnView.allowsCameraControl = true

        // show statistics such as fps and timing information
        scnView.showsStatistics = true

        // configure the view
         scnView.backgroundColor = UIColor.white

    }

请注意,我刚刚将相机视角向后推到+Z轴上,以便更好地查看您的网格。
网格截图
如果您想为每个几何体分配新材料,则需要创建几何体的副本并将新材料分配给该几何体的副本。请参阅下面的代码,它会随机为每个漫反射属性分配一个UIImage,从名为1.png到8.png的七个图像集中选择。
import Foundation
import SceneKit

class GameScene: SCNScene {

    override init() {
        super.init()

        let geometry = SCNBox(width: 6 , height: 6,
                              length: 6, chamferRadius: 0.5)

        for xIndex:Int in stride(from: 0, to: 32, by:8) {
            for yIndex:Int in stride(from: 0, to: 32, by: 8) {
                // create a geometry copy
                let geoCopy = geometry.copy() as! SCNGeometry

                // create a random material
                let r = arc4random_uniform(7) + 1
                let img = UIImage(named: "\(r).png")
                let mat = SCNMaterial()
                mat.diffuse.contents = img
                geoCopy.firstMaterial = mat

                // create a copy node with new material and geo copy
                let boxnode = SCNNode(geometry: geoCopy)
                let boxCopy = boxnode.copy() as! SCNNode
                boxCopy.position.x = Float(xIndex - offset)
                boxCopy.position.y = Float(yIndex - offset)
                self.rootNode.addChildNode(boxCopy)
            }
        }
    }

    required init?(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)
    }
}

截图

在此输入图片描述


谢谢!但我还有一个问题。我尝试在每个几何体上添加SCNMaterial(),我该如何操作?每个节点的材质都不同,我只有15个节点。谢谢。我刚刚更新了我的问题。 - John
材质属性的内容可以是颜色、图像甚至动画内容。因此,设置 mat.diffuse.contents = UIImage(named:"some.png") 也可以工作。您可以为每个节点分配单独的图像,就像我们使用颜色一样。希望这可以帮助您。请参阅:https://developer.apple.com/reference/scenekit/scnmaterialproperty/1395372-contents - 3d-indiana-jones
当我使用“let r = arc4random_uniform(7) + 1”时,它是有效的,但在For循环中它不起作用。你能否最后一次尝试使用For循环吗?只有最后一次。谢谢。 - John
我尝试了你的代码,正如我所怀疑的那样,总是应用图像编号5。你的代码也清楚地表明,你从1到5循环,由于所有的1.png到5.png都存在,因此每个节点都应用了5.png。基本上,你正在使用图像1.png到5.png覆盖每个节点的材质,最终总是以5.png结束。你需要修复你的代码,使得每个节点都获得不同的图像。问题不在UIImage或其他任何东西上,而是在应用图像到材质的逻辑上。 - 3d-indiana-jones
如何在SceneKit中创建类似于附图的3D地板?https://blog.pusher.com/wp-content/uploads/2017/08/building-ar-with-arkit-and-scenekit-10-blender-cube.png - madhuiOS
显示剩余5条评论

0
你需要把语句 -let boxnode = SCNNode(geometry: self.geometry) - 放在循环内部。如果你希望使用相同的材质,可以为所有节点使用相同的几何体(只需将几何体存储在一个变量中并赋值)。否则,如果你希望有不同的材质,就复制几何体并每次分配不同的材质。

谢谢@Karl Sigiscar,我刚刚更新了我的问题。但是它还是不起作用。我仍然只看到一个框。你能帮我吗? - John
1
您还需要将addChildNode部分的代码移到循环内。在迭代过程中创建的每个新节点都必须添加到场景中。 - Karl Sigiscar

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