I'm trying to raycast / raymarch through a (virtual) volume of 64^3 voxels for about 90x90 rays. The marching algorithm looks like this:
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;
}
This takes over a second to perform. Shouldn't this be way faster or do I have a bottleneck?
Update: I measured a bit and it turns out the most time is lost fetching the actual color from the voxel. I improved it already, but it still costs a total of over 300 ms:
Time total: 659 Time make: 659 Time setup: 3 Time march: 102 Time color: 318 Time update image: 0
I don't really understand where I'm missing 200 ms though. Also I'm confused why it takes so long to fetch the color for each voxel. I've updated the code and I'll include the voxel fetching method:
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);
}
here is the getVoxel method:
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;
}
and the actual voxel fetching:
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;
}
I'm testing with only two layers, the root layer which is empty (voxelData is null) and one child layer with the actualy voxel data. Somehow ~300 ms are lost on a simple array fetch.