基于SceneKit的swift数学移动网格

基于SceneKit的swift数学移动网格,swift,animation,scenekit,metal,Swift,Animation,Scenekit,Metal,我是一名数学家,想编写一个几何游戏 我有一些需要显示的网格的精确坐标和数学公式,以及它们的单位法线 我只需要一个纹理(彩色反射金属)每个网格 我需要让用户移动块,即改变网格的坐标,再次通过一个简单的数学公式 所以我不需要导入3D文件,而是可以计算所有内容 想象一种魔方。计算立方体坐标,并由用户旋转立方体。我的程序在Mathematica中运行 我有一个非常困难的时间,为失眠的日子现在,试图找到确切的如何显示一个计算网格在SceneKit-与每个顶点和法线分别动画 任何工作示例,例如,一个带有计算

我是一名数学家,想编写一个几何游戏

我有一些需要显示的网格的精确坐标和数学公式,以及它们的单位法线

我只需要一个纹理(彩色反射金属)每个网格

我需要让用户移动块,即改变网格的坐标,再次通过一个简单的数学公式

所以我不需要导入3D文件,而是可以计算所有内容

想象一种魔方。计算立方体坐标,并由用户旋转立方体。我的程序在Mathematica中运行

我有一个非常困难的时间,为失眠的日子现在,试图找到确切的如何显示一个计算网格在SceneKit-与每个顶点和法线分别动画

任何工作示例,例如,一个带有计算坐标(而不是库存提供的形状)的单个三角形,由SceneKit以动画坐标显示,都将非常受欢迎

我看了更多,似乎网格的各个点在SceneKit中可能无法移动。我喜欢SceneKit(与OpenGL不同)的特性,即用户可以在手指下获取对象。可以在项目中混合使用OpenGL和SceneKit吗


我可以从那里接手……

通常,为顶点位置单独设置动画是一个棘手的问题。但是在SceneKit中有很好的方法来处理它

GPU确实希望在开始渲染帧之前将所有顶点数据上传到一个块中。这意味着,如果您在CPU上不断计算新的顶点位置/法线/等,那么每次即使只是部分数据发生变化,您都会遇到将所有数据传送到GPU的问题

因为你已经在用数学方法描述你的曲面了,你就可以在GPU上完成这项工作了。如果每个顶点位置都是某个变量的函数,则可以在着色器中写入该函数,并找到每个顶点传递输入变量的方法

您可以考虑以下几个选项:

  • 着色器修改器。从具有所需拓扑的虚拟几何体开始(顶点的数量以及它们作为多边形的连接方式)。将输入变量作为额外纹理传递,并在着色器修改器代码(用于几何体入口点)中查找纹理,执行函数,然后使用结果设置顶点位置

  • 金属计算着色器。创建一个几何体源,然后在渲染时将计算着色器排队,该着色器根据您的函数将顶点数据写入该缓冲区。(链接处有部分代码的骨架。)


  • 更新:从你的评论看来,你可能处于一个更容易的位置

    如果您拥有的是由相对自身静止且相对彼此移动的片段组成的几何体(如魔方立方体的立方体),则在渲染时计算顶点是多余的。相反,您可以将几何体的静态部分上载到GPU一次,然后使用变换将它们相对定位

    在SceneKit中执行此操作的方法是创建单独的节点,每个节点都有各自的(静态)几何体,然后设置节点变换(或位置/方向/比例)以相对移动节点。若要同时移动多个节点,请使用“节点层次结构”-使其中几个节点成为另一个节点的子节点。如果某个子集在某一时刻需要一起移动,而另一个子集稍后需要一起移动,则可以更改层次结构

    这里有一个魔方概念的具体例子。首先,创建一些立方体:

    // convenience for creating solid color materials
    func materialWithColor(color: NSColor) -> SCNMaterial {
        let mat = SCNMaterial()
        mat.diffuse.contents = color
        mat.specular.contents = NSColor.whiteColor()
        return mat
    }
    
    // create and arrange a 3x3x3 array of cubelets
    var cubelets: [SCNNode] = []
    for x in -1...1 {
        for y in -1...1 {
            for z in -1...1 {
                let box = SCNBox()
                box.chamferRadius = 0.1
                box.materials = [
                    materialWithColor(NSColor.greenColor()),
                    materialWithColor(NSColor.redColor()),
                    materialWithColor(NSColor.blueColor()),
                    materialWithColor(NSColor.orangeColor()),
                    materialWithColor(NSColor.whiteColor()),
                    materialWithColor(NSColor.yellowColor()),
                ]
                let node = SCNNode(geometry: box)
                node.position = SCNVector3(x: CGFloat(x), y: CGFloat(y), z: CGFloat(z))
                scene.rootNode.addChildNode(node)
                cubelets += [node]
            }
        }
    }
    
    // create a temporary node for the rotation
    let rotateNode = SCNNode()
    scene.rootNode.addChildNode(rotateNode)
    
    // grab the set of cubelets whose position is along the right face of the puzzle,
    // and add them to the rotation node
    let rightCubelets = cubelets.filter { node in 
        return abs(node.position.x - 1) < 0.001 
    }
    rightCubelets.map { rotateNode.addChildNode($0) }
    
    // animate a rotation
    SCNTransaction.begin()
    SCNTransaction.setAnimationDuration(2)
    rotateNode.eulerAngles.x += CGFloat(M_PI_2)
    SCNTransaction.setCompletionBlock {
        // after animating, remove the cubelets from the rotation node,
        // and re-add them to the parent node with their transforms altered
        rotateNode.enumerateChildNodesUsingBlock { cubelet, _ in
            cubelet.transform = cubelet.worldTransform
            cubelet.removeFromParentNode()
            scene.rootNode.addChildNode(cubelet)
        }
        rotateNode.removeFromParentNode()
    }
    SCNTransaction.commit()
    
    接下来,进行旋转的过程。这是一个特定的旋转,但您可以将其推广到对立方体的任何子集进行任何变换的函数:

    // convenience for creating solid color materials
    func materialWithColor(color: NSColor) -> SCNMaterial {
        let mat = SCNMaterial()
        mat.diffuse.contents = color
        mat.specular.contents = NSColor.whiteColor()
        return mat
    }
    
    // create and arrange a 3x3x3 array of cubelets
    var cubelets: [SCNNode] = []
    for x in -1...1 {
        for y in -1...1 {
            for z in -1...1 {
                let box = SCNBox()
                box.chamferRadius = 0.1
                box.materials = [
                    materialWithColor(NSColor.greenColor()),
                    materialWithColor(NSColor.redColor()),
                    materialWithColor(NSColor.blueColor()),
                    materialWithColor(NSColor.orangeColor()),
                    materialWithColor(NSColor.whiteColor()),
                    materialWithColor(NSColor.yellowColor()),
                ]
                let node = SCNNode(geometry: box)
                node.position = SCNVector3(x: CGFloat(x), y: CGFloat(y), z: CGFloat(z))
                scene.rootNode.addChildNode(node)
                cubelets += [node]
            }
        }
    }
    
    // create a temporary node for the rotation
    let rotateNode = SCNNode()
    scene.rootNode.addChildNode(rotateNode)
    
    // grab the set of cubelets whose position is along the right face of the puzzle,
    // and add them to the rotation node
    let rightCubelets = cubelets.filter { node in 
        return abs(node.position.x - 1) < 0.001 
    }
    rightCubelets.map { rotateNode.addChildNode($0) }
    
    // animate a rotation
    SCNTransaction.begin()
    SCNTransaction.setAnimationDuration(2)
    rotateNode.eulerAngles.x += CGFloat(M_PI_2)
    SCNTransaction.setCompletionBlock {
        // after animating, remove the cubelets from the rotation node,
        // and re-add them to the parent node with their transforms altered
        rotateNode.enumerateChildNodesUsingBlock { cubelet, _ in
            cubelet.transform = cubelet.worldTransform
            cubelet.removeFromParentNode()
            scene.rootNode.addChildNode(cubelet)
        }
        rotateNode.removeFromParentNode()
    }
    SCNTransaction.commit()
    
    //为旋转创建临时节点
    设rotateNode=SCNNode()
    scene.rootNode.addChildNode(rotateNode)
    //抓取位置沿拼图右侧的一组立方体,
    //并将它们添加到旋转节点
    让rightCubelets=cubelets.filter{node in
    返回abs(节点位置x-1)<0.001
    }
    rightCubelets.map{rotateNode.addChildNode($0)}
    //为旋转设置动画
    SCNTransaction.begin()
    SCNTransaction.setAnimationDuration(2)
    rotateNode.eulerAngles.x+=CGFloat(M_PI_2)
    SCNTransaction.setCompletionBlock{
    //设置动画后,从旋转节点移除立方体,
    //并将其重新添加到父节点,同时更改其变换
    rotateNode.EnumerateChildNodes使用块{cubelet,uu
    cubelet.transform=cubelet.worldTransform
    cubelet.removeFromParentNode()
    scene.rootNode.addChildNode(cubelet)
    }
    rotateNode.removeFromParentNode()
    }
    SCNTransaction.commit()
    
    动画结束后,神奇的部分在清理中。立方体开始时是场景根节点的子节点,我们暂时将它们重新设置为另一个节点的父节点,以便将它们一起变换。在将它们再次返回为根节点的子节点时,我们将每个节点的本地
    变换设置为其
    世界变换
    ,以便保持临时节点变换变化的效果

    然后,可以重复此过程以获取(新)世界空间位置集中的任何节点集,并使用另一个临时节点来变换这些节点


    我不太确定你的问题有多像魔方,但听起来你可能可以从这样的东西中归纳出一个解决方案。

    看看(有“geometryWithSources:”方法,我相信这正是你想要的)谢谢你,Kametrixom。在SceneKit中,网格各个顶点的位置和法线是否不能设置动画?如果是这样的话,我们可以将SceneKit与OpenGL结合起来制作各个顶点的动画吗?我不知道,但是如果你真的需要更多地控制到底发生了什么,我建议使用Metal。我