3D 三角形光栅化为体素网格

2023-12-13

序幕:

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

所以问题是如何高效地光栅化 3D 三角形 into 均匀体素网格表示为3D array体素表示为浮点值(密度或颜色,如果您愿意)。

Using C++无需任何额外的光栅化库。


所以首先更新算法:

  1. 定义

    让三角形由它的 3 给出int3D 顶点和一个float color:

    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];
    

    where 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. 将三角形边缘栅格化到左/右缓冲区中

    现在要选择边缘是否应该进入左侧缓冲区或右侧缓冲区,我们可以使用主要坐标差的符号。因此,选择第一个,然后按主坐标对边顶点进行排序(以避免连接三角形上出现孔)。

    对于边缘(线)的光栅化只需使用DDA 算法的整数版本它很容易移植到更高的维度,并且在现代 CPU 上比 Bresenham 更快。

    现在不再渲染体素(x,y,z)的线进入map我们只是将其渲染到左/右缓冲区中。所以如果长轴是x剩下的目标缓冲区将是:

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

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

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

  4. 用直线填充三角形

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

将所有内容放在一起:

// 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);
            }
        }
    }

边缘函数_pline如果您意识到主轴与迭代轴相同,则可以加快速度i您可以少进行一次计算。另外,这 3 个化身可以合并为一个(只需重新排序)x,y,z调用参数的顺序并传递左/右缓冲区指针。将剪辑移到 DDA 迭代之外也会大大提高性能。

这里是三角形相交圆柱体的预览:

preview1

这里预览使用三角形光栅化的材料去除:

preview2

橙色三角形不是体素一,它只是叠加,体素一不是直接渲染的,而是从体素网格中减去,而不是在动画过程中删除材料......

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

3D 三角形光栅化为体素网格 的相关文章

随机推荐

  • LinkedIn 共享显示大图像的要求

    我的问题是 为什么图像很小以及如何使其变大 我已经实现了linkedIn共享 为此 我添加了元标记 og title og image og description og url 一切正常 但我的形象很小 小图像示例 大图示例 我们已经向
  • 0.5 时向上或向下舍入

    我遇到了 Javascript 在达到 0 5 时对数字进行四舍五入的方式的问题 我正在编写征费计算器 并注意到结果有 0 1c 的差异 问题是他们的结果是21480 705我的应用程序翻译成21480 71 而关税说21480 70 这是
  • 确定-取消对话框:处理“Enter”键按下

    我正在建造一个Dialog在Winforms中 当您创建它时 它有两个 确定 和 取消 按钮 这就是我想要的 在此对话框中我还有一个TextBox and a Sub VB NET 中的编码 处理其KeyPress事件 我需要按下 Ente
  • 裸星号作为方法定义中的参数:def f(*) [重复]

    这个问题在这里已经有答案了 我知道这意味着什么 def f args end 但这是什么意思以及为什么要使用它 它也可以与命名参数一起出现吗 def f end def f 具有相同的效果def f args 只不过它没有命名通配参数数组
  • Flexdashboard 侧边栏选择器输入溢出与滚动条

    在我之前的问题中shinyWidgets pickerInput 宽度问题溢出侧边栏我能够解决侧边栏中的 pickerInput 框如果值太宽会在侧边栏中被截断的问题 但是通过使用overflow visible 为了解决这个问题 当输入框
  • 如何唯一标识 ContactsContract.Contacts 表上的联系人

    我有一个应用程序可以获取ContactsContract Contacts LOOKUP KEY设备上的联系人并将其保存在应用程序数据库中 看完之后this页面我想我可以使用LOOKUP KEY唯一标识联系人 即使在编辑联系人时 例如编辑联
  • 奇怪的浮点除法结果

    我在一个grails项目中发生了这个奇怪的除法错误 但我认为grails与它无关 我认为是一个groovy或java问题 如果在常规控制台中我运行这个 float money 1 30 float r 0 01 println money
  • 绑定祖先不工作 WPF

    我有一个 TreeView 它被修改为在文本前面显示图像 所以我修改后的TreeViewItem叫做ImagedTreeViewItem 此 ImagedTreeViewItem 有一个属性 其中包含要显示的图像控件的图像 ImagedTr
  • WebDriver 测试未在 IE11 上运行

    当我运行 WebDriver 测试时 它们根本不工作 到目前为止我所做的 1 我已更新我的注册表项以包含 FEATURE BFCACHE 2 所有区域的保护模式设置均相同 3 增强保护模式被禁用 4 我还尝试在我的 PATH 中设置 IED
  • 推荐用于 JavaScript 编码的 Vim 插件? [关闭]

    就目前情况而言 这个问题不太适合我们的问答形式 我们希望答案得到事实 参考资料或专业知识的支持 但这个问题可能会引发辩论 争论 民意调查或扩展讨论 如果您觉得这个问题可以改进并可能重新开放 访问帮助中心以获得指导 我是 JS 和 Vim 的
  • 当位置设置为固定时,导航栏会缩小

    我目前遇到一个问题 当我将导航栏和横幅的位置设置为固定时 它们会缩小 我有很多事情 例如更改 z index 将其顶部位置设置为 0 添加自动边距等 但这些都不起作用 我希望有人能指出我的错误 这是我的html代码 html body ma
  • 有没有办法对VBS中存储的密码进行加密

    我有一个在工作中使用的 VBS 脚本 用于在连接到 Cisco 路由器和交换机时自动执行任务 包括自动执行登录过程 人们对于将密码存储在纯文本 VBS 文件中有些紧张 这并非没有道理 因此我为他们提供了每次提示输入密码或将其存储在脚本中的选
  • Canvas.toDataURL() 未捕获类型错误:未定义不是函数

    我正在使用一个名为 html2canvas 的插件将页面上的一些 html 转换为 canvas 元素 然后我想将该画布保存为图像 不幸的是我一直遇到标题中的错误 我尝试过使用不同的变量名 不同的 html 等 但不断遇到相同的错误 这是我
  • 复制 local = false 文件未找到异常问题

    嗨 我知道这个已被询问但并没有得到答复 当我想使用安装在 C Program files x86 Dummu API dll 上的 dll 时遇到问题 当我运行我的应用程序时 它抛出异常 无法加载文件或程序集 Dummy API Versi
  • 硒点击表td内的锚标记

    我的 html 代码如下所示 div class fd food search all fd loading style display block ul class fd breadcrumbs li a href class bread
  • 如何“加入”一个数组,将第一个要加入的字符添加到结果字符串的开头?

    我正在使用 Ruby on Rails 3 并且正在尝试join一个数组 特点 我阅读了 Ruby 文档关于那个 我的数组是 name1 name2 If I do name1 name2 join 结果是 name1 name2 我希望结
  • 类型别名的目的

    我想今天我终于明白什么是 typealias 了 我没有 让我们看一个例子 typealias Graph String String let futurama Graph you bender hermes scruffy bender
  • 从 iframe 访问父页面中的变量

    我有一个带有 iframe 的页面 其中包含 html 页面 我想从 iframe 中访问父页面中的 Javascript 变量 主页中变量的名称是observer 我已经尝试过这个 parent observer aadasds 但我收到
  • Distinct() 如何在对象列表中查找唯一元素

    有一个非常简单的类 public class LinkInformation public LinkInformation string link string text string group this Link link this T
  • 3D 三角形光栅化为体素网格

    序幕 这是将 3D 三角形光栅化为体素网格的问答 我被要求解决与以下相关的不同问题材料侵蚀 去除在制造过程模拟期间 这个问题背后的主要思想是如何移植基于扫描线的 2D 三角形光栅化 例如this转化为 3D 体素 所以问题是如何高效地光栅化