GAMES101 作业3(附三角形重心坐标,Blinn-Phong光照模型及法线贴图推导)

目录

写在前面

第一题 三角形重心坐标

第二题 Blinn-Phong光照模型

第三题 纹理贴图

第四题 凹凸贴图实现及法线贴图推导

第五题 位移贴图


写在前面

        main 函数中  std::function<Eigen::Vector3f(fragment_shader_payload)> active_shader = displacement_fragment_shader 这一句是指定当前的着色方式,  将等号后的displacement_fragment_shader 改为 phong_fragment_shader 等方法函数名即可, 或者在属性中修改命令行参数也可以。


第一题 三角形重心坐标

题目: 对三角形顶点处的各属性进行插值, 并进行着色。

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

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

    // TODO: Interpolate the attributes:
    // auto interpolated_color
    // auto interpolated_normal
    // auto interpolated_texcoords
    // auto interpolated_shadingcoords

    // Use: fragment_shader_payload payload( interpolated_color, interpolated_normal.normalized(), interpolated_texcoords, texture ? &*texture : nullptr);
    // Use: payload.view_pos = interpolated_shadingcoords;
    // Use: Instead of passing the triangle's color directly to the frame buffer, pass the color to the shaders first to get the final color;
    // Use: auto pixel_color = fragment_shader(payload);

 
}

解析: 

1. 三角形重心坐标的推导:

        首先推导直线重心坐标, 然后由直线推广到三角形。

1.1 直线重心坐标: 

        设有向量 AB, A点坐标(a1, a2), B点坐标(b1, b2),  直线上有任意一点 C(c1, c2)。如何用 A, B 两点来表示 C?可以用点起始点坐标加上 k 倍的向量得到 C = A + k\vec{AB} = A + kB - kA = (1-k)A + kB 

因为 kAB 这个向量的起点是原点,所以 A + kAB 也就是把 kAB 这个向量的起点从原点挪到了 A 点。又因为 C 点在 AB 上, 所以要满足 0 <= k <= 1。公式中有 (1-k) + k = 1, 即 A, B 前的系数之和为 1。

 

1.2 三角形重心坐标:

        设三角形的三个顶点分别为 A(a1, a2, a3), B(b1, b2, b3), C(c1, c2, c3), AB 上有一点 D, CD上有一点 E。

        由直线重心坐标得到, D = (1 - m)A + mB, E = (1 - n)C + nD, 将 D 代入 E, 得:

        E = (1 - n)C + n((1 - m)A + mB) = (n - mn)A + mnB + (1 - n)C

        A, B, C 前面的系数之和为 n - mn + mn + 1 - n = 1, 则用 α, β, γ 分别替换 A, B, C 前面的系数, 可得 E =  αA + βB + γC, 且当时, E 点位于三角形内部。

        此时可以联立方程(Ax 为 A 的 x 坐标值, Ay 为 A 的 y 坐标值, 以此类推):

    \left\{\begin{matrix}\alpha + \beta + \gamma = 1 \\ \alpha A_x + \beta B_x + \gamma C_x = E_x \\ \alpha A_y + \beta B_y + \gamma C_y = E_y \end{matrix}\right.

        最后可以得到闫老师给出的公式, 这里偷下懒, 直接贴图。在这里插入图片描述

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

    // 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;
    auto v = t.toVector4();
    float x_max = std::max(std::max(v[0].x(), v[1].x()), v[2].x());
    float y_max = std::max(std::max(v[0].y(), v[1].y()), v[2].y());
    float x_min = std::min(std::min(v[0].x(), v[1].x()), v[2].x());
    float y_min = std::min(std::min(v[0].y(), v[1].y()), v[2].y());


    for (int i = x_min; i < x_max + 1; ++i)
    {
        for (int j = y_min; j < y_max + 1; ++j)
        {
            if (insideTriangle(i, j, t.v))
            {
                auto [alpha, beta, gamma] = computeBarycentric2D(i, j, 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 (depth_buf[get_index(i, j)] > z_interpolated)    //获取当前点在深度缓存中的值, 如果大于插值计算出的 z 值, 则上色
                {
                    //计算顶点的插值属性
                    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);
                    //初始化 payload 这个可供之后 Bling-Phong 模型和贴图使用
                    fragment_shader_payload payload(interpolated_color, interpolated_normal.normalized(), interpolated_texcoords, texture ? &*texture : nullptr);
                    payload.view_pos = interpolated_shadingcoords;
                    depth_buf[get_index(i, j)] = z_interpolated;
                    Vector2i temp(i, j);
                    //fragment_shader(payload) 的含义是: 获得当前激活的着色方法返回的颜色值
                    set_pixel(temp, fragment_shader(payload));
                }
            }
        }
    }
    // TODO: Interpolate the attributes:
    // auto interpolated_color
    // auto interpolated_normal
    // auto interpolated_texcoords
    // auto interpolated_shadingcoords

    // Use: fragment_shader_payload payload( interpolated_color, interpolated_normal.normalized(), interpolated_texcoords, texture ? &*texture : nullptr);
    // Use: payload.view_pos = interpolated_shadingcoords;
    // Use: Instead of passing the triangle's color directly to the frame buffer, pass the color to the shaders first to get the final color;
    // Use: auto pixel_color = fragment_shader(payload);
}

第二题 Blinn-Phong光照模型

题目: 实现 Blinn-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 *ambient*, *diffuse*, and *specular* 
        // components are. Then, accumulate that result on the *result_color* object.
        
    }

    return result_color * 255.f;
}

解析:

1. 漫反射:

kd: 漫反射系数, 个人认为是入射点对于不同颜色光的反射率

I / r²: 到达入射点的光照强度, r 是光源到入射点的距离, 因为在三维空间中, 所以光的强度与 r²成反比, 如果是二维空间, 则光强与 r 成反比。

max(0, n · l): 因为 n 和 l 都是单位向量, 所以 n · l 就是光照方向与法线的余弦值, 而取 0 和 余弦值的最大值的原因是余弦值可能是负值, 此时漫反射的结果会变为负值, 这显然不正确, 此时结果应该为 0 才对。

2. 镜面反射(高光)

        当观察方向和镜面反射方向十分接近时,  则法线方向和半程向量 h 也很接近, 因为计算半程向量的难度远小于计算镜面反射方向, 因此使用半程向量来计算高光项 Ls。此处 Ks 时镜面反射系数, 通常来说高光为白色, 因此 Ks 一般也设置为白色。系数 p 是为了增大 Ls 对α 的敏感度, p越大, Ls 衰减的速度就越快, 如下图。

3. 环境光

        环境光是一个常数, 在任何一个地方看到的颜色都是一个常数结果, 因此环境光的结果就是将这一点上反射的所有光都加起来(暂时这样认为, 实际上计算要用到全局光照技术)。

4. Blinn-Phong 反射模型

        将前三个计算结果相加, 则得到了 Blinn-Phong 反射模型的结果。

5. 代码: 

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.
        Eigen::Vector3f l = light.position - point;
        Eigen::Vector3f v = eye_pos - point;
        Eigen::Vector3f h = (l + v).normalized();
        Vector3f diffuse = kd.cwiseProduct(light.intensity / (l.dot(v)) * std::max(0.0f, (normal.normalized().dot(l.normalized()))));
        Vector3f specular = ks.cwiseProduct(light.intensity / (l.dot(l)) * pow(std::max(0.0f, payload.normal.normalized().dot(h)), 200));
        Vector3f ambient = ka.cwiseProduct(amb_light_intensity);
        result_color += diffuse + specular + ambient;
    }

    return result_color * 255.f;
}

第三题 纹理贴图

题目: 实现纹理贴图

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

    }
    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.

    }

    return result_color * 255.f;
}

解析:

        将 Blinn-Phong 中的漫反射系数换为片元在该点的纹理颜色向量即可。

Eigen::Vector3f texture_fragment_shader(const fragment_shader_payload& payload)
{
    Eigen::Vector3f return_color = { 0, 0, 0 };
    if (payload.texture)
    {
        // 获取纹理坐标的颜色
        return_color = payload.texture->getColor(payload.tex_coords.x(), payload.tex_coords.y());
    }
    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 };
    Eigen::Vector3f ambient = ka * amb_light_intensity[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 light_dir = light.position - point;
        Eigen::Vector3f view_dir = eye_pos - point;
        float r = light_dir.dot(light_dir);
        // ambient
        Eigen::Vector3f La = ka.cwiseProduct(amb_light_intensity);
        // diffuse
        Eigen::Vector3f Ld = kd.cwiseProduct(light.intensity / r);
        Ld *= std::max(0.0f, normal.normalized().dot(light_dir.normalized()));
        // specular
        Eigen::Vector3f h = (light_dir + view_dir).normalized();
        Eigen::Vector3f Ls = ks.cwiseProduct(light.intensity / r);
        Ls *= std::pow(std::max(0.0f, normal.normalized().dot(h)), p);

        result_color += (La + Ld + Ls);
    }
    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)


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

    return result_color * 255.f;
}

解析: 

        关于法线贴图这部分我的理解不一定准确, 只能现在这里记录, 如果日后发现错误再来修改。

1. 凹凸贴图的原理:

        利用凹凸贴图来改变原本光滑的平面的法线, 使原本光滑的平面产生凹凸感。

2. TBN 矩阵:

        这道题的难点应该就是 TBN 矩阵的理解。

        想理解 TBN 矩阵, 首先要理解切线空间, 我读了一些博客和帖子, 觉得这篇讲的最详尽且容易理解——切线空间(Tangent Space)完全解析

        下图是 UV 空间(切线空间), 假设映射到纹理上的三角形如图中所示, 其中向量

        E1 = ΔU1U + ΔV1V

        E2 = ΔU2U + ΔV2V

        再看 E1, E2 向量在三维空间中的表示, 如下图:         此时可以得到(再次偷懒, 公式都是从切线空间(Tangent Space) 的计算与应用复制来的):

E_{1} = \Delta U_{1}T + \Delta V_{1}B

E_{2} = \Delta U_{2}T + \Delta V_{2}B

        E1 在三维空间中的坐标记为 (E1x, E1y, E1z),  E2 的坐标记为 (E2x, E2y, E2z),所以上面的等式用矩阵形式表达。

        对 T 和 B 求解,计算比较简单:

 设:

则有:

T_{z}=ratio*(\Delta V_{2}E_{1}z-\Delta V_{1}E_{2}z)

        因为 E1, E2 可以由三角形顶点在三维空间中的坐标(顶点坐标信息记录在属性中,可以直接读到)求出, 所以可以解出 T。最后求解 B,因为 B 同时垂直于 T 和法向量,而法向量又能直接读到所以直接用 T 叉乘法向量即可求出。

        题目代码中给出的公式:Vector t = (x*y/sqrt(x*x+z*z),sqrt(x*x+z*z),z*y/sqrt(x*x+z*z)),个人猜测(没有推导求证, 因为实际计算中不一定使用这个公式): 此处的 x, y, z 是当前 payload 的法线坐标,而这个法线坐标是根据三角形重心坐标插值求出来的, 也就是说法线其实包含了三个顶点的坐标信息,所以此处 T 采用的不是前面推导出来的计算公式, 而是用法线坐标来计算。

        如果有大佬知道这个公式的推导过程也请不吝赐教。

3. 代码:

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;
    auto x = n.x();
    auto y = n.y();
    auto 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);
    auto u = payload.tex_coords.x();    //在切线空间中的 x 坐标
    auto v = payload.tex_coords.y();    //在切线空间中的 y 坐标

    //切线空间向世界空间的转换矩阵
    Matrix3f tbn;
    tbn << t.x(), t.y(), t.z(),
        b.x(), b.y(), b.z(),
        n.x(), n.y(), n.z();

    // 纹理的实际宽度, 因为 u, v实际是通过重心插值计算出的值, 需要乘以实际宽度才是对应纹理中的坐标
    auto w = payload.texture->width;
    // 同理, v 也要乘以纹理的实际高度
    auto h = payload.texture->height;
    //U 方向的切线(导数)
    auto dU = kh * kn * (payload.texture->getColor(u + 1.0f / w, v).norm() - payload.texture->getColor(u, v).norm());
    //V 方向的切线(导数)
    auto dV = kh * kn * (payload.texture->getColor(u, v + 1.0f / h).norm() - payload.texture->getColor(u, v).norm());
    
    //当前点的法向量
    Vector3f ln(-dU, -dV, 1);

    //使用tbn矩阵将法线变换到世界空间中
    normal = (tbn * ln).normalized(); //normal = ln.normalized();

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

    return result_color * 255.f;
}

得到的结果: 

         法线贴图的原理在代码中也可以实验,  将 normal = (tbn * ln).normalized() 这一句替换为 normal = ln.normalized(), 运行代码后得到结果:

       

        此时 normal 就是每个点在切线空间中的法线, 可以看到图片中基本都是蓝色, 也就证明这些点在切线空间中的法线基本都是 (0,0,1), 从 dU 和 dV 的计算过程也可以看出, 凹凸贴图的凹凸感来自颜色的变化, 当这个变化越剧烈(导数越大), 则凹凸感也越明显。这点可以通过将auto texture_path 这一句改为 auto texture_path = "spot_texture.png" 得到, 在贴图黑白交界处, 凹凸感最强烈。


第五题 位移贴图

题目: 在 bump_fragment_shader 的基础上实现 displacement_fragment_shader。

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)


    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.


    }

    return result_color * 255.f;
}

解析:

1. 代码:

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

    return result_color * 255.f;
}

  • 6
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值