Opengl 平滑着色生成的对象

Opengl smooth shading generated object

我使用柏林噪声生成了一个 3d 形状。 我试图对其进行平滑处理,为此我计算了每个三角形的面法线,然后通过对它们所属的面的法线进行平均并归一化最终结果来计算每个三角形顶点的法线。 最终结果看起来很像平面着色(见附件截图)

法线对我来说看起来是正确的。 我不能使用着色器,必须使用旧的已弃用的渲染方式。

形状生成器:

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

解决方案很简单。我很困惑,以为我的边就是顶点。更正后,由于这次新顶点共享正确的点,因此新法线现在是根据 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);
  }
}

其余代码实际上是正确的。以下是结果: