我尝试通过光线投射/ raymarch通过(虚拟)体积为64 ^ 3体素的大约90x90射线。行进算法如下所示:
public byte[] GetVoxel(Vec3 rayStart, Vec3 direction)
{
direction.Normalize();
bool wasInside = false;
IVec3 step = new IVec3(Math.Sign(direction.x), Math.Sign(direction.y), Math.Sign(direction.z));
IVec3 p = new IVec3(rayStart);
if (step.x > 0)
p.x += 1;
if (step.y > 0)
p.y += 1;
if (step.z > 0)
p.z += 1;
Vec3 max = new Vec3(
direction.x != 0f ? Math.Abs((p.x - rayStart.x) / direction.x) : float.PositiveInfinity,
direction.y != 0f ? Math.Abs((p.y - rayStart.y) / direction.y) : float.PositiveInfinity,
direction.z != 0f ? Math.Abs((p.z - rayStart.z) / direction.z) : float.PositiveInfinity
);
Vec3 delta = new Vec3(
direction.x != 0f ? Math.Abs(1f / direction.x) : float.PositiveInfinity,
direction.y != 0f ? Math.Abs(1f / direction.y) : float.PositiveInfinity,
direction.z != 0f ? Math.Abs(1f / direction.z) : float.PositiveInfinity
);
byte[] col = new byte[4] {0,0,0,0};
byte[] newCol;
int maxSteps = voxelResolution * 2;
int k = 0;
do
{
if(max.x < max.y)
{
if(max.x < max.z)
{
p.x += step.x;
max.x += delta.x;
}
else
{
p.z += step.z;
max.z += delta.z;
}
}
else
{
if(max.y < max.z)
{
p.y += step.y;
max.y += delta.y;
}
else
{
p.z += step.z;
max.z += delta.z;
}
}
if(p.x >= 0 && p.x < voxelResolution && p.y >= 0 && p.y < voxelResolution)
{
if (!wasInside)
wasInside = true;
}
else if(wasInside)
{
break;
}
newCol = GetVoxel(p);
if(newCol[3] > 0)
col = ColorHelper.Mix(col, newCol);
k++;
}
while (col[3] <= 255 && k < maxSteps);
return col;
}
这需要花费一秒钟才能完成。难道这不是更快,还是我有瓶颈?
更新: 我测量了一下,结果发现从体素中取出实际颜色的时间最多。我已经改进了它,但它总共花费了300多天:
总时间:659 时间:659 时间设置:3 时间游行:102 时间颜色:318 时间更新图片:0
我真的不明白我错过200毫秒的地方。另外,我很困惑为什么需要这么长时间来获取每个体素的颜色。我已经更新了代码,并且我将包含体素提取方法:
public void DrawImage(Vec3 camCenter, Vec3 viewDir)
{
int w = ResInternal;
int h = ResInternal;
viewDir.y *= -1;
long tSetup = 0, tTotal = 0, tMake = 0, tUpdateImage = 0, tMarch = 0, tGetColor = 0;
Stopwatch sw = new Stopwatch();
sw.Start();
if (w > 0 && h > 0)
{
int x, y;
viewDir.Normalize();
Vec3 right = Vec3.Cross(new Vec3(0, 1, 0), viewDir);
Vec3 up = Vec3.Cross(viewDir, right);
Vec3 halfS = new Vec3(w, h, 0) * 0.5f;
Vec3 loc;
for (int i = 0; i < bitmapData.Length / bpp; i++)
{
x = i % w;
y = i / w;
loc = camCenter + right * (x - w * 0.5f) + up * (y - h * 0.5f);
isoObject.GetVoxel(loc, viewDir,ref bitmapData, i * bpp, ref sw, ref tSetup, ref tMarch, ref tGetColor);
}
tMake = sw.ElapsedMilliseconds;
UpdateImage();
tUpdateImage = sw.ElapsedMilliseconds - tMake;
tTotal = sw.ElapsedMilliseconds;
}
Console.WriteLine("Time total: " + tTotal);
Console.WriteLine("Time make: " + tMake);
Console.WriteLine("Time setup: " + tSetup);
Console.WriteLine("Time march: " + tMarch);
Console.WriteLine("Time color: " + tGetColor);
Console.WriteLine("Time update image: " + tUpdateImage);
}
这是getVoxel方法:
public void GetVoxel(Vec3 rayStart, Vec3 direction, ref byte[] imgData, int index, ref System.Diagnostics.Stopwatch stopwatch, ref long timeSetup, ref long timeMarch, ref long timeColor)
{
long t = stopwatch.ElapsedMilliseconds;
direction.Normalize();
bool wasInside = false;
IVec3 step = new IVec3(Math.Sign(direction.x), Math.Sign(direction.y), Math.Sign(direction.z));
IVec3 p = new IVec3(rayStart);
if (step.x > 0)
p.x += 1;
if (step.y > 0)
p.y += 1;
if (step.z > 0)
p.z += 1;
Vec3 max = new Vec3(
direction.x != 0f ? Math.Abs((p.x - rayStart.x) / direction.x) : float.PositiveInfinity,
direction.y != 0f ? Math.Abs((p.y - rayStart.y) / direction.y) : float.PositiveInfinity,
direction.z != 0f ? Math.Abs((p.z - rayStart.z) / direction.z) : float.PositiveInfinity
);
Vec3 delta = new Vec3(
direction.x != 0f ? Math.Abs(1f / direction.x) : float.PositiveInfinity,
direction.y != 0f ? Math.Abs(1f / direction.y) : float.PositiveInfinity,
direction.z != 0f ? Math.Abs(1f / direction.z) : float.PositiveInfinity
);
byte[] col = new byte[4] {0,0,0,0};
byte[] newCol;
int maxSteps = voxelResolution * 2;
int k = 0;
timeSetup += stopwatch.ElapsedMilliseconds - t;
t = stopwatch.ElapsedMilliseconds;
do
{
if(max.x < max.y)
{
if(max.x < max.z)
{
p.x += step.x;
max.x += delta.x;
}
else
{
p.z += step.z;
max.z += delta.z;
}
}
else
{
if(max.y < max.z)
{
p.y += step.y;
max.y += delta.y;
}
else
{
p.z += step.z;
max.z += delta.z;
}
}
if(p.x >= 0 && p.x < voxelResolution && p.y >= 0 && p.y < voxelResolution)
{
if (!wasInside)
wasInside = true;
}
else if(wasInside)
{
break;
}
timeMarch += stopwatch.ElapsedMilliseconds - t;
t = stopwatch.ElapsedMilliseconds;
newCol = Root.GetVoxel(p);
if(newCol[3] > 0)
col = ColorHelper.Mix(col, newCol);
timeColor += stopwatch.ElapsedMilliseconds - t;
t = stopwatch.ElapsedMilliseconds;
k++;
}
while (col[3] <= 255 && k < maxSteps);
Buffer.BlockCopy(col, 0, imgData, index, 4);
//return col;
}
和实际的体素提取:
public byte[] GetVoxel(IVec3 location)
{
byte[] col = new byte[] { 0, 0, 0, 0 };
if (voxelData != null)
{
if (location.x >= 0 && location.y >= 0 && location.z >= 0 && location.x < resolution && location.y < resolution && location.z < resolution)
{
col = voxelData[location.x, location.y, location.z];
}
}
foreach(var c in children.Values)
{
if(c.Carve)
{
col[3] = (byte)(col[3] * (1f - c.GetVoxel(location)[3] / 255f));
}
}
if(col[3] < 255)
{
foreach (var c in children.Values)
{
if (!c.Carve)
{
col = ColorHelper.Mix(c.GetVoxel(location), col);
if (col[3] >= 255)
break;
}
}
}
return col;
}
我只用两层进行测试,根层为空(voxelData为空)和一个带有实体体素数据的子层。不知何故,在一个简单的数组提取中会丢失大约300毫秒。