GAMES101作业3

作业要求:

作业效果:

💡我们需要做的:
| 在rasterizer.cpp中修改:
|
| 1. 函数rasterize_triangle(const Triangle& t) //实现与作业 2 类似的插值算法,实现法向量、颜色、纹理颜色的插值
|
| 在main.cpp中修改:
|
| 1. 函数 get_projection_matrix() :将你自己之前的实验中实现的投影矩阵//此时运行就是法向量的结果------Normal shader.
| 2. 函数phong_fragment_shader(): 实现Blinn-Phong模型计算Fragment Color.
| 3. 函数texture_fragment_shader() :在实现Blinn-Phong 模型的基础上,将纹理颜色是为公式中kd,实现Texture shading Fragment Shader.
| 4. 函数bump_fragment_shader() :在实现Blinn-Phong 模型的基础上,实现凹凸贴图bump fragment shader.
| 5. 函数displacement_fragment_shader():在实现Blinn-Phong 模型的基础上,实现位移映射Displacement fragment shader.

关键词:法向量;插值计算;光照模型;凹凸贴图;位移映射;

1.在rasterizer.cpp中修改:

cpp 复制代码
void rst::rasterizer::rasterize_triangle(const Triangle& t, const std::array<Eigen::Vector3f, 3>& view_pos) 
{
    // TODO: From your HW3, 
    // 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);

    auto v = t.toVector4();//把三角形面片的顶点坐标装入容器
    int min_x = INT_MAX;
    int max_x = INT_MIN;
    int min_y = INT_MAX;
    int max_y = INT_MIN;
    for (auto point : v)
    {
        if (point[0] < min_x)min_x = point[0];
        if (point[0] > max_x)max_x = point[0];
        if (point[1] < min_y)min_y = point[1];
        if (point[1] > max_y)max_y = point[1];
    }
    for (int y = min_y; y <= max_y; y++)
    {
        for (int x = min_x; x <= max_x; x++)
        {
            if (insideTriangle((float)x + 0.5, (float)y + 0.5, t.v))
            {
                //得到点的重心坐标
                auto abg = computeBarycentric2D((float)x + 0.5, (float)y + 0.5, t.v);
                float alpha = std::get<0>(abg);
                float beta = std::get<1>(abg);
                float gamma = std::get<2>(abg);
                //z-buff插值
                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[get_index(x, y)])
                {
                    Eigen::Vector2i p = { (float)x,(float)y };
                    //颜色插值
                    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;
                    auto pixel_color = fragment_shader(payload);
                    set_pixel(p, pixel_color);
                    depth_buf[get_index(x, y)] = z_interpolated;//更新z值
                }

            }
        }
    }
 
}

2.在main.cpp中修改:

分五步:

1.函数 get_projection_matrix() :将你自己之前的实验中实现的投影矩阵

此时运行就是法向量的结果------Normal shader.

cpp 复制代码
igen::Matrix4f get_projection_matrix(float eye_fov, float aspect_ratio, float zNear, float zFar)
{
    // TODO: Use the same projection matrix from the previous assignments
    Eigen::Matrix4f projection = Eigen::Matrix4f::Identity();   //初始化单位矩阵
    Eigen::Matrix4f M_trans;                                    //平移变换矩阵
    Eigen::Matrix4f M_persp;                                    //透视变换矩阵
    Eigen::Matrix4f M_ortho;                                    //正交变换矩阵
    M_persp <<
        zNear, 0, 0, 0,
        0, zNear, 0, 0,
        0, 0, zNear + zFar, -zFar * zNear,
        0, 0, 1, 0;

    float alpha = 0.5 * eye_fov * MY_PI / 180.0f;               //角度制转换
    float yTop = -zNear * std::tan(alpha); //
    float yBottom = -yTop;
    float xRight = yTop * aspect_ratio;
    float xLeft = -xRight;

    M_trans <<
        1, 0, 0, -(xLeft + xRight) / 2,
        0, 1, 0, -(yTop + yBottom) / 2,
        0, 0, 1, -(zNear + zFar) / 2,
        0, 0, 0, 1;
    M_ortho <<
        2 / (xRight - xLeft), 0, 0, 0,
        0, 2 / (yTop - yBottom), 0, 0,
        0, 0, 2 / (zNear - zFar), 0,
        0, 0, 0, 1;

    M_ortho = M_ortho * M_trans;
    projection = M_ortho * M_persp * projection;            //矩阵乘法是从右到左
    return projection;
}
cpp 复制代码
//运行normal_fragment_shader
r.set_texture(Texture(Utils::PathFromAsset("model/spot/hmap.jpg")));
std::function<Eigen::Vector3f(fragment_shader_payload)> active_shader = normal_fragment_shader;

2.函数phong_fragment_shader(): 实现Blinn-Phong模型计算Fragment Color.

cpp 复制代码
igen::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 v = eye_pos - point;                                       //v为出射光方向(指向眼睛)
        auto l = light.position - point;                                //l为指向入射光源方向
        auto h = (v + l).normalized();                                  //h为半程向量,即v+l归一化后的单位向量
        auto r = l.dot(l);                                              //衰减因子
        //环境光
        auto ambient = ka.cwiseProduct(amb_light_intensity);
        //漫反射
        auto diffuse = kd.cwiseProduct(light.intensity / r) * std::max(0.0f, normal.normalized().dot(l.normalized()));
        //镜面反射
        auto specular = ks.cwiseProduct(light.intensity / r) * std::pow(std::max(0.0f, normal.normalized().dot(h)), p);
        //将光照效果累加
        result_color += (ambient + diffuse + specular);
    }

    return result_color * 255.f;
}
cpp 复制代码
//运行phong_fragment_shader
r.set_texture(Texture(Utils::PathFromAsset("model/spot/hmap.jpg")));
std::function<Eigen::Vector3f(fragment_shader_payload)> active_shader = phong_fragment_shader;

3.函数texture_fragment_shader() :在实现Blinn-Phong模型的基础上,将纹理颜色是为公式中kd,实现Texture shading Fragment Shader.

cpp 复制代码
  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
        //获取当前片段纹理坐标处的纹理值
        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);
    //漫反射系数直接使用纹理颜色(归一化到[0,1]范围)
    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};

    //Blinn-Phong
    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 v = eye_pos - point; //v为出射光方向(指向眼睛)
        auto l = light.position - point; //l为指向入射光源方向
        auto h = (v + l).normalized(); //h为半程向量即v+l归一化后的单位向量
        auto r = l.dot(l); //衰减因子
        auto ambient = ka.cwiseProduct(amb_light_intensity);
        //漫反射,使用纹理颜色
        auto diffuse = kd.cwiseProduct(light.intensity / r) * std::max(0.0f, normal.normalized().dot(l.normalized()));
        auto specular = ks.cwiseProduct(light.intensity / r) * std::pow(std::max(0.0f, normal.normalized().dot(h)), p);
        result_color += (ambient + diffuse + specular);
    }
    //将颜色从[0,1]范围转回[0,255]范围
    return result_color * 255.f;
}
cpp 复制代码
 //运行texture_fragment_shader时注意要改纹理文件
 //纹理文件hmap.jpg改为spot_texture.png
 r.set_texture(Texture(Utils::PathFromAsset("model/spot/spot_texture.png")));
 std::function<Eigen::Vector3f(fragment_shader_payload)> active_shader = texture_fragment_shader;

4.函数bump_fragment_shader() :在实现Blinn-Phong模型的基础上,实现凹凸贴图bump fragment shader.

核心原理:法线扰动不改变模型的实际几何形状,而是通过修改表面法线方向来模拟光照变化。当光线照射到这些 "虚拟" 的凹凸表面时,会产生明暗变化,从而欺骗眼睛感知到表面细节。

cpp 复制代码
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;

    //扰动强度参数
    //kh:控制高度变化的强度
    //kn:控制法线扰动的强度
    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();
    //计算切线向量 t
    Eigen::Vector3f t(x * y / sqrt(x * x + z * z), sqrt(x * x + z * z), z * y / sqrt(x * x + z * z));
    // 计算副切线向量 b(法线与切线的叉积)
    Eigen::Vector3f b = normal.cross(t);
    //TBN矩阵: 将纹理坐标对应到模型空间中
    Eigen::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).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());
    
    // 纹理空间中的扰动法线(Z分量为1,表示向上)
    Eigen::Vector3f ln{ -dU,-dV,1.0f };
    // 将扰动法线从纹理空间转换到世界空间
    normal = TBN * ln;
    Eigen::Vector3f result_color = normal.normalized();//归一化

    return result_color * 255.f;
}
cpp 复制代码
//运行bump_fragment_shader
//纹理文件记得改回hmap.jpg
r.set_texture(Texture(Utils::PathFromAsset("model/spot/hmap.jpg")));
std::function<Eigen::Vector3f(fragment_shader_payload)> active_shader = bump_fragment_shader;

5.函数displacement_fragment_shader():在实现Blinn-Phong 模型的基础上,实现位移映射Displacement fragment shader.

位移映射:与法线扰动不同,位移映射不仅改变表面法线,还实际移动顶点位置,从而创建更真实的凹凸效果,尤其是在边缘和轮廓处。

**核心原理:**位移映射通过高度图(Height Map)修改顶点位置,使平坦的表面在渲染时看起来像有真实的几何起伏。这需要在着色阶段动态调整顶点位置,并相应地更新法线方向。

cpp 复制代码
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)
    
    // 计算切线向量 t(沿纹理U方向)
    auto x = normal.x();
    auto y = normal.y();
    auto z = normal.z();
    Eigen::Vector3f t(x * y / sqrt(x * x + z * z), sqrt(x * x + z * z), z * y / sqrt(x * x + z * z));
    // 计算副切线向量 b(法线与切线的叉积,沿纹理V方向)
    Eigen::Vector3f b = normal.cross(t);
    // 构建TBN矩阵(将局部坐标转换到世界坐标)
    Eigen::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).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 * normal * payload.texture->getColor(u, v).norm()); 
    // 将扰动法线从纹理空间转换到世界空间
    normal = TBN * ln;
    normal.normalized();
    
    //Phong 光照模型
    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 v = eye_pos - point; //v为出射光方向(指向眼睛)
        auto l = light.position - point; //l为指向入射光源方向
        auto h = (v + l).normalized(); //h为半程向量即v+l归一化后的单位向量
        auto r = l.dot(l); //衰减因子
        auto ambient = ka.cwiseProduct(amb_light_intensity);
        auto diffuse = kd.cwiseProduct(light.intensity / r) * std::max(0.0f, normal.normalized().dot(l.normalized()));
        auto specular = ks.cwiseProduct(light.intensity / r) * std::pow(std::max(0.0f, normal.normalized().dot(h)), p);
        result_color += (ambient + diffuse + specular);

    }

    return result_color * 255.f;
}
cpp 复制代码
//运行displacement_fragment_shader
r.set_texture(Texture(Utils::PathFromAsset("model/spot/hmap.jpg")));
std::function<Eigen::Vector3f(fragment_shader_payload)> active_shader = displacement_fragment_shader;

提高项 3分\] :尝试更多模型: 找到其他可用的.obj 文件,提交渲染结果并把模型保存在 /models 目录下。这些模型也应该包含 Vertex Normal 信息。 \[提高项 5分\]:双线性纹理插值: 使用双线性插值进行纹理采样, 在 Texture类中实现一个新方法 Vector3f getColorBilinear(float u, float v) 并通过 fragment shader 调用它。为了使双线性插值的效果更加明显,你应该考虑选择更小的纹理图。请同时提交纹理插值与双线性纹理插值的结果,并进行比较。

相关推荐
四谎真好看4 天前
SSM学习笔记(SpringBoot篇)
spring boot·笔记·学习·学习笔记·ssm
~黄夫人~6 天前
Linux 权限管理:用户组 + 特殊权限 + ACL 解析
linux·运维·计算机·学习笔记·权限管理
四谎真好看8 天前
SSM学习笔记(SpringMVC篇 Day02)
笔记·学习·学习笔记·ssm
四谎真好看10 天前
SSM学习笔记(SpringMVC篇 Day01)
笔记·学习·学习笔记·ssm
heartbeat..13 天前
Java 微服务初学者入门指南(CSDN 博客版)
java·运维·微服务·学习笔记·入门
四谎真好看14 天前
SSM学习笔记(Spring篇Day03)
笔记·学习·学习笔记·ssm
四谎真好看15 天前
SSM学习笔记(Spring篇 Day02)
笔记·学习·学习笔记·ssm
四谎真好看17 天前
SSM学习笔记(Spring篇 Day01)
笔记·学习·学习笔记·ssm
四谎真好看18 天前
JavaWeb学习笔记(Day14)
笔记·学习·学习笔记·javaweb
四谎真好看22 天前
JavaWeb学习笔记(Day13)
笔记·学习·学习笔记·javaweb