(转载)3D 图形编程的数学基础(1) 向量及其运算
2016-12-01 19:15
393 查看
原文地址:http://blog.csdn.net/vagrxie/article/details/4960473
版权声明:本作品由九天雁翎创作,采用知识共享署名-非商业性使用4.0国际许可协议进行许可。http://www.jtianling.com
目录(?)[+]
writeby九天雁翎(JTianLing)--blog.csdn.NET/vagrxie
讨论新闻组及文件
Technorati标签:向量,3D,坐标系,规范化,点积,叉积
算法具体实现啥的基本看不懂,于是还是决定回来好好的将基础部分弄明白,当然,首先是数学部分。为了更好的达到直观的效果,还有在复杂矩阵运算的时候验证运算结果,将引入freemat或者scilab(5.1.1)或者GNUOctave(3.2.3)的使用,将此三个软件作为matlab的替代品来使用。不能用庞大的matlab也是种解脱,默认使用freemat,不行的时候考虑其他替代。具体牵涉到计算的时尽量实现DirectX与Irrlicht两个版本,也会参考部分源代码。(主要用于看看公式用C/C++的实现)基本上,我希望能以概念的讲解为主,最好是直观的讲解。
在irrlicht中获取向量模的函数是vector3d的成员函数
//!Getlengthofthevector.
TgetLength()const{returncore::squareroot(X*X+Y*Y+Z*Z);}
//!Getsquaredlengthofthevector.
/**ThisisusefulbecauseitismuchfasterthangetLength().
/returnSquaredlengthofthevector.*/
TgetLengthSQ()const{returnX*X+Y*Y+Z*Z;}
可以看出公式的实现,其中getLengthSQ用于某些时候使用不开根号,直接使用平方值的方法来优化代码。
DirectX中的实现差不多一样,只是使用的是C风格的接口没有使用C++的类而已。
D3DXINLINEFLOATD3DXVec3Length
(CONSTD3DXVECTOR3*pV)
{
#ifdefD3DX_DEBUG
if(!pV)
return0.0f;
#endif
#ifdef__cplusplus
returnsqrtf(pV->x*pV->x+pV->y*pV->y+pV->z*pV->z);
#else
return(FLOAT)sqrt(pV->x*pV->x+pV->y*pV->y+pV->z*pV->z);
#endif
}
D3DXINLINEFLOATD3DXVec3LengthSq
(CONSTD3DXVECTOR3*pV)
{
#ifdefD3DX_DEBUG
if(!pV)
return0.0f;
#endif
returnpV->x*pV->x+pV->y*pV->y+pV->z*pV->z;
}
FreeMat:
Irrlicht的实现:
//!Getdistancefromanotherpoint.
/**Here,thevectorisinterpretedaspointin3dimensionalspace.*/
TgetDistanceFrom(constvector3d<T>&other)const
{
returnvector3d<T>(X-other.X,Y-other.Y,Z-other.Z).getLength();
}
//!Returnssquareddistancefromanotherpoint.
/**Here,thevectorisinterpretedaspointin3dimensionalspace.*/
TgetDistanceFromSQ(constvector3d<T>&other)const
{
returnvector3d<T>(X-other.X,Y-other.Y,Z-other.Z).getLengthSQ();
}
也有平方的SQ函数版本。
基本的公式:
在irrlicht中的调用函数及实现:
//!Normalizesthevector.
/**Incaseofthe0vectortheresultisstill0,otherwise
thelengthofthevectorwillbe1.
/returnReferencetothisvectorafternormalization.*/
vector3d<T>&normalize()
{
f64length=(f32)(X*X+Y*Y+Z*Z);
if(core::equals(length,0.0))//thischeckisn'tanoptimizationbutpreventsgettingNANinthesqrt.
return*this;
length=core::reciprocal_squareroot((f64)(X*X+Y*Y+Z*Z));
X=(T)(X*length);
Y=(T)(Y*length);
Z=(T)(Z*length);
return*this;
}
上述代码中首先计算length以防其为0,然后直接计算1/||u||,(这样做的目的从代码实现上来看是因为SSE,Nviadia都有可以直接计算此值的能力)然后再分别与各坐标值进行乘法运算。
DirectX中的调用函数:(无实现可看)
D3DXVECTOR3*WINAPID3DXVec3Normalize
(D3DXVECTOR3*pOut,CONSTD3DXVECTOR3*pV);
为了完整,这里从百度百科拷贝一段资料过来:(以下都是2维的,放到3维也差不多)
设a=(x,y),b=(x',y')。
AB+BC=AC。
a+b=(x+x',y+y')。
a+0=0+a=a。
向量加法的运算律:
交换律:a+b=b+a;
结合律:(a+b)+c=a+(b+c)。
AB-AC=CB.即“共同起点,指向被减”
a=(x,y)b=(x',y')则a-b=(x-x',y-y').
当λ>0时,λa与a同方向;
当λ<0时,λa与a反方向;
当λ=0时,λa=0,方向任意。
当a=0时,对于任意实数λ,都有λa=0。
注:按定义知,如果λa=0,那么λ=0或a=0。
实数λ叫做向量a的系数,乘数向量λa的几何意义就是将表示向量a的有向线段伸长或压缩。
当∣λ∣>1时,表示向量a的有向线段在原方向(λ>0)或反方向(λ<0)上伸长为原来的∣λ∣倍;
当∣λ∣<1时,表示向量a的有向线段在原方向(λ>0)或反方向(λ<0)上缩短为原来的∣λ∣倍。
数与向量的乘法满足下面的运算律
结合律:(λa)·b=λ(a·b)=(a·λb)。
向量对于数的分配律(第一分配律):(λ+μ)a=λa+μa.
数对于向量的分配律(第二分配律):λ(a+b)=λa+λb.
数乘向量的消去律:①如果实数λ≠0且λa=λb,那么a=b。②如果a≠0且λa=μa,那么λ=μ。
所以向量的点积结果是一个数,而非向量。
点积等于向量v0的长度乘以v1的长度,再乘以它们之间夹角的余弦,即|v0|*|v1|*cos(θ).
通过点积,可以计算两个向量之间的夹角。
cos(θ)=v0.v1/|v0||v1|;
θ=Math.acos(v0.v1/|v0||v1|);
如果两个向量都是单位向量,上面的公式可以简化为
θ=Math.acos(v0.v1);
V0.v1=0=》两个向量互相垂直
V0.v1>0=》两个向量的夹角小于90度
V0.v1<0=》两个向量的夹角大于90度
Irrlicht中的实现:(很简单的公式,很直白的实现)
//!Getthedotproductwithanothervector.
TdotProduct(constvector3d<T>&other)const
{
returnX*other.X+Y*other.Y+Z*other.Z;
}
DirectX中的实现:(很简单的公式,也是很直白的实现)
D3DXINLINEFLOATD3DXVec3Dot
(CONSTD3DXVECTOR3*pV1,CONSTD3DXVECTOR3*pV2)
{
#ifdefD3DX_DEBUG
if(!pV1||!pV2)
return0.0f;
#endif
returnpV1->x*pV2->x+pV1->y*pV2->y+pV1->z*pV2->z;
}
公式:
注意:叉积不满足交换律,反过来相乘得到的向量与原向量方向相反。
左手坐标系可以通过左手法则来确定叉积返回的向量的方向,从第一个向量向第二个向量弯曲左手,这是拇指所指的方向就是求得的向量的方向。右手坐标系同样的,可以通过右手法则来确定叉积返回的向量的方向,从第一个向量向第二个向量弯曲右手,这是拇指所指的方向就是求得的向量的方向。因此,事实上叉积获得的向量总是垂直于原来两个向量所在的平面。
如果两个向量方向相同或相反,叉积结果将是一个零向量。(即a//b)
叉乘的一个重要应用就是求三角形的法向量。
//!Calculatesthecrossproductwithanothervector.
/**/parampVectortomultiplywith.
/returnCrossproductofthisvectorwithp.*/
vector3d<T>crossProduct(constvector3d<T>&p)const
{
returnvector3d<T>(Y*p.Z-Z*p.Y,Z*p.X-X*p.Z,X*p.Y-Y*p.X);
}
D3DXINLINED3DXVECTOR3*D3DXVec3Cross
(D3DXVECTOR3*pOut,CONSTD3DXVECTOR3*pV1,CONSTD3DXVECTOR3*pV2)
{
D3DXVECTOR3v;
#ifdefD3DX_DEBUG
if(!pOut||!pV1||!pV2)
returnNULL;
#endif
v.x=pV1->y*pV2->z-pV1->z*pV2->y;
v.y=pV1->z*pV2->x-pV1->x*pV2->z;
v.z=pV1->x*pV2->y-pV1->y*pV2->x;
*pOut=v;
returnpOut;
}
基本上也就是按公式来了。
作为最后一个概念,这里用代码实践一下。
求a=(2,2,1)和b=(4,5,3)的叉积。
#include<stdio.h>
#include<irrlicht.h>
usingnamespaceirr::core;
int_tmain(intargc,_TCHAR*argv[])
{
vector3dfa(2.0f,2.0f,1.0f);
vector3dfb(4.0f,5.0f,3.0f);
vector3dfc=a.crossProduct(b);
printf("c=(%f,%f,%f)",c.X,c.Y,c.Z);
return0;
}
输出:
c=(1.000000,-2.000000,2.000000)
#include<stdio.h>
#include<d3dx9.h>
int_tmain(intargc,_TCHAR*argv[])
{
D3DXVECTOR3a(2.0f,2.0f,1.0f);
D3DXVECTOR3b(4.0f,5.0f,3.0f);
D3DXVECTOR3c;
D3DXVec3Cross(&c,&a,&b);
printf("c=(%f,%f,%f)",c.x,c.y,c.z);
return0;
}
输出:
c=(1.000000,-2.000000,2.000000)
这里给出个较为完整的例子是希望大家了解一下Irrlicht这种C++风格的接口及DirectX的C风格接口使用上的不同,这里就不对两种风格的接口提出更多评论了,以防引起口水战。
下一篇预计讲矩阵的计算
参考资料:
1.《DirectX9.03D游戏开发编程基础》,(美)FrankD.Luna著,段菲译,清华大学出版社
2.《大学数学》湖南大学数学与计量经济学院组编,高等教育出版社
3.百度百科及wikipedia
原创文章作者保留版权转载请注明原作者并给出链接
writeby九天雁翎(JTianLing)--blog.csdn.Net/vagrxie
版权声明:本作品由九天雁翎创作,采用知识共享署名-非商业性使用4.0国际许可协议进行许可。http://www.jtianling.com
目录
Technorati标签:
说明
因为大学时在高等数学课程中学习过线性代数相关的内容,所以学习3D编程的时候这一段事实上是跳过去了,学习到某些内容的时候觉得很郁闷,(4,5年没有用了,难免忘掉)最后常常依靠高级API完成,但是事实上这些高级API的向量
只用大小就能表示的量叫数量,比如温度,质量等。既需要用大小表示,同时还要指明方向的量叫向量,比如位移,速度等。几何学中,我们用有向线段来表示向量。有两个变量可以确定一个向量,即向量的长度和向量的方向。量与位置无关,有相同长度和方向的两个向量是相等的。在irrlicht中有专门的类vector2d,vector3d分别来表示2维的,3维的向量。在DirectX中用于表示向量的是结构D3DXVECTOR2,D3DXVECTOR3,D3DXVECTOR4。左右手坐标系
一图胜前言,不懂怎么用手扭曲的去比划的看看图,就明白啥是左手,啥是右手坐标系了。在OpenGL中使用的是右手坐标系,DirectX,Irrlicht中使用的是左手坐标系。(图片来自于网络)向量的模
向量的大小(或长度)称为向量的模,向量a的模记为||a||。下面以3维的向量(3D中用的最多)为例:在irrlicht中获取向量模的函数是vector3d的成员函数
可以看出公式的实现,其中getLengthSQ用于某些时候使用不开根号,直接使用平方值的方法来优化代码。
DirectX中的实现差不多一样,只是使用的是C风格的接口没有使用C++的类而已。
FreeMat:
-->a=[1,1,1] a= 111 -->b=norm(a) b= 1.7321 --> |
三维空间中两点的距离
公式:Irrlicht的实现:
也有平方的SQ函数版本。
向量的规范化
向量的规范化也称(归一化)就是使向量的模变为1,即变为单位向量。可以通过将向量都除以该向量的模来实现向量的规范化。规范化后的向量相当于与向量同方向的单位向量,可以用它表示向量的方向。由于方向的概念在3D编程中非常重要,所以此概念也很重要,单位向量有很多重要的性质,在表示物体表面的法线向量时用的更是频繁。基本的公式:
在irrlicht中的调用函数及实现:
上述代码中首先计算length以防其为0,然后直接计算1/||u||,(这样做的目的从代码实现上来看是因为SSE,Nviadia都有可以直接计算此值的能力)然后再分别与各坐标值进行乘法运算。
DirectX中的调用函数:(无实现可看)
向量的加减法,数乘
太简单,不多描述,无非就是对应的加,减,乘罢了,几何意义讲一下,加法可以看做是两个向量综合后的方向,减法可以看做两个向量的差异方向(甚至可以用于追踪算法),数乘用于对向量进行缩放。为了完整,这里从
设a=(x,y),b=(x',y')。
1、向量的加法
向量的加法满足平行四边形法则和三角形法则。AB+BC=AC。
a+b=(x+x',y+y')。
a+0=0+a=a。
向量加法的运算律:
交换律:a+b=b+a;
结合律:(a+b)+c=a+(b+c)。
2、向量的减法
如果a、b是互为相反的向量,那么a=-b,b=-a,a+b=0.0的反向量为0AB-AC=CB.即“共同起点,指向被减”
a=(x,y)b=(x',y')则a-b=(x-x',y-y').
3、数乘向量
实数λ和向量a的乘积是一个向量,记作λa,且∣λa∣=∣λ∣·∣a∣。当λ>0时,λa与a同方向;
当λ<0时,λa与a反方向;
当λ=0时,λa=0,方向任意。
当a=0时,对于任意实数λ,都有λa=0。
注:按定义知,如果λa=0,那么λ=0或a=0。
实数λ叫做向量a的系数,乘数向量λa的几何意义就是将表示向量a的有向线段伸长或压缩。
当∣λ∣>1时,表示向量a的有向线段在原方向(λ>0)或反方向(λ<0)上伸长为原来的∣λ∣倍;
当∣λ∣<1时,表示向量a的有向线段在原方向(λ>0)或反方向(λ<0)上缩短为原来的∣λ∣倍。
数与向量的乘法满足下面的运算律
结合律:(λa)·b=λ(a·b)=(a·λb)。
向量对于数的分配律(第一分配律):(λ+μ)a=λa+μa.
数对于向量的分配律(第二分配律):λ(a+b)=λa+λb.
数乘向量的消去律:①如果实数λ≠0且λa=λb,那么a=b。②如果a≠0且λa=μa,那么λ=μ。
点积(dotproduct)又称数量积或内积
v0.v1=v0.x*v1.x+v0.y*v1.y+v0.z*v1.z;所以向量的点积结果是一个数,而非向量。
点积等于向量v0的长度乘以v1的长度,再乘以它们之间夹角的余弦,即|v0|*|v1|*cos(θ).
通过点积,可以计算两个向量之间的夹角。
cos(θ)=v0.v1/|v0||v1|;
θ=Math.acos(v0.v1/|v0||v1|);
如果两个向量都是单位向量,上面的公式可以简化为
θ=Math.acos(v0.v1);
V0.v1=0=》两个向量互相垂直
V0.v1>0=》两个向量的夹角小于90度
V0.v1<0=》两个向量的夹角大于90度
Irrlicht中的实现:(很简单的公式,很直白的实现)
TdotProduct(constvector3d<T>&other)const
{
returnX*other.X+Y*other.Y+Z*other.Z;
}
DirectX中的实现:(很简单的公式,也是很直白的实现)
(CONSTD3DXVECTOR3*pV1,CONSTD3DXVECTOR3*pV2)
{
#ifdefD3DX_DEBUG
if(!pV1||!pV2)
return0.0f;
#endif
returnpV1->x*pV2->x+pV1->y*pV2->y+pV1->z*pV2->z;
}
叉积(crossproduct):也称向量积
叉积的结果是一个向量,该向量垂直于相乘的两个向量。公式:
注意:叉积不满足交换律,反过来相乘得到的向量与原向量方向相反。
左手坐标系可以通过左手法则来确定叉积返回的向量的方向,从第一个向量向第二个向量弯曲左手,这是拇指所指的方向就是求得的向量的方向。右手坐标系同样的,可以通过右手法则来确定叉积返回的向量的方向,从第一个向量向第二个向量弯曲右手,这是拇指所指的方向就是求得的向量的方向。因此,事实上叉积获得的向量总是垂直于原来两个向量所在的平面。
如果两个向量方向相同或相反,叉积结果将是一个零向量。(即a//b)
叉乘的一个重要应用就是求三角形的法向量。
Irrlicht的实现:
/**/parampVectortomultiplywith.
/returnCrossproductofthisvectorwithp.*/
vector3d<T>crossProduct(constvector3d<T>&p)const
{
returnvector3d<T>(Y*p.Z-Z*p.Y,Z*p.X-X*p.Z,X*p.Y-Y*p.X);
}
DirectX的实现:
(D3DXVECTOR3*pOut,CONSTD3DXVECTOR3*pV1,CONSTD3DXVECTOR3*pV2)
{
D3DXVECTOR3v;
#ifdefD3DX_DEBUG
if(!pOut||!pV1||!pV2)
returnNULL;
#endif
v.x=pV1->y*pV2->z-pV1->z*pV2->y;
v.y=pV1->z*pV2->x-pV1->x*pV2->z;
v.z=pV1->x*pV2->y-pV1->y*pV2->x;
*pOut=v;
returnpOut;
}
基本上也就是按公式来了。
作为最后一个概念,这里用代码实践一下。
求a=(2,2,1)和b=(4,5,3)的叉积。
freemat:
-->a=[2,2,1] |
Irrlicht:
#include<irrlicht.h>
usingnamespaceirr::core;
int_tmain(intargc,_TCHAR*argv[])
{
vector3dfa(2.0f,2.0f,1.0f);
vector3dfb(4.0f,5.0f,3.0f);
vector3dfc=a.crossProduct(b);
printf("c=(%f,%f,%f)",c.X,c.Y,c.Z);
return0;
}
输出:
c=(1.000000,-2.000000,2.000000)
DirectX:
#include<d3dx9.h>
int_tmain(intargc,_TCHAR*argv[])
{
D3DXVECTOR3a(2.0f,2.0f,1.0f);
D3DXVECTOR3b(4.0f,5.0f,3.0f);
D3DXVECTOR3c;
D3DXVec3Cross(&c,&a,&b);
printf("c=(%f,%f,%f)",c.x,c.y,c.z);
return0;
}
输出:
c=(1.000000,-2.000000,2.000000)
这里给出个较为完整的例子是希望大家了解一下Irrlicht这种C++风格的接口及DirectX的C风格接口使用上的不同,这里就不对两种风格的接口提出更多评论了,以防引起口水战。
下一篇预计讲矩阵的计算
参考资料:
1.《DirectX9.03D游戏开发编程基础》,(美)FrankD.Luna著,段菲译,清华大学出版社
2.《大学数学》湖南大学数学与计量经济学院组编,高等教育出版社
3.百度百科及wikipedia
原创文章作者保留版权转载请注明原作者并给出链接
相关文章推荐
- (转载)3D 图形编程的数学基础(2) 矩阵及其运算
- 3D 图形编程的数学基础(1) 向量及其运算
- /LGC图形渲染/3D 图形编程的数学基础(1) 向量及其运算
- 3D 图形编程的数学基础(2) 矩阵及其运算
- (转载)3D 图形编程的数学基础(3) 矩阵基本变换
- 1_向量_《3D图形编程基础——基于DirectX11》笔记
- 3D 图形编程的数学基础(3) 矩阵基本变换
- 3d数学基础:常用向量相关运算
- 3D数学基础及图形与游戏开发的学习 (一)
- 3D游戏基础 空间几何(二) 向量运算
- 3D Math Primer for Graphics and Game Development -- 图形与游戏开发(3D数学基础) (简介)
- 3D数学基础及图形与游戏开发的学习 (二)
- 3d数学基础-向量
- DirectX&Direct 3D 游戏开发之3D图形的数学基础
- 《DirectX 9.0 3D游戏开发编程基础》必备的数学知识 读书笔记
- 3D数学基础 图形与游戏开发的学习 (六)[多坐标系]
- OpenGL基础图形编程 - OpenGL与3D图形世界
- 3D程序开发数学基础之向量
- 3D数学基础:图形与游戏开发
- [career] 3D图形向量基础