Games101作业3

1.修改函数 rasterize_triangle(const Triangle& t) in rasterizer.cpp: 在此 处实现与作业 2 类似的插值算法,实现法向量、颜色、纹理颜色的插值。

与作业2的差别就是需要自己计算插值后的颜色,uv等信息。函数参数新增了一个参数是观察空间的顶点坐标。set_pixel(Vector2i(x, y), pixel_color);这个也相对作业2进行了改动,节省了空间。计算了插值后的信息以后存储到结构体中,然后将结构体的信息传递给shader计算出颜色,之后设置像素颜色并存储深度值。这里会出现异常因为模型的存储位置不对,需要更改

//Screen space rasterization
void rst::rasterizer::rasterize_triangle(const Triangle& t, const std::array<Eigen::Vector3f, 3>& view_pos) 
{
    auto v = t.toVector4();

    // TODO : Find out the bounding box of current triangle.
    float min_x = width;
    float max_x = 0;
    float min_y = height;
    float max_y = 0;

    // find out the bounding box of current triangle
    for (int i = 0; i < 3; i++) {
        min_x = min(v[i].x(), min_x);
        max_x = max(v[i].x(), max_x);
        min_y = min(v[i].y(), min_y);
        max_y = max(v[i].y(), max_y);
    }


    for (int y = min_y; y < max_y; y++)
    {
        for (int x = min_x; x < max_x; x++)
        {

            int index = get_index(x, y);

            if (insideTriangle(x+0.5, y+0.5, t.v))
            {
                auto [alpha, beta, gamma] = computeBarycentric2D(x, y, t.v);
                float w_reciprocal = 1.0 / (alpha / v[0].w() + beta / v[1].w() + gamma / v[2].w());
                float z_interpolated = alpha * v[0].z() / v[0].w() + beta * v[1].z() / v[1].w() + gamma * v[2].z() / v[2].w();
                z_interpolated *= w_reciprocal;

                if (z_interpolated < depth_buf[index]) {

                    Vector3f interpolated_color = interpolate(alpha, beta, gamma, t.color[0], t.color[1], t.color[2], 1);
                    Vector3f interpolated_normal = interpolate(alpha, beta, gamma, t.normal[0], t.normal[1], t.normal[2], 1);
                    Vector2f interpolated_texcoords = interpolate(alpha, beta, gamma, t.tex_coords[0], t.tex_coords[1], t.tex_coords[2], 1);
                    Vector3f 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);
                    depth_buf[index] = z_interpolated;
                }
            }
        }
    }


 
}

2. 修改函数 get_projection_matrix() in main.cpp

这一步将作业2的投影矩阵函数复制过来就可以。

完成这两步以后就可以将main函数的着色模型先改成normal_fragment_shader显示法线颜色了

3. 修改函数 phong_fragment_shader() in main.cpp: 实现 Blinn-Phong 模型计 算 Fragment Color.

这部分要计算环境光,漫反射和高光反射,先分别计算各部分计算需要的参数,最后带入公式计算即可,这里我还顺便计算了一下phong着色模型

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 *amor* object.
        Vector3f l = (light.position - point);
        Vector3f v = eye_pos - point;
        Vector3f h = l.normalized() + v.normalized();
        Vector3f r = reflect(-l,normal);
        float r_square = l.dot(l);
        float ndotl = normal.normalized().dot(l.normalized());
        float vdotr = v.normalized().dot(r.normalized());
        float ndoth = normal.normalized().dot(h.normalized());

        Vector3f ambientColor = ka.cwiseProduct(amb_light_intensity);
        Vector3f diffuseColor = kd.cwiseProduct(light.intensity / r_square * max(0.0f, ndotl));
        Vector3f specularColor = ks.cwiseProduct(light.intensity / r_square) * max(0.0f, pow(ndoth, p));
       // Vector3f specularColor = ks.cwiseProduct(light.intensity / r_square * max(0.0f, pow(vdotr,p)));

        result_color += ambientColor + diffuseColor + specularColor;
        
    }

    return result_color * 255.f;
}

最后结果如下

4. 修改函数 texture_fragment_shader() in main.cpp: 在实现 Blinn-Phong 的基础上,将纹理颜色视为公式中的 kd,实现 Texture Shading Fragment Shader.

这一步用uv坐标对纹理采样获得颜色即可,但是直接采样的话会报错,发现是有一些u坐标出现了负值,直接将u坐标限制在0-1之间即可解决,其它代码和phong shader一样

    Eigen::Vector3f return_color = {0, 0, 0};
    if (payload.texture)
    {
        // TODO: Get the texture value at the texture coordinates of the current fragment
       // return_color = payload.texture->getColor( clamp(payload.tex_coords.x(),0.0f,1.0f), payload.tex_coords.y());
        float u = payload.tex_coords.x();
        float v = payload.tex_coords.y();
        return_color = payload.texture->getColor(clamp(u,0.0f,1.0f), v);

    }
    Eigen::Vector3f texture_color;
    texture_color << return_color.x(), return_color.y(), return_color.z();

5.修改函数 bump_fragment_shader() in main.cpp: 在实现 Blinn-Phong 的 基础上,仔细阅读该函数中的注释,实现 Bump mapping

这部分通过uv采样法线贴图之后再通过TBN矩阵将法线转换到世界空间即可

这里也出现了一个异常,定位问题会出现在texture.hpp文件的Texture类的成员函数getColor里image_data.at函数里面。发现该函数的两个参数传入,超过了图片像素大小700*700的限制,导致出现越界的情况。所以在这个函数之前增加限定。

Eigen::Vector3f getColor(float u, float v)
    {
        if (u < 0)u = 0.0f; 
        if (u > 1)u = 0.999f; 
        if (v < 0)v = 0.0f; 
        if (v > 1)v = 0.999f;

        auto u_img = u * width;
        auto v_img = (1 - v) * height;
        auto color = image_data.at<cv::Vec3b>(v_img, u_img);
        return Eigen::Vector3f(color[0], color[1], color[2]);
    }
Eigen::Vector3f bump_fragment_shader(const fragment_shader_payload& payload)
{
    Eigen::Vector3f normal = payload.normal;
    float kh = 0.2, kn = 0.1;

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

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

    return result_color * 255.f;
}

6. 修改函数 displacement_fragment_shader() in main.cpp: 在实现 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)
        Vector3f n = normal;
    float x = n.x(), y = n.y(), z = n.z();
    Vector3f t(x * y / sqrt(x * x + z * z), sqrt(x * x + z * z), z * y / sqrt(x * x + z * z));
    Vector3f b = n.cross(t);
    Matrix3f TBN;
    TBN << t.x(), b.x(), n.x(),
        t.y(), b.y(), n.y(),
        t.z(), b.z(), n.z();
    int w = payload.texture->width;
    int h = payload.texture->height;
    float u = payload.tex_coords.x();
    float v = payload.tex_coords.y();
    float dU = 0, dV = 0;
    if (payload.texture)
    {
        dU = kh * kn * (payload.texture->getColor(u + 1.0f / w, v).norm() - payload.texture->getColor(u, v).norm());
        dV = kh * kn * (payload.texture->getColor(u, v + 1.0f / h).norm() - payload.texture->getColor(u, v).norm());
        point += kn * n * (payload.texture->getColor(u, v).norm());
    }
    Vector3f ln(-dU, -dV, 1);
    normal = (TBN * ln).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.
        Vector3f l = (light.position - point);
        Vector3f v = eye_pos - point;
        Vector3f h = l.normalized() + v.normalized();
        float r_square = l.dot(l);
        float ndotl = normal.normalized().dot(l.normalized());
        float ndoth = normal.normalized().dot(h.normalized());
        Vector3f ambient = ka.cwiseProduct(amb_light_intensity);
        Vector3f diffuse = kd.cwiseProduct(light.intensity / r_square) * max(0.0f, ndotl);
        Vector3f specular = ks.cwiseProduct(light.intensity / r_square) * pow(max(0.0f, ndoth), p);
        result_color += ambient + diffuse + specular;
    }

    return result_color * 255.f;
}

7.实现双线性纹理插值

双线性插值有两个步骤,

  1. 寻找目标点周边最近的4个纹理像素;
  2. 根据目标点与4个纹理像素中心的距离计算插值权重。

主要原理可以参照下图

第一步要计算周围的四个点就要先找到周围的最近的整数坐标值,接着就可以计算周围四个点的uv坐标,用新的四个uv坐标进行采样之后进行插值就可以了,主要代码如下

    cv::Vec3b lerp(cv::Vec3b color0, cv::Vec3b color1, float t)
    {
        cv::Vec3b result_color;
        for (int i = 0; i < 3; i++)
        {
            result_color[i] = color0[i] + (color1[i] - color0[i]) * t;
        }
        return result_color;
    }

    Eigen::Vector3f getColorBilinear(float u, float v)
    {
        if (u < 0)u = 0.0f;
        if (u > 1)u = 0.999f;
        if (v < 0)v = 0.0f;
        if (v > 1)v = 0.999f;
        float u_img = u * width;
        float v_img = (1 - v) * height;

        int u_center = round(u_img);
        int v_center = round(v_img);

        int u0 = std::clamp(u_center - 1, 0, width);
        int u1 = std::clamp(u_center + 1, 0, width);
        int v0 = std::clamp(v_center - 1, 0, width);
        int v1 = std::clamp(v_center + 1, 0, width);

        std::array<cv::Vec3b, 4> colors;
        colors[0] = image_data.at<cv::Vec3b>(v0, u0);
        colors[1] = image_data.at<cv::Vec3b>(v1, u0);
        colors[2] = image_data.at<cv::Vec3b>(v0, u1);
        colors[3] = image_data.at<cv::Vec3b>(v1, u1);

        float s = u_img - std::clamp(u_center - 0.5, 0.5, width - 0.5);
        float t = v_img - std::clamp(v_center - 0.5, 0.5, height - 0.5);


        // TODO: Implement Bilinear Interpolation Sampler

        cv::Vec3b color = lerp(lerp(colors[0], colors[1], t), lerp(colors[2], colors[3], t), s);
        return Eigen::Vector3f(color[0], color[1], color[2]);

  • 18
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值