以下是在 C# 中定义对象的方式,以便 Unity 能够使用它:
(请注意:我留下了所有字段——甚至是这个问题中未使用的字段——以防问题出现在与我正在搜索的地方看似无关的地方)
[StructLayout(LayoutKind.Explicit)]
public struct CSOrbitFunction
{
[FieldOffset(0)]
public OrbitTypes type; // sizeof(enum-int) == 4
#region IOffsetOrbitFunction Members
[FieldOffset(4)]
public float offsetX;
[FieldOffset(8)] // sizeof(float) == 4
public float offsetY;
[FieldOffset(12)]
public float offsetZ;
#endregion
#region output
[FieldOffset(16)]
public float outX;
[FieldOffset(20)] // sizeof(float) == 4
public float outY;
[FieldOffset(24)]
public float outZ;
#endregion
#region ILagueKeplerOrbitFunction Members
[FieldOffset(28)]
public double periapsis;
[FieldOffset(36)] // sizeof(double) == 8
public double apoapsis;
[FieldOffset(44)]
public int durationSeconds;
#endregion
[FieldOffset(48)]
public int previous; // linked list
[FieldOffset(52)]
public int id;
public static int SizeInBytes = 56;
}
值以完全标准的方式传递给着色器:
CSOrbitFunction[] functions = new CSOrbitFunction[1];
var f = new CSOrbitFunction();
f.periapsis = 10.0;
f.apoapsis = 40.0;
f.durationSeconds = 10;
functions[0] = f;
int sizeInBytes = CSOrbitFunction.SizeInBytes;
ComputeBuffer b = new ComputeBuffer(functions.Length, sizeInBytes);
b.SetData(functions);
ComputeShader shader = new ComputeShader();
shader.SetBuffer(0, "functions", computeBuffer);
着色器是这样“调用”的(仍然很标准)
var timeMs_ID = Shader.PropertyToID("timeMs");
shader.SetInt(timeMs_ID, (int)timeMs);
// Run the shader
shader.Dispatch(0, functions.Length, 1, 1);
// Read result (overwrite input)
computeBuffer.GetData(functions);
这是着色器本身:
// Each #kernel tells which function to compile; you can have many kernels
#pragma kernel CSMain
// Must match exactly CSOrbitFunction.cs
struct CSOrbitFunction
{
int type; // from enum OrbitTypes
// shared by all types
float offsetX;
float offsetY;
float offsetZ;
// store result
float outX;
float outY;
float outZ;
// if the type is LAGUE_KEPLER
double periapsis;
double apoapsis;
int durationSeconds;
// linked list
int previous;
int id;
};
RWStructuredBuffer<CSOrbitFunction> functions;
int timeMs;
void foobar(uint3 id : SV_DispatchThreadID);
[numthreads(1024,1,1)] // dimensions
void CSMain (uint3 id : SV_DispatchThreadID)
{
foobar(id);
}
void foobar(uint3 id : SV_DispatchThreadID)
{
functions[id.x].outX = (float)functions[id.x].periapsis;
functions[id.x].outY = (float)functions[id.x].apoapsis;
functions[id.x].outZ = (float)functions[id.x].durationSeconds;
}
======================
现在,问题来了。
GetData 之后,值如下:
outX、outY 和 outZ 都是错误的。然而,当值发生变化时,确实会发生一些事情。另外,近点和远点似乎是正确的。
作为调试测试,我尝试在 foobar 中执行此操作(如下),它确实被调用并在 outX 中返回该值。
functions[id.x].outX = 666.666;
那么为什么它只在近点和远点失败呢?是不是因为float和double之间存在转换问题?
有两个基本问题。
由于某种原因,“double”在语法上是允许的,但不起作用。我不确定我做错了什么(FieldOffsets 似乎是正确的),但随后强制转换为浮动失败了。所以我把所有的双打都改为浮动。 cos、sin 和 sqrt 等函数无论如何都采用浮点数。