作业三

作业

光栅化插值算法

实现对三角形三个点绑定的法向量、颜色、纹理颜色的插值

重心坐标插值

重心坐标

对于三角形内部的点我们都可以通过形如:
$(x, y) =\alpha A + \beta B + \gamma C$
其中 $\alpha + \beta + \gamma = 1$

我们称$(\alpha, \beta, \gamma)$为重心坐标(三维坐标依旧成立)

重心坐标

计算重心坐标

三角形内的点的$\alpha \beta \gamma$的值是其三角形顶点的对边与三角形内点构成的三角形的面积 下图所示

重心坐标值

通过上面推到,重心坐标计算公式如图

有了重心坐标 我们就可以对三角形顶点上标定的向量愉快的插值了^^
插值对象可以是深度 法线 颜色等 如图所示

代码

// 计算重心坐标
static std::tuple<float, float, float> computeBarycentric2D(float x, float y, const Vector4f* v){
    float c1 = (x*(v[1].y() - v[2].y()) + (v[2].x() - v[1].x())*y + v[1].x()*v[2].y() - v[2].x()*v[1].y()) / (v[0].x()*(v[1].y() - v[2].y()) + (v[2].x() - v[1].x())*v[0].y() + v[1].x()*v[2].y() - v[2].x()*v[1].y());
    float c2 = (x*(v[2].y() - v[0].y()) + (v[0].x() - v[2].x())*y + v[2].x()*v[0].y() - v[0].x()*v[2].y()) / (v[1].x()*(v[2].y() - v[0].y()) + (v[0].x() - v[2].x())*v[1].y() + v[2].x()*v[0].y() - v[0].x()*v[2].y());
    float c3 = (x*(v[0].y() - v[1].y()) + (v[1].x() - v[0].x())*y + v[0].x()*v[1].y() - v[1].x()*v[0].y()) / (v[2].x()*(v[0].y() - v[1].y()) + (v[1].x() - v[0].x())*v[2].y() + v[0].x()*v[1].y() - v[1].x()*v[0].y());
    return {c1,c2,c3};
}

// 三维向量插值 二维等类似
static Eigen::Vector3f interpolate(float alpha, float beta, float gamma, const Eigen::Vector3f& vert1, const Eigen::Vector3f& vert2, const Eigen::Vector3f& vert3, float weight)
{
    return (alpha * vert1 + beta * vert2 + gamma * vert3) / weight;
}

实现Blinn-Phong模型计算

Bline-Phong模型主要分为三种反射

光的强度

光的强度衰减和距离的平方成反比,公式为 $I/r^2$ 其中$I$是原始光强,$r$是光于物体之间的距离

环境光照

环境光照描述的是间接光照的影响 由于间接光照计算过于困难 因此Blinn-Phong模型用个常量表示间接光照

漫反射

漫反射描述的是光打在物体表面上向四面八方反射
因此可以想到漫反射只会受光的入射$L$和物体表面法线$N$影响

镜面反射

镜面反射描述光反射时物体的高光
这于观察者的视线V和入射光线的反射R相关(Phone)
我们也可以用半程向量H代替R的计算(Blinn-Phone)

代码

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 light_dir = (light.position - point);
        auto V = (eye_pos - point).normalized();
        auto L = (light.position - point).normalized();
        auto H = (V + L).normalized();

        float r = light_dir.dot(light_dir);

        auto diffuse = std::max(L.dot(normal), 0.0f) * kd.cwiseProduct((light.intensity / r));
        auto specular = std::pow(std::max(0.0f, H.dot(normal)), p) * ks.cwiseProduct((light.intensity / r));
        auto ambient = ka.cwiseProduct(amb_light_intensity);

        result_color += (diffuse + specular + ambient);
    }

    return result_color * 255.f;
}

纹理查找

  • 纹理查询主要是通过插值获得纹理坐标([0.0..1.0]) 然后去texture中找寻对应的像素
  • 由于纹理坐标是浮点(且归一化了) 且每个纹理坐标不一定能一一对应texture中找的像素
  • 这样我们需要对查询的纹理进行过滤 过滤主要分为邻近点过滤线性过滤

邻近点过滤

顾名思义,找纹理坐标最对应的点(即当纹理坐标浮点数转换为像素坐标的定点时 最靠近顶点的像素)

线性插值

对于纹理坐标中对应四个像素进行双线性插值

凹凸贴图

凹凸贴图主要是通过贴图的高度差来算出法线位置(感觉和法线贴图作用一样 处理比法线贴图麻烦?) 通过光照实现视觉效果 需要将其转换到切线空间

通过求导算出切线 将其旋转90就能得出法线位置

代码

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)

    float x = normal.x();
    float y = normal.y();
    float z = normal.z();

    Eigen::Vector3f t = {x * y / std::sqrt(x*x + z*z), std::sqrt(x*x + z*z), z*y / std::sqrt(x*x + z*z)};
    Eigen::Vector3f b = normal.cross(t);
    Eigen::Matrix3f TBN;
      TBN << t.x(), b.x(), normal.x(),
             t.y(), b.y(), normal.y(),
             t.z(), b.z(), normal.z();

    float u = payload.tex_coords.x();
    float v = payload.tex_coords.y();
    float w = payload.texture->width;
    float h = payload.texture->height;
    float dU = kh * kn * (payload.texture->getColor(u + 1.0f / w , v).norm() - payload.texture->getColor(u, v).norm());
    float dV = kh * kn * (payload.texture->getColor(u, v + 1.0f / h).norm() - payload.texture->getColor(u, v).norm());

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

    Eigen::Vector3f ln{-dU, -dV, 1.0f};
    normal = TBN * ln;
    normal = 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 light_dir = (light.position - point);
        auto V = (eye_pos - point).normalized();
        auto L = (light.position - point).normalized();
        auto H = (V + L).normalized();

        float r = light_dir.dot(light_dir);

        auto diffuse = std::max(L.dot(normal), 0.0f) * kd.cwiseProduct((light.intensity / r));
        auto specular = std::pow(std::max(0.0f, H.dot(normal)), p) * ks.cwiseProduct((light.intensity / r));
        auto ambient = ka.cwiseProduct(amb_light_intensity);

        result_color += (diffuse + specular + ambient);
    }

    return result_color * 255.f;
}

置换贴图

通过贴图改变物体的点位

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)

    float x = normal.x();
    float y = normal.y();
    float z = normal.z();

    Eigen::Vector3f t = {x * y / std::sqrt(x*x + z*z), std::sqrt(x*x + z*z), z*y / std::sqrt(x*x + z*z)};
    Eigen::Vector3f b = normal.cross(t);
    Eigen::Matrix3f TBN;
      TBN << t.x(), b.x(), normal.x(),
             t.y(), b.y(), normal.y(),
             t.z(), b.z(), normal.z();

    float u = payload.tex_coords.x();
    float v = payload.tex_coords.y();
    float w = payload.texture->width;
    float h = payload.texture->height;
    float dU = kh * kn * (payload.texture->getColor(u + 1.0f / w , v).norm() - payload.texture->getColor(u, v).norm());
    float 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;

    auto n = normal;

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

    return result_color * 255.f;
}

引用

最后修改:2023 年 02 月 02 日
如果觉得我的文章对你有用,请随意赞赏