GAMES101-ASSIGNMENT3(作业3)

 任务说明:

在本次实验中,你需要完成的任务是:
1. 修改函数 rasterize_triangle(const Triangle& t) in rasterizer.cpp: 在此处实现与作业 2 类似的插值算法,实现法向量、颜色、纹理颜色的插值。
2. 修改函数 get_projection_matrix() in main.cpp: 将你自己在之前的实验中实现的投影矩阵填到此处,此时你可以运行 ./Rasterizer output.png normal 来观察法向量实现结果。
3. 修改函数 phong_fragment_shader() in main.cpp: 实现 Blinn-Phong 模型计算 Fragment Color.
4. 修改函数 texture_fragment_shader() in main.cpp: 在实现 Blinn-Phong的基础上,将纹理颜色视为公式中的 kd,实现 Texture Shading Fragment Shader.
5. 修改函数 bump_fragment_shader() in main.cpp: 在实现 Blinn-Phong 的基础上,仔细阅读该函数中的注释,实现 Bump mapping.
6. 修改函数 displacement_fragment_shader() in main.cpp: 在实现 Bump mapping 的基础上,实现 displacement mapping.

 编译与使用

在虚拟机上,下载本次实验的基础代码之后,请在 SoftwareRasterizer 目录下按照如下方式构建程序:
1$ mkdir build
2$ cd build
3$ cmake . .
4$ make -j4
这将会生成命名为 Rasterizer 的可执行文件。使用该可执行文件时,你传入的第二个参数将会是生成的图片文件名,而第三个参数可以是如下内容:
• texture: 使用代码中的 texture shader.
使用举例: ./Rasterizer output.png texture
• normal: 使用代码中的 normal shader.
使用举例: ./Rasterizer output.png normal
• phong: 使用代码中的 blinn-phong shader.
使用举例: ./Rasterizer output.png phong
• bump: 使用代码中的 bump shader.
使用举例: ./Rasterizer output.png bump
• displacement: 使用代码中的 displacement shader.
使用举例: ./Rasterizer output.png displacement
当你修改代码之后,你需要重新 make 才能看到新的结果。

 代码框架说明

相比上次实验,我们对框架进行了如下修改:
1. 我们引入了一个第三方.obj 文件加载库来读取更加复杂的模型文件,这部分库文件在 OBJ_Loader.h file. 你无需详细理解它的工作原理,只需知道这个库将会传递给我们一个被命名被 TriangleList 的 Vector,其中每个三角形都有对应的点法向量与纹理坐标。此外,与模型相关的纹理也将被一同加载。注意:如果想尝试加载其他模型,你目前只能手动修改模型路径。
2. 我们引入了一个新的 Texture 类以从图片生成纹理,并且提供了查找纹理颜色的接口:Vector3f getColor(float u, float v)
3. 我们创建了 Shader.hpp 头文件并定义了 fragment_shader_payload,其中包括了 Fragment Shader 可能用到的参数。目前 main.cpp 中有三个 FragmentShader,其中 fragment_shader 是按照法向量上色的样例 Shader,其余两个将由你来实现。
4. 主渲染流水线开始于 rasterizer::draw(std::vector<Triangle> &TriangleList).我们再次进行一系列变换,这些变换一般由 Vertex Shader 完成。在此之后,我们调用函数 rasterize_triangle.
5. rasterize_triangle 函数与你在作业 2 中实现的内容相似。不同之处在于被设定的数值将不再是常数,而是按照 Barycentric Coordinates 对法向量、颜色、纹理颜色与底纹颜色 (Shading Colors) 进行插值。回忆我们上次为了计算z value 而提供的 [alpha, beta, gamma],这次你将需要将其应用在其他参数的插值上。你需要做的是计算插值后的颜色,并将 Fragment Shader 计算得到的颜色写入 framebuffer,这要求你首先使用插值得到的结果设置 fragmentshader payload,并调用 fragment shader 得到计算结果。

 作业代码

 rasterize_triangle(const Triangle& t)


//Screen space rasterization
void rst::rasterizer::rasterize_triangle(const Triangle& t, const std::array<Eigen::Vector3f, 3>& view_pos) 
{
    auto v = t.toVector4();
    float alpha, beta, gamma, lmin=INT_MAX, rmax=INT_MIN, tmax=INT_MIN, bmin=INT_MAX;
    float min_x = std::min(v[0].x(),std::min(v[1].x(),v[2].x()));
    float min_y = std::min(v[0].y(),std::min(v[1].y(),v[2].y()));
    float max_x = std::max(v[0].x(),std::max(v[1].x(),v[2].x()));
    float max_y = std::max(v[0].y(),std::max(v[1].y(),v[2].y()));

    for (int x = int(min_x);x<=max_x;x++){
        for(int y = int(min_y);y<=max_y;y++){
            if (insideTriangle(x,y,t.v)){
                std::tie(alpha, beta, gamma) = computeBarycentric2D(x, y, t.v);
                //直接进行深度插值,但是投影时三角形重心会变,所以要使用透视校正插值
                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;
                if(zp < depth_buf[get_index(x,y)]){//如果当前z值比像素z值小
                    // TODO: Interpolate the attributes:
                    // auto interpolated_color
                    // auto interpolated_normal
                    // auto interpolated_texcoords
                    // auto interpolated_shadingcoords

                    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).normalized();
                    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;
                    // 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;
                    auto pixel_color = fragment_shader(payload);

                    // TODO : set the current pixel (use the set_pixel function) to the color of the triangle (use getColor function) if it should be painted.
                    depth_buf[get_index(x,y)] = zp;//修改像素当前深度   
                    set_pixel(Eigen::Vector2i(x, y), pixel_color);
                    
                }
            }
        }
    }
}

get_projection_matrix() in main.cpp


Eigen::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 pj;
    pj<<1/(tan(eye_fov/2)),0,0,0,
    0,aspect_ratio*(1/tan(eye_fov/2)),0,0,
    0,0,-(zNear+zFar)/(zFar-zNear),-(2*zNear*zFar)/(zFar-zNear),
    0,0,-1,0;
    projection = pj*projection;
    return projection;
}

phong_fragment_shader() in main.cpp


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).normalized();
        Eigen::Vector3f v = (eye_pos - point).normalized();
        Eigen::Vector3f h = (l+v).normalized();
        double r_2 = (light.position - point).dot(light.position - point);
        //漫反射 Ld = kd *(I/r^2) *max(0,n.l) kd:diffuse coefficient(color), (I/r^2):energy arrived at the shading point , max(0,n.l):energy received by the shading point
        Eigen::Vector3f Ld = kd.cwiseProduct(light.intensity /r_2) * std::max(0.0f,normal.dot(l));
        //高光反射 Ls
        Eigen::Vector3f Ls = ks.cwiseProduct(light.intensity / r_2) * std::pow(std::max(0.0f,normal.dot(h)),p);
        result_color += (Ld +Ls);
    }
    //环境光:La
    Eigen::Vector3f La = ka.cwiseProduct(amb_light_intensity);
    result_color += La;
    return result_color * 255.f;
}

texture_fragment_shader() in main.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);
    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.
        Eigen::Vector3f l = (light.position - point).normalized();
        Eigen::Vector3f v = (eye_pos - point).normalized();
        Eigen::Vector3f h = (l+v).normalized();
        double r_2 = (light.position - point).dot(light.position - point);
        //漫反射 Ld
        Eigen::Vector3f Ld = kd.cwiseProduct(light.intensity / r_2) * std::max(0.0f,normal.dot(l));
        //高光反射 Ls
        Eigen::Vector3f Ls = ks.cwiseProduct(light.intensity / r_2) * std::pow(std::max(0.0f,normal.dot(h)),p);
        result_color += (Ld +Ls);
    }
    //环境光:La
    Eigen::Vector3f La = ka.cwiseProduct(amb_light_intensity);
    result_color += La;

    return result_color * 255.f;
}

bump_fragment_shader() in main.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;


    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)

    //求切线空间TBN
    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;
    
    //求点(u,v)的切线
    float dU = kh * kn * (payload.texture->getColor(u+1/w,v).norm() - payload.texture->getColor(u,v).norm());
    float dV = kh * kn * (payload.texture->getColor(u,v+1/h).norm() - payload.texture->getColor(u,v).norm());
    //求点(u,v)的法线
    Eigen::Vector3f ln{-dU,-dV,1};
    Eigen::Vector3f result_color = (TBN*ln).normalized();
    return result_color * 255.f;
}

 displacement_fragment_shader() in main.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;

    //求切线空间TBN
    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;

    //求点(U,V)的切线
    float dU = kh * kn * (payload.texture->getColor(u+1/w,v).norm() - payload.texture->getColor(u,v).norm());
    float dV = kh * kn * (payload.texture->getColor(u,v+1/h).norm() - payload.texture->getColor(u,v).norm());

    //求(U,V)的法向量
    Eigen::Vector3f ln{-dU,-dV,1};
    point += (kn * normal * payload.texture->getColor(h,v).norm());

    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).normalized();
        Eigen::Vector3f v = (eye_pos - point).normalized();
        Eigen::Vector3f h = (l+v).normalized();
        double r_2 = (light.position - point).dot(light.position - point);
        //漫反射 Ld = kd *(I/r^2) *max(0,n.l) kd:diffuse coefficient(color), (I/r^2):energy arrived at the shading point , max(0,n.l):energy received by the shading point
        Eigen::Vector3f Ld = kd.cwiseProduct(light.intensity /r_2) * std::max(0.0f,normal.dot(l));
        //高光反射 Ls
        Eigen::Vector3f Ls = ks.cwiseProduct(light.intensity / r_2) * std::pow(std::max(0.0f,normal.dot(h)),p);
        result_color += (Ld +Ls);
    }
    //环境光:La
    Eigen::Vector3f La = ka.cwiseProduct(amb_light_intensity);
    result_color += La;
    return result_color * 255.f;
}

运行结果

./Rasterizer output.png normal

 ./Rasterizer output.png phong

 ./Rasterizer output.png texture

 ./Rasterizer output.png bump

 

 ./Rasterizer output.png displacement

猜你喜欢

转载自blog.csdn.net/qq_48626761/article/details/126053848