GAMES101-LAB3

一、作业总览

本次作业框架添加了Object Loader(用于加载三维模型), Vertex Shader 与Fragment Shader,并且支持了纹理映射。

需要完成的任务:

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

二、参考代码

2.1 rasterize_triangle(const Triangle& t)

  • 类似作业2的插值
void rst::rasterizer::rasterize_triangle(const Triangle& t, const std::array<Eigen::Vector3f, 3>& view_pos) 
{
    // TODO: From your HW3, get the triangle rasterization code.
    auto v = t.toVector4();

    // TODO : Find out the bounding box of current triangle.
    int bounding_box_x_left = std::min(v[0].x(), std::min(v[1].x(), v[2].x()));
    int bounding_box_x_right = std::max(v[0].x(), std::max(v[1].x(), v[2].x()));
    int bounding_box_y_left = std::min(v[0].y(), std::min(v[1].y(), v[2].y()));
    int bounding_box_y_right = std::max(v[0].y(), std::max(v[1].y(), v[2].y()));

    // TODO: Inside your rasterization loop:
    //    * v[i].w() is the vertex view space depth value z.
    //    * Z is interpolated view space depth for the current pixel
    //    * zp is depth between zNear and zFar, used for z-buffer
    for(int x = bounding_box_x_left; x <= bounding_box_x_right; x++){
        for(int y = bounding_box_y_left; y <= bounding_box_y_right; 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;
                if(zp < depth_buf[get_index(x, y)]){
                    // TODO: Interpolate the attributes:
                    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;
                    //Instead of passing the triangle's color directly to the frame buffer, pass the color to the shaders first to get the final color;
                    auto pixel_color = fragment_shader(payload);

                    set_pixel(Eigen::Vector2i(x, y), pixel_color);
                }
            }
        }
    }
}

2.2 get_projection_matrix()

  • 投影矩阵,详细内容参考LAB1
Eigen::Matrix4f get_projection_matrix(float eye_fov, float aspect_ratio, float zNear, float zFar)
{
    // TODO: Use the same projection matrix from the previous assignment
    Eigen::Matrix4f projection = Eigen::Matrix4f::Identity();

    // TODO: Implement this function
    // Create the projection matrix for the given parameters.
    // Then return it.
    Eigen::Matrix4f persp_matrix;//透视矩阵 
    Eigen::Matrix4f translate_matrix;//移动矩阵
    Eigen::Matrix4f scale_matirx;//缩放矩阵

    persp_matrix << zNear, 0, 0, 0,
                    0, zNear, 0, 0,
                    0, 0, zNear + zFar, -zNear * zFar,
                    0, 0, 1, 0;
    
    float halfAngle = eye_fov / 2 / 180 * MY_PI;
    float height = -2 * std::tan(halfAngle) * zNear;//没有负号则三角形上下颠倒
    float width = height * aspect_ratio;

    translate_matrix << 1, 0, 0, 0,
                        0, 1, 0, 0, 
                        0, 0, 1, -(zNear + zFar) / 2,
                        0, 0, 0, 1;
    scale_matirx << 2 / width, 0, 0, 0,
                    0, 2 / height, 0, 0,
                    0, 0, 2 / (zNear - zFar), 0,
                    0, 0, 0, 1;

    projection = scale_matirx * translate_matrix * persp_matrix;

    return projection;
}

2.3 phong_fragment_shader()

GAMES101-LAB3_第1张图片
GAMES101-LAB3_第2张图片

Eigen::Vector3f phong_fragment_shader(const fragment_shader_payload& payload)
{
    Eigen::Vector3f ka = Eigen::Vector3f(0.005, 0.005, 0.005);
    Eigen::Vector3f kd = payload.color;
    Eigen::Vector3f ks = Eigen::Vector3f(0.7937, 0.7937, 0.7937);

    auto l1 = light{{20, 20, 20}, {500, 500, 500}};
    auto l2 = light{{-20, 20, 0}, {500, 500, 500}};

    std::vector<light> lights = {l1, l2};
    Eigen::Vector3f amb_light_intensity{10, 10, 10};
    Eigen::Vector3f eye_pos{0, 0, 10};

    float p = 150;

    Eigen::Vector3f color = payload.color;
    Eigen::Vector3f point = payload.view_pos;
    Eigen::Vector3f normal = payload.normal;

    Eigen::Vector3f result_color = {0, 0, 0};
    for (auto& light : lights)
    {
        // TODO: For each light source in the code, calculate what the *ambient*, *diffuse*, and *specular* 
        // components are. Then, accumulate that result on the *result_color* object.
        auto l = (light.position - point).normalized();
        auto v = (eye_pos - point).normalized();
        auto h = (l + v).normalized();
        //距离平方
        auto r_square = (light.position - point).squaredNorm();
        //cwiseProduct: 逐元素进行乘法
        auto La = ka.cwiseProduct(amb_light_intensity);
        auto Ld = kd.cwiseProduct(light.intensity / r_square) * MAX(0, normal.dot(l)); 
        auto Ls = ks.cwiseProduct(light.intensity / r_square) * (pow (MAX(0, normal.dot(h)), p));

        result_color = La + Ld + Ls;
    }

    return result_color * 255.f;
}

2.4 texture_fragment_shader()

  • 在实现Blinn-Phong的基础上,将纹理颜色视为公式中的kd,实现Texture Shading Fragment Shader.
Eigen::Vector3f texture_fragment_shader(const fragment_shader_payload& payload)
{
    Eigen::Vector3f return_color = {0, 0, 0};
    if (payload.texture)
    {
        // TODO: Get the texture value at the texture coordinates of the current fragment
        float u = payload.tex_coords.x();
        float v = payload.tex_coords.y();
        //获得u,v对应的纹理颜色
        return_color = payload.texture->getColor(u, v);
    }
    Eigen::Vector3f texture_color;
    texture_color << return_color.x(), return_color.y(), return_color.z();

    Eigen::Vector3f ka = Eigen::Vector3f(0.005, 0.005, 0.005);
    Eigen::Vector3f kd = texture_color / 255.f;
    Eigen::Vector3f ks = Eigen::Vector3f(0.7937, 0.7937, 0.7937);

    auto l1 = light{{20, 20, 20}, {500, 500, 500}};
    auto l2 = light{{-20, 20, 0}, {500, 500, 500}};

    std::vector<light> lights = {l1, l2};
    Eigen::Vector3f amb_light_intensity{10, 10, 10};
    Eigen::Vector3f eye_pos{0, 0, 10};

    float p = 150;

    Eigen::Vector3f color = texture_color;
    Eigen::Vector3f point = payload.view_pos;
    Eigen::Vector3f normal = payload.normal;

    Eigen::Vector3f result_color = {0, 0, 0};

    for (auto& light : lights)
    {
        // TODO: For each light source in the code, calculate what the *ambient*, *diffuse*, and *specular* 
        // components are. Then, accumulate that result on the *result_color* object.
        auto l = (light.position - point).normalized();
        auto v = (eye_pos - point).normalized();
        auto r_square = (light.position - point).squaredNorm();
        auto h = (l + v).normalized();

        auto La = ka.cwiseProduct(amb_light_intensity);
        auto Ld = kd.cwiseProduct(light.intensity / r_square) * MAX(0, normal.dot(l));
        auto Ls = ks.cwiseProduct(light.intensity / r_square) * pow( MAX(0, normal.dot(h)), p);

        result_color += La + Ld + Ls;
    }

    return result_color * 255.f;
}

2.5 bump_fragment_shader()

  • 在实现Blinn-Phong 的基础上,实现Bump mapping.

    • 法线贴图可以定义表面上任何不同位置的相对高度
      • 相对高度发生变化,导致法线发生变化,导致shading发生变化(出现明暗对比)
      • 逻辑上的高度改变
  • 对于二维:
    GAMES101-LAB3_第3张图片

    • 通过凹凸贴图算切线(1, dp);
    • 求法线(-dp, 1)
  • 对于三维:
    GAMES101-LAB3_第4张图片

Eigen::Vector3f bump_fragment_shader(const fragment_shader_payload& payload)
{
    
    Eigen::Vector3f ka = Eigen::Vector3f(0.005, 0.005, 0.005);
    Eigen::Vector3f kd = payload.color;
    Eigen::Vector3f ks = Eigen::Vector3f(0.7937, 0.7937, 0.7937);

    auto l1 = light{{20, 20, 20}, {500, 500, 500}};
    auto l2 = light{{-20, 20, 0}, {500, 500, 500}};

    std::vector<light> lights = {l1, l2};
    Eigen::Vector3f amb_light_intensity{10, 10, 10};
    Eigen::Vector3f eye_pos{0, 0, 10};

    float p = 150;

    Eigen::Vector3f color = payload.color; 
    Eigen::Vector3f point = payload.view_pos;
    Eigen::Vector3f normal = payload.normal;


    float kh = 0.2, kn = 0.1;

    // TODO: Implement bump mapping here
    // Let n = normal = (x, y, z)
    // Vector t = (x*y/sqrt(x*x+z*z),sqrt(x*x+z*z),z*y/sqrt(x*x+z*z))
    // Vector b = n cross product t
    // Matrix TBN = [t b n]
    // dU = kh * kn * (h(u+1/w,v)-h(u,v))
    // dV = kh * kn * (h(u,v+1/h)-h(u,v))
    // Vector ln = (-dU, -dV, 1)
    // Normal n = normalize(TBN * ln)
    auto x = normal.x();
    auto y = normal.y();
    auto z = normal.z();

    Vector3f t(x*y / sqrt(x*x+z*z), sqrt(x*x+z*z), z*y/sqrt(x*x+z*z));
    Vector3f b(normal.cross(t));
    Matrix3f TBN;
    TBN << t.x(), b.x(), normal.x(),
           t.y(), b.y(), normal.y(),
           t.z(), b.z(), normal.z();

    auto u = payload.tex_coords.x();
    auto v = payload.tex_coords.y();
    auto w = payload.texture->width;
    auto h = payload.texture->height;

    auto du = kh * kn * ( (payload.texture->getColor(u + 1.0f / w, v) - payload.texture->getColor(u, v)).norm() );
    auto dv = kh * kn * ( (payload.texture->getColor(u, v + 1.0 / h) - payload.texture->getColor(u, v)).norm() );
    Vector3f ln(-du, -dv, 1);
    normal = TBN * ln;
    Vector3f result_color = normal.normalized();

    return result_color * 255.f;
}

2.6 displacement_fragment_shader()

  • 在实现Bump mapping 的基础上,实现displacement mapping.
    • 几何体物理上表面高度改变
Eigen::Vector3f displacement_fragment_shader(const fragment_shader_payload& payload)
{
    
    Eigen::Vector3f ka = Eigen::Vector3f(0.005, 0.005, 0.005);
    Eigen::Vector3f kd = payload.color;
    Eigen::Vector3f ks = Eigen::Vector3f(0.7937, 0.7937, 0.7937);

    auto l1 = light{{20, 20, 20}, {500, 500, 500}};
    auto l2 = light{{-20, 20, 0}, {500, 500, 500}};

    std::vector<light> lights = {l1, l2};
    Eigen::Vector3f amb_light_intensity{10, 10, 10};
    Eigen::Vector3f eye_pos{0, 0, 10};

    float p = 150;

    Eigen::Vector3f color = payload.color; 
    Eigen::Vector3f point = payload.view_pos;
    Eigen::Vector3f normal = payload.normal;

    float kh = 0.2, kn = 0.1;
    
    // TODO: Implement displacement mapping here
    // Let n = normal = (x, y, z)
    // Vector t = (x*y/sqrt(x*x+z*z),sqrt(x*x+z*z),z*y/sqrt(x*x+z*z))
    // Vector b = n cross product t
    // Matrix TBN = [t b n]
    // dU = kh * kn * (h(u+1/w,v)-h(u,v))
    // dV = kh * kn * (h(u,v+1/h)-h(u,v))
    // Vector ln = (-dU, -dV, 1)
    // Position p = p + kn * n * h(u,v)
    // Normal n = normalize(TBN * ln)
    auto x = normal.x();
    auto y = normal.y();
    auto z = normal.z();

    Vector3f t(x*y / sqrt(x*x+z*z), sqrt(x*x+z*z), z*y/sqrt(x*x+z*z));
    Vector3f b(normal.cross(t));
    Matrix3f TBN;
    TBN << t.x(), b.x(), normal.x(),
           t.y(), b.y(), normal.y(),
           t.z(), b.z(), normal.z();

    auto u = payload.tex_coords.x();
    auto v = payload.tex_coords.y();
    auto w = payload.texture->width;
    auto h = payload.texture->height;

    auto du = kh * kn * ( (payload.texture->getColor(u + 1.0f / w, v) - payload.texture->getColor(u, v)).norm() );
    auto dv = kh * kn * ( (payload.texture->getColor(u, v + 1.0 / h) - payload.texture->getColor(u, v)).norm() );
    Vector3f ln{-du, -dv, 1.0f};
    // 目标点位置发生变化
    point = point + kn * normal * (payload.texture->getColor(u, v).norm());
    normal = TBN * ln;
    normal.normalized();
    Eigen::Vector3f result_color = {0, 0, 0};

    for (auto& light : lights)
    {
        // TODO: For each light source in the code, calculate what the *ambient*, *diffuse*, and *specular* 
        //components are. Then, accumulate that result on the *result_color* object.
        auto l = (light.position - point).normalized();
        auto v = (eye_pos - point).normalized();
        auto r_square = (light.position - point).squaredNorm();
        auto h = (l + v).normalized();

        auto La = ka.cwiseProduct(amb_light_intensity);
        auto Ld = kd.cwiseProduct(light.intensity / r_square) * MAX(0, normal.dot(l));
        auto Ls = ks.cwiseProduct(light.intensity / r_square) * pow( MAX(0, normal.dot(h)), p);

        result_color += La + Ld + Ls;
    }

    return result_color * 255.f;
}

三、编译

mkdir build
cd ./build
cmake …
make

texture: 使用代码中的texture shader.
使用举例: ./Rasterizer output.png texture

normal: 使用代码中的normal shader.
使用举例: ./Rasterizer output.png normal

phong: 使用代码中的blinn-phong shader.
使用举例: ./Rasterizer output.png phong

bump: 使用代码中的bump shader.
使用举例: ./Rasterizer output.png bump

displacement: 使用代码中的displacement shader.
使用举例: ./Rasterizer output.png displacement

当你修改代码之后,你需要重新make 才能看到新的结果


附件

作业3压缩包

你可能感兴趣的:(Lab,#,GAMES101-Lab,图形渲染,Lab)