Gems101Homework【3】Pipeline and Shading(Include framework Interpretation)

Renderer Pipeline:

视图变换(投影变换,视口变换) - 知乎 (zhihu.com)

Framework Interpretation:

Code execution logic diagram:

To make it easier for you to understand, I will explain in Chinese:

main.cpp:Load model, configure parameters, select shader, draw call.

Core: Instantiate the rasterized class, pass arguments, and draw call .

int main(int argc, const char** argv)
{
    // 初始化三角形列表
    std::vector<Triangle*> TriangleList;
   // 初始化变量
    float angle = 140.0;
    bool command_line = false;
    // 默认输出文件名
    std::string filename = "output.png";
    // 加载器对象
    objl::Loader Loader;
    // OBJ文件路径前缀
    std::string obj_path = "../models/spot/";

    // 加载OBJ文件
    bool loadout = Loader.LoadFile("../models/spot/spot_triangulated_good.obj");
   
    for(auto mesh:Loader.LoadedMeshes)
    { 
        // 为每个面创建三角形并添加到列表中
        for(int i=0;i<mesh.Vertices.size();i+=3)
        {
            Triangle* t = new Triangle();
            for(int j=0;j<3;j++)
            { 
                // 设置三角形的顶点、法线和纹理坐标
                t->setVertex(j,Vector4f(mesh.Vertices[i+j].Position.X,mesh.Vertices[i+j].Position.Y,mesh.Vertices[i+j].Position.Z,1.0));
                t->setNormal(j,Vector3f(mesh.Vertices[i+j].Normal.X,mesh.Vertices[i+j].Normal.Y,mesh.Vertices[i+j].Normal.Z));
                t->setTexCoord(j,Vector2f(mesh.Vertices[i+j].TextureCoordinate.X, mesh.Vertices[i+j].TextureCoordinate.Y));
            }
            TriangleList.push_back(t);
        }
    }
    // 初始化栅格化对象
    rst::rasterizer r(700, 700);

    auto texture_path = "hmap.jpg";
    r.set_texture(Texture(obj_path + texture_path));

    std::function<Eigen::Vector3f(fragment_shader_payload)> active_shader = phong_fragment_shader;
      // 处理命令行参数,以决定使用哪种着色器
    if (argc >= 2)
    {
        command_line = true;
        filename = std::string(argv[1]);

        if (argc == 3 && std::string(argv[2]) == "texture")
        {
            std::cout << "Rasterizing using the texture shader\n";
            active_shader = texture_fragment_shader;
            texture_path = "spot_texture.png";
            r.set_texture(Texture(obj_path + texture_path));
        }
        else if (argc == 3 && std::string(argv[2]) == "normal")
        {
            std::cout << "Rasterizing using the normal shader\n";
            active_shader = normal_fragment_shader;
        }
        else if (argc == 3 && std::string(argv[2]) == "phong")
        {
            std::cout << "Rasterizing using the phong shader\n";
            active_shader = phong_fragment_shader;
        }
        else if (argc == 3 && std::string(argv[2]) == "bump")
        {
            std::cout << "Rasterizing using the bump shader\n";
            active_shader = bump_fragment_shader;
        }
        else if (argc == 3 && std::string(argv[2]) == "displacement")
        {
            std::cout << "Rasterizing using the bump shader\n";
            active_shader = displacement_fragment_shader;
        }
    }

    Eigen::Vector3f eye_pos = {0,0,10};

    r.set_vertex_shader(vertex_shader);
    r.set_fragment_shader(active_shader);

    int key = 0;
    int frame_count = 0;
   // 如果是命令行模式,直接渲染并保存图像
    if (command_line)
    {
        r.clear(rst::Buffers::Color | rst::Buffers::Depth);
        r.set_model(get_model_matrix(angle));
        r.set_view(get_view_matrix(eye_pos));
        r.set_projection(get_projection_matrix(45.0, 1, 0.1, 50));

        r.draw(TriangleList);
        cv::Mat image(700, 700, CV_32FC3, r.frame_buffer().data());
        image.convertTo(image, CV_8UC3, 1.0f);
        cv::cvtColor(image, image, cv::COLOR_RGB2BGR);

        cv::imwrite(filename, image);

        return 0;
    }
// 键盘交互模式,通过按键控制视角变化和渲染
    while(key != 27)
    {
        r.clear(rst::Buffers::Color | rst::Buffers::Depth);

        r.set_model(get_model_matrix(angle));
        r.set_view(get_view_matrix(eye_pos));
        r.set_projection(get_projection_matrix(45.0, 1, 0.1, 50));

        //r.draw(pos_id, ind_id, col_id, rst::Primitive::Triangle);
        r.draw(TriangleList);
        //最后使用OpenCV对缓冲区进行绘制
        cv::Mat image(700, 700, CV_32FC3, r.frame_buffer().data());
        image.convertTo(image, CV_8UC3, 1.0f);
        cv::cvtColor(image, image, cv::COLOR_RGB2BGR);

        cv::imshow("image", image);
        cv::imwrite(filename, image);
        key = cv::waitKey(10);
   // 通过按键调整视角
        if (key == 'a' )
        {
            angle -= 0.1;
        }
        else if (key == 'd')
        {
            angle += 0.1;
        }

    }
    return 0;
}

 rasterizer.cpp:base on arguments rasterize,lerp and drawcall.

//
// Created by goksu on 4/6/19.
//

#include <algorithm>
#include "rasterizer.hpp"
#include <opencv2/opencv.hpp>
#include <math.h>

/**
 * 加载位置数据到Rasterizer中。
 * 
 * @param positions 包含位置数据的向量,每个位置由一个三维向量(Eigen::Vector3f)表示。
 * @return 返回一个pos_buf_id结构体,包含加载位置数据后生成的唯一标识符。
 */
rst::pos_buf_id rst::rasterizer::load_positions(const std::vector<Eigen::Vector3f> &positions)
{
    auto id = get_next_id();
    pos_buf.emplace(id, positions);

    return {id};
}

rst::ind_buf_id rst::rasterizer::load_indices(const std::vector<Eigen::Vector3i> &indices)
{
    auto id = get_next_id();
    ind_buf.emplace(id, indices);

    return {id};
}

rst::col_buf_id rst::rasterizer::load_colors(const std::vector<Eigen::Vector3f> &cols)
{
    auto id = get_next_id();
    col_buf.emplace(id, cols);

    return {id};
}

rst::col_buf_id rst::rasterizer::load_normals(const std::vector<Eigen::Vector3f>& normals)
{
    auto id = get_next_id();
    nor_buf.emplace(id, normals);

    normal_id = id;

    return {id};
}

/**
 * 使用Bresenham算法绘制线段
 * @param begin 线段的起点,使用Eigen::Vector3f表示(x, y, z)坐标
 * @param end 线段的终点,使用Eigen::Vector3f表示(x, y, z)坐标
 * 该函数不返回任何值。
 */
void rst::rasterizer::draw_line(Eigen::Vector3f begin, Eigen::Vector3f end)
{
    // 提取起点和终点的x, y坐标
    auto x1 = begin.x();
    auto y1 = begin.y();
    auto x2 = end.x();
    auto y2 = end.y();
    // 默认线颜色为白色
    Eigen::Vector3f line_color = {255, 255, 255};
    // 初始化Bresenham算法所需变量
    int x,y,dx,dy,dx1,dy1,px,py,xe,ye,i;
    // 计算x和y方向的增量及绝对值
    dx=x2-x1;
    dy=y2-y1;
    dx1=fabs(dx);
    dy1=fabs(dy);
    // 预计算Bresenham算法中的决策参数
    px=2*dy1-dx1;
    py=2*dx1-dy1;
    // 根据dx和dy的大小决定是x方向还是y方向进行迭代
    if(dy1<=dx1)
    {// 确定起点和终点,以及迭代方向
        if(dx>=0)
        {
            x=x1;
            y=y1;
            xe=x2;
        }
        else
        {
            x=x2;
            y=y2;
            xe=x1;
        }
        // 在起点处设置像素颜色
        Eigen::Vector2i point = Eigen::Vector2i(x, y);
        set_pixel(point,line_color);
        // 使用Bresenham算法绘制线段
        for(i=0;x<xe;i++)
        {
            x=x+1;
            if(px<0)
            {
                px=px+2*dy1;
            }
            else
            {// 根据决策参数更新y坐标
                if((dx<0 && dy<0) || (dx>0 && dy>0))
                {
                    y=y+1;
                }
                else
                {
                    y=y-1;
                }
                px=px+2*(dy1-dx1);
            }
        // 在当前像素位置设置颜色
            Eigen::Vector2i point = Eigen::Vector2i(x, y);
            set_pixel(point,line_color);
        }
    }
    else
    {// 确定起点和终点,以及迭代方向
        if(dy>=0)
        {
            x=x1;
            y=y1;
            ye=y2;
        }
        else
        {
            x=x2;
            y=y2;
            ye=y1;
        }
         // 在起点处设置像素颜色
        Eigen::Vector2i point = Eigen::Vector2i(x, y);
        set_pixel(point,line_color);
        for(i=0;y<ye;i++)
        {
            y=y+1;
            if(py<=0)
            {
                py=py+2*dx1;
            }
            else
            {
                if((dx<0 && dy<0) || (dx>0 && dy>0))
                {
                    x=x+1;
                }
                else
                {
                    x=x-1;
                }
                py=py+2*(dx1-dy1);
            }
            // 在当前像素位置设置颜色
            Eigen::Vector2i point = Eigen::Vector2i(x, y);
            set_pixel(point,line_color);
        }
    }
}

auto to_vec4(const Eigen::Vector3f& v3, float w = 1.0f)
{
    return Vector4f(v3.x(), v3.y(), v3.z(), w);
}

static bool insideTriangle(int x, int y, const Vector4f* _v){
    Vector3f v[3];
    for(int i=0;i<3;i++)
        v[i] = {_v[i].x(),_v[i].y(), 1.0};
    Vector3f f0,f1,f2;
    f0 = v[1].cross(v[0]);
    f1 = v[2].cross(v[1]);
    f2 = v[0].cross(v[2]);
    Vector3f p(x,y,1.);
    if((p.dot(f0)*f0.dot(v[2])>0) && (p.dot(f1)*f1.dot(v[0])>0) && (p.dot(f2)*f2.dot(v[1])>0))
        return true;
    return false;
}
/**
 * 计算二维空间中点 (x, y) 对于给定的三个顶点坐标(v[0], v[1], v[2])形成的三角形的 barycentric 坐标。
 * 
 * @param x 点的 x 坐标。
 * @param y 点的 y 坐标。
 * @param v 包含三个顶点坐标的向量数组,每个顶点是一个 Vector4f 对象(尽管这里只用到 x 和 y 分量)。
 * @return 一个包含三个 float 值的 tuple,分别代表点 (x, y) 在三角形三个顶点上的权重(barycentric 坐标)。
 */
static std::tuple<float, float, float> computeBarycentric2D(float x, float y, const Vector4f* v){
    // 计算点 (x, y) 对于三角形 v[0], v[1], v[2] 的 barycentric 坐标 c1, c2, c3(就那个贼长的公式)
    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};
}
/**
 * @brief 对给定的三角形列表进行绘制。
 * 
1.计算视口的中心点坐标。
2.计算模型视图投影矩阵。
3.遍历三角形列表,对每个三角形进行以下操作:
    将三角形的顶点从模型空间变换到视图空间。
    从齐次坐标转换为非齐次坐标。
    将三角形的顶点从视图空间变换到齐次裁剪空间。
    进行齐次除法。
    计算三角形法线在视图空间的表示。
    进行视口变换。
    更新变换后的顶点和法线到新的三角形对象。
    设置三角形颜色。
    调用rasterize_triangle函数进行三角形光栅化。
 * 
 * @param TriangleList 一个包含待绘制三角形指针的向量。
 */
void rst::rasterizer::draw(std::vector<Triangle *> &TriangleList) {
    // 计算视口的中心点坐标
    float f1 = (50 - 0.1) / 2.0;
    float f2 = (50 + 0.1) / 2.0;
    // 计算模型视图投影矩阵
    Eigen::Matrix4f mvp = projection * view * model;
    for (const auto& t:TriangleList)
    {
        Triangle newtri = *t;
        // 将三角形的顶点从模型空间变换到视图空间
        std::array<Eigen::Vector4f, 3> mm {
                (view * model * t->v[0]),
                (view * model * t->v[1]),
                (view * model * t->v[2])
        };
        // 从齐次坐标转换为非齐次坐标
        std::array<Eigen::Vector3f, 3> viewspace_pos;
        // 将mm容器中的每个元素(假定为具有多个元素的向量)的前三个元素提取出来,
        //并将这些提取出的子向量存储到viewspace_pos容器中
        std::transform(mm.begin(), mm.end(), viewspace_pos.begin(), [](auto& v) {
            return v.template head<3>();
        });
        // 将三角形的顶点从视图空间变换到齐次裁剪空间
        Eigen::Vector4f v[] = {
                mvp * t->v[0],
                mvp * t->v[1],
                mvp * t->v[2]
        };
         // 进行齐次除法
        for (auto& vec : v) {
            vec.x()/=vec.w();
            vec.y()/=vec.w();
            vec.z()/=vec.w();
        }
        // 计算三角形法线在视图空间的表示
        Eigen::Matrix4f inv_trans = (view * model).inverse().transpose();
        Eigen::Vector4f n[] = {
                inv_trans * to_vec4(t->normal[0], 0.0f),
                inv_trans * to_vec4(t->normal[1], 0.0f),
                inv_trans * to_vec4(t->normal[2], 0.0f)
        };

        // 进行视口变换
        for (auto & vert : v)
        {
            vert.x() = 0.5*width*(vert.x()+1.0);
            vert.y() = 0.5*height*(vert.y()+1.0);
            vert.z() = vert.z() * f1 + f2;
        }
        // 更新变换后的顶点和法线到新的三角形对象
        for (int i = 0; i < 3; ++i)
        {
             // 更新屏幕空间顶点坐标
            newtri.setVertex(i, v[i]);
        }

        for (int i = 0; i < 3; ++i)
        {
             // 更新视图空间法线向量
            newtri.setNormal(i, n[i].head<3>());
        }
        // 设置三角形颜色
        newtri.setColor(0, 148,121.0,92.0);
        newtri.setColor(1, 148,121.0,92.0);
        newtri.setColor(2, 148,121.0,92.0);

        // 传入视图空间顶点位置进行三角形光栅化
        rasterize_triangle(newtri, viewspace_pos);
    }
}
/**
 * 通过线性插值计算三维向量的加权平均值。
 * @param alpha、beta、gamma 三个插值系数,用于计算加权平均值。
 * @param vert1、vert2、vert3 三个三维向量,作为插值的基向量。
 * @param weight 加权因子,用于调整插值结果的权重。
 * @return 返回根据插值系数和基向量计算得到的加权平均值向量。
 */
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;
}
/**
 * 通过线性插值计算在三个顶点构成的三角形内的一点坐标。
 * 
 * @param alpha、beta、gamma 用于确定点在三角形内的位置的权重参数。这三个参数的和应为1。
 * @param vert1、vert2、vert3 三角形的三个顶点。
 * @param weight 对插值结果进行缩放的权重值。 
 * @return 返回根据给定权重参数和顶点计算得到的插值点的坐标,作为一个Eigen::Vector2f对象。
 */
static Eigen::Vector2f interpolate(float alpha, float beta, float gamma, const Eigen::Vector2f& vert1, const Eigen::Vector2f& vert2, const Eigen::Vector2f& vert3, float weight)
{
    auto u = (alpha * vert1[0] + beta * vert2[0] + gamma * vert3[0]);
    auto v = (alpha * vert1[1] + beta * vert2[1] + gamma * vert3[1]);

    u /= weight;
    v /= weight;

    return Eigen::Vector2f(u, v);
}

/**
 * 屏幕空间光栅化
 * 
 * 该函数用于将一个三角形光栅化到屏幕空间上,具体包括顶点深度值的插值、属性(如颜色、法向量、纹理坐标等)的插值,
 * 以及调用片段着色器来计算最终像素颜色。
 * 
 * @param t 待光栅化的三角形对象,包含顶点信息等。
 * @param view_pos 包含三个顶点在视图空间中的位置的数组。
 */
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.
    auto v = t.toVector4();
    int left,right,top,bottom;
    // TODO : Find out the bounding box of current triangle.
    left =std::min(std::min(v[0].x(),v[1].x()),v[2].x());
    right =std::max(std::max(v[0].x(),v[1].x()),v[2].x());
    top=std::max(std::max(v[0].y(),v[1].y()),v[2].y());
    bottom=std::min(std::min(v[0].y(),v[1].y()),v[2].y());

    for(int y=bottom;y<=top;y++){
        for(int x=left;x<=right;x++){
            // iterate through the pixel and find if the current pixel is inside the triangle
            if(insideTriangle(x+0.5,y+0.5,t.v)){
                     // If so, use the following code to get the interpolated z value.
                    auto[alpha, beta, gamma] = computeBarycentric2D(x, y, 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(x,y)]>z_interpolated){
                         depth_buf[get_index(x,y)]=z_interpolated;
                          // TODO : set the current pixel (use the set_pixel function) to the color
                          // of the triangle (use getColor function) if it should be painted.
        
                        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);  
                        // 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;
                        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({x,y},pixel_color);    
                    }
            }
        }
    }

}

void rst::rasterizer::set_model(const Eigen::Matrix4f& m)
{
    model = m;
}

void rst::rasterizer::set_view(const Eigen::Matrix4f& v)
{
    view = v;
}

void rst::rasterizer::set_projection(const Eigen::Matrix4f& p)
{
    projection = p;
}

void rst::rasterizer::clear(rst::Buffers buff)
{
    if ((buff & rst::Buffers::Color) == rst::Buffers::Color)
    {
        std::fill(frame_buf.begin(), frame_buf.end(), Eigen::Vector3f{0, 0, 0});
    }
    if ((buff & rst::Buffers::Depth) == rst::Buffers::Depth)
    {
        std::fill(depth_buf.begin(), depth_buf.end(), std::numeric_limits<float>::infinity());
    }
}

rst::rasterizer::rasterizer(int w, int h) : width(w), height(h)
{
    frame_buf.resize(w * h);
    depth_buf.resize(w * h);

    texture = std::nullopt;
}

int rst::rasterizer::get_index(int x, int y)
{
    return (height-y)*width + x;
}

void rst::rasterizer::set_pixel(const Vector2i &point, const Eigen::Vector3f &color)
{
    //old index: auto ind = point.y() + point.x() * width;
    int ind = (height-point.y())*width + point.x();
    frame_buf[ind] = color;
}

void rst::rasterizer::set_vertex_shader(std::function<Eigen::Vector3f(vertex_shader_payload)> vert_shader)
{
    vertex_shader = vert_shader;
}

void rst::rasterizer::set_fragment_shader(std::function<Eigen::Vector3f(fragment_shader_payload)> frag_shader)
{
    fragment_shader = frag_shader;
}

Texture.hpp:Store the image and get the color according to uv.

//
// Created by LEI XU on 4/27/19.
//

#ifndef RASTERIZER_TEXTURE_H
#define RASTERIZER_TEXTURE_H
#include "global.hpp"
#include <eigen3/Eigen/Eigen>
#include <opencv2/opencv.hpp>
//说人话就是该类存储纹理贴图,并根据uv获取颜色
class Texture{
private:
    //用来存储图像数据(矩阵)  
    cv::Mat image_data;

public:
    Texture(const std::string& name)
    {
        image_data = cv::imread(name);//从文件读取图像到 cv::Mat
        //执行的操作是将输入图像 image_data 从 RGB 颜色空间转换到 BGR 颜色空间。
        //OpenCV 内部默认的存储顺序是 BGR 而不是标准的 RGB。
        //这意味着如果你有一个原本是 RGB 格式的图像数据,
        //当它被读入 cv::Mat 结构后,如果要与 OpenCV 的大多数图像处理函数兼容,
        //可能需要将其转换为 BGR 格式。
        cv::cvtColor(image_data, image_data, cv::COLOR_RGB2BGR);
        //int cv::Mat::rows:获取矩阵的行数
        //int cv::Mat::cols:获取矩阵的列数
        width = image_data.cols;
        height = image_data.rows;
    }
    //图像宽高
    int width, height;
    //根据u,v坐标获取图像颜色(u,v是0-1之间,所以要映射一下)
    Eigen::Vector3f getColor(float u, float v)
    {
        //映射
        auto u_img = u * width;
        auto v_img = (1 - v) * height;
        //按指定位置访问元素并获取颜色值。
        auto color = image_data.at<cv::Vec3b>(v_img, u_img);
        return Eigen::Vector3f(color[0], color[1], color[2]);
    }

};
#endif //RASTERIZER_TEXTURE_H

Theory:

Lerp  according to barycenter:

diffuse:

Specular:bilne phong model 

bump mapping:

Take two adjacent points and find the tangent, then rotate 90 degrees counterclockwise:

Displacement mapping :

There's an extra process of shifting the vertices

Addition:

We need to determine the coordinates of the four pixels adjacent to the point.

Point 1 represents the original uv coordinates. We first floor the coordinates to obtain Point 2. Then, based on Point 2, we can calculate Points 3, 4, 5, and 6:

Code:        

rasterize_triangle:Find the barycentric coordinates of a pixel and interpolate the colors based on these coordinates.

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.
    auto v = t.toVector4();
    int left,right,top,bottom;
    // TODO : Find out the bounding box of current triangle.
    left =std::min(std::min(v[0].x(),v[1].x()),v[2].x());
    right =std::max(std::max(v[0].x(),v[1].x()),v[2].x());
    top=std::max(std::max(v[0].y(),v[1].y()),v[2].y());
    bottom=std::min(std::min(v[0].y(),v[1].y()),v[2].y());

    for(int y=bottom;y<=top;y++){
        for(int x=left;x<=right;x++){
            // iterate through the pixel and find if the current pixel is inside the triangle
            if(insideTriangle(x+0.5,y+0.5,t.v)){
                     // If so, use the following code to get the interpolated z value.
                    auto[alpha, beta, gamma] = computeBarycentric2D(x, y, 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(x,y)]>z_interpolated){
                         depth_buf[get_index(x,y)]=z_interpolated;
                          // TODO : set the current pixel (use the set_pixel function) to the color
                          // of the triangle (use getColor function) if it should be painted.
        
                        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);  
                        // 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;
                        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({x,y},pixel_color);    
                    }
            }
        }
    }

}

 attention:There is an interpolate function above, directly call interpolate, try not to write their own (because of the return value problem, their own write bad will give an error)

phong_fragment_shader:

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,h,v;

      v=eye_pos-point;

      l=(light.position -point).normalized();

      float x0,y0,z0;
      x0=light.position.x() -point.x();
      y0=light.position.y() -point.y();
      z0=light.position.z() -point.z();
      float r =sqrt(x0*x0+y0*y0+z0*z0); 

      h=(l+v)/(l+v).norm();
   
        
        Eigen::Vector3f la = ka.cwiseProduct(amb_light_intensity);
        Eigen::Vector3f ld=  kd.cwiseProduct(light.intensity/(r*r))*std::max(normal.normalized().dot(l),0.0f);
        Eigen::Vector3f ls = ks.cwiseProduct(light.intensity/(r*r))*pow(std::max(normal.dot(h),0.0f),64);
        result_color+=la+ld+ls;
    }

    return result_color * 255.f;
}

attention:cwiseProduct, element-by-element multiplication of a vector, with * cannot be multiplied bit by bit, otherwise it will be an error.

texture_fragment_shader:Compared with the phong model, the additional one maps the base color from the texture.

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,h,v;

      v=eye_pos-point;

      l=(light.position -point).normalized();

      float x0,y0,z0;
      x0=light.position.x() -point.x();
      y0=light.position.y() -point.y();
      z0=light.position.z() -point.z();
      float r =sqrt(x0*x0+y0*y0+z0*z0); 

      h=(l+v)/(l+v).norm();
   
        
        Eigen::Vector3f la = ka.cwiseProduct(amb_light_intensity);
        Eigen::Vector3f ld=  kd.cwiseProduct(light.intensity/(r*r))*std::max(normal.normalized().dot(l),0.0f);
        Eigen::Vector3f ls = ks.cwiseProduct(light.intensity/(r*r))*pow(std::max(normal.dot(h),0.0f),64);
        result_color+=la+ld+ls;

    }

    return result_color * 255.f;
}

attention:payload.texture->getColor,Notice the arrow and use the dot (.) You get an error.

bump_fragment_shader:Realization formula.

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;


    // 根据法线向量计算TBN矩阵(切线空间到世界空间的变换矩阵)纹理坐标空间到世界空间的转换
    Eigen::Vector3f t ;

    t<<normal.x()*normal.y()/sqrt(normal.x()*normal.x()+normal.z()*normal.z()),
    sqrt(normal.x()*normal.x()+normal.z()*normal.z()),
    normal.z()*normal.y()/sqrt(normal.x()*normal.x()+normal.z()*normal.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();
    int w=payload.texture->width;
    int 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 ;
ln<<-dU, -dV, 1;
normal = (TBN * ln).normalized();

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

    return result_color * 255.f;
}

attention:Note that the division becomes 1.0f, which would cause an error if you used the int 1 effect directly.

displacement_fragment_shader:

Additional code:

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

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

    t<<normal.x()*normal.y()/sqrt(normal.x()*normal.x()+normal.z()*normal.z()),
    sqrt(normal.x()*normal.x()+normal.z()*normal.z()),
    normal.z()*normal.y()/sqrt(normal.x()*normal.x()+normal.z()*normal.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();
    int w=payload.texture->width;
    int 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 ;

ln<<-dU, -dV, 1;
normal = (TBN * ln).normalized();
point +=  kn * normal * payload.texture->getColor(u,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,h,v;

      v=eye_pos-point;

      l=(light.position -point).normalized();

      float x0,y0,z0;
      x0=light.position.x() -point.x();
      y0=light.position.y() -point.y();
      z0=light.position.z() -point.z();
      float r =sqrt(x0*x0+y0*y0+z0*z0); 

      h=(l+v)/(l+v).norm();
   
        
        Eigen::Vector3f la = ka.cwiseProduct(amb_light_intensity);
        Eigen::Vector3f ld=  kd.cwiseProduct(light.intensity/(r*r))*std::max(normal.normalized().dot(l),0.0f);
        Eigen::Vector3f ls = ks.cwiseProduct(light.intensity/(r*r))*pow(std::max(normal.dot(h),0.0f),64);
        result_color+=la+ld+ls;

    }

    return result_color * 255.f;
}

Attention:Remember to get the color.norm () (I don't know why yet)

Addtion:

  Eigen::Vector3f getColorBilinear(float u,float v)
    {
      
        //防止溢出
        if(u<0)u=0;
        if(u>1)u=1;
        if(v<0)v=0;
        if(v>1)v=1;
        
        auto u_img = u * width;
        auto v_img = (1 - v) * height;
        //leftX,RightX...
        float LX,RX,TY,BY;
        LX=std::max(1.0f,floor(u_img-0.5f));
        RX=floor(u_img+0.5f);
        TY=floor(v_img+0.5f);
        BY=std::max(1.0f,floor(v_img-0.5f));

    
    float tx,ty;
    tx=(u_img-LX)/(RX-LX);
    tx=(v_img-BY)/(TY-BY);

    //注意这里是(v,u)写反了会出问题
    auto c00=image_data.at<cv::Vec3b>(BY,LX);
    auto c01=image_data.at<cv::Vec3b>(TY,LX);  
    auto c10=image_data.at<cv::Vec3b>(BY,RX);
    auto c11=image_data.at<cv::Vec3b>(TY,RX);
   
  
    Eigen::Vector3f p0,p1,p2;
    auto color0 =c00+tx*(c01-c00);
    auto color1 =c10+tx*(c11-c10);

    auto color2 =color0+ty*(color1-color0);
 
return Eigen::Vector3f(color2[0], color2[1], color2[2]);
    }

Attention: 

When obtaining the color based on coordinates, it's important to note that the coordinates are (v, u). Writing them in reverse order may lead to the following situation:

 

Games101:作业3(管线分析、深度插值、libpng warning、双线性插值等)_games101作业3-CSDN博客

Homework:

  

Addition:Sleek little calf!

cool! 

  • 28
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值