矩阵
标准化矩阵
- 遍历矩阵每列求特征的平均值。
- 遍历矩阵每个元素减去该列的平均值。
1 | Mat<T> normalize() { |
矩阵变换(可看做modelMatrix)
平移
1 | Matrix translation(Vec3f v) { |
缩放
1 | Matrix zoom(float factor) { |
旋转
绕x轴旋转:
1 | Matrix rotation_x(float cosangle, float sinangle) { |
绕y轴旋转
1 | Matrix rotation_y(float cosangle, float sinangle) { |
1 | Matrix rotation_z(float cosangle, float sinangle) { |
绕任意轴旋转
视图矩阵(viewMatrix)
类似于glm的视线,输入相机所在位置eye
,相机照向的中心点位置center
,相机的上向量up
,一般为Vec3(0,1,0)
:
1 | Matrix lookat(Vec3f eye, Vec3f center, Vec3f up) { |
透视矩阵(ProjectionMatrix)
代码来自kimi:
1 | float fovY = 45.0f * static_cast<float>(M_PI / 180.0f); // 45度转换为弧度 |
视口变换(viewPort)
1 | Matrix viewport(int x, int y, int w, int h) { |
这个代码用于创建以下矩阵,其将坐标从 [-1,1]*[-1,1]*[-1,1]
被映射到屏幕立方体 [x,x+w]*[y,y+h]*[0,d]
上,其中d 是 z 缓冲区的分辨率。可以使其等于 255,因为这样可以方便地转储 z 缓冲区的黑白图像以进行调试。
协方差矩阵
- 矩阵的转置乘以该矩阵。
- 有偏估计?
1 | Mat<T> covariance() { |
矩阵的行列式
- 递归式调用求取子矩阵的行列式。
1 | T determinant() { |
矩阵的特征值和特征向量
求解PCA协方差矩阵的特征值和特征向量可以表达成矩阵特征值问题的标准形式AX = λX
,其中A
是协方差矩阵,X
是特征向量矩阵,λ
是特征值矩阵。
我们可以将这个问题转化为标准形式AX = B
,其中A = A
,X
是一个矩阵,每一列是特征向量v
,B
是一个矩阵,每一列是对应的特征值λ。
因此,通过求解矩阵特征值问题的标准形式AX = λX
,我们可以得到PCA协方差矩阵的特征值和特征向量。
雅克比(Jacobi)迭代法
参考:https://blog.csdn.net/Reborn_Lee/article/details/80959509
一般形式
方程组:
的系数矩阵$A$非奇异,不妨设,方程组变形为
对应上述的方程组,可得到迭代公式为:
其中,为第 $k$ 次迭代向量。
一般形式代码实现:
1 |
|
矩阵形式:
形如 AX=b
的方程组求解:
其中 $A$ 非奇异且 . 将 $A$ 分裂为 $A=D+L+U$,其中
因此可以将 Ax=b
表示为 Dx=-(L+U)x+b)
,即 ,简记为 ,因此Jacobi迭代公式的矩阵形式为:
1 | std::vector<T> JacobiIteration(const Mat<T>& A, const std::vector<T>& b, int max_iter = 100, T tol = 1e-6) { |
图形学
OBJ解析
原文链接:https://blog.csdn.net/xiongzai2016/article/details/108052800
现在来解释各参数的含义:
- v: 表示顶点。
- vt: 纹理坐标,其值为u, v。
- vn:顶点法向量,其值为x,y,z。
- f:表示一个面,由三个v/vt/vn的索引形式组成。比如obj文件中f 5/15/7 4/14/6 6/16/8 ,表示由第5、第4、第6这三个顶点组成了一个三角平面,平面的纹理由第15、第14、第16这三个纹理坐标形成,这个平面的朝向是第7、第6、第8这三个顶点的法向量求平均值。
绘制直线
Bresenham’s line算法:
1 | void line(int x0, int y0, int x1, int y1, TGAImage& image, TGAColor color) { |
上面的代码结合uv有些bug,下面ok,代码自tinyrenderer, Lesson4 final:
1 | void triangle(Vec3i t0, Vec3i t1, Vec3i t2, Vec2i uv0, Vec2i uv1, Vec2i uv2, TGAImage& image, float intensity, int* zbuffer) { |
三角形光栅化
1 | // 这个算法本质是通过三角形左右两边y各自比例关系来确定x绘制的起始和终止位置。 |
三角形的重心坐标
给定三角形三个点和P,用三个点表达出P:
其中一种方法:
1 | // Compute barycentric coordinates (u, v, w) for |
还有一种方法:
1 | Vec3f barycentric(Vec3f A, Vec3f B, Vec3f C, Vec3f P) { |
back-face-culling
目的是减少渲染背面被遮挡的三角形,只渲染能被用户观察到的三角形,这通过判断三角形法向量和光线方向的关系来进行。
在顶点数据中,我们将两个三角形都以逆时针顺序定义(如下图所示,正面的三角形是1、2、3,背面的三角形也是1、2、3(如果我们从正面看这个三角形的话))。然而,如果从观察者 当前视角使用1、2、3的顺序来绘制的话,从观察者的方向来看,背面的三角形将会是以顺时针顺序渲染的。我们只有以特定顺序定义三角形(下图是中为逆时针)才会被渲染:
根据右手定则可以计算当前顺序定义的三角形的法向量,相机默认位置是-z轴,看向z轴,可以通过比较观察方向是相反,或者和光的反射方向是一致来判断该三角形相对相机是正面还是背面,被渲染或者被剔除,代码自tinyrenderer, Lesson2:
1 | Vec3f light_dir(0,0,-1); // define light_dir |
光线和三角形求交
Moller-Trumbore算法:
- 三角形三维空间的三个顶点。
- 射线的原点和方向。
输出:
- 射线和三角形是否有交点。另外 u, v
代码自GAME101 作业五
1 | bool rayTriangleIntersect(const Vector3f& v0, const Vector3f& v1, const Vector3f& v2, const Vector3f& orig, |
Z-buffer(深度测试)
painter’s algorithm不work的场景:
1 | void triangle(Vec3f* pts, float* zbuffer, TGAImage& image, TGAColor color) { |
纹理采样
注意是纹理坐标插值后采样,而不是采样后对纹理进行插值:
1 | Vec3f n = (world_coords[2] - world_coords[0]) ^ (world_coords[1] - world_coords[0]); |
SDF
8SSEDT算法
核心:进4退1
1 | 进4 |
- 模板:http://www.codersnotes.com/notes/signed-distance-fields/
- case见:https://github.com/Lisapple/8SSEDT
1 | void compare(std::vector<std::vector<Point>>& grid, Point& p, const int& row, const int& col, int offsetx, int offsety) { |
Marching Parabolas算法
https://prideout.net/blog/distance_fields/
1 | void generateSDF(std::vector<std::vector<Point>>& grid) { |
OBB
基于PCA的生成
1 | void OBB(std::vector<glm::vec3> points) { |
光线求交算法
https://zhuanlan.zhihu.com/p/138259656
1 | bool CalculateIntersectionWithOBB( |