games101 作业3 shading

1 总览
在这次编程任务中,我们会进一步模拟现代图形技术。我们在代码中添加了
Object Loader(用于加载三维模型), Vertex Shader 与 Fragment Shader,并且支持
了纹理映射。
而在本次实验中,你需要完成的任务是:
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.
 
 
首先让我们传入我们作业1中的投影矩阵
 
注意,其实我作业1错了,要把zNear和Zfar都乘上-1的
这里我借鉴了网上的一份答案,这不重要,其实最重要的是我们的渲染
 
Eigen::Matrix4f get_projection_matrix(float eye_fov, float aspect_ratio, float zNear, float zFar)
{
    Eigen::Matrix4f projection = Eigen::Matrix4f::Identity();
    Eigen::Matrix4f M_persp2ortho(4, 4);
    Eigen::Matrix4f M_ortho_scale(4, 4);
    Eigen::Matrix4f M_ortho_trans(4, 4);

    float angle = eye_fov * MY_PI / 180.0; // half angle
    float height = zNear * tan(angle) * 2;
    float width = height * aspect_ratio;

    auto t = -zNear * tan(angle / 2);
    auto r = t * aspect_ratio;
    auto l = -r;
    auto b = -t;

    M_persp2ortho << zNear, 0, 0, 0,
        0, zNear, 0, 0,
        0, 0, zNear + zFar, -zNear * zFar,
        0, 0, 1, 0;
    // 之前我这里用的是 宽和高的长度,实际上r-l 和 t-b 应该是它们的两倍
    // 为了避免麻烦,我这里改成了坐标形式
    M_ortho_scale << 2 / (r - l), 0, 0, 0,
        0, 2 / (t - b), 0, 0,
        0, 0, 2 / (zNear - zFar), 0,
        0, 0, 0, 1;
    M_ortho_trans << 1, 0, 0, -(r + l) / 2,
        0, 1, 0, -(t + b) / 2,
        0, 0, 1, -(zNear + zFar) / 2,
        0, 0, 0, 1;
    Eigen::Matrix4f M_ortho = M_ortho_scale * M_ortho_trans;
    projection = M_ortho * M_persp2ortho * projection;

    return projection;
}

 

然后我们进行插值(也就是所谓的重心坐标插值)

在这里我们插值了颜色,法向量,viewport,zbuffer,纹理

 1 void rst::rasterizer::rasterize_triangle(const Triangle& t, const std::array<Eigen::Vector3f, 3>& view_pos) 
 2 {
 3     auto v = t.toVector4();
 4     float min_x = std::min(v[0][0], std::min(v[1][0], v[2][0]));
 5     float max_x = std::max(v[0][0], std::max(v[1][0], v[2][0]));
 6     float min_y = std::min(v[0][1], std::min(v[1][1], v[2][1]));
 7     float max_y = std::max(v[0][1], std::max(v[1][1], v[2][1]));
 8 
 9     min_x = (int)std::floor(min_x);
10     max_x = (int)std::ceil(max_x);
11     min_y = (int)std::floor(min_y);
12     max_y = (int)std::ceil(max_y);
13 
14     for (int x = min_x; x <= max_x; x++) {
15             for (int y = min_y; y <= max_y; y++) {
16                 if (insideTriangle(x,y,t.v)) {
17                     auto tup = computeBarycentric2D((float)x + 0.5, (float)y + 0.5, t.v);
18                     float alpha;
19                     float beta;
20                     float gamma;
21                     std::tie(alpha, beta, gamma) = tup;
22                     
23                     float w_reciprocal = 1.0 / (alpha / v[0].w() + beta / v[1].w() + gamma / v[2].w());
24                     float z_interpolated = alpha * v[0].z() / v[0].w() + beta * v[1].z() / v[1].w() + gamma * v[2].z() / v[2].w();
25                     z_interpolated *= w_reciprocal;
26 
27                     float weight = 1;
28 
29                     auto interpolated_color = interpolate(alpha,beta,gamma,t.color[0],t.color[1],t.color[2],weight);
30                     auto interpolated_normal =interpolate(alpha,beta,gamma,t.normal[0],t.normal[1],t.normal[2],weight);
31                     auto interpolated_texcoords = interpolate(alpha,beta,gamma,t.tex_coords[0],t.tex_coords[1],t.tex_coords[2],weight);
32                     auto interpolated_shadingcoords = interpolate(alpha,beta,gamma,view_pos[0],view_pos[1],view_pos[2],weight);
33                     fragment_shader_payload payload( interpolated_color, interpolated_normal.normalized(), interpolated_texcoords, texture ? &*texture : nullptr);
34                     payload.view_pos = interpolated_shadingcoords;
35                     auto pixel_color = fragment_shader(payload);
36                     if (depth_buf[get_index(x, y)] > z_interpolated) {
37                         Vector2i point;
38                         point << x,y;
39                         depth_buf[get_index(x, y)] = z_interpolated;
40                         set_pixel(point, pixel_color);
41                     }
42                 }
43             }
44         }
45         
46     // TODO: From your HW3, get the triangle rasterization code.
47     // TODO: Inside your rasterization loop:
48     //    * v[i].w() is the vertex view space depth value z.
49     //    * Z is interpolated view space depth for the current pixel
50     //    * zp is depth between zNear and zFar, used for z-buffer
51 
52     // float Z = 1.0 / (alpha / v[0].w() + beta / v[1].w() + gamma / v[2].w());
53     // float zp = alpha * v[0].z() / v[0].w() + beta * v[1].z() / v[1].w() + gamma * v[2].z() / v[2].w();
54     // zp *= Z;
55 
56     // TODO: Interpolate the attributes:
57     // auto interpolated_color
58     // auto interpolated_normal
59     // auto interpolated_texcoords
60     // auto interpolated_shadingcoords
61 
62     // Use: fragment_shader_payload payload( interpolated_color, interpolated_normal.normalized(), interpolated_texcoords, texture ? &*texture : nullptr);
63     // Use: payload.view_pos = interpolated_shadingcoords;
64     // 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;
65     // Use: auto pixel_color = fragment_shader(payload);
66 
67  
68 }

下面,到了我们的重点.,布林冯着色,这里我们和glsl语言不同,作业框架里面其实帮我们模拟了一个着色器的过程

注意我们的代码。

 

这里踩到了一坑,居然是std::max的问题,我以前从来没有发现这个问题,服了,貌似是这里不能强制转换,晕掉了

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)
    {
        Eigen::Vector3f v  = eye_pos - point;
        Eigen::Vector3f L = light.position - point;
        Eigen::Vector3f H = (v.normalized()+L.normalized()).normalized();
        float r = L.dot(L);
        float n_l = normal.normalized().dot(L.normalized());
        Eigen::Vector3f ld = kd.cwiseProduct(light.intensity)*(std::max(0.0f,n_l))/(r);
        Eigen::Vector3f la = ka.cwiseProduct(amb_light_intensity);
        float n_h = (std::max(0.0f,normal.dot(H)));
        Eigen::Vector3f ls = ks.cwiseProduct(light.intensity/(r))*std::pow(n_h,p);
        result_color += (ls+la+ld);
        // 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;
}

然后是纹理坐标的着色器,这里恶心的一点没有提到,games论坛上有人提出了,也就是纹理坐标要规范在0-1之内,不然渲染出的结果出错了。(去修改shader.h)

 

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());
        // 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)
    {
        Eigen::Vector3f v  = eye_pos - point;
        Eigen::Vector3f L = light.position - point;
        Eigen::Vector3f H = (v.normalized()+L.normalized()).normalized();
        float r = L.dot(L);
        float n_l = normal.normalized().dot(L.normalized());
        float tmp = std::max(0.0f,n_l);
        Eigen::Vector3f ld = kd.cwiseProduct(light.intensity)*tmp/(r);
        Eigen::Vector3f la = ka.cwiseProduct(amb_light_intensity);
        float n_h = (std::max(0.0f,normal.normalized().dot(H)));
        Eigen::Vector3f ls = ks.cwiseProduct(light.intensity/(r))*std::pow(n_h,p);
        result_color += (ls+la+ld);
        // 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;
}

然后是 bump ,这里我们不用考虑光照,渲染出的就是一张bump的图

 

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)
    {
        Eigen::Vector3f v  = eye_pos - point;
        Eigen::Vector3f L = light.position - point;
        Eigen::Vector3f H = (v.normalized()+L.normalized()).normalized();
        float r = L.dot(L);
        float n_l = normal.normalized().dot(L.normalized());
        Eigen::Vector3f ld = kd.cwiseProduct(light.intensity)*(std::max(0.0f,n_l))/(r);
        Eigen::Vector3f la = ka.cwiseProduct(amb_light_intensity);
        float n_h = (std::max(0.0f,normal.dot(H)));
        Eigen::Vector3f ls = ks.cwiseProduct(light.intensity/(r))*std::pow(n_h,p);
        result_color += (ls+la+ld);
        // 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;
}

最后是displacement,这里注意一点,就是不光要算法向量,由于displacement(位移贴图)是考虑光照的,所以我们的point也要加上对应的值

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;
     float x = normal.x();
    float y = normal.y();
    float z = normal.z();
    Eigen::Vector3f  n = normal;
    Eigen::Vector3f  t(x*y/sqrt(x*x+z*z),sqrt(x*x+z*z),z*y/sqrt(x*x+z*z));
    Eigen:;Vector3f b = n.cross(t);
    float u = payload.tex_coords.x();
    float v = payload.tex_coords.y();
    float w = payload.texture->width;
    float h = payload.texture->height;
    Eigen::Matrix3f TBN;
    TBN << t.x(),b.x(),n.x(), t.y(),b.y(),n.y(), t.z(),b.z(),n.z();
    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(-dU,-dV,1.00f);
  
    point += kn*normal*payload.texture->getColor(u,v).norm();

    normal = (TBN*ln).normalized();
    
    // 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)
    {
        Eigen::Vector3f v  = eye_pos - point;
        Eigen::Vector3f L = light.position - point;
        Eigen::Vector3f H = (v.normalized()+L.normalized()).normalized();
        float r = L.dot(L);
        float n_l = normal.normalized().dot(L.normalized());
        Eigen::Vector3f ld = kd.cwiseProduct(light.intensity)*(std::max(0.0f,n_l))/(r);
        Eigen::Vector3f la = ka.cwiseProduct(amb_light_intensity);
        float n_h = (std::max(0.0f,normal.dot(H)));
        Eigen::Vector3f ls = ks.cwiseProduct(light.intensity/(r))*std::pow(n_h,p);
        result_color += (ls+la+ld);
        // 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;
}

最后,渲染效果如下

posted @ 2021-01-28 02:08  coolwx  阅读(1030)  评论(0编辑  收藏  举报