CSG-операции на неявных поверхностях с марширующими кубами - PullRequest
7 голосов
/ 21 мая 2010

Я рендерим изоповерхности с помощью движущихся кубов (или, возможно, марширующих квадратов , поскольку это 2D), и я хочу выполнять операции над множествами, такие как разность множеств, пересечение и объединение. Я думал, что это легко реализовать, просто выбрав один из двух вершинных скаляров из двух разных неявных поверхностей, но это не так.

Для первоначального тестирования я попытался с двумя сферами кружками и операцией установки разница . т.е. 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]);
  }
}

Это вызывает у меня странные неровности: здесь http://www.mechcore.net/images/gfx/csgbug2.png
Похоже, что операция CSG выполняется без интерполяции. Это просто «отбрасывает» весь треугольник. Нужно ли интерполировать каким-либо другим способом или объединять скалярные значения вершин? Я хотел бы помочь с этим. Полный тестовый пример можно загрузить ЗДЕСЬ

EDIT: В принципе, моя реализация марширующих квадратов работает нормально. Это мое скалярное поле , которое сломано, и мне интересно, как будет выглядеть правильный путь. Предпочтительно я ищу общий подход для реализации трех операций над множествами, которые я обсуждал выше, для обычных примитивов (круг, прямоугольник / квадрат, плоскость)

РЕДАКТИРОВАТЬ 2: Вот некоторые новые изображения после реализации документа ответчика:

1.Difference
2.Intersection
3.Union

РЕДАКТИРОВАТЬ 3: Я реализовал это также в 3D, с надлежащим затенением / освещением:

1. Разница между большой сферой и меньшей сферой
2. Разница между большей сферой и меньшей сферой в центре, обрезанной двумя плоскостями с обеих сторон, и затем соединение с сферой в центре.
3.Соединение между двумя цилиндрами.

1 Ответ

3 голосов
/ 24 мая 2010

Это не то, как вы смешиваете скалярные поля. Ваши скаляры говорят одно, а ваши флаги вне зависимости от того, находитесь вы внутри или нет. Сначала объедините поля, затем выполните рендеринг, как если бы вы делали один составной объект:

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

Эта статья может быть полезна: Объединение моделирования CSG с мягким смешиванием с использованием Неявные поверхности на основе липшица .

...