说明

本次作业主要是实现对一个obj文件表示的物体利用贴图进行渲染

rasterizer.cpp框架分析

和作业二类似,只不过颜色不再是固定值,而是通过纹理获得

//draw 函数
// Also pass view space vertice position
rasterize_triangle(newtri, viewspace_pos);

viewspace_pos为视口空间中顶点的位置,传入顶点位置是为了方便后续求解Blinn-Phong光照。

通过注释可以发现在这个函数中主要实现的就是求解一下四个插值

// TODO: Interpolate the attributes:
// auto interpolated_color // 颜色插值
// auto interpolated_normal //法线插值
// auto interpolated_texcoords //纹理贴图插值
// auto interpolated_shadingcoords //位置坐标插值

rasterize_triangle函数上方就是两个重载的插值函数,通过传入重心坐标,以及对应的坐标即可求得该pixel的四个插值

最后一部分说明了该框架是如何进行着色的,通过构建一个payload 然后调用fragment_shader()函数进行对像素的着色

fragment_shader是一个是std::function<>可以通过参数来控制调用的shader函数,这就是在命令行中输入的第3个参数决定的

payload的定义是在Shader.hpp文件中,本质是一个结构体,通过构造函数来初始化payload中的color,normal,tex_coods(纹理坐标),texture(使用的纹理贴图)



所以最终代码

void rst::rasterizer::rasterize_triangle(const Triangle& t, const std::array<Eigen::Vector3f, 3>& view_pos)
{
auto v = t.toVector4();
//b-box 包围盒
int minx = std::min(v[0].x(),std::min(v[1].x(),v[2].x()));
int miny = std::min(v[0].y(),std::min(v[1].y(),v[2].y()));
int maxx = std::max(v[0].x(),std::max(v[1].x(),v[2].x()));
int maxy = std::max(v[0].y(),std::max(v[1].y(),v[2].y()));
Eigen::Vector3i P;
for(P.x() = minx;P.x()<=maxx;P.x()++)
{
for(P.y() = miny;P.y()<=maxy;P.y()++)
{
//barycentric 判断是否在三角形内
if(!insideTriangle(P.x(), P.y(), t.v)) continue;
int cur_index = get_index(P.x(),P.y());
//重心坐标
auto [alpha, beta, gamma] = computeBarycentric2D(P.x(), P.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[cur_index])
{
depth_buf[cur_index] = zp;
// auto interpolated_color
auto interpolated_color = interpolate(alpha,beta,gamma,t.color[0],t.color[1],t.color[2],1);
// auto interpolated_normal
auto interpolated_normal = interpolate(alpha,beta,gamma,t.normal[0],t.normal[1],t.normal[2],1);
// auto interpolated_texcoords u,v
auto interpolated_texcoords = interpolate(alpha, beta, gamma, t.tex_coords[0], t.tex_coords[1], t.tex_coords[2], 1);
// auto interpolated_shadingcoords camer location -- r and l
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);
//pixel的点坐标 用于blinn-phong
payload.view_pos = interpolated_shadingcoords;
auto pixel_color = fragment_shader(payload);
Vector2i p2;
p2 << P.x(),P.y();
set_pixel(p2,pixel_color);
}
}
}

各个Shader

phone shading

可以看到在环境中存在两个光照,我们只需要在循环中计算Blinn-Phong的三个值相加即可

入射向量,观测向量和半程向量

Eigen::Vector3f l = (light.position-point).normalized();
Eigen::Vector3f v = (eye_pos-point).normalized();
Eigen::Vector3f h = (l+v).normalized();

光源到照射点的距离

float r =  std::sqrt((light.position[0]-point[0])*(light.position[0]-point[0])+(light.position[1]-point[1])*(light.position[1]-point[1])+(light.position[2]-point[2])*(light.position[2]-point[2]));

求解漫反射,环境光照和高光

//ambient
Eigen::Vector3f La = ka.cwiseProduct(amb_light_intensity);
//diffuse
Eigen::Vector3f Ld = kd.cwiseProduct((light.intensity/(r*r)) * std::max(0.0f,normal.dot(l)));
//specular
Eigen::Vector3f Ls = ks.cwiseProduct((light.intensity/(r*r)) * std::pow(std::max(0.0f,normal.dot(h)),p));

使用纹理贴图

纹理贴图的主要作用就是Blinn-Phong模型中的kd值,在对应的Shader函数中,可以看到相应的注释,我们需要做的就是利用payload获取当前pixel对应的kd值

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

凹凸贴图

凹凸贴图存储的是当前点的高度值,通过课程的学习,可以通过高度求解切线,然后利用切线求解法线。通过注释也可以理解这样一个流程

//cur pixel normal 当前pixel的法线
Eigen::Vector3f n = normal;
float x = normal.x();
float y = normal.y();
float z = normal.z();
//计算矩阵
//由于结算法线是由(0,0,1)改变的,所以需要在计算之后进行变换
Eigen::Vector3f t = Eigen::Vector3f(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 = Eigen::Vector3f(n.y()*t.z()-n.z()*t.y(),-1.0*(n.x()*t.z()-n.z()*t.x()),n.x()*t.y()-n.y()*t.x());
Eigen::Matrix3f TBN;
TBN << t.x(),b.x(),n.x(),
t.y(),b.y(),n.y(),
t.z(),b.z(),n.z();
//get u,v获取当前点的u,v值即高度变换值
float u = payload.tex_coords.x();
float v = payload.tex_coords.y();
//get width and height 获取整个纹理贴图的宽和高
float w =payload.texture->width;
float h = payload.texture->height;
//calculate the changed normal
//计算当前点的变化量
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 = Eigen::Vector3f(-1.0*dU,-1.0*dV,1.0);
//求解实际的法线向量
n = TBN*ln;
normal = n.normalized();

注意:为什么在求解dU和dV时,我们计算相邻点的高度值是+1/w或者+1/h。因为通过观察getColor函数可以发现最后实际计算颜色的位置是\(u*width和(1-v)*height\),我们带入就可以发现最终相邻点就是\(u*width+1\)

计算dU 和 dV为什么采用norm



可以查看FAQ中的解释

displacement 纹理

课上讲过,这种纹理会实际改变点的位置,而注释中也给出点的计算方法

Position p = p + kn * n * h(u,v)

所以我们只需要在bumpshader的基础上,在求得改变后的normal之后更改点的位置,然后再利用Blinn-Phone模型求解着色情况。

Games101 -- 作业3的更多相关文章

  1. GAMES101作业2

    作业任务: 填写并调用函数 rasterize_triangle(const Triangle& t). 即实现光栅化 该函数的内部工作流程如下: 创建三角形的 2 维 bounding bo ...

  2. 【UE4】GAMES101 图形学作业2:光栅化和深度缓存

    总览 在上次作业中,虽然我们在屏幕上画出一个线框三角形,但这看起来并不是那么的有趣.所以这一次我们继续推进一步--在屏幕上画出一个实心三角形,换言之,栅格化一个三角形.上一次作业中,在视口变化之后,我 ...

  3. GAMES101课程 作业6 源代码概览

    GAMES101课程 作业6 源代码概览 Written by PiscesAlpaca(双鱼座羊驼) 一.概述 本篇将从main函数为出发点,按照各cpp文件中函数的调用顺序和层级嵌套关系,简单分析 ...

  4. 【UE4】GAMES101 图形学作业5:光线与物体相交(球、三角面)

    总览 在这部分的课程中,我们将专注于使用光线追踪来渲染图像.在光线追踪中最重要的操作之一就是找到光线与物体的交点.一旦找到光线与物体的交点,就可以执行着色并返回像素颜色. 在这次作业中,我们要实现两个 ...

  5. 【UE4】GAMES101 图形学作业4:贝塞尔曲线

    总览 Bézier 曲线是一种用于计算机图形学的参数曲线. 在本次作业中,你需要实现de Casteljau 算法来绘制由4 个控制点表示的Bézier 曲线(当你正确实现该算法时,你可以支持绘制由更 ...

  6. 【UE4】GAMES101 图形学作业3:Blinn-Phong 模型与着色

    总览 在这次编程任务中,我们会进一步模拟现代图形技术.我们在代码中添加了Object Loader(用于加载三维模型), Vertex Shader 与Fragment Shader,并且支持了纹理映 ...

  7. 【UE4】GAMES101 图形学作业1:mvp 模型、视图、投影变换

    总览 到目前为止,我们已经学习了如何使用矩阵变换来排列二维或三维空间中的对象.所以现在是时候通过实现一些简单的变换矩阵来获得一些实际经验了.在接下来的三次作业中,我们将要求你去模拟一个基于CPU 的光 ...

  8. 【UE4】GAMES101 图形学作业0:矩阵初识

    作业描述 给定一个点P=(2,1), 将该点绕原点先逆时针旋转45◦,再平移(1,2), 计算出变换后点的坐标(要求用齐次坐标进行计算). UE4 知识点 主要矩阵 FMatrix FBasisVec ...

  9. python10作业思路及源码:类Fabric主机管理程序开发(仅供参考)

    类Fabric主机管理程序开发 一,作业要求 1, 运行程序列出主机组或者主机列表(已完成) 2,选择指定主机或主机组(已完成) 3,选择主机或主机组传送文件(上传/下载)(已完成) 4,充分使用多线 ...

  10. SQLServer2005创建定时作业任务

    SQLServer定时作业任务:即数据库自动按照定时执行的作业任务,具有周期性不需要人工干预的特点 创建步骤:(使用最高权限的账户登录--sa) 一.启动SQL Server代理(SQL Server ...

随机推荐

  1. 使用SYS_CONTEXT

    使用SYS_CONTEXT 1.什么是SYS_CONTEXT? SYS_CONTEXT 函数是Oracle提供的一个获取环境上下文信息的预定义函数. 该函数用来返回一个指定namespace下的par ...

  2. ubuntu16.04 ssh启用root连接

    安装好ubuntu16.04 server版默认是不允许客户端ssh工具连接root的. 启用方法如下: 1.设置root密码 dylan@ubuntu:~$ sudo passwd root [su ...

  3. 服务端高性能网络IO编程模型简析

    服务端高性能网络IO编程模型简析 一.客户端与服务器端 多数网络应用可以分为客户端(client)和服务器端(server)模型,然后中间通过各种定义的协议来进行两端的通信. 比如常用的 Nginx ...

  4. 通过 saltstack 批量更新 SSL 证书

    哈喽大家好,我是咸鱼. 之前写过两篇关于 SSL 过期巡检脚本的文章: SSL 证书过期巡检脚本 SSL 证书过期巡检脚本(Python 版) 这两篇文章都是讲如何通过脚本去自动检测 SSL 过期时间 ...

  5. You can't specify target table for update in FROM clause

    mysql中You can't specify target table for update in FROM clause错误的意思是说,不能先select出同一表中的某些值,再update这个表( ...

  6. Linux或者Mac解压乱码问题

    1.unar : 命令行解压工具 2.安装: ubuntu等Linux安装方法:sudo apt install unar mac系统安装方法:brew install unar 现在mac电脑用 T ...

  7. 【Azure API 管理】在 Azure API 管理中使用 OAuth 2.0 授权和 Azure AD 保护 Web API 后端,在请求中携带Token访问后报401的错误

    问题描述 在 Azure API 管理中使用 OAuth 2.0 授权和 Azure AD 保护 Web API 后端的文档中操作 "在开发人员门户中启用 OAuth 2.0 用户授权&qu ...

  8. C程序分别实现下列字符阵列的输出

    C程序分别实现下列字符阵列的输出:(https://zhuanlan.zhihu.com/p/443989560    可以参考这个博主写的) 1,左下三角形(代码) 1 #include <s ...

  9. 用CFF Explorer隐藏文件格式

    1.首先我们加载两个PNG文件,可以看到 文件格式头部是一样的,我们如何将一个PDF文件格式改成PNG,修改之后的文件虽然含有图片的文件头格式,但是并不能打开. 将PNG的文件头复制写入到PDF文件头 ...

  10. Springboot-1.5.10.RELEASE 升级到 2.2.4.RELEASE版本遇到的一些问题总结

    由于现在springboot的主流版本都已经是2.x的了,因此自己也打算将项目中使用的springboot1.5.10升级到2.2.4这个版本. 自己也能够预料得到,在升级过程中肯定会遇到各种各样的问 ...