IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 游戏开发 -> games101——作业3 -> 正文阅读

[游戏开发]games101——作业3


总览

在这次编程任务中,我们会进一步模拟现代图形技术。我们在代码中添加了Object Loader(用于加载三维模型), Vertex ShaderFragment 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,实现 TextureShading 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 目录下按照如下方式构建程序:

mkdir build
cd ./build
cmake ..
make

这将会生成命名为 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 中有三个 Fragment Shader,其中 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,这要求你首先使用插值得到的结果设置 fragment shader payload,并调用 fragment shader 得到计算结果。

运行与结果

在你按照上述说明将上次作业的代码复制到对应位置,并作出相应修改之后(请务必认真阅读说明),你就可以运行默认的 normal shader 并观察到如下结果:

实现 Blinn-Phong 反射模型之后的结果应该是:

实现纹理之后的结果应该是:

实现 Bump Mapping 后,你将看到可视化的凹凸向量:

实现 Displacement Mapping 后,你将看到如下结果:


作业代码

插值

使用重心坐标插值出对应像素的法向量、颜色、纹理坐标、以及其三维空间中的坐标,然后计算经过着色模型之后的颜色作为该像素的颜色

//Screen space rasterization
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();

    // TODO : Find out the bounding box of current triangle.
    int boundingbox_x_left = std::min(v[0].x(), std::min(v[1].x(), v[2].x()));
    int boundingbox_x_right = std::max(v[0].x(), std::max(v[1].x(), v[2].x()));
    int boundingbox_y_left = std::min(v[0].y(), std::min(v[1].y(), v[2].y()));
    int boundingbox_y_right = std::max(v[0].y(), std::max(v[1].y(), v[2].y()));

    // iterate through the pixel and find if the current pixel is inside the triangle
    for (auto x=boundingbox_x_left; x<=boundingbox_x_right; ++x)
        for (auto y=boundingbox_y_left; y<=boundingbox_y_right; ++y) {
            if (insideTriangle(x, y, t.v)) {
                // 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
                auto[alpha, beta, gamma] = computeBarycentric2D(1.0*x+0.5, 1.0*y+0.5, 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)]) {
                    // TODO: Interpolate the attributes:
                    // auto interpolated_color
                    auto interpolated_color = interpolate(alpha, beta, gamma, t.color[0], t.color[1], t.color[2], 1.0); 
                    // auto interpolated_normal
                    auto interpolated_normal = interpolate(alpha, beta, gamma, t.normal[0], t.normal[1], t.normal[2], 1.0); 
                    // auto interpolated_texcoords
                    auto interpolated_texcoords = interpolate(alpha, beta, gamma, t.tex_coords[0], t.tex_coords[1], t.tex_coords[2], 1.0); 
                    // std::cout<<interpolated_texcoords<<std::endl;
                    // auto interpolated_shadingcoords
                    auto interpolated_shadingcoords = interpolate(alpha, beta, gamma, view_pos[0], view_pos[1], view_pos[2], 1.0); 

                    // Use: fragment_shader_payload payload( interpolated_color, interpolated_normal.normalized(), interpolated_texcoords, texture ? &*texture : nullptr);
                    fragment_shader_payload payload( interpolated_color, interpolated_normal.normalized(), interpolated_texcoords, texture ? &*texture : nullptr);
                    // Use: payload.view_pos = interpolated_shadingcoords;
                    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 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);
                }
            }
        }
}

这里三维空间中的坐标 payload.view_pos 表示经过模型变换与视图变换之后的物体三维坐标插值。这个三维空间坐标插值主要用于着色模型中,计算 l ? \vec{l} l v ? \vec{v} v ,至于为什么二维重心可以插值三维坐标,其实这里是一种近似,因为误差不是很大,就直接使用二维重心坐标插值三维坐标了。需要注意的是在 rst::rasterizer::draw 代码中,对于初始法向量要经过 inv_trans 变换,这是因为开始给出的法向量是未经过模型变换与视图变换的,因此变换的法向量 n ′ ? \vec{n'} n 要满足 v ′ ? T ? n ′ ? = 0 \vec{v'}^{T}*\vec{n'}=0 v T?n =0,而 v ? T ? n ? = 0 \vec{v}^{T}*\vec{n}=0 v T?n =0,所以 ( v i e w ? m o d e l ? v ? ) T ? ( M ? n ? ) = 0 (view*model*\vec{v})^{T}*(M*\vec{n})=0 (view?model?v )T?(M?n )=0,我们就是要知道这个 M M M,而上面的式子可以写为 v ? T ? ( v i e w ? m o d e l ) T ? M ? n ? \vec{v}^{T}*(view*model)^{T}*M*\vec{n} v T?(view?model)T?M?n ,所以可以得出 M = ( v i e w ? m o d e l ) T ? 1 M = (view*model)^{T^{-1}} M=(view?model)T?1

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)
};

法向量实现

法向量对应颜色这里使用 法向量+ ( 0.5 , 0.5 , 0.5 ) (0.5,0.5,0.5) (0.5,0.5,0.5)

Eigen::Vector3f normal_fragment_shader(const fragment_shader_payload& payload)
{
    Eigen::Vector3f return_color = (payload.normal.head<3>().normalized() + Eigen::Vector3f(1.0f, 1.0f, 1.0f)) / 2.f;
    Eigen::Vector3f result;
    result << return_color.x() * 255, return_color.y() * 255, return_color.z() * 255;
    return result;
}

运行 ./Rasterizer output.png normal 其结果如下

Blinn-Phong 模型

Blinn-Phong 反射模型的公式如下

在代码实现过程中,需要注意,角度的计算需要为单位向量。其次这里我把相机位置改为 ( 0 , 0 , 0 ) (0,0,0) (0,0,0),我的想法是,这里的插值三维空间坐标是已经经过模型变换和视图变换之后的了,那么视图变换之后,相机按道理就应该在 ( 0 , 0 , 0 ) (0,0,0) (0,0,0) 这个位置,不知道我的理解是否有问题,欢迎大佬指正。

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, 0};

    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*
        auto ambient = amb_light_intensity;
        // std::cout<<ambient<<std::endl;
        Eigen::Vector3f l = light.position - point; // light direction vector3f
        auto diffuse = (light.intensity/l.dot(l)) * std::max(0.f, normal.dot(l.normalized()));
        Eigen::Vector3f v = eye_pos - point; // view direction vector3f
        Eigen::Vector3f h = (v + l).normalized(); // normalize half vector3f
        auto specular = (light.intensity/l.dot(l)) * std::max(0.f, std::pow(normal.dot(h), p));
        // components are. Then, accumulate that result on the *result_color* object.
        result_color = result_color + Eigen::Vector3f(ambient[0]*ka[0],ambient[1]*ka[1],ambient[2]*ka[2]) + Eigen::Vector3f(diffuse[0]*kd[0],diffuse[1]*kd[1],diffuse[2]*kd[2]) + Eigen::Vector3f(specular[0]*ks[0],specular[1]*ks[1],specular[2]*ks[2]);
    }

    return result_color * 255.f;
}

运行 ./Rasterizer output.png phong 结果如下

应用纹理

我们之前已经插值出该采样点对应的纹理坐标,使用 payload.texture->getColor(u,v) 就能获得 ( u , v ) (u,v) (u,v) 对应的纹理颜色,并将其传给 kd

Eigen::Vector3f texture_fragment_shader(const fragment_shader_payload& payload)
{
    Eigen::Vector3f return_color = {0, 0, 0};
    if (payload.texture)
    {   
        // std::cout<<"1"<<std::endl;
        // TODO: Get the texture value at the texture coordinates of the current fragment
        float u = std::min(1.0f, std::max(0.0f, payload.tex_coords[0]));
        float v = std::min(1.0f, std::max(0.0f, payload.tex_coords[1]));
        // std::cout<<u<<" "<<v<<std::endl;
        return_color = payload.texture->getColor(u, v);
    }
    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, 0};

    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.
        // TODO: For each light source in the code, calculate what the *ambient*, *diffuse*, and *specular*
        auto ambient = amb_light_intensity;
        // std::cout<<ambient<<std::endl;
        Eigen::Vector3f l = light.position - point; // light direction vector3f
        auto diffuse = (light.intensity/l.dot(l)) * std::max(0.f, normal.dot(l.normalized()));
        Eigen::Vector3f v = eye_pos - point; // view direction vector3f
        Eigen::Vector3f h = (v + l).normalized(); // normalize half vector3f
        auto specular = (light.intensity/l.dot(l)) * std::max(0.f, std::pow(normal.dot(h), p));
        // components are. Then, accumulate that result on the *result_color* object.
        result_color = result_color + Eigen::Vector3f(ambient[0]*ka[0],ambient[1]*ka[1],ambient[2]*ka[2]) + Eigen::Vector3f(diffuse[0]*kd[0],diffuse[1]*kd[1],diffuse[2]*kd[2]) + Eigen::Vector3f(specular[0]*ks[0],specular[1]*ks[1],specular[2]*ks[2]);
    }

    return result_color * 255.f;
}

运行 ./Rasterizer output.png texture 结果如下

凹凸贴图

TVB切向量空间具体可以看这篇文章

这里我感觉代码框架里的 t 只是做一个近似,正常 t 也是根据三角形三个顶点的 t 插值产生。代码框架给出的方法就是按照上图的方法,求出一个 t,而且这里还有点错误, t = ( ? x y x 2 + z 2 , x 2 + z 2 , ? y z x 2 + z 2 ) t=(-\frac{xy}{\sqrt{x^{2}+z^{2}}}, \sqrt{x^{2}+z^{2}},-\frac{yz}{\sqrt{x^{2}+z^{2}}}) t=(?x2+z2 ?xy?,x2+z2 ?,?x2+z2 ?yz?)

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;
    float u = std::min(1.0f, std::max(0.0f, payload.tex_coords[0]));
    float v = std::min(1.0f, std::max(0.0f, payload.tex_coords[1]));
    // std::cout<<u<<" "<<v<<std::endl;

    // 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))
    float x = normal.x();
    float y = normal.y();
    float z = normal.z();
    Vector3f 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
    Vector3f b = normal.cross(t);
    // Matrix TBN = [t b n]
    Matrix3f TBN;
    TBN<<t.x(),b.x(),normal.x(),t.y(),b.y(),normal.y(),t.z(),b.z(),normal.z();
    // TBN<<t.x(), t.y(), t.z(), b.x(), b.y(), b.z(), n.x(), n.y(), n.z();
    // dU = kh * kn * (h(u+1/w,v)-h(u,v))
    float dU = kh * kn * (h(u+1.0f/payload.texture->width,v,payload)-h(u,v,payload));
    // dV = kh * kn * (h(u,v+1/h)-h(u,v))
    float dV = kh * kn * (h(u,v+1.0f/payload.texture->height,payload)-h(u,v,payload));
    // Vector ln = (-dU, -dV, 1)
    Eigen::Vector3f ln(-dU, -dV, 1.0f);
    // Normal n = normalize(TBN * ln)
    normal = (TBN * ln).normalized();

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

    return result_color * 255.f;
}

h 函数用于计算 ( u , v ) (u,v) (u,v) 对应高度值,这里用对应 RGB 颜色的平方和的平方根表示,其代码如下:

float h(float u, float v, const fragment_shader_payload& payload) 
{
    return payload.texture->getColor(u, v).norm();
}

运行 ./Rasterizer output.png bump 结果如下

位移贴图

位移贴图这里除了类似凹凸贴图,改变了法向量,并且实际移动了三维空间中点的位置,进而直接影响 Blinn-Phong 模型中的 l ? \vec{l} l v ? \vec{v} v

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)
    float u = std::min(1.0f, std::max(0.0f, payload.tex_coords[0]));
    float v = std::min(1.0f, std::max(0.0f, payload.tex_coords[1]));
    // std::cout<<u<<" "<<v<<std::endl;

    // 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))
    float x = normal.x();
    float y = normal.y();
    float z = normal.z();
    Vector3f 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
    Vector3f b = normal.cross(t);
    // Matrix TBN = [t b n]
    Matrix3f TBN;
    TBN<<t.x(),b.x(),normal.x(),t.y(),b.y(),normal.y(),t.z(),b.z(),normal.z();
    // TBN<<t.x(), t.y(), t.z(), b.x(), b.y(), b.z(), n.x(), n.y(), n.z();
    // dU = kh * kn * (h(u+1/w,v)-h(u,v))
    float dU = kh * kn * (h(u+1.0f/payload.texture->width,v,payload)-h(u,v,payload));
    // dV = kh * kn * (h(u,v+1/h)-h(u,v))
    float dV = kh * kn * (h(u,v+1.0f/payload.texture->height,payload)-h(u,v,payload));
    // Vector ln = (-dU, -dV, 1)
    Eigen::Vector3f ln(-dU, -dV, 1.0f);
    point = point + kn * normal * h(u, v, payload);
    // Normal n = normalize(TBN * ln)
    normal = (TBN * ln).normalized();

    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.
        // TODO: For each light source in the code, calculate what the *ambient*, *diffuse*, and *specular*
        auto ambient = amb_light_intensity;
        // std::cout<<ambient<<std::endl;
        Eigen::Vector3f l = light.position - point; // light direction vector3f
        auto diffuse = (light.intensity/l.dot(l)) * std::max(0.f, normal.dot(l.normalized()));
        Eigen::Vector3f v = eye_pos - point; // view direction vector3f
        Eigen::Vector3f h = (v + l).normalized(); // normalize half vector3f
        auto specular = (light.intensity/l.dot(l)) * std::max(0.f, std::pow(normal.dot(h), p));
        // components are. Then, accumulate that result on the *result_color* object.
        result_color = result_color + Eigen::Vector3f(ambient[0]*ka[0],ambient[1]*ka[1],ambient[2]*ka[2]) + Eigen::Vector3f(diffuse[0]*kd[0],diffuse[1]*kd[1],diffuse[2]*kd[2]) + Eigen::Vector3f(specular[0]*ks[0],specular[1]*ks[1],specular[2]*ks[2]);
    }

    return result_color * 255.f;
}

运行 ./Rasterizer output.png displacement 结果如下


进阶代码

更换模型

更换模型就是把对应 obj 以及图片(.png.jpg.tif)文件路径进行修改



这里以 cube 模型为例,加上纹理之后结果好像有问题,不知道什么原因,欢迎大佬指正
请添加图片描述

双线性纹理插值

首先下采样为 200x200 像素的图,这里 cv::pyrDown 进行一次 2x2 的下采样

down_width = width / 4;
down_height = height / 4;
// std::cout<<down_width<<" "<<down_height<<std::endl;
cv::pyrDown(image_data, image_down2_data);
cv::pyrDown(image_down2_data, image_down4_data);

其结果为

然后采用双线性插值,我们根据当前位置离哪块像素点接近,判断取左上,左下,还是右上或右下的像素进行双线性插值,需要注意的是opencv的坐标系是x轴朝右y轴朝下的请添加图片描述

Eigen::Vector3f getBilinearInterpolationColor(float u, float v)
{   
		float u_down_img = std::min((down_width - 1) * 1.0f, std::max(0.0f, u * down_width));
        float v_down_img = std::min((down_height - 1)* 1.0f, std::max(0.0f, (1 - v) * down_height));
        cv::Vec3b bilinear_interpolate_color;

        float u_down_img_round = std::min((down_width - 1) * 1.0f, std::max(0.0f, round(u_down_img)));
        float v_down_img_round = std::min((down_height - 1) * 1.0f, std::max(0.0f, round(v_down_img)));

        if(u_down_img - u_down_img_round >= 0.0f)
        {
            if(v_down_img - v_down_img_round >= 0.0f) // near to left_top
            {
                float s = u_down_img - u_down_img_round + 0.5f;
                float t = v_down_img - v_down_img_round + 0.5f;
                int x2 = std::max(0, std::min(down_width-1, (int)u_down_img_round));
                int x1 = std::max(0, std::min(down_width-1, x2 - 1));
                int y2 = std::max(0, std::min(down_height-1, (int)v_down_img_round));
                int y1 = std::max(0, std::min(down_height-1, y2 - 1));
                bilinear_interpolate_color = lerp(t, lerp(s, image_down4_data.at<cv::Vec3b>(y1, x1), image_down4_data.at<cv::Vec3b>(y1, x2)), 
                                                    lerp(s, image_down4_data.at<cv::Vec3b>(y2, x1), image_down4_data.at<cv::Vec3b>(y2, x2)));
            } else { // near to left_bottom
                float s = u_down_img - u_down_img_round + 0.5f;
                float t = v_down_img_round - v_down_img + 0.5f;
                int x2 = std::max(0, std::min(down_width-1, (int)u_down_img_round));
                int x1 = std::max(0, std::min(down_width-1, x2 - 1));
                int y2 = std::max(0, std::min(down_height-1, (int)v_down_img_round));
                int y1 = std::max(0, std::min(down_height-1, y2 - 1));
                bilinear_interpolate_color = lerp(t, lerp(s, image_down4_data.at<cv::Vec3b>(y2, x1), image_down4_data.at<cv::Vec3b>(y2, x2)), 
                                                    lerp(s, image_down4_data.at<cv::Vec3b>(y1, x1), image_down4_data.at<cv::Vec3b>(y1, x2)));
            }
        } else {
            if(v_down_img - v_down_img_round >= 0.0f) // near to right_top
            {
                float s = u_down_img_round - u_down_img + 0.5f;
                float t = v_down_img - v_down_img_round + 0.5f;
                int x2 = std::max(0, std::min(down_width-1, (int)u_down_img_round));
                int x1 = std::max(0, std::min(down_width-1, x2 - 1));
                int y2 = std::max(0, std::min(down_height-1, (int)v_down_img_round));
                int y1 = std::max(0, std::min(down_height-1, y2 - 1));
                bilinear_interpolate_color = lerp(t, lerp(s, image_down4_data.at<cv::Vec3b>(y1, x2), image_down4_data.at<cv::Vec3b>(y1, x1)), 
                                                    lerp(s, image_down4_data.at<cv::Vec3b>(y2, x2), image_down4_data.at<cv::Vec3b>(y2, x1)));
            } else { // near to right_bottom
                float s = u_down_img_round - u_down_img + 0.5f;
                float t = v_down_img_round - v_down_img + 0.5f;
                int x2 = std::max(0, std::min(down_width-1, (int)u_down_img_round));
                int x1 = std::max(0, std::min(down_width-1, x2 - 1));
                int y2 = std::max(0, std::min(down_height-1, (int)v_down_img_round));
                int y1 = std::max(0, std::min(down_height-1, y2 - 1));
                bilinear_interpolate_color = lerp(t, lerp(s, image_down4_data.at<cv::Vec3b>(y2, x2), image_down4_data.at<cv::Vec3b>(y2, x1)), 
                                                    lerp(s, image_down4_data.at<cv::Vec3b>(y1, x2), image_down4_data.at<cv::Vec3b>(y1, x1)));
            }
        }

        return Eigen::Vector3f(bilinear_interpolate_color[0], bilinear_interpolate_color[1], bilinear_interpolate_color[2]);
    }
}

lerp 函数如下

cv::Vec3b lerp(float ratio, cv::Vec3b color1, cv::Vec3b color2)
{
    return cv::Vec3b((1.0f-ratio)*color1[0] + ratio*1.0f*color2[0], (1.0f-ratio)*color1[1] + ratio*1.0f*color2[1], (1.0f-ratio)*color1[2] + ratio*1.0f*color2[2]);
}

其结果如下

  游戏开发 最新文章
6、英飞凌-AURIX-TC3XX: PWM实验之使用 GT
泛型自动装箱
CubeMax添加Rtthread操作系统 组件STM32F10
python多线程编程:如何优雅地关闭线程
数据类型隐式转换导致的阻塞
WebAPi实现多文件上传,并附带参数
from origin ‘null‘ has been blocked by
UE4 蓝图调用C++函数(附带项目工程)
Unity学习笔记(一)结构体的简单理解与应用
【Memory As a Programming Concept in C a
上一篇文章      下一篇文章      查看所有文章
加:2022-10-17 13:05:24  更:2022-10-17 13:06:46 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2025年1日历 -2025/1/17 6:07:52-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码