1

我使用柏林噪声生成了一个 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);
}
4

1 回答 1

2

解决方案很简单。我很困惑,认为我的边缘是顶点。一旦更正,由于这次新顶点共享正确的点,新法线现在是从 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);
  }
}

其余的代码实际上是正确的。以下是结果: 在此处输入图像描述

于 2018-05-08T09:59:24.167 回答