渲染器的诞生(四)--Transformation和MVP
TransformationWhy主要有两种变换
模型变换
视图变换
从三维世界到二维平面的投影
基础变换
缩放
旋转
切变
缩放(x, y) –> (sx, sy)
切变(x, y) –> (x+ay, y)(x, y) –> (x, y+bx)
旋转旋转默认逆时针旋转
旋转的逆矩阵就是旋转的转置
旋转矩阵是正交矩阵
(x, y)–> (ax+by, cx+dy)
齐次坐标线性变换矩阵无法表示平移变换(x, y) –> (x+tx, y+ty)
平移引入齐次坐标就是为了能将平移变换也纳入线性变换的形式
2D 点 = (x, y, 1)2D 向量 = (x, y, 0)为什么为0,因为向量表示一种方向性,具有平移不变性,做任何平移还是等于原来的向量可进行的操作:
向量 + 向量 = 向量
点 - 点 = 向量
点 + 向量 = 点
点 + 点 = 表示这两个点的中点
仿射变换通过齐次坐标的形式(x, y, 1) –> (x’, y’, 1)
复合变换变换顺序是非常重要的,矩阵不满足交换律变换是从右到左的顺序进行的先应用线性变换,再平移 ...
渲染器的诞生(三)--深度缓冲
深度缓冲由于画家算法的缺陷,我们常常在3D渲染中碰到错位的情况,因此引入深度缓冲来解决这样的问题。通过深度缓冲来存放场景中点距离照相机的远近,以此来判断当前像素点应该渲染出的颜色。这样渲染器就可以顺序的去处理场景。
算法思路
遍历每一个三角形
遍历三角形中的样本
如果当前深度值小于之前的值
更新样本颜色
更新深度缓存
否则什么都不做
实现1234567891011121314151617// 遍历包围盒for (P.x=bboxmin.x; P.x<=bboxmax.x; P.x++) { for (P.y=bboxmin.y; P.y<=bboxmax.y; P.y++) { // 通过重心坐标判断位置 Vec3f bc_screen = barycentric(pts[0], pts[1], pts[2], P); // 在三角形外就跳过 if (bc_screen.x<0 || bc_screen.y<0 || bc_screen.z<0) continue ...
渲染器的诞生(二)--三角光栅
三角光栅化通过之前的Bresenham画线算法,最简单的方式便是通过三组两两点的直线算法来实现画出三角形
在三角形之间通过平行的扫描线进行渲染
一个好的光栅化算法需要做到以下几点
简单快速
不会受到输入的数据顺序的影响
如果两个三角形有公共点,那么这两个三角形之间重合的部分不能有断点之类出现
算法思路光栅化的过程,扫描线算法line sweeping:
将三角形的点根据y的值按照升序进行排列
将三角形分成平顶和平底两部分
扫描线进行渲染
实现12345678910111213141516171819202122232425262728293031323334353637void triangle(Vec2i t0, Vec2i t1, Vec2i t2, TGAImage& image, TGAColor color) { // 这不是三角形哈哈哈哈哈哈哈哈 if (t0.y == t1.y && t0.y == t2.y)return; // 将三个点进行降序排序 if (t0.y > t1.y)std::swap(t0, t1); i ...
渲染器的诞生(一)--Bresenham直线算法
Bresenham 直线算法来自tinyrenderer的第一课,目标是能够画出网格
常见的画线算法除了Bresenham算法,还有数值微分法(每次运算都需要一个浮点乘法和舍入运算)和中点画线法
算法介绍在屏幕上直线是由一个像素一个像素小块儿组成的因此没法像现实世界中在纸张上那样直接连线,只是由于像素小块在屏幕上足够多,而看起来像直线。Bresenham算法主要目的便是处理画线。
算法思路这里先默认斜率在0-1之间,直线指向第一象限
假设斜率k在0-1之间
在x的变化上,每一次都是递增1
y要么保持不变要么递增1
我们通过一个参数 d,d的范围也在0-1之间,通过这个参数来确定y的变化。
d的初始值为0,每一次变化相应的增加直线的斜率值,即 d=d+k。
当d>1的时候就前去1,保证d始终在0-1之间变换。
若d>=0.5,那么与垂直网格的交点更加接近,则取当前像素右上方的像素(x+1, y+1)
若d<0.5,那么取(x+1, y)
误差项的优化
由于d在与0.5比较,因此假设 e = d-0.5,那么每次只需判定e>=0即可
但是上述的计算需要用 ...
GAMES101Assignment4
GAMES101 Assignment4 (对应Lecture10-11)课程作业框架来自GAMES
基础部分:目标要求:需要实现de Casteljau 算法来绘制由4 个控制点表示的Bézier 曲线
main.cpp
12345678910111213141516171819202122232425262728293031323334353637383940/*** 贝塞尔递归**/cv::Point2f recursive_bezier(const std::vector<cv::Point2f> &control_points, float t) { // TODO: Implement de Casteljau's algorithm int control_points_num = control_points.size(); if (control_points_num == 1) { cv::Point2f pix_center_point(0.5,0.5); pix ...
GAMES101Assignment3
GAMES101 Assignment3 (对应Lecture7-9)课程作业框架来自GAMES
基础部分:目标要求:实现与作业2 类似的插值算法,实现法向量、颜色、纹理颜色的插值。
Rasterizer.cpp
12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879/** 在三角形内检测函数* parameters:* point:x,y* triangle:_v*/static bool insideTriangle(int x, int y, const Vector3f* _v){ // TODO : Implement this function to check if the point (x, y) is inside the triangle represented by _v[0], _v[1], _v ...
GAMES101Assignment2
GAMES101 Assignment2 (对应Lecture5-6)课程作业框架来自GAMES
基础部分:目标要求:正确实现三角形得栅格化算法,测试点是否在三角形内,正确实现z-buffer算法
Rasterizer.cpp
1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071/** 在三角形内检测函数* parameters:* point:x,y* triangle:_v*/static bool insideTriangle(int x, int y, const Vector3f* _v){ // TODO : Implement this function to check if the point (x, y) is inside the triangle represented by _v[0], _v[1], _v[2] // ...
GAMES101 Assignment1
GAMES101 Assignment1 (对应Lecture3-4)课程作业框架来自GAMES
基础部分:目标要求:需要完成一个旋转矩阵和一个透视投影矩阵,根据给定的三维坐标在屏幕上绘制相应的三角形。
main.cpp
1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586/* 绕Z轴旋转矩阵* return model* parameters:* rotation_angle:旋转角度**/Eigen::Matrix4f get_model_matrix(float rotation_angle){ Eigen::Matrix4f model = Eigen::Matrix4f::Identity(); // TODO: Implement this function // Creat ...