【GAMES101】作业3 Pipeline and Shading(补档)

【GAMES101】作业3 Pipeline and Shading

之前一篇文章完成了作业3的任务1-4,留下5、6两个任务分别为凹凸贴图和位移贴图,由于这两个问题没有在课程上细说,所以实现起来需要自己额外花费一些功夫,通过这篇文章详细讲解一下这两个问题的解决步骤。

目录

凹凸贴图原理

作业程序实现

5、修改bump_fragment_shader函数,实现Bump Mapping

 6、修改函数displacement_fragment_shader,实现displacement mapping(位移贴图)

TBN矩阵原理 


凹凸贴图原理

先给出课程中关于凹凸贴图的说明:所谓凹凸贴图,是指在着色的时候扰动物体表面法向量,达到在计算的时候改变着色值的效果,黑色的圆滑曲线为物体表面,黄色曲线为凹凸贴图,可以看出来,原本需要着色的位置法线向量为p,但是经过凹凸贴图修改后,法线向量变成了n,自然会改变漫反射和高光的值,达到修改着色的作用。

作业程序实现

5、修改bump_fragment_shader函数,实现Bump Mapping

所以本质问题是怎么计算每个面修改后的法线向量,在程序框架中,已经以注释的方式给出了计算过程。

    // 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)

根据注释内容可以将具体代码完善出来,大概可以看得出来,求法线向量的过程中最关键的一步是求解TBN矩阵,这个矩阵由三个列向量t,b,n组成,具体求TBN矩阵的方法我打算放到最后讨论(因为太难了,现在还不会)。

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 n = normal.normalized();
    auto x = n.x();
    auto y = n.y();
    auto z = n.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 = n.cross(t);
    Eigen::Matrix3f TBN;
    TBN << t.x(), b.x(), n.x(),
           t.y(), b.y(), n.y(),
           t.z(), b.z(), n.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).norm() - payload.texture->getColor(u, v).norm());
    auto dV = kh * kn * (payload.texture->getColor(u, v + 1.0f / h).norm() - payload.texture->getColor(u, v).norm());
    Eigen::Vector3f ln(-dU, -dV, 1.0f);
    normal = (TBN * ln).normalized();

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

    return result_color * 255.f;
}

完善代码并运行,我们可以得到一个看起来凹凸不平的牛牛

 6、修改函数displacement_fragment_shader,实现displacement mapping(位移贴图)

 相较于Bump Mapping,位移贴图的代码注释仅仅增加了一句

// Position p = p + kn * n * h(u,v)

这句话是什么意思呢,这就涉及到位移贴图的原理,位移贴图使用与凹凸贴图一样的纹理,不同之处在于,位移贴图实实在在地修改了顶点的位置(point),根据修改之后的顶点位置,考虑光照因素,需要再次使用Blinn-Phong模型进行计算。

 整个代码只需要把Bump Mapping和Blinn-Phong模型融合在一起,再增加一行代码

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

用于修改三角形的顶点Vertices

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 n = normal.normalized();
    auto x = n.x();
    auto y = n.y();
    auto z = n.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 = n.cross(t);
    Eigen::Matrix3f TBN;
    TBN << t.x(), b.x(), n.x(),
           t.y(), b.y(), n.y(),
           t.z(), b.z(), n.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).norm() - payload.texture->getColor(u, v).norm());
    auto dV = kh * kn * (payload.texture->getColor(u, v + 1.0f / h).norm() - payload.texture->getColor(u, v).norm());
    Eigen::Vector3f ln(-dU, -dV, 1.0f);
    point += kn * n * (payload.texture->getColor(u, v).norm());
    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.
        Eigen::Vector3f l = light.position - point;
        Eigen::Vector3f v = eye_pos - point;
        Eigen::Vector3f h = l.normalized() + v.normalized();
        Eigen::Vector3f n = normal.normalized();
        auto r_square = l.dot(l);
        auto ambient = ka.cwiseProduct(amb_light_intensity);
        auto diffuse = kd.cwiseProduct(light.intensity / r_square) * std::max(0.0f, n.dot(l.normalized()));
        auto specular = ks.cwiseProduct(light.intensity / r_square) * std::pow(std::max(0.0f, n.dot(h.normalized())), p);
        result_color += (ambient + diffuse + specular);
    }

    return result_color * 255.f;
}

最后我们就可以得到一个凹凸不平并且考虑光照强度的牛牛了。

TBN矩阵原理 

最后来仔细讨论一下什么是TBN矩阵,以及是如何通过该矩阵求出修改后的法线向量的。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
pipeline and shading是计算机图形学中的两个重要概念。在Games101课程中,我们学习了这两个概念以及它们在游戏开发中的应用。 首先,管线(pipeline)是指图形渲染过程中的一系列阶段。这些阶段依次执行,以将3D模型转换为最终的2D图像。管线的主要阶段包括顶点处理、光栅化和片元处理。在顶点处理阶段,模型的顶点坐标被转换为屏幕空间坐标,并进行光照计算等操作。在光栅化阶段,模型的三角形被转化为像素,在屏幕上显示。在片元处理阶段,对每个像素执行颜色计算,以确定最终的像素颜色。 其次,着色(shading)是指确定模型表面每个像素的颜色和光照效果。常见的着色模型包括平面着色、高洛德着色和菲格斯特随机着色等。平面着色是最简单的一种,忽略了光照效果,给每个像素分配一个固定的颜色。高洛德着色则考虑了光照的方向和强度,使得模型表面看起来更加真实。菲格斯特随机着色则引入了随机过程,给模型表面增加纹理和细节。 在游戏开发中,管线和着色技术被广泛应用。通过管线的处理,可以实现更高的渲染效率和图像质量。而着色技术可以使游戏中的模型更加真实,增加玩家的沉浸感。此外,我们还可以通过改变管线和着色的参数来实现特殊的效果,如阴影、光线投射和折射等。 总的来说,pipeline and shading是计算机图形学中的重要概念。在Games101课程中,我们深入学习了这两个概念以及它们在游戏开发中的应用。通过理解和掌握管线和着色技术,我们可以实现更加高效和逼真的游戏图形效果。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值