Сгенерированный объект opengl smooth shading

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

введите здесь описание изображения  введите описание изображения здесь

Нормали мне кажутся правильными. Я не могу использовать шейдеры и вынужден использовать старый устаревший способ рендеринга.

Генератор формы:

void Island::generateTopTriangles() {
  float xStep = 2 * _xmax / _tess;
  float zStep = 2 * _zmax / _tess;

  PointMap top;
  for (int i = 0; i <= _tess; i++) {
    float z = -_zmax + i * zStep;
    std::vector<Vector3f> rowTop;
    for (int j = 0; j <= _tess; j++) {
      float x = -_xmax + j * xStep;
      rowTop.emplace_back(x, islandPerlin(x, z), z);
    }
    top.emplace_back(rowTop);
  }

  for (int i = 0; i < top.size() - 1; i++) {
    const std::vector<Vector3f> &pointRow = top[i];
    const std::vector<Vector3f> &pointUpRow = top[i + 1];
    std::vector<Triangle> newRow;
    for (int j = 0; j < pointRow.size() - 1; j++) {
      const Vector3f &p1 = pointRow.at(j);
      const Vector3f &p2 = pointRow.at(j + 1);
      const Vector3f &p3 = pointUpRow.at(j);
      const Vector3f &p4 = pointUpRow.at(j + 1);

      Vertex::Ptr v1, v2, v3, v4, v5;
      if (j == 0) {
        v1 = std::make_shared<Vertex>(Vertex(p1, p3, Vector3f()));
      } else { //Retrieve existing Vertex
        v1 = newRow[newRow.size() - 1].v2;
      }
      v2 = std::make_shared<Vertex>(Vertex(p3, p2, Vector3f()));
      if (i == 0) {
        v3 = std::make_shared<Vertex>(Vertex(p2, p1, Vector3f()));
      } else { //Retrieve existing Vertex
        v3 = _triangles[_triangles.size() - 1][j == 0 ? 1 : newRow.size() + 1].v3;
      }
      v4 = std::make_shared<Vertex>(Vertex(p2, p4, Vector3f()));
      v5 = std::make_shared<Vertex>(Vertex(p4, p3, Vector3f()));

      //Create triangles
      newRow.emplace_back(v1, v2, v3, computeNormal(v1->p, v2->p, v3->p));
      newRow.emplace_back(v2, v4, v5, computeNormal(v2->p, v4->p, v5->p).invert());
    }
    _triangles.emplace_back(newRow);
  }
}

Я вычисляю нормали граней с помощью простого векторного произведения между двумя векторами:

Vector3f Island::computeNormal(const Vector3f &p1, const Vector3f &p2, const Vector3f &p3) {
  Vector3f u = {p2.x - p1.x,
                p2.y - p1.y,
                p2.z - p1.z};
  Vector3f v = {p3.x - p1.x,
                p3.y - p1.y,
                p3.z - p1.z};
  Vector3f n = {u.y * v.z - u.z * v.y,
                u.z * v.x - u.x * v.z,
                u.x * v.y - u.y * v.x};
  return n.normalize();
}

Нормали на вершину (инициализированы до 0):

void Island::computePerVertexNormal() {
  for (auto row : _triangles) {
    for (auto t : row) {
      t.v1->n.x += t.n.x;
      t.v1->n.y += t.n.y;
      t.v1->n.z += t.n.z;
      t.v2->n.x += t.n.x;
      t.v2->n.y += t.n.y;
      t.v2->n.z += t.n.z;
      t.v3->n.x += t.n.x;
      t.v3->n.y += t.n.y;
      t.v3->n.z += t.n.z;
    }
  }
  for (auto row : _triangles) {
    for (auto t : row) {
      t.v1->n.normalize();
      t.v2->n.normalize();
      t.v3->n.normalize();
    }
  }
}

И, наконец, часть чертежа:

void Island::draw() const {
  glEnable(GL_LIGHTING);
  glEnable(GL_LIGHT0);
  glEnable(GL_BLEND);
  glEnable(GL_COLOR_MATERIAL);

  GLfloat specular[] = {0.1f, 0.1f, 0.1f, 0.0f};
  glMaterialfv(GL_FRONT_AND_BACK, GL_SPECULAR, specular);
  GLfloat diffuse[] = {0.5f, 0.5f, 0.5f, 1.0f};
  glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, diffuse);
  GLfloat emission[] = {0.0f, 0.0f, 0.0f, 1.0f};
  glMaterialfv(GL_FRONT_AND_BACK, GL_EMISSION, emission);
  GLfloat shininess = 128.0f;
  glMaterialf(GL_FRONT_AND_BACK, GL_SHININESS, shininess);

  glShadeModel(GL_SMOOTH);
  glColor4f(1.0f, 0.5f, 0.0f, 1.0f);
  glBegin(GL_TRIANGLES);
  for (auto &row : _triangles) {
    for (auto &t : row) {
      glNormal3f(t.v1->n.x, t.v1->n.y, t.v1->n.z);
      glVertex3f(t.v1->p.x, t.v1->p.y, t.v1->p.z);
      glNormal3f(t.v2->n.x, t.v2->n.y, t.v2->n.z);
      glVertex3f(t.v2->p.x, t.v2->p.y, t.v2->p.z);
      glNormal3f(t.v3->n.x, t.v3->n.y, t.v3->n.z);
      glVertex3f(t.v3->p.x, t.v3->p.y, t.v3->p.z);
    }
  }
  glEnd();

  glDisable(GL_COLOR_MATERIAL);
  glDisable(GL_BLEND);
  glDisable(GL_LIGHT0);
  glDisable(GL_LIGHTING);
}

person Guillaume Wilmot    schedule 08.05.2018    source источник
comment
Для этого вы можете посмотреть некоторые алгоритмы взвешенных нормалей, citeseerx.ist.psu.edu/viewdoc/   -  person Jay Wai Tan    schedule 08.05.2018
comment
похоже, что ваши нормали для каждой вершины неверны, см. Как добиться гладких нормалей касательного пространства?. Поэтому проверьте, нормализованы ли нормали лица перед сглаживанием. Однако код выглядит нормально, если только макрос auto или что-то еще не делает чего-то, чего вы не ожидаете (например, не перебирает все треугольники или не использует некоторые треугольники более одного раза). row подозрительно, если вы делаете это только для каждого фрагмента карты, чем это ошибка, поскольку вы не учитываете все соседние лица, тогда ...   -  person Spektre    schedule 08.05.2018
comment
также ознакомьтесь с моим простым генератором случайных островов C ++ с биомами, это может вас заинтересовать.   -  person Spektre    schedule 08.05.2018
comment
@Spektre Нормали граней действительно нормализованы при создании. Разве у вершины не должно быть только две соседних грани? Если да, то они учтены. Кроме того, я попытался удалить авто, но безрезультатно. Я попробую разделить нормали для каждой вершины на количество нормалей лица, которые они усредняют, и посмотрю, получу ли я какие-либо изменения.   -  person Guillaume Wilmot    schedule 08.05.2018
comment
@GuillaumeWilmot зависит от свойств триангуляции ... Вершина может иметь любое количество граней, которым она принадлежит (представьте TRIANGLE_FAN в круге) на вашем изображении Я вижу вершины, принадлежащие 6 граням, а не только 2 ...   -  person Spektre    schedule 08.05.2018
comment
@GuillaumeWilmot другая возможность - наличие повторяющихся точек (у вас было больше точек с тем же x,y,z), которые полностью испортили бы сглаживание нормалей   -  person Spektre    schedule 08.05.2018
comment
Хорошо, теперь я понимаю. Я запутался между вершиной и ребром и вычислил нормали для ребер. Я уверен, что это сработает, как только я это изменю. Спасибо вам обоим.   -  person Guillaume Wilmot    schedule 08.05.2018
comment
Ты был прав. Код теперь намного проще и чище, а свет сглаживается правильно. Я отвечу на свой вопрос, когда закончу.   -  person Guillaume Wilmot    schedule 08.05.2018


Ответы (1)


Решение было простым. Я запутался и подумал, что мои ребра - это вершины. После исправления, поскольку на этот раз новые вершины имеют правильные точки, новые нормали теперь вычисляются из 6 смежных граней треугольника и теперь являются правильными. код генерации также стал намного проще.

void Island::generateTopTriangles() {
  float xStep = 2 * _xmax / _tess;
  float zStep = 2 * _zmax / _tess;

  float z;
  for (int i = 0; i <= _tess; i++) {
    z = -_zmax + i * zStep;
    std::vector<Vertex::Ptr> row;
    for (int j = 0; j <= _tess; j++) {
      float x = -_xmax + j * xStep;
      row.emplace_back(std::make_shared<Vertex>(Vector3f(x, islandPerlin(x, z), z)));
    }
    _vertices.emplace_back(row);
  }

  for (int i = 0; i < _vertices.size() - 1; i++) {
    const std::vector<Vertex::Ptr> &pointRow = _vertices[i];
    const std::vector<Vertex::Ptr> &pointUpRow = _vertices[i + 1];
    std::vector<Triangle::Ptr> newRow;
    for (int j = 0; j < pointRow.size() - 1; j++) {
      const Vertex::Ptr p1 = pointRow.at(j);
      const Vertex::Ptr p2 = pointRow.at(j + 1);
      const Vertex::Ptr p3 = pointUpRow.at(j);
      const Vertex::Ptr p4 = pointUpRow.at(j + 1);

      newRow.emplace_back(std::make_shared<Triangle>(p1, p2, p3, computeNormal(p3->p, p2->p, p1->p)));
      newRow.emplace_back(std::make_shared<Triangle>(p3, p2, p4, computeNormal(p4->p, p2->p, p3->p)));
    }
    _triangles.emplace_back(newRow);
  }
}

Остальной код действительно был правильным. Вот результаты:  введите описание изображения здесь

person Guillaume Wilmot    schedule 08.05.2018