gpt4 book ai didi

swift - 使用 SceneKit 快速移动数学网格

转载 作者:搜寻专家 更新时间:2023-10-31 22:07:59 25 4
gpt4 key购买 nike

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

我有一些我需要显示的网格及其单位法线的确切坐标和数学公式。

每个网格我只需要一个纹理(彩色反光 Metal )。

我需要让用户移动棋子,即通过简单的数学公式再次更改网格的坐标。

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

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

我正经历一段非常艰难的时期,现在已经失眠了好几天,我试图找到如何在 SceneKit 中准确显示计算的网格 - 每个顶点和法线分别设置动画。

任何工作示例,例如,具有计算坐标(而不是股票提供的形状)的单个三角形,由 SceneKit 以可动画坐标显示,我们将非常感激。

我看多了,似乎网格的个别点在SceneKit中可能是不可移动的。我喜欢 SceneKit(与 OpenGL 不同)的一项功能,即可以在用户的​​手指下获取对象。可以在项目中混合使用 OpenGL 和 SceneKit 吗?

我可以从那里接手....

最佳答案

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

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

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

为此,您可以考虑几个选项:

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

  2. Metal 计算着色器。创建几何源 backed by a Metal buffer ,然后在渲染时,根据您的函数将顶点数据写入该缓冲区的计算着色器入队。 (链接中有部分框架代码。)


更新:从您的评论来看,您可能处于更轻松的位置。

如果您拥有的是由相对于自身静止但相对于彼此移动的部分组成的几何体——就像魔方的小立方体——在渲染时计算顶点是多余的。相反,您可以将几何体的静态部分一次性上传到 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()

神奇的部分在于动画之后的清理。立方体从场景根节点的子节点开始,我们暂时将它们重新设置为另一个节点的父节点,以便我们可以一起转换它们。再次将它们返回为根节点的子节点后,我们将每个本地的 transform 设置为它的 worldTransform,以便它保持临时节点的转换变化的效果。

然后您可以重复此过程以获取一组(新的)世界空间位置中的任何一组节点,并使用另一个临时节点来转换它们。

我不太确定你的问题有多像 Rubik's-cube,但听起来你可能可以从类似的东西中概括出一个解决方案。

关于swift - 使用 SceneKit 快速移动数学网格,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/31082361/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com