3D数学学习记录一

学习3D数学对以后学习图形学有一定的帮助。

课程介绍:

1D(数轴):关于计数和度量的数学。

2D (笛卡尔坐标系):关于平面的数学。
由2个平行或相交的数轴组成,并且两条数轴的度量单位相同。

3D (空间直角坐标系):由3个2D笛卡尔坐标系组成。

右手坐标系:3D笛卡尔坐标系,OpenGL
左手坐标系:Direct3D,Unity3D

2.

拇指朝右,食指朝上,中指伸展,拇指为X,食指为Y,中指为Z(全正轴)

全局坐标系:又称:世界坐标系,在Unity中将一个物体的Transition重置Reset后,(0,0,0)是世界坐标系的原点,可使用transform.position获取物体的世界坐标系,当一个物体添加入另外一个物体下,成为子物体,其Transform的Position会发生改变,是因为这个成为了子物体的坐标系改变了,原本(以(0,0,0)为原点的世界坐标系)变为(以父物体的中心点为原点的局部坐标系)来计算出它的【本地坐标点】。transform.position获取物体的世界坐标系与物体在编辑状态上的Transform的position不同,它依然是没有父物体的世界坐标系下的坐标,也就是完全不受父物体影响。

本地坐标:transform.localPosition获取,本地坐标是一个物体移动到另外一个物体下,成为其子物体,然后根据父物体的局部坐标系来计算出的坐标。

局部坐标系:模型坐标系或者物体坐标系,每个物体都有自身独立的物体坐标系,当物体移动或改变方向时,和该物体相关联的坐标系将随之移动或改变方向。模型Mesh保存的顶点坐标均为局部坐标系下的坐标。
Unity3D用于保存一个物体的各种顶点的信息,肯定是保存局部坐标系下的顶点坐标,因为这样保存后,当一个物体的世界坐标改变了,其顶点的信息也不用修改,因为局部坐标系是以自身中心点为原点的,无论你物体移动还是旋转了,局部坐标系下的所记录的点永远不用改变,因为物体移动或者旋转了,其中心点肯定也随之移动或旋转了,这就同步信息了,所以我们存坐标都存的是局部坐标系的坐标 。(归结为一句话:凡是要记录顶点坐标的都是用局部坐标系,一个物体若无父物体是以世界坐标系为准来计算出坐标的,而当有了父物体就会以父物体的中心作为原点的局部坐标系来计算它的坐标)

屏幕坐标系:建立在屏幕上的二维坐标,以像素来定义,左下角为屏幕的(0,0),右上角为(Screen.width,Screen.height),z轴的坐标是相机的世界坐标中z轴坐标的负值。
鼠标位置坐标属于屏幕坐标,Input.mousePosition可以获取鼠标位置坐标。

手指触摸屏幕属于屏幕坐标,Input.GetTouch(0).position可以获得单个手指触摸屏幕时手指的坐标。

视口坐标系:将Game视图的屏幕坐标系单位化,左下角(0,0),右上角(1,1)。z轴的坐标是相机的世界坐标中z轴坐标的负值。(视口坐标系是以百分比为单位的,视口坐标(x,y)是屏幕坐标系(x1,y1)对应x,y除以屏幕的宽度和高度获取。

坐标系之间的关联与互相转换:
1.全局坐标系和局部坐标系
转换:
Transform.TransformPoint(Vector3 position);将一个坐标点从局部坐标系转换到全局坐标系。
Transfomr.InverseTransformPoint(Vector3 position);将一个坐标点从全局坐标系转换到局部坐标系。
Transform.TransformDirection(Vector3 direction);将一个单位向量(方向)从局部坐标系转换到全局坐标系。
Transfomr.InverseTransformDirection(Vector3 direction);将一个单位向量(方向)从全局坐标系转换到局部坐标系。
Transform.TransformVector(Vector3 vector);将一个向量从局部坐标系转换到全局坐标系。
Transfomr.InverseTransformVector(Vector3 vector);将一个向量从全局坐标系转换到局部坐标系。

其他:
Transform.forward,Transform.right,Transform.up:当前物体的物体坐标系(局部坐标系)的z轴,x轴,y轴在世界坐标系上的指向。

Vector3.forward,(0,0,1)的缩写。在transform.Translate()中使用时,若不表明坐标系,默认为物体的局部坐标系,既物体自身的正前方。
Vector3.right,(1,0,0)的缩写。
Vector3.up,(0,1,0)的缩写。

注意:transform.Translate(Transform.forward,Space.World)和transform.Translate(Transform.forward,Space.Self),该方法的运算方法其实是这样的,先是第二个参数会在世界坐标系的基础上对它进行偏转,如果是Space.World,则不会对物体进行这个偏转,如果是Space.Self 并且物体自身发生偏转了,则会对它(世界坐标系)进行偏转,我们以虚拟世界坐标系来说吧,因为世界坐标系是不会被外界改变的,我们就当成是把这个世界坐标系Copy出来进行偏转,这样进行了第一个偏转!第一个参数是物体局部坐标系的朝向,这个局部坐标系是会随着世界坐标系发生偏转而偏转的,你可以想象世界坐标系是局部坐标系的父物体,父物体旋转了,子物体肯定也会旋转,那么朝向就这样神奇地发生了2次偏转!既如果你物体自身绕着y轴旋转45°(顺时针),用transform.Translate(Transform.forward,Space.Self)就是朝着世界坐标系上的x轴方向移动了。(我现在只能这样理解了,小白解释法^_^)

2.屏幕坐标系与全局坐标系
转换:Camera.ScreenToWorldPoint(Vector3 position):将屏幕坐标转换为全局坐标。(常用)

Camera.WorldToScreenPoint(Vector3 position);将世界坐标转换为屏幕坐标。
Input.mousePosition: 获得鼠标在屏幕坐标系中的坐标。

将屏幕坐标系下的坐标既Input.mousePosition,使用Camera.ScreenToWorldPoint()转为世界坐标系下的坐标是与主摄像机的2个截面有关的,近截面和远截面,在摄像机属性Clipping Planes的Near属性设置近截面离摄像机的距离,Far属性设置远截面离摄像机的距离。
直接Input.mousePosition获取的屏幕坐标下的鼠标点的Z轴是摄像机的Z轴的相反数,那么仅仅是这样写Camera.ScreenToWorldPoint(Input.mousePosition)的话,转换为世界坐标系下的鼠标坐标就只是映射到了摄像机Z轴往前Input.mousePosition.z米的那一层(笛卡尔2D平面(由x,y轴组成)),那一层的宽度和高度是由摄像机的映射模式有关的,如果是3D的模式下,离摄像机越远的层就它的宽度和高度就越大,若是2D的话,则同样大。

举例子:
Vector3 screenPosition=Input.mousePosition;
摄像机位置为(0,0,-5)的)的话,Camera.ScreenToWorldPoint(new Vector3(screenPosition.x,screenPosition.y,100));
这样的话就是将鼠标的点映射到了世界坐标系下的摄像机Z轴是(-5+100)的地方,也就是Z=95的那一层,鼠标的点就会在那一层进行作用。其实上,这里就有个疑问了,Input.mousePosition的Z轴真的是摄像机的Z轴的相反数吗?我电脑坏了,过几天修好了就测试下。这2个方法的用途普遍都在确定玩家点击的点有关,而且与射线检测有关,一般都会用Input.mousePosition来取得用户点击屏幕的点,然后将这个点转为世界坐标系下的点,然后从摄像机位置发射射线,那么摄像机的点与世界坐标系下用户点击的点就会形成一个射线方向,这样就能检测出玩家点击的是什么鬼东西了,至于世界坐标系改变为屏幕坐标系,能用于什么还不不清楚。

3.屏幕坐标系与视口坐标系
转换:Camera.ScreenToViewportPoint(Vector3 position)屏幕坐标转换为视口坐标,实际上就是将屏幕坐标X/Screen.Width,Y/Screen.Height变成了视口坐标系下的坐标。

Camera.ViewportToScreenPoint(Vector3 position)视口坐标转换为屏幕坐标。这里就是将视口坐标下的X*Screen.Width,Y*Screen.Height变成了屏幕坐标系下的坐标。假如(0.5,0.5,0)转为屏幕坐标就是(Screen.Width/2,Screen.Height.2,0)

4.全局坐标系与视口坐标系
转换:

Camera.WorldToViewportPoint(Vector3)全局转为视口。
Camera.ViewportToWorldPoint(Vector3)视口转为全局。

点和向量在Unity中都是用Vector2和Vector3代表的,2表示2D的点和向量,3表示3D的点和向量,比如new Vector3(1,1,1)这样就创建了一个3D的点,而不是三维向量,要获得向量需要用2个点相减,但是有些在方法里面使用new Vector3()这样的不一定都代表的是点,可能也是会被认为是向量,例如transform.Translate(new Vector3(1,0,0),Space.World);这里面的第一个参数就是指定物体移动方向的向量。

向量方面的基础知识
零向量是唯一一个没有方向和大小的向量, Vector3.zero就是零向量(0,0,0)

负向量是与自身向量的大小一样,方向相反的向量,例如:(2,-3,1)的负向量是(-2,3,-1)。

在Unity中,通过Vector3.magnitude计算向量的长度。
Vector3.sqrMagnitude则返回向量长度的平方。
Vector3.Distance(A,B)可以计算2个点A,B之间的距离,既返回向量AB或向量BA的长度。等同于(B-A).magnitude或者(A-B). magnitude。

单位向量:
将一个(非零)向量A 转为单位向量,使用A.Normalization,既长度边变成为1,方向不变。向量归一化后A就发生变化了,长度不再是之前的归一化之前的那个A了;如果想不对这个A向量发生改变,那么可用A.normalization来归一化A,A不会发生改变,它会返回一个单位向量。

这叫向量归一化,先求出向量的长度,然后用向量除以它的长度。

例如:(3,2)二维向量,它的长度是根号13,那么归一化之后变为:(3/根号13,2/根号13),这个归一化之后的向量称之为单位向量,既它的长度是1, 因为((3/根号13)的平方+(2/根号13)的平方)=9/13+4/13=13/13=1,开方后也是1,所以长度为1了。

向量的点积:
a向量=(1,0),b向量=(2,2), a和b的点积=a·b=1*2+0*2=2
公式1:a·b=|a|*|b|*cos(x), 这个x是a和b之间的小夹角, |a|和|b|代表是a,b向量的长度,这样计算2=1*根号8*cos(x),故cos(x)=2/根号8=(根号2)/2,那么x就是45°。

一个向量在另外一个向量的投影长度和投影都与这个公式1有关,详细介绍不说了,学过三角函数都知道怎么求,投影向量的求法先求出投影长度,再用这个投影长度乘以被投影的向量的单位向量就可以了,单位向量的求法:自身向量/自身长度

在Unity中,向量的点积可以通过Vector3.Dot来计算
可以使用Vector3.Angle来获取两个向量之间的夹角的大小(0-180°)

向量的叉积:a x b=(ax,ay,az) x (bx,by,bz)=(ay*bz-az*by,az*bx-ax*bz,ax*by-ay*bx)

叉积后求出的向量c与向量a和向量b是垂直的,回顾一下向量的点积,c·a=|c|*|a|*cos90°=0,这可看出两个向量的点积是否为0,来判断是否垂直。
公式2:|a x b|=|a|*|b|*sin(x)(求出两个向量所包含的面积)

a x b叉积向量的朝向代表了b在a的顺时针方向还是逆时针方向,若叉积向量朝上,则b在a的顺时针方向,若是朝下,则b在a的逆时针方向。
叉积朝向是根据y的数值决定的,若为正数代表朝上,若为负数代表朝下。
(注意:点积是数量,叉积是向量)

在Unity中,向量的叉积通过Vector3.Cross计算。


矩阵:
在Unity中,可以使用Matrix4x4.SetRow,Matrix4x4.SetColumn来设置一个4*4矩阵的某行或某列。

用Matrix4x4.GetRow,Matrix4x4.GetColumn来获取一个4*4矩阵的某行或某列,结果为Vector4类型。

index从0开始。
单位矩阵:

可通过用 Matrix4x4.identity来获取一个4*4的单位矩阵[1,0,0,0][0,1,0,0][0,0,1,0][0,0,0,1],任何矩阵乘以与它相同行列的单位矩阵都等于它自身。

可通过 Matrix4x4.isIdentity来判断一个矩阵是不是单位矩阵。
通过 Matrix4x4.zero获取一个4*4的所有元素为0的矩阵。

转置矩阵:
一个矩阵r*c转置运算后变为c*r矩阵,转置运算是把第i行变为第i列,也可以是将第i列变为第i行。
例如:[1,4,5][2,3,2]=>[1,2][4,3][5,2],自己动手写写大概能明白。

Unity中使用Matrix4x4.transpose获取一个矩阵的转置矩阵。

逆矩阵:

逆矩阵和自身矩阵相乘等于一个单位矩阵,必须是方块矩阵才可能有逆矩阵。

一个方块矩阵的行列式若非0则可逆,若等于0则不可逆。

(行列式的求法百度下)
一个4*4矩阵的逆矩阵的求解:A的逆矩阵=(1/A的行列式数值)*[4*4矩阵,它是以代数余矩阵为元素的].

(4*4矩阵是大概这样的意思:[a11,a21,a31,a41][a12,a22,a32,a42][a13,a23,a33,a43][a14,a24,a34,a44];
注意这里的写法不是[a11,a12,a13,a14]而是a[11,a21,a31,a41]搞错了就算错了!

其中a(x)(y)代表一个代数余矩阵,求法是将x行y列从原矩阵去划除,然后得到一个3*3的余矩阵,再乘以一个(-1)的(x+y)次方。

这里我说的4*4矩阵还是不理解的话可以百度下)
上面先求出行列式,判断一下是否可逆,若可逆继续求它的逆矩阵,求逆矩阵就是计算的数有点多,4*4的可逆矩阵算代数余矩阵这个部分困难,因为求16个3*3这些余矩阵的行列式是什么概念!   

而在Unity中通过Matrix4x4.inverse就可获取一个4*4矩阵的逆矩阵。(niubility...)

齐次坐标:
(-1,1,1,0)代表向量(-1,1,1)其中最后一个0是向量标志位。

(-1,1,1,1)代表点(-1,1,1)其中最后一个1是点标志位。

点与点相减变为向量,因为最后一个数值变为了0。
点与点相加无数学意义。

点与向量相加等于点,因为最后一个数值还是1,不过这个点会朝着向量方向移动了向量长度。

向量与向量相加和相减都等于向量,因为最后一个数值还是0,不过会形成新的向量。
(为什么Unity要用4*4矩阵就是这个原因,用于区分好点和向量,然后对它们进行数学运算)

仿射变换:平移、旋转、缩放都属于仿射变换。
满足以下条件属于仿射变换:1.变换前是直线的,变换后依然是直线。2.直线比例保持不变,既一条线段有A,B,C,D四个点,A是起点,D是终点,AB:BC:CD=1:1:1的,那么变换后这个比例依然是1:1:1。

线性变换:平移、旋转属于线性变换。
线性变换要比仿射变换多一个条件:变换前后的原点不变。

2D变换:

2D平移矩阵M:

1 0 tx
0 1 ty
0 0 1

tx,ty代表对一个2D点的X轴与Y轴的偏移。
P*=MP=P+d=P+[tx,ty,0],P是一个2D点,一个点加上一个偏移向量d就会对P进行平移得到P*。

(自己手写一个点的齐次坐标来测试以下上面的方法是否正确)

假如以[px,py,1]作为P的齐次坐标,那么MP就等于[px+tx,py+ty,1]。

2D缩放矩阵:
Sx 0 0
0 Sy 0
0 0 1

Sx和Sy对应X和Y的缩放比例,假如一个这个旋转矩阵的Sx是3,Sy是2,乘上一个向量(Px,Py,0)就等于(Sx*Px,Sy*Py,0)了。
点也一样。

2D旋转矩阵:
cosx -sinx 0
sinx cosx 0
0 0
1

猜你喜欢

转载自blog.csdn.net/qq_39574690/article/details/79566528