穿过体素体积需要花费一秒钟

时间:2015-02-01 14:25:21

标签: c# volume voxel

我尝试通过光线投射/ 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毫秒。

0 个答案:

没有答案