adr*_*n O 3 animation scenekit swift metal
我是一名想要编写几何游戏的数学家.
我有几个网格的精确坐标和数学公式,我需要显示它们的单位法线.
我每个网格只需要一个纹理(彩色反光金属).
我需要让用户移动棋子,即通过简单的数学公式更改网格的坐标.
所以我不需要导入3D文件,而是我可以计算所有内容.
想象一种魔方.计算立方体坐标,并由用户旋转立方体.我的程序在Mathematica中运行.
我现在很困难,因为现在不眠不休,试图找到如何在SceneKit中显示计算网格 - 每个顶点和法线分别进行动画处理.
比如,一个具有计算坐标的单个三角形(而不是股票提供的形状)的任何工作示例,由SceneKit以可动画的坐标显示将非常受欢迎.
我看起来更多,似乎网格中的各个点可能无法在SceneKit中移动.我喜欢SceneKit(与OpenGL不同)的功能,即可以在用户的手指下获取对象.可以在项目中将OpenGL和SceneKit混合在一起吗?
我可以从那里接管......
通常,单独设置顶点位置是一个棘手的问题.但是有很好的方法可以在SceneKit中实现它.
GPU真的希望在开始渲染帧之前将顶点数据全部上传到一个块中.这意味着如果你不断计算CPU上的新顶点位置/法线/等,你就会遇到这样的问题,即每次只有部分数据发生变化时,所有数据都会转移到GPU上.
因为您已经在数学上描述了表面,所以您可以在GPU本身上完成这项工作.如果每个顶点位置是某个变量的函数,则可以在着色器中编写该函数,并找到一种方法来传递每个顶点的输入变量.
你可以看到几个选项:
着色器修饰符.从具有所需拓扑的虚拟几何体开始(顶点数量以及它们如何作为多边形连接).将输入变量作为额外纹理传递,并在着色器修改器代码中(对于几何入口点),查找纹理,执行功能,并使用结果设置顶点位置.
金属计算着色器.创建由Metal缓冲区支持的几何源,然后在渲染时,将根据您的函数将顶点数据写入该缓冲区的计算着色器排入队列.(链接上有部分内容的骨架代码.)
更新:从您的评论中听起来您可能处于更容易的位置.
如果你拥有的几何体是由相对于它们自身静止并相互移动的碎片组成的 - 就像魔方的立方体一样 - 在渲染时计算顶点是过度的.相反,您可以将几何体的静态部分上传到GPU一次,并使用变换将它们相对于彼此定位.
在SceneKit中执行此操作的方法是创建单独的节点,每个节点都有自己的(静态)几何体,然后设置节点变换(或位置/方向/比例)以相对于彼此移动节点.要一次移动多个节点,请使用节点层次结构 - 将其中几个节点作为另一个节点的子节点.如果某些人需要一起移动,而另一个子集需要稍后移动,则可以更改层次结构.
这是魔方概念的具体例子.首先,创建一些cubelets:
// 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]
}
}
}
Run Code Online (Sandbox Code Playgroud)
接下来,进行旋转的过程.这是一个特定的旋转,但您可以将其概括为一个函数,该函数对任何子集的子集进行任何转换:
// 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()
Run Code Online (Sandbox Code Playgroud)
神奇的部分在动画之后的清理中.cubelets以场景的根节点的子节点开始,我们暂时将它们重新父节点到另一个节点,以便我们可以将它们一起转换.在将它们再次作为根节点的子节点返回后,我们将每个节点设置transform为其本地worldTransform,以便它保持临时节点变换的效果.
然后,您可以重复此过程以捕获(新)世界空间位置集中的任何节点集,并使用另一个临时节点来转换这些节点.
我不太清楚Rubik的立方体就像你的问题一样,但听起来你可以概括出类似这样的解决方案.
| 归档时间: |
|
| 查看次数: |
1906 次 |
| 最近记录: |