Opengl平滑着色生成对象

时间:2018-05-08 06:59:39

标签: c++ opengl lighting normals shading

我用perlin噪音生成了一个3d形状。 我试图平滑它的阴影,这样做我计算了每个三角形的面法线然后我通过平均它们所属的面的法线并将最终结果标准化来计算每个三角形顶点的法线。 最终结果看起来很像平面阴影(参见附件截图)

enter image description here enter image description here

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

形状生成器:

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

1 个答案:

答案 0 :(得分: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);
  }
}

其余代码实际上是正确的。结果如下: enter image description here