3D数学 向量和矩阵
向量和矩阵
- 坐标系
- 左手系、右手系
- 向量常用公式
- 向量的模(长度)
- 标准化向量
- 点积
- 叉积
- 矩阵常用性质
- 逆矩阵性质
- 正交矩阵
- 齐次空间
- 坐标空间变换推到过程
- MVP矩阵
- 局部空间
- 世界空间
- 模型矩阵
- 观察空间
- 观察矩阵
- 裁剪空间
- 透视投影
- 正交投影
- 屏幕空间
- 容易让人产生误会的地方
- MVP后的透视除法
坐标系
左手系、右手系
也就是把拇指当作xxx轴,食指当作yyy轴,中指当作zzz轴,来考虑三维坐标。
向量常用公式
向量的模(长度)
公式:∣v⃗∣=vx∗vx+vy∗vy+vz∗vz|\vec{v}| = \sqrt{v_x*v_x+v_y*v_y+v_z*v_z}∣v∣=vx∗vx+vy∗vy+vz∗vz
代码:float length(vec3 v) { return sqrt(v.x*v.x+v.y*v.y+v.z*v.z); }
几何意义:得到v⃗\vec{v}v的长度。
标准化向量
公式:v⃗norm=v⃗∣v⃗∣\vec{v}_{norm} = \frac{ \vec{v} }{ |\vec{v}| }vnorm=∣v∣v
代码:vec3 normal(vec3 v) { return v / length(v); }
几何意义:得到v⃗\vec{v}v的单位向量。
点积
公式 | 几何意义 |
---|---|
公式A:a⃗⋅b⃗=b⃗⋅a⃗=axbx+ayby+azbz\vec{a} \cdot \vec{b} = \vec{b} \cdot \vec{a} = a_x b_x+a_yb_y+a_zb_za⋅b=b⋅a=axbx+ayby+azbz | |
公式B1:a⃗⋅b⃗=∣a⃗∣∣b⃗∣cos(θ)\vec{a} \cdot \vec{b} = \vert \vec{a} \vert \vert \vec{b} \vert cos{(\theta)}a⋅b=∣a∣∣b∣cos(θ) 公式B2:θ=arccos(a⃗⋅b⃗∣a⃗∣∣b⃗∣)\theta = \arccos{(\frac{\vec{a} \cdot \vec{b}}{\vert \vec{a} \vert \vert \vec{b} \vert})}θ=arccos(∣a∣∣b∣a⋅b) |
计算两个向量的夹角 |
公式C:v⃗p=n⃗v⃗⋅n⃗∣n∣2\vec{v}_p = \vec{n}\frac{\vec{v} \cdot \vec{n}}{{\vert n \vert}^2}vp=n∣n∣2v⋅n | v⃗\vec{v}v在n⃗\vec{n}n上的投影v⃗p\vec{v}_pvp |
公式A代码:float dot(vec3 a, vec3 b) { return a.x*b.x+a.y*b.y+a.z*b.z; }
公式B1代码:float dot(vec3 a, vec3 b) { return length(a)*length(b)*angle(a, b); }
公式B2代码:float angle(vec3 a, vec3 b) { return acos(dot(a, b)/(length(a)*length(b))); }
公式C代码:vec3 vec_projection(vec3 v, vec3 n) { return n*(dot(v, n)/pow(length(n), 2)); }
叉积
公式 | 几何意义 |
---|---|
公式A:a⃗×b⃗=[aybz−azbyazbx−axbzaxby−aybx]\vec{a}\times\vec{b} = \begin{bmatrix} a_yb_z-a_zb_y & a_zb_x-a_xb_z & a_xb_y-a_yb_x \end{bmatrix}a×b=[aybz−azbyazbx−axbzaxby−aybx] | 由a⃗\vec{a}a、b⃗\vec{b}b组成的面的法向量; |
公式B:S=∣a⃗×b⃗∣=∣a⃗∣∣b⃗∣sin(θ)S=\vert \vec{a}\times\vec{b} \vert = \vert \vec{a} \vert \vert \vec{b} \vert \sin{(\theta)}S=∣a×b∣=∣a∣∣b∣sin(θ) |
由a⃗\vec{a}a、b⃗\vec{b}b组成的平行四边形的面积; 也是由a⃗\vec{a}a、b⃗\vec{b}b组成的面的法向量长度; |
矩阵常用性质
矩阵能够表示线性变换,平常会用的有旋转、缩放、平移、镜像、仿射。
逆矩阵性质
公式:M(M−1)=IM(M^{-1})=IM(M−1)=I
几何解释:逆矩阵可以“撤销”原变换。 (v⃗M)M−1=v⃗(MM−1)=v⃗I=v⃗(\vec{v}M)M^{-1}=\vec{v}(MM^{-1})=\vec{v}I=\vec{v}(vM)M−1=v(MM−1)=vI=v
逆矩阵:如果矩阵是非奇异矩阵,那么该矩阵可逆。
非奇异矩阵:非奇异矩阵的行列式不为零。
可逆矩阵求逆方法:
- 求解伴随矩阵再除以行列式
- 高斯消元法
正交矩阵
定义:若方阵MMM是正交的,则当且仅当MMM与它的转置MTM^{T}MT的乘积等于单位矩阵。
公式:M正交⟺MMT=IM_{正交} \Longleftrightarrow MM^{T}=IM正交⟺MMT=I
推到公式:M正交⟺MT=M−1M_{正交} \Longleftrightarrow M^{T} = M^{-1}M正交⟺MT=M−1
正交矩阵必须满足的条件:
- 矩阵的每一行都是单位向量
- 矩阵的所有行互相垂直
- 对矩阵的列也要做到类似的条件
齐次空间
齐次空间有以下意义:
- 平移:由于Matrix3×3Matrix3\times3Matrix3×3不能够表示平移,而齐次空间的Matrix4×4Matrix4\times4Matrix4×4能够表示平移。
- W分量:v⃗\vec{v}v在经过MVP变换后;如果是透视投影,则需要将视锥体投影到屏幕空间,也就是需要进行一次透视除法,透视除法就是用v⃗\vec{v}v的w分量除以x、y、z分量。公式:v⃗NDC=v⃗xyzv⃗w\vec{v}_{NDC}=\frac{\vec{v}_{xyz}}{\vec{v}_w}vNDC=vwvxyz。此时v⃗xyz∈[−1,1]\vec{v}_{xyz}\in[-1, 1]vxyz∈[−1,1]。
坐标空间变换推到过程
转自:知乎的一篇文章
MVP矩阵
移步:坐标系统
局部空间
局部空间是指物体所在的坐标空间,即对象最开始所在的地方。想象你在一个建模软件(比如说Blender)中创建了一个立方体。你创建的立方体的原点有可能位于(0, 0, 0),即便它有可能最后在程序中处于完全不同的位置。甚至有可能你创建的所有模型都以(0, 0, 0)为初始位置(译注:然而它们会最终出现在世界的不同位置)。所以,你的模型的所有顶点都是在局部空间中:它们相对于你的物体来说都是局部的。
我们一直使用的那个箱子的顶点是被设定在-0.5到0.5的坐标范围中,(0, 0)是它的原点。这些都是局部坐标。
从数据结构的理解角度为:每个顶点都在局部空间中。
typedef struct Vectex {vec3 position;vec2 uv;vec3 normal;
};// 记录模型中每一个点的信息
typedef struct Mesh {Vectex *vectexs; // 顶点数组unsigned int *indices; // 索引数组
}
世界空间
如果我们将我们所有的物体导入到程序当中,它们有可能会全挤在世界的原点(0, 0, 0)上,这并不是我们想要的结果。我们想为每一个物体定义一个位置,从而能在更大的世界当中放置它们。世界空间中的坐标正如其名:是指顶点相对于(游戏)世界的坐标。如果你希望将物体分散在世界上摆放(特别是非常真实的那样),这就是你希望物体变换到的空间。物体的坐标将会从局部空间变换到世界空间;该变换是由模型矩阵(Model Matrix)实现的。
模型矩阵
在拥有了世界空间的前提条件下,我们能够自然的求出模型矩阵。模型矩阵是一种变换矩阵,它能通过对物体进行位移、缩放、旋转来将它置于它本应该在的位置或朝向。
假设世界空间的原点为(0, 0, 0),那么从数据结构上面看:每个独立的GameObject都会有一个Transform信息。 其中modelMatrix * go.meshInfos[i].vertex => 局部空间 -> 世界空间
。
class Transform {Matrix4x4 translation;Matrix4x4 rotate;Matrix4x4 scale;
}class GameObject {Mesh *meshInfos; // 可能有多个网格Transform transform; // model矩阵// model矩阵的方法Matrix4x4 matrix() { return transform.translation * transform.rotate * transform.scale; }
}void drawCall() {// 假设是一个模型了GameObject go;// 将transform输入给顶点着色器shader.setUniformMatrix4fv("modelMatrix", go.transform.matrix());// 渲染shader.drawElements(go.meshInfos);
}
观察空间
观察空间经常被人们称之OpenGL的摄像机(Camera)(所以有时也称为摄像机空间(Camera Space)或视觉空间(Eye Space))。观察空间是将世界空间坐标转化为用户视野前方的坐标而产生的结果。因此观察空间就是从摄像机的视角所观察到的空间。而这通常是由一系列的位移和旋转的组合来完成,平移/旋转场景从而使得特定的对象被变换到摄像机的前方。这些组合在一起的变换通常存储在一个观察矩阵(View Matrix)里,它被用来将世界坐标变换到观察空间。
观察矩阵
观察矩阵是一种变换矩阵,一般有2种方式构造这个矩阵。
- 已知摄像机在世界空间的transform,可以构建出从观察空间变换到世界空间的变化矩阵,即
transform.matrix()
;进而求transform.matrix()
矩阵的逆。 - Look At Camera方法,需要知道摄像机的世界空间下的位置Eye、一个Up向量、观察点At。
void drawCall() {GameObject go;// 观察矩阵获取方式Matrix4x4 viewMatrix = Matrix4x4.inverse(Camera.main.matrix());Matrix4x4 viewMatrix = Camera.lookAt(eye, up, at);shader.setUniformMatrix4fv("viewMatrix", viewMatrix);shader.setUniformMatrix4fv("modelMatrix", go.transform.matrix());shader.drawElements(go.meshInfos);
}
裁剪空间
在一个顶点着色器运行的最后,OpenGL期望所有的坐标都能落在一个特定的范围内,且任何在这个范围之外的点都应该被裁剪掉(Clipped)。被裁剪掉的坐标就会被忽略,所以剩下的坐标就将变为屏幕上可见的片段。这也就是裁剪空间(Clip Space)名字的由来。为了将顶点坐标从观察变换到裁剪空间,我们需要定义一个投影矩阵(Projection Matrix)。
由投影矩阵创建的观察箱(Viewing Box)被称为平截头体(Frustum),每个出现在平截头体范围内的坐标都会最终出现在用户的屏幕上。将特定范围内的坐标转化到标准化设备坐标系NDC的过程(而且它很容易被映射到2D观察空间坐标)被称之为投影(Projection),因为使用投影矩阵能将3D坐标投影(Project)到很容易映射到2D的标准化设备坐标系中。
一旦所有顶点被变换到裁剪空间,最终的操作——透视除法(Perspective Division)将会执行,在这个过程中我们将位置向量的x,y,z分量分别除以向量的齐次w分量;透视除法是将4D裁剪空间坐标变换为3D标准化设备坐标的过程。这一步会在每一个顶点着色器运行的最后被自动执行。
透视投影
一个透视平截头体可以被看作一个不均匀形状的箱子,在这个箱子内部的每个坐标都会被映射到裁剪空间上的一个点。
API:perspective(glm::radians(45.0f), (float)width/(float)height, 0.1f, 100.0f);
它的第一个参数定义了fov的值,它表示的是视野(Field of View),并且设置了观察空间的大小。如果想要一个真实的观察效果,它的值通常设置为45.0f,但想要一个末日风格的结果你可以将其设置一个更大的值。第二个参数设置了宽高比,由视口的宽除以高所得。第三和第四个参数设置了平截头体的近和远平面。我们通常设置近距离为0.1f,而远距离设为100.0f。所有在近平面和远平面内且处于平截头体内的顶点都会被渲染。
正交投影
正交投影矩阵定义了一个类似立方体的平截头箱,它定义了一个裁剪空间,在这空间之外的顶点都会被裁剪掉。创建一个正交投影矩阵需要指定可见平截头体的宽、高和长度。在使用正交投影矩阵变换至裁剪空间之后,任何在这个平截头体以外的坐标将会受到裁剪。
API:ortho(0.0f, 800.0f, 0.0f, 600.0f, 0.1f, 100.0f);
前两个参数指定了平截头体的左右坐标,第三和第四参数指定了平截头体的底部和顶部。通过这四个参数我们定义了近平面和远平面的大小,然后第五和第六个参数则定义了近平面和远平面的距离。这个投影矩阵会将处于这些x,y,z值范围内的坐标变换为标准化设备坐标。
屏幕空间
在完成以上MVP矩阵运算后,映射到屏幕空间的事情将交给glViewport,由它来设置屏幕显示区域。
容易让人产生误会的地方
MVP后的透视除法
1、 顶点着色器乘上MVP矩阵后,输出的顶点为裁剪空间的点
并不是NDC坐标空间的点
,NDC转换是由GPU自己去做的。
2、 片段着色器的输入不是NDC坐标空间or
裁剪空间的点
,而是屏幕空间的点。
3D数学 向量和矩阵相关推荐
- 3D数学之矩阵的各种求逆
经过三天的准备终于把矩阵的各种求逆方法以及代码完成了.心里有点小激动,come on,来吧,点燃你的心中的那团火,跟着游戏音乐的律动一起跟我走入神秘的3D世界. 下面介绍三种方法: 1.用伴随矩阵求逆 ...
- 3D数学读书笔记——矩阵基础番外篇之线性变换
本系列文章由birdlove1987编写.转载请注明出处. 文章链接:http://blog.csdn.net/zhurui_idea/article/details/25102425 前面有一篇文章 ...
- 3D数学-裁剪空间与透视投影矩阵的推导
3D数学-裁剪空间与透视投影矩阵的推导 透视投影矩阵的变换本质,是将视锥体变换到裁剪空间中 视锥体的具有六个面,近裁剪面,远裁剪面,左裁剪面,右裁剪面,上裁剪面,下裁剪面 所有超出视锥体的都会被舍弃, ...
- 3D数学系列之——从“蒙的挺准”到“蒙的真准”解密蒙特卡洛积分!
目录 1.前言 2.积分概念简单回顾 3.积分在程序计算上的困难 4.蒙特卡洛积分 5.一些扩展应用 1.前言 在学习3D数学的过程中,或者说在学习游戏开发.引擎开发.渲染器开发.Shader开发 ...
- 数学基础(向量和矩阵)
原文地址:OpenGL 2d旋转坐标公式为:newx = x*cos(a) - y * sin(a) newy = x*sin(a) + y * cos(a) 推导这个公式主要用到了sin(a + ...
- Unity3D for VR 学习(6): 再次温故知新-3D数学
一年前,系统学习过3D数学,并记录了一篇博客<C#程序员整理的Unity 3D笔记(十):Unity3D的位移.旋转的3D数学模型>. 一年后,再次温习之. 坐标系:Unity3D使用左手 ...
- c++向量和数组的区别_Spark机器学习-Java版(一)-向量和矩阵
1.概述 Spark早期版本时,MLlib是基于RDD来进行分析的,其使用的是 spark. mllib包.而言2.0版本后,由RDD这种抽象数据结构转换到了基于 dataframe上,其相关API也 ...
- 数组,向量和矩阵以及空间的维数
数组,向量和矩阵以及空间的维数 1.Python列表和Numpy数组的区别: 2.Numpy数组和矩阵的区别: 3.matlab关于矩阵的维数的解释 1.Python列表和Numpy数组的区别: 此处 ...
- numpy中向量和矩阵相关乘法总结
numpy中矩阵相关乘法总结 一.numpy中向量和矩阵的概念 向量:1维 矩阵:至少是2维 注意:numpy中对于向量的定义与数学中对向量的定义有些不同,数学中对向量的定义是竖向写法,但由于nump ...
最新文章
- Codeforces Round #581 (Div. 2)
- 关于JavaWeb项目加密的实现 2021-04-24
- [转载] 递归函数python基例_python递归函数详解 python 递归函数使用装饰器
- EXCEL VLOOKUP函数的使用
- linux添加mysql到服务_Linux下将MySQL服务添加到服务器的系统服务中
- 深入分析MFC之GDI原理透析
- java OA开源办公系统源码下载
- Linux环境安装ghostscript
- url 转码 java_URL 转码遇到的坑
- python判断一个数是素数_使用Python语言判断质数(素数)的简单方法讲解
- Redis恢复被删掉了的key值
- 实验(五)键盘检测实验
- 【名称解释】上解中央支出
- Postman使用小技巧 - 用Postman生成Request代码
- 很多人读书,追求的是干货,寻求的是立刻行之有效的解决方案。 其实这是一种留在舒适区的阅读方法。 在这个充满不确定的年代,答案不会简单的出现在书里
- java语音识别(科大讯飞版)
- 微信小程序中使用vant组件库(超详细)
- YXcms(mvc架构php的cms)简单学习与审计
- 六种STM32开发板光盘资料免费下载
- 雷达传感器模组技术,在智能安防系统中的应用,雷达智能感应