【问题标题】:3D triangle rasterization into voxel grid3D 三角形光栅化成体素网格
【发布时间】:2022-10-30 16:43:59
【问题描述】:

序幕:

这是将 3D 三角形光栅化为体素网格的问答,我被要求解决与制造过程模拟期间材料侵蚀/去除相关的不同问题。这个问题背后的主要思想是如何将基于扫描线的 2D 三角形光栅化(如 this)移植到 3D 体素中。

所以问题是如何有效地栅格化 3D 三角形进入均匀体素网格表示为3D 阵列体素表示为浮点值(如果您愿意,可以使用密度或颜色)。

使用C++光栅化本身没有任何额外的库。

【问题讨论】:

    标签: c++ algorithm 3d voxel rasterizing


    【解决方案1】:

    所以首先更新算法:

    1. 定义

      让三角形由它的 3 个int 3D 顶点和一个float 颜色给出:

      void triangle(int x0,int y0,int z0,int x1,int y1,int z1,int x2,int y2,int z2,float c);
      

      目标体素网格是这样的 3D 数组:

      float map[vxs][vys][vzs];
      

      其中vxs,vys,vzs 是网格的分辨率。扫描线算法需要左右缓冲区,但是在 3D 中我们不能使用单轴,所以我选择了主轴(输入三角形的 AABB BBOX 的最大边的轴)所以缓冲区大小vsz 应该是最大的 3决议。由于它只是一维数组,它的内存并不大,所以为了方便起见,我选择了这个:

      const int vsz=vxs|vys|vzs;
      int lx[vsz],ly[vsz],lz[vsz]; // left buffers for filling
      int rx[vsz],ry[vsz],rz[vsz]; // right buffers for filling
      
    2. 计算 AABB 并选择主轴

       int X0,Y0,Z0,X1,Y1,Z1,dx,dy,dz;
       // BBOX
                  X0=x0;            X1=x0;
       if (X0>x1) X0=x1; if (X1<x1) X1=x1;
       if (X0>x2) X0=x2; if (X1<x2) X1=x2;
                  Y0=y0;            Y1=y0;
       if (Y0>y1) Y0=y1; if (Y1<y1) Y1=y1;
       if (Y0>y2) Y0=y2; if (Y1<y2) Y1=y2;
                  Z0=z0;            Z1=z0;
       if (Z0>z1) Z0=z1; if (Z1<z1) Z1=z1;
       if (Z0>z2) Z0=z2; if (Z1<z2) Z1=z2;
       dx=X1-X0;
       dy=Y1-Y0;
       dz=Z1-Z0;
      

      现在从dx,dy,dz(最大的一个)的值中选择主轴。例如,如果dx 最大,那么从现在开始,左右缓冲区索引将是x 坐标。

    3. 将三角形边缘光栅化为左/右缓冲区

      现在要选择边缘应该去左缓冲区还是右缓冲区,我们可以使用主要坐标差的符号。所以首先选择哪个,然后按主要坐标对边顶点进行排序(以避免连接三角形上的孔)。

      对于边缘(线)的光栅化,只需使用integer version of DDA algortihm,它可以轻松移植到更高的维度,并且在现代 CPU 上也比 Bresenham 更快。

      现在不是将线的体素(x,y,z) 渲染到map 中,我们只需将其渲染到左/右缓冲区。因此,如果主轴是 x 并且留下目标缓冲区,它将是:

      ly[x]=y;
      lz[x]=z;
      

      由于有 3 个可能的主轴,我们需要此函数的 3 个版本......还有目标缓冲区的 3 种情况(左、右、两者),但为此您可能会使用指针,因此不需要每个代码两次.. .

      此外,如果边缘可能超出体素网格范围,则需要添加裁剪。我在 DDA 迭代中使用了简单的if 条件,但是为了提高速度,最好在此之前剪断线......

    4. 使用线条填充三角形

      只需通过左/右缓冲区并在它们用线表示的相同索引处连接顶点。这条线本身可能会被简化,因为我们知道它的长轴不会改变,但是为简单起见,我使用了普通的 3D DDA 线(与#3 相同,只是直接渲染到map 而不是左/右缓冲区)。

      放在一起:

      #include <math.h>
      // voxel grid resolution
      const int vxs=100;
      const int vys=100;
      const int vzs=100;
      // helper buffers size should be max(vxs,vys,vzs)
      const int vsz=vxs|vys|vzs;
      float map[vxs][vys][vzs];       // voxel space map
      int lx[vsz],ly[vsz],lz[vsz];    // left buffers for filling
      int rx[vsz],ry[vsz],rz[vsz];    // right buffers for filling
      
      void line(int x0,int y0,int z0,int x1,int y1,int z1,float c) // rencer line
          {
          int i,n,cx,cy,cz,sx,sy,sz;
          // line DDA parameters
          x1-=x0; sx=0; if (x1>0) sx=+1; if (x1<0) { sx=-1; x1=-x1; } if (x1) x1++;           n=x1;
          y1-=y0; sy=0; if (y1>0) sy=+1; if (y1<0) { sy=-1; y1=-y1; } if (y1) y1++; if (n<y1) n=y1;
          z1-=z0; sz=0; if (z1>0) sz=+1; if (z1<0) { sz=-1; z1=-z1; } if (z1) z1++; if (n<z1) n=z1;
          // single pixel (not a line)
          if (!n)
              {
              if ((x0>=0)&&(x0<vxs)&&(y0>=0)&&(y0<vys)&&(z0>=0)&&(z0<vzs)) map[x0][y0][z0]=c;
              return;
              }
          // ND DDA algo i is parameter
          for (cx=cy=cz=n,i=0;i<n;i++)
              {
              if ((x0>=0)&&(x0<vxs)&&(y0>=0)&&(y0<vys)&&(z0>=0)&&(z0<vzs)) map[x0][y0][z0]=c;
              cx-=x1; if (cx<=0){ cx+=n; x0+=sx; }
              cy-=y1; if (cy<=0){ cy+=n; y0+=sy; }
              cz-=z1; if (cz<=0){ cz+=n; z0+=sz; }
              }
          }
      
      void _plinex(int x0,int y0,int z0,int x1,int y1,int z1) // helper edge render
          {
          int i,n,cx,cy,cz,sx,sy,sz,*by,*bz;
          // target buffer & order points by x
          if (x1>=x0)
              {
              i=x0; x0=x1; x0=i;
              i=y0; y0=y1; y0=i;
              i=z0; z0=z1; z0=i; by=ly; bz=lz;
              } else {           by=ry; bz=rz; }
          // line DDA parameters
          x1-=x0; sx=0; if (x1>0) sx=+1; if (x1<0) { sx=-1; x1=-x1; } if (x1) x1++;           n=x1;
          y1-=y0; sy=0; if (y1>0) sy=+1; if (y1<0) { sy=-1; y1=-y1; } if (y1) y1++; if (n<y1) n=y1;
          z1-=z0; sz=0; if (z1>0) sz=+1; if (z1<0) { sz=-1; z1=-z1; } if (z1) z1++; if (n<z1) n=z1;
          // single pixel (not a line)
          if (!n)
              {
              if ((x0>=0)&&(x0<vxs))
                  {
                  ly[x0]=y0; lz[x0]=z0;
                  ry[x0]=y0; rz[x0]=z0;
                  }
              return;
              }
          // ND DDA algo i is parameter
          for (cx=cy=cz=n,i=0;i<n;i++)
              {
              if ((x0>=0)&&(x0<vxs)){ by[x0]=y0; bz[x0]=z0; }
              cx-=x1; if (cx<=0){ cx+=n; x0+=sx; }
              cy-=y1; if (cy<=0){ cy+=n; y0+=sy; }
              cz-=z1; if (cz<=0){ cz+=n; z0+=sz; }
              }
          }
      
      void _pliney(int x0,int y0,int z0,int x1,int y1,int z1) // helper edge render
          {
          int i,n,cx,cy,cz,sx,sy,sz,*bx,*bz;
          // target buffer & order points by y
          if (y1>=y0)
              {
              i=x0; x0=x1; x0=i;
              i=y0; y0=y1; y0=i;
              i=z0; z0=z1; z0=i; bx=lx; bz=lz;
              } else {           bx=rx; bz=rz; }
          // line DDA parameters
          x1-=x0; sx=0; if (x1>0) sx=+1; if (x1<0) { sx=-1; x1=-x1; } if (x1) x1++;           n=x1;
          y1-=y0; sy=0; if (y1>0) sy=+1; if (y1<0) { sy=-1; y1=-y1; } if (y1) y1++; if (n<y1) n=y1;
          z1-=z0; sz=0; if (z1>0) sz=+1; if (z1<0) { sz=-1; z1=-z1; } if (z1) z1++; if (n<z1) n=z1;
          // single pixel (not a line)
          if (!n)
              {
              if ((y0>=0)&&(y0<vys))
                  {
                  lx[y0]=x0; lz[y0]=z0;
                  rx[y0]=x0; rz[y0]=z0;
                  }
              return;
              }
          // ND DDA algo i is parameter
          for (cx=cy=cz=n,i=0;i<n;i++)
              {
              if ((y0>=0)&&(y0<vys)){ bx[y0]=x0; bz[y0]=z0; }
              cx-=x1; if (cx<=0){ cx+=n; x0+=sx; }
              cy-=y1; if (cy<=0){ cy+=n; y0+=sy; }
              cz-=z1; if (cz<=0){ cz+=n; z0+=sz; }
              }
          }
      
      void _plinez(int x0,int y0,int z0,int x1,int y1,int z1) // helper edge render
          {
          int i,n,cx,cy,cz,sx,sy,sz,*bx,*by;
          // target buffer & order points by z
          if (z1>=z0)
              {
              i=x0; x0=x1; x0=i;
              i=y0; y0=y1; y0=i;
              i=z0; z0=z1; z0=i; bx=lx; by=ly;
              } else {           bx=rx; by=ry; }
          // line DDA parameters
          x1-=x0; sx=0; if (x1>0) sx=+1; if (x1<0) { sx=-1; x1=-x1; } if (x1) x1++;           n=x1;
          y1-=y0; sy=0; if (y1>0) sy=+1; if (y1<0) { sy=-1; y1=-y1; } if (y1) y1++; if (n<y1) n=y1;
          z1-=z0; sz=0; if (z1>0) sz=+1; if (z1<0) { sz=-1; z1=-z1; } if (z1) z1++; if (n<z1) n=z1;
          // single pixel (not a line)
          if (!n)
              {
              if ((z0>=0)&&(z0<vzs))
                  {
                  lx[z0]=x0; ly[z0]=y0;
                  rx[z0]=x0; ry[z0]=y0;
                  }
              return;
              }
          // ND DDA algo i is parameter
          for (cx=cy=cz=n,i=0;i<n;i++)
              {
              if ((z0>=0)&&(z0<vzs)){ bx[z0]=x0; by[z0]=y0; }
              cx-=x1; if (cx<=0){ cx+=n; x0+=sx; }
              cy-=y1; if (cy<=0){ cy+=n; y0+=sy; }
              cz-=z1; if (cz<=0){ cz+=n; z0+=sz; }
              }
          }
      
      void triangle(int x0,int y0,int z0,int x1,int y1,int z1,int x2,int y2,int z2,float c) // render triangle
          {
          int X0,Y0,Z0,X1,Y1,Z1,dx,dy,dz,x,y,z;
          // BBOX
                     X0=x0;            X1=x0;
          if (X0>x1) X0=x1; if (X1<x1) X1=x1;
          if (X0>x2) X0=x2; if (X1<x2) X1=x2;
                     Y0=y0;            Y1=y0;
          if (Y0>y1) Y0=y1; if (Y1<y1) Y1=y1;
          if (Y0>y2) Y0=y2; if (Y1<y2) Y1=y2;
                     Z0=z0;            Z1=z0;
          if (Z0>z1) Z0=z1; if (Z1<z1) Z1=z1;
          if (Z0>z2) Z0=z2; if (Z1<z2) Z1=z2;
          dx=X1-X0;
          dy=Y1-Y0;
          dz=Z1-Z0;
               if ((dx>=dy)&&(dx>=dz))    // x is major axis
              {
              // render circumference into left/right buffers
              _plinex(x0,y0,z0,x1,y1,z1);
              _plinex(x1,y1,z1,x2,y2,z2);
              _plinex(x2,y2,z2,x0,y0,z0);
              // fill the triangle
              if (X0<0) X0=0; if (X1>=vxs) X1=vxs-1;
              for (x=X0;x<=X1;x++)
                  {
                  y0=ly[x]; z0=lz[x];
                  y1=ry[x]; z1=rz[x];
                  line(x,y0,z0,x,y1,z1,c);
                  }
              }
          else if ((dy>=dx)&&(dy>=dz))    // y is major axis
              {
              // render circumference into left/right buffers
              _pliney(x0,y0,z0,x1,y1,z1);
              _pliney(x1,y1,z1,x2,y2,z2);
              _pliney(x2,y2,z2,x0,y0,z0);
              // fill the triangle
              if (Y0<0) Y0=0; if (Y1>=vys) Y1=vys-1;
              for (y=Y0;y<=Y1;y++)
                  {
                  x0=lx[y]; z0=lz[y];
                  x1=rx[y]; z1=rz[y];
                  line(x0,y,z0,x1,y,z1,c);
                  }
              }
          else if ((dz>=dx)&&(dz>=dy))    // z is major axis
              {
              // render circumference into left/right buffers
              _plinez(x0,y0,z0,x1,y1,z1);
              _plinez(x1,y1,z1,x2,y2,z2);
              _plinez(x2,y2,z2,x0,y0,z0);
              // fill the triangle
              if (Z0<0) Z0=0; if (Z1>=vzs) Z1=vzs-1;
              for (z=Z0;z<=Z1;z++)
                  {
                  x0=lx[z]; y0=ly[z];
                  x1=rx[z]; y1=ry[z];
                  line(x0,y0,z,x1,y1,z,c);
                  }
              }
          }
      

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2015-12-14
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2023-03-19
      • 2020-12-15
      相关资源
      最近更新 更多