# 作业要求

完善以下函数:

rasterize_triangle(const Triangle& t) // 在此处实现与作业 2 类似的插值算法,实现法向量、颜色、纹理颜色的插值。
get_projection_matrix() // 将你自己在之前的实验中实现的投影矩阵填到此处,此时你可以运行./Rasterizer output.png normal 来观察法向量实现结果。
phong_fragment_shader() // 实现 Blinn-Phong 模型计算 Fragment Color.
texture_fragment_shader() // 在实现 Blinn-Phong 的基础上,将纹理颜色视为公式中的 kd,实现 Texture Shading Fragment Shader.
bump_fragment_shader() // 在实现 Blinn-Phong 的基础上,仔细阅读该函数中的注释,实现 Bump mapping.
displacement_fragment_shader() // 在实现 Bump mapping 的基础上,实现 displacement mapping.

# 作业实现

# 三角形光栅化

直接按照注释中的提示,实现三角形光栅化即可。

void rst::rasterizer::rasterize_triangle(
    const Triangle &t, const std::array<Eigen::Vector3f, 3> &view_pos) {
  auto v = t.toVector4();
  const int x_min =
      static_cast<int>(std::min(v[0].x(), std::min(v[1].x(), v[2].x())));
  const int x_max =
      static_cast<int>(std::max(v[0].x(), std::max(v[1].x(), v[2].x())));
  const int y_min =
      static_cast<int>(std::min(v[0].y(), std::min(v[1].y(), v[2].y())));
  const int y_max =
      static_cast<int>(std::max(v[0].y(), std::max(v[1].y(), v[2].y())));
  for (auto x = x_min; x <= x_max; x++) {
    for (auto y = y_min; y <= y_max; y++) {
      if (insideTriangle(x, y, t.v)) {
        auto [alpha, beta, gamma] = computeBarycentric2D(x, y, t.v);
        float Z = 1.0 / (alpha / v[0].w() + beta / v[1].w() + gamma / v[2].w());
        float zp = alpha * v[0].z() / v[0].w() + beta * v[1].z() / v[1].w() +
                   gamma * v[2].z() / v[2].w();
        zp *= Z;
        Z = -Z;
        zp = -zp;
        int ind = get_index(x, y);
        if (zp < depth_buf[ind]) {
          depth_buf[ind] = zp;
          auto interpolated_color = interpolate(alpha, beta, gamma, t.color[0],
                                                t.color[1], t.color[2], 1);
          auto interpolated_normal = interpolate(
              alpha, beta, gamma, t.normal[0], t.normal[1], t.normal[2], 1);
          auto interpolated_texcoords =
              interpolate(alpha, beta, gamma, t.tex_coords[0], t.tex_coords[1],
                          t.tex_coords[2], 1);
          auto interpolated_shadingcoords = interpolate(
              alpha, beta, gamma, view_pos[0], view_pos[1], view_pos[2], 1);
          fragment_shader_payload payload(
              interpolated_color, interpolated_normal.normalized(),
              interpolated_texcoords, texture ? &*texture : nullptr);
          payload.view_pos = interpolated_shadingcoords;
          auto pixel_color = fragment_shader(payload);
          set_pixel(Vector2i(x, y), pixel_color);
        }
      }
    }
  }
}

# 实现 Blinn-Phong 模型计算 Fragment Color

L=La+Ld+LsL = L_a + L_d + L_s

其中

La=kaIaLd=kd(I/r2)max(0,nl)Ls=ks(I/r2)max(0,nh)ph=l+vl+vL_a = k_a \cdot I_a\\ L_d = k_d \cdot (I/r^2) \cdot \max(0, \mathbf{n} \cdot \mathbf{l})\\ L_s = k_s \cdot (I/r^2) \cdot \max(0, \mathbf{n} \cdot \mathbf{h})^p\\ \mathbf{h} = \frac{\mathbf{l} + \mathbf{v}}{||\mathbf{l} + \mathbf{v}||}

Eigen::Vector3f light_vector = (light.position - point).normalized();
Eigen::Vector3f view_vector = (eye_pos - point).normalized();
Eigen::Vector3f half_vector = (light_vector + view_vector).normalized();
Eigen::Vector3f n_vector = normal.normalized();
float r2 = (light.position - point).dot(light.position - point);
// ambient
Eigen::Vector3f la = ka.cwiseProduct(amb_light_intensity);
// diffuse
Eigen::Vector3f ld = kd.cwiseProduct(light.intensity / r2) *
                     std::max(0.0f, n_vector.dot(light_vector));
// specular
Eigen::Vector3f ls = ks.cwiseProduct(light.intensity / r2) *
                     std::pow(std::max(0.0f, n_vector.dot(half_vector)), p);
result_color += la + ld + ls;

# 实现 Texture Shading Fragment Shader

把纹理颜色视为公式中的 kdk_d,实现 Texture Shading Fragment Shader。

return_color = payload.texture->getColor(payload.tex_coords.x(),
                                         payload.tex_coords.y());

# 实现 Bump mapping

Bump mapping 是一种通过法线贴图来模拟表面细节的技术。在实现 Bump mapping 的基础上,我们需要计算出偏移后的法线。这里参考注释。

float x = normal.x();
float y = normal.y();
float z = normal.z();
Eigen::Vector3f t = {x * y / sqrt(x * x + z * z), sqrt(x * x + z * z),
                     z * y / sqrt(x * x + z * z)};
Eigen::Vector3f b = normal.cross(t);
Eigen::Matrix3f TBN;
TBN << t, b, normal;
float u = payload.tex_coords.x();
float v = payload.tex_coords.y();
float dU =
    kh * kn *
    (payload.texture->getColor(u + 1.0f / payload.texture->width, v).norm() -
     payload.texture->getColor(u, v).norm());
float dV =
    kh * kn *
    (payload.texture->getColor(u, v + 1.0f / payload.texture->height).norm() -
     payload.texture->getColor(u, v).norm());
Eigen::Vector3f ln = {-dU, -dV, 1};
normal = (TBN * ln).normalized();
Eigen::Vector3f result_color = {0, 0, 0};
result_color = normal;

# 实现 displacement mapping

在实现 Bump mapping 的基础上,我们需要计算出偏移后的顶点位置。这里参考注释。

point = point + kn * normal * payload.texture->getColor(u, v).norm();

# 双线性纹理插值

Eigen::Vector3f getColorBilinear(float u, float v) {
  u = u * width;
  v = (1 - v) * height;
  int u0 = (int)u;
  int v0 = (int)v;
  int u1 = std::min(u0 + 1, width - 1);
  int v1 = std::min(v0 + 1, height - 1);
  float du = u - u0;
  float dv = v - v0;
  auto color0 = image_data.at<cv::Vec3b>(v0, u0);
  auto color1 = image_data.at<cv::Vec3b>(v0, u1);
  auto color2 = image_data.at<cv::Vec3b>(v1, u0);
  auto color3 = image_data.at<cv::Vec3b>(v1, u1);
  Eigen::Vector3f color;
  for (int i = 0; i < 3; i++) {
    color[i] = (1 - du) * (1 - dv) * color0[i] + du * (1 - dv) * color1[i] +
               (1 - du) * dv * color2[i] + du * dv * color3[i];
  }
  return color;
}