使用Marching Cubes算法进行CSG运算的隐式表面

8
我使用Marching Cubes(或者marching squares,因为这是2D)来渲染等值面,并且想要进行集合操作,如差集、交集和并集。我以为这很容易实现,只需从两个不同的隐式表面中选择两个顶点标量即可,但事实并非如此。
在我的初始测试中,我尝试了两个圆形,并进行了集合操作difference,即A-B。一个圆形在移动,另一个圆形保持静止。在选择顶点标量和将角落顶点分类为内部或外部时,我尝试了以下方法。代码是用C++编写的,OpenGL用于渲染,但这并不重要。没有任何CSG操作的正常渲染会给出预期结果。
       void march(const vec2& cmin, //min x and y for the grid cell
                  const vec2& cmax, //max x and y for the grid cell
                  std::vector<vec2>& tri, 
                  float iso,
                  float (*cmp1)(const vec2&), //distance from stationary circle
                  float (*cmp2)(const vec2&) //distance from moving circle
)
{
  unsigned int squareindex = 0;
  float scalar[4];
  vec2 verts[8];
  /* initial setup of the grid cell */
  verts[0] = vec2(cmax.x, cmax.y);
  verts[2] = vec2(cmin.x, cmax.y);
  verts[4] = vec2(cmin.x, cmin.y);
  verts[6] = vec2(cmax.x, cmin.y);

  float s1,s2;
  /**********************************
   ********For-loop of interest******
   *******Set difference between ****
   *******two implicit surfaces******
   **********************************/
  for(int i=0,j=0; i<4; ++i, j+=2){
    s1 = cmp1(verts[j]);
    s2 = cmp2(verts[j]);
    if((s1 < iso)){ //if inside circle1
      if((s2 < iso)){ //if inside circle2
        scalar[i] = s2; //then set the scalar to the moving circle
      } else {
        scalar[i] = s1; //only inside circle1
        squareindex |= (1<<i); //mark as inside
      }
    }
    else {
      scalar[i] = s1; //inside neither circle
    }
  }

  if(squareindex == 0)
    return;
  /* Usual interpolation between edge points to compute
     the new intersection points */
  verts[1] = mix(iso, verts[0], verts[2], scalar[0], scalar[1]);
  verts[3] = mix(iso, verts[2], verts[4], scalar[1], scalar[2]);
  verts[5] = mix(iso, verts[4], verts[6], scalar[2], scalar[3]);
  verts[7] = mix(iso, verts[6], verts[0], scalar[3], scalar[0]);

  for(int i=0; i<10; ++i){ //10 = maxmimum 3 triangles, + one end token
    int index = triTable[squareindex][i]; //look up our indices for triangulation
    if(index == -1)
      break;
    tri.push_back(verts[index]);
  }
}

这给我带来了奇怪的锯齿形:here
(来源: mechcore.net)

看起来CSG操作是没有插值完成的。它只是“丢弃”整个三角形。我需要用其他方式进行插值,或者组合顶点标量值吗?我需要一些帮助。 完整的测试用例可以在此处下载。 编辑:基本上,我的 marching squares 实现运行良好。它是我的 标量场 出了问题,我想知道正确的方法是什么。最好是我正在寻找一种通用方法来实现我上面讨论的三种集合操作,适用于常见的基元(圆形、矩形/正方形、平面)。 编辑 2:这里是实现答案白皮书后的一些新图像:

1.Difference
2.Intersection
3.Union

编辑3:我还将其实现为3D,具有适当的阴影/光照:

1.大球和小球之间的区别
2.中心为小球的大球与两侧被剪切平面夹住的大球的区别,然后与中心的球联合。
3.两个圆柱体的并集。

1个回答

4

这不是正确的混合标量场的方法。你的标量场可能会告诉你一件事,但是你的标志是否在内部却会告诉你另一件事。首先合并这些场,然后渲染时就像你正在处理单个复合对象一样:

for(int i=0,j=0; i<4; ++i, j+=2){
  s1 = cmp1(verts[j]);
  s2 = cmp2(verts[j]);
  s = max(s1, iso-s2); // This is the secret sauce
  if(s < iso) { // inside circle1, but not inside circle2
    squareindex |= (1<<i);
  }
  scalar[i] = s;
}

这篇文章可能会有帮助:使用基于Lipschitz的隐式曲面将CSG建模与软混合相结合

嗯,奇怪。这确实可以工作,但边缘有点奇怪。我要调查一下这是否是精度问题。不过对于普通圆形来说,它并不会出现。这是一个100x100网格的截图:http://www.mechcore.net/images/gfx/csgbug3.png - Mads Elvheim
没错,这是一个精度问题。更大的圆形效果很好,但增加网格细分不行(我正在使用浮点数)。非常好的答案和论文。非常感谢您,先生。 - Mads Elvheim
不用谢!祝你的项目好运,隐式曲面很有趣! - Roman Zenka

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接