GAMES101(2~3作业)

news/2024/9/20 3:57:37 标签: 图形渲染

 作业2

基础题目:

栅格化:在屏幕绘制一个实心三角形,函数 rasterize_triangle(const Triangle& t),需要找出当前三角形的边界框,然后遍历像素,查找当前像素是否在三角形内static bool insideTriangle(),

深度测试:如果在三角形内,代码已经自动计算z值(插值得到)存储在z_interpolated中,如果当前点更靠近相机,需要请设置像素颜色(使用 set_pixel 函数)并更新深度缓冲区

解:

首先创建三角形边界,分别为3个顶点的,xy坐标的最大最小值

 auto v = t.toVector4();
 float alpha, beta, gamma, lmin=INT_MAX, rmax=INT_MIN, tmax=INT_MIN, bmin=INT_MAX;
for(auto &k:v){
    lmin = int(std::min(lmin,k.x()));
    rmax = std::max(rmax,k.x());rmax = rmax == int(rmax) ? int(rmax)-1 : rmax;
    tmax = std::max(tmax,k.y());tmax = tmax == int(tmax) ? int(tmax)-1 : tmax;
    bmin = int(std::min(bmin,k.y()));
}

 接着遍历每个像素,如果像素中心在三角形内,用插值计算深度,如果深度值 < 屏幕坐标的深度值,就更新颜色值和深度缓冲,总体来说使用了作业框架的API

for(float i = lmin; i <= rmax; i++){
    for(float j = bmin; j <= tmax; j++){
        if(insideTriangle(i, j, t.v)){
            /* 计算z深度 */
            std::tie(alpha, beta, gamma) = computeBarycentric2D(i+0.5, j+0.5, 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(-z_interpolated < depth_buf[get_index(i,j)]){/* 当前深度 < 深度缓冲中的深度值 */
                set_pixel({i,j,1},t.getColor());/* 设置当前像素颜色值 */
                depth_buf[get_index(i,j)] = -z_interpolated;/* 更新深度缓冲 */
            }
        }
    }
}

下一步判断坐标是否在三角形内部,根据老师课上讲的叉积判断,如果一个点在三角形所有边的同一侧,就为内部 。ver存储的是三个线框的向量(需要同一的顺时针或逆时针),ver2存储的是从xy坐标为终点到三个点的向量。循环比较每个边和xy向量的叉积,

比如顺时针绘制三角形,需要判断z值是否为负

static bool insideTriangle(int x, int y, const Vector3f* _v)
{   
    std::vector<Vector3f> ver, ver2;

    ver.push_back({_v[1].x()-_v[0].x(),_v[1].y()-_v[0].y(),0}); ver2.push_back({x-_v[0].x(),y-_v[0].y(),0});    
    ver.push_back({_v[2].x()-_v[1].x(),_v[2].y()-_v[1].y(),0}); ver2.push_back({x-_v[1].x(),y-_v[1].y(),0});
    ver.push_back({_v[0].x()-_v[2].x(),_v[0].y()-_v[2].y(),0}); ver2.push_back({x-_v[2].x(),y-_v[2].y(),0});

    for(int i=0;i<3;i++){
        if(ver[i].cross(ver2[i]).z() < 0)
            return false;
    }
    return true;
}

图像颠倒和镜像翻转问题:

发现绘制的三角形是上下颠倒的,错误原因其实是opencv原点在左上角,而代码框架用的是左下,

注意opegl规范化设备坐标NDC是右手坐标系而非左手:direct和opencv采用的都是左手,因为定义opencv原点为左上,需要将左手坐标系翻转,导致+y向下的坐标系

比如传入一下坐标点:按照右手坐标系带入这组顶点,符合作业的输出结果,但是由于OpenCV是左手坐标系,带入完全会得到相反的结果,所以应该将图像翻转

{2, 0, -2},
{0, 2, -2},
{-2, 0, -2},

{3.5, -1, -5},
{2.5, 1.5, -5},
{-1, 0.5, -5}

经过查找网上的题解,需要更改透视矩阵,在转换的时候直接应用翻转

    proj << 
        zNear, 0, 0, 0,
        0, zNear, 0, 0,
        0, 0, (zNear + zFar) / (zFar - zNear), (2 * zNear * zFar) / (zFar - zNear),
        0, 0, -1, 0;

得到结果: 

提高题目:

解决走样问题,实现MSAA:每一个像素建立4个采样点,需要维护一个 sample list,你得到的三角形不应该有不正常的黑边

解:

首先建立采样点偏移量,然后对每个像素采样4次,判断是否在三角形内,如果在就加上权重值,最后乘以平均权重值

黑边问题:黑边出现两个三角交界处,这是因为这个像素由两部分组成,假设靠前(深度值小)的仅仅覆盖了1/4的采样点,那么最终颜色会接近黑色,展现黑边

所以应该对每个像素的所有采样点中存储颜色,然后求平均值,

比如这里的颜色,由于靠后物体更新了3/4,靠前物体更新了1/4,所以求它们的平均值

通过4倍的后台深度缓冲区和颜色缓冲区,存储每个采样点的颜色和深度,

而对于正常的前台缓冲区,颜色为所有采样点的颜色平均值,深度为最小深度

std::vector<float> a{0.25,0.25,0.75,0.75,0.25};
mindep = INT_MAX;//像素的深度
eid = get_index(i,j)*4;//像素的索引
……
for(int k = 0; k < 4; k++){{
    if(insideTriangle(i+a[k], j+a[k+1], t.v)){
        if (-z_interpolated < depth_sample[eid + k])
        {
          depth_sample[eid + k] = -z_interpolated;//后台深度缓冲--采样点
          frame_sample[eid + k] = t.getColor() / 4;//后台颜色缓冲--采样点
        }
        mindep = std::min(depth_sample[eid + k], mindep);
    }
}
color = frame_sample[eid] + frame_sample[eid + 1] + frame_sample[eid + 2] +frame_sample[eid + 3];//像素颜色为采样点的平均值
set_pixel({i,j,1}, color);//仅更新1次像素颜色
depth_buf[get_index(i,j)] = std::min(depth_buf[get_index(i,j)], mindep);//深度为采样点中最小值

作业三

框架

这次作业框架通过Object Loader加载一个3维模型,并提供 了Vertex Shader 与 Fragment Shader着色器

  1. main入口点
    1. active_shader通用函数绑定,表示当前绑定哪个着色器

    2. 包含各种fragment shader

  2. rasterizer屏幕光栅器类
    1. draw计算顶点数据
    2. rasterize_triangle插值,并设置最终颜色

  3. obj_loader第三方.obj 文件加载库
  4. global全局常量数据
  5. shader着色器类
    1. 提供了,片段和顶点shader的数据加载,存储了数据信息
  6. texture纹理类
    1. 通过opencv,从图片生成纹理
    2. getColor查找纹理颜 色的接口
  7. triangle三角形网格类

题目:

我们需要编写的代码部分:

  1. rasterize_triangle实现插值
  2. phong_fragment_shader() 实现 Blinn-Phong 模型fragment shader
  3. texture_fragment_shader()纹理fragment shader
  4. bump_fragment_shader()凹凸贴图fragment shader
  5. displacement_fragment_shader()位移贴图的fragment shader

其中在运行可执行文件时,第二个参数是生成的图片文件名,第3个参数可以是texture,normal,phong,bump,shader其中之一

解:

rasterize_triangle:

/* ……调用interpolate  API计算插值 */
auto interpolated_color = interpolate(alpha, beta, gamma, t.color[0], t.color[1], t.color[2], 1);
/* 将插值的数据传入fragment_shader */
fragment_shader_payload payload(interpolated_color, interpolated_normal.normalized(), interpolated_texcoords, texture ? &*texture : nullptr);
payload.view_pos = interpolated_shadingcoords;
/* 设置缓冲数据 ,深度缓冲,颜色缓冲*/
depth_buf[id] = -z_interpolated;    
frame_buf[id] = fragment_shader(payload);
set_pixel({i,j}, frame_buf[id]);

normal_fragment_shader

使用纹理,直接光照模型转为代码就行

双线性插值纹理;

/* 双线性插值 */
Eigen::Vector3f getColorBilinear(float u, float v){
    /* 乘以纹理宽高,获取周围4个像素的坐标 */
    float w1 = int(u * width), h1 = int(v * height);
    float w2 = w1 + 1, h2 = h1;
    float w3 = w1, h3 = h1 + 1;
    float w4 = w1 + 1, h4 = h1 + 1;
    /* 获取每个采样点的颜色 */
    Eigen::Vector3f color1, color2, color3, color4, color5, color6, color;
    color1 = getColor(w1 / width, h1 / height);
    color2 = getColor(w2 / width, h2 / height);
    color3 = getColor(w3 / width, h3 / height);
    color4 = getColor(w4 / width, h4 / height);
    /* 线性插值:水平两次,竖直一次 */
    color5 = color1 + (color2 - color1) * (u * width - w1);/* w1 = int(u * width)取整后的坐标 */
    color6 = color3 + (color4 - color3) * (u * width - w1);
    color = color5 + (color6 - color5) * (v * height - h1);
    return color;
}
texture_fragment_shader

根据法线(这里已经乘以了m矩阵)计算TBN矩阵

float x, y, z;
Vector3f t, b;
x = normal.x(), y = normal.y(), z = normal.z();

t << x * y / sqrt(x * x + z * z), sqrt(x * x + z * z), z * y / sqrt(x * x + z * z);
b = normal.cross(t);//叉乘

Matrix3f TBN;
TBN << t.x(), b.x(), normal.x(),
       t.y(), b.y(), normal.y(),
       t.z(), b.z(), normal.z();

 根据UV方向分别计算切线,获得采样的切线

float u, v, w, h;
u = payload.tex_coords.x();
v = payload.tex_coords.y();
w = payload.texture->width;
h = payload.texture->height;
float dU = kh * kn * (payload.texture->getColor(u + 1.0 / w,v).norm() - payload.texture->getColor(u,v).norm());
float dV = kh * kn * (payload.texture->getColor(u,v + 1.0 / h).norm() - payload.texture->getColor(u,v).norm());

根据切线计算采样的法线,还需变换到TBN空间

ln << -dU, dV, 1;
normal = (TBN * ln).normalized();

最后将法线输出为颜色

bump_fragment_shader

 接着应用blinn_Phong光照模型

blinn_Phong+bump_Texture

http://www.niftyadmin.cn/n/5666520.html

相关文章

java -- JDBC

一.JDBC概述: 过java语言操作数据库中的数据。 1.JDBC概念 JDBC&#xff08;Java DataBase Connectivity,java数据库连接&#xff09;是一种用于 执行SQL语句的Java API。JDBC是Java访问数据库的标准规范&#xff0c;可以 为不同的关系型数据库提供统一访问&#xff0c;它由…

C++ | Leetcode C++题解之第405题数字转换为十六进制数

题目&#xff1a; 题解&#xff1a; class Solution { public:string toHex(int num) {if (num 0) {return "0";}string sb;for (int i 7; i > 0; i --) {int val (num >> (4 * i)) & 0xf;if (sb.length() > 0 || val > 0) {char digit val …

瓦工交底你家做了吗?真的很重要

有一个工地开始贴瓷砖&#xff0c;和业主约着一块去现场瓦工交底&#xff0c;业主买的瓷砖很有意思      先说一下为什么瓦工交底&#xff0c;瓦工包含的有包下水管&#xff0c;做防水贴墙砖和地砖。      瓦工交底最主要的就是确定墙砖和地砖的排版方式。      如…

小猫回收站(Windows 11)

注意&#xff1a;需要准备素材&#xff01;&#xff01;&#xff01; 也下面可以直接拿走&#xff08;需要自己去转.ico格式哈&#xff09;&#xff1a; 点击键并单击设置。Windows选择个性化&#xff0c;然后单击主题。单击桌面图标设置。在下一个对话框中&#xff0c;确保勾选…

解决VSCode文件的中文GBK和UTF-8编码之间乱码问题

NOTE&#xff1a;近日笔者在使用VSCode编码环境的时候&#xff0c;出现了中文和UTF-8两者之间乱码的问题&#xff0c;特编写本片文章&#xff0c;以作学习记录。 1.需求 用VSCode打开外部的GBK2312编码文件&#xff0c;想在VSCode中统一以UTF-8编码查看&#xff08;笔者推荐U…

Datawhile 组队学习Tiny-universe Task01

Task01&#xff1a;LLama3模型讲解 仓库链接&#xff1a;GitHub - datawhalechina/tiny-universe: 《大模型白盒子构建指南》&#xff1a;一个全手搓的Tiny-Universe 参考博客&#xff1a;LLaMA的解读与其微调(含LLaMA 2)&#xff1a;Alpaca-LoRA/Vicuna/BELLE/中文LLaMA/姜子…

圆周阵列元件的间距增加操作方法

在进行器件圆周阵列时&#xff0c;内圈的角度和外圈的旋转角度都相同&#xff0c;由于内圈的圆周长小于外圈的圆周长&#xff0c;有可能在内圈造成部分元件之间有两个焊盘会有覆盖的情况&#xff0c;此时需要对内圈的元件位置进行微调&#xff0c;需要增加在同一半径位置的元件…

代码随想录_刷题笔记_第二次

链表 — 环形链表 题目链接&#xff1a;142. 环形链表 II - 力扣&#xff08;LeetCode&#xff09; 题目要求&#xff1a; 给定一个链表的头节点 head &#xff0c;返回链表开始入环的第一个节点。 如果链表无环&#xff0c;则返回 null。 如果链表中有某个节点&#xff0c…