如果这不是混合多个世界的话,这不会太难:算法世界、C# 世界和计算着色器世界(在 Unity 中)。
编辑:这有些相关。我的情况更糟糕,因为每个节点不仅有一个值,还有一个值列表。
================
这是我在 C# 中的结构(为了解释而简化):
class Function {
public int type; // 0 or 1
public int param1;
public int param2;
}
class Node {
public string Id;
public List<Function> Functions;
public List<Node> Children;
}
一旦加载,你可以像这样想象它,如果它是 json 风格渲染的:
{
"id": "node0",
"functions": [
{ "type": 0, "param1": 5, "param2" : 6},
{ "type": 1, "param1": 7, "param2" : 8}
],
"children": [
{
"id": "node00",
"functions": [
{ "type": 0, "param1": 9, "param2" : 10},
{ "type": 1, "param1": 11, "param2" : 12}
],
"children": [
{
"id" : "node000",
"functions": [
{ "type": 0, "param1": 13, "param2" : 14},
{ "type": 1, "param1": 15, "param2" : 16},
],
"children": [] //this one's a leaf
}
]
},
{
"id": "node01",
"functions": [{ "type": 0, "param1": 15, "param2" : 16}],
"children": [] // also a leaf
}
]
}
我想使用计算着色器来计算每个节点的“该”值(不仅仅是叶子!) 每个节点的值是这样计算的:
例如,node000 将计算如下:
int result0 = computeFunction(type: 0, param1: 13, param2: 14);
int result1 = computeFunction(type: 1, param1: 15, param2: 16);
int node000Sum = result0 + result1 // there can be as many terms as there are functions in the node.
// (not detailed) : repeat with parent, node00
// (not detailed) : repeat with grandparent, node0, which is also root.
int finalResult = node000Sum + node00Sum + node0Sum
我们不只是想要叶子的最终总和。我们还将在需要的地方使用node00Sum和node0Sum(换句话说:每个节点的计算结果都在某个地方使用)。
====================
这就是设置。现在我的问题是:我无法想象如何组织该数据以使其由计算着色器有效地处理。
我认为最简单的第一步是将all节点的函数存储在一个大平面数组中(伪json): 所有功能:[ {“类型”:0,“参数1”:5,“参数2”:6}, {“类型”:1,“参数1”:7,“参数2”:8}, {“类型”:0,“参数1”:9,“参数2”:10}, {“类型”:1,“参数1”:11,“参数2”:12}, {“类型”:0,“参数1”:13,“参数2”:14}, {“类型”:1,“参数1”:15,“参数2”:16}, {“类型”:0,“参数1”:15,“参数2”:16} ] ...然后让着色器计算每个函数(并且仅计算它)。
...然后(也许)有第二个着色器通道来进行求和...(否则,如果太复杂,则在 C# 中进行)。 ...然后将这些总和与它们所属的原始节点相匹配。
但无论我尝试什么,数据结构很快就会变得令人头疼。创建所有函数的数组并计算它们很容易,但求和并将它们匹配回节点却很困难。为了提高效率,我必须使用数组(着色器不处理链接列表),但我还需要以某种方式“记住”树结构。结果,我很快就陷入了一些穷人的链表(和整个树)中,试图处理数组中项目的索引。
即使我做到了,也很难将总和与原始节点相匹配。
你将如何实现这一目标? (即一般方法需要改变什么)?文学中有类似的问题吗?(我打赌有,但我不想需要博士学位才能理解它。)
struct Node
{
public int parentNodeId
public int[] functionIds
}
您还将把 id 存储到函数中(它看起来像函数的参数,而不是函数本身)。每个节点的 functionId 数组的大小必须是恒定的(如果您不想创建更多间接,则无法动态表示它)。
现在算法应该如何工作了。您将有一个带有节点的平面数组,一个带有函数的平面数组(它们的数据?),一个带有 int 的平面数组(每个深度的节点数),以及某种结果数组。您需要计算每个节点的总和。数组中的节点应按深度排序,父节点在开头,叶子在末尾。您还将有另一个数组,其中包含每个深度的节点数(从根到叶排序,就像节点数组本身一样)。之后,您可以启动计算机着色器。
我们从数组中取出索引为 0 的元素,其中包含深度节点数(最有可能有一个,即根)- N0,计算带有节点的数组中 N0 个元素的结果(我们将这些结果写入一些结果数组),取索引1处的元素(深度的下一层),从从N0开始的节点数组中取出N1个元素,计算它们自己的函数值,添加父级的值(节点中的父级索引),写入结果到我们某种数组,取索引 2 处的元素...
在每个深度/层执行结束时,您需要调用“barrier”函数(Unity中很可能有一个,或其类似物;一般来说,这在GLSL中使用),以便执行对于该层的所有元素,对于尚未计算父母的孩子,该函数不会开始执行。
很抱歉,答案和描述太抽象了