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 调用它。为了使双线性插值的效果更加明显,你应该考虑选择更小的纹理图。请同时提交纹理插值与双线性纹理插值的结果,并进行比较。

相关推荐
鱼鱼莲2 天前
GAMES101作业2
学习笔记·games101
丶Darling.20 天前
26考研 | 王道 | 第六章 应用层
计算机网络·考研·学习笔记·王道
丶Darling.25 天前
26考研 | 王道 | 第五章 传输层
计算机网络·考研·学习笔记·王道
Be_Somebody25 天前
计算机图形学——Games101深度解析_第二章
游戏开发·计算机图形学·games101
Be_Somebody1 个月前
计算机图形学——Games101深度解析_第一章
游戏开发·计算机图形学·games101
Alice-YUE1 个月前
【CSS学习笔记1】css基础知识介绍
css·学习笔记
再睡一夏就好1 个月前
从硬件角度理解“Linux下一切皆文件“,详解用户级缓冲区
linux·服务器·c语言·开发语言·学习笔记
Asymptote02131 个月前
【内网渗透】——S4u2扩展协议提权以及KDC欺骗提权
网络协议·信息安全·学习笔记·渗透·内网渗透·奇安信·kaliklinux
再睡一夏就好1 个月前
C语言常见的文件操作函数总结
c语言·开发语言·c++·笔记·学习笔记