`
icheng
  • 浏览: 821492 次
  • 性别: Icon_minigender_1
  • 来自: 珠海
文章分类
社区版块
存档分类
最新评论

3D 图形编程的数学基础(1) 向量及其运算

 
阅读更多

write by 九天雁翎(JTianLing) -- blog.csdn.net/vagrxie

讨论新闻组及文件

说明

因为大学时在高等数学课程中学习过线性代数相关的内容,所以学习3D编程的时候这一段事实上是跳过去了,学习到某些内容的时候觉得很郁闷,(4,5年没有用了,难免忘掉)最后常常依靠高级API完成,但是事实上这些高级API的算法具体实现啥的基本看不懂,于是还是决定回来好好的将基础部分弄明白,当然,首先是数学部分。为了更好的达到直观的效果,还有在复杂矩阵运算的时候验证运算结果,将引入freemat或者scilab(5.1.1)或者GNU Octave(3.2.3)的使用,将此三个软件作为matlab的替代品来使用。不能用庞大的matlab也是种解脱,默认使用freemat,不行的时候考虑其他替代。具体牵涉到计算的时尽量实现DirectX与Irrlicht两个版本,也会参考部分源代码。(主要用于看看公式用C/C++的实现)基本上,我希望能以概念的讲解为主,最好是直观的讲解。

向量

只用大小就能表示的量叫数量,比如温度,质量等。既需要用大小表示,同时还要指明方向的量叫向量,比如位移,速度等。几何学中,我们用有向线段来表示向量。有两个变量可以确定一个向量,即向量的长度和向量的方向。量与位置无关,有相同长度和方向的两个向量是相等的。在irrlicht中有专门的类vector2d,vector3d分别来表示2维的,3维的向量。在DirectX中用于表示向量的是结构D3DXVECTOR2,D3DXVECTOR3,D3DXVECTOR4。

左右手坐标系

一图胜前言,不懂怎么用手扭曲的去比划的看看图,就明白啥是左手,啥是右手坐标系了。在OpenGL中使用的是右手坐标系,DirectX,Irrlicht中使用的是左手坐标系。(图片来自于网络)

image

向量的模

向量的大小(或长度)称为向量的模,向量a的模记为||a||。下面以3维的向量(3D中用的最多)为例:abc2_html_8d5f713

在irrlicht中获取向量模的函数是vector3d的成员函数

//! Get length of the vector.
T getLength() const { return core::squareroot( X*X + Y*Y + Z*Z ); }

//! Get squared length of the vector.
/** This is useful because it is much faster than getLength().
/return Squared length of the vector. */
T getLengthSQ() const { return X*X + Y*Y + Z*Z; }

可以看出公式的实现,其中getLengthSQ用于某些时候使用不开根号,直接使用平方值的方法来优化代码。

DirectX中的实现差不多一样,只是使用的是C风格的接口没有使用C++的类而已。

D3DXINLINE FLOAT D3DXVec3Length
    ( CONST D3DXVECTOR3 *pV )
{
#ifdef D3DX_DEBUG
    if(!pV)
        return 0.0f;
#endif

#ifdef __cplusplus
    return sqrtf(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
}

D3DXINLINE FLOAT D3DXVec3LengthSq
    ( CONST D3DXVECTOR3 *pV )
{
#ifdef D3DX_DEBUG
    if(!pV)
        return 0.0f;
#endif

    return pV->x * pV->x + pV->y * pV->y + pV->z * pV->z;
}

FreeMat:

--> a = [1, 1, 1]
a =
 1 1 1
--> b = norm(a)
b =
    1.7321
--> 

三维空间中两点的距离

公式:abc4_html_m257962b9

Irrlicht的实现:

//! Get distance from another point.
/** Here, the vector is interpreted as point in 3 dimensional space. */
T getDistanceFrom(const vector3d<T>& other) const
{
    return vector3d<T>(X - other.X, Y - other.Y, Z - other.Z).getLength();
}

//! Returns squared distance from another point.
/** Here, the vector is interpreted as point in 3 dimensional space. */
T getDistanceFromSQ(const vector3d<T>& other) const
{
    return vector3d<T>(X - other.X, Y - other.Y, Z - other.Z).getLengthSQ();
}

也有平方的SQ函数版本。

向量的规范化

向量的规范化也称(归一化)就是使向量的模变为1,即变为单位向量。可以通过将向量都除以该向量的模来实现向量的规范化。规范化后的向量相当于与向量同方向的单位向量,可以用它表示向量的方向。由于方向的概念在3D编程中非常重要,所以此概念也很重要,单位向量有很多重要的性质,在表示物体表面的法线向量时用的更是频繁。

基本的公式: clip_image002

在irrlicht中的调用函数及实现:

//! Normalizes the vector.
/** In case of the 0 vector the result is still 0, otherwise
the length of the vector will be 1.
/return Reference to this vector after normalization. */
vector3d<T>& normalize()
{
    f64 length = (f32)(X*X + Y*Y + Z*Z);
    if (core::equals(length, 0.0)) // this check isn't an optimization but prevents getting NAN in the sqrt.
        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* WINAPI D3DXVec3Normalize
    ( D3DXVECTOR3 *pOut, CONST D3DXVECTOR3 *pV );



向量的加减法,数乘

太简单,不多描述,无非就是对应的加,减,乘罢了,几何意义讲一下,加法可以看做是两个向量综合后的方向,减法可以看做两个向量的差异方向(甚至可以用于追踪算法),数乘用于对向量进行缩放。

为了完整,这里从百度百科拷贝一段资料过来:(以下都是2维的,放到3维也差不多)

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、向量的减法


如果ab是互为相反的向量,那么a=-bb=-aa+b=0. 0的反向量为0

AB-AC=CB. 即“共同起点,指向被减”

a=(x,y) b=(x',y') 则 a-b=(x-x',y-y').

3、数乘向量


实数λ和向量a的乘积是一个向量,记作λa,且∣λa∣=∣λ∣·∣a∣。

当λ>0时,λaa同方向;

当λ<0时,λaa反方向;

当λ=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)。

向量对于数的分配律(第一分配律):(λ+μ)aaa.

数对于向量的分配律(第二分配律):λ(a+b)=λab.

数乘向量的消去律:① 如果实数λ≠0且λa=λb,那么a=b。② 如果a0且λa=μa,那么λ=μ。

点积(dot product)又称数量积或内积

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中的实现:(很简单的公式,很直白的实现)

//! Get the dot product with another vector.
T dotProduct(const vector3d<T>& other) const
{
    return X*other.X + Y*other.Y + Z*other.Z;
}

DirectX中的实现:(很简单的公式,也是很直白的实现)

D3DXINLINE FLOAT D3DXVec3Dot
    ( CONST D3DXVECTOR3 *pV1, CONST D3DXVECTOR3 *pV2 )
{
#ifdef D3DX_DEBUG
    if(!pV1 || !pV2)
        return 0.0f;
#endif

    return pV1->x * pV2->x + pV1->y * pV2->y + pV1->z * pV2->z;
}



叉积(cross product):也称向量积

叉积的结果是一个向量,该向量垂直于相乘的两个向量。

公式:yyyy_html_1b72d65e

注意:叉积不满足交换律,反过来相乘得到的向量与原向量方向相反。
左手坐标系可以通过左手法则来确定叉积返回的向量的方向,从第一个向量向第二个向量弯曲左手,这是拇指所指的方向就是求得的向量的方向。右手坐标系同样的,可以通过右手法则来确定叉积返回的向量的方向,从第一个向量向第二个向量弯曲右手,这是拇指所指的方向就是求得的向量的方向。因此,事实上叉积获得的向量总是垂直于原来两个向量所在的平面。
如果两个向量方向相同或相反,叉积结果将是一个零向量。(即a//b)
叉乘的一个重要应用就是求三角形的法向量。

Irrlicht的实现:

//! Calculates the cross product with another vector.
/** /param p Vector to multiply with.
/return Crossproduct of this vector with p. */
vector3d<T> crossProduct(const vector3d<T>& p) const
{
    return vector3d<T>(Y * p.Z - Z * p.Y, Z * p.X - X * p.Z, X * p.Y - Y * p.X);
}

DirectX的实现:

D3DXINLINE D3DXVECTOR3* D3DXVec3Cross
    ( D3DXVECTOR3 *pOut, CONST D3DXVECTOR3 *pV1, CONST D3DXVECTOR3 *pV2 )
{
    D3DXVECTOR3 v;

#ifdef D3DX_DEBUG
    if(!pOut || !pV1 || !pV2)
        return NULL;
#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;
    return pOut;
}
基本上也就是按公式来了。

作为最后一个概念,这里用代码实践一下。

求a=(2,2,1)和b=(4,5,3)的叉积。

freemat:

--> a = [2,2,1]
a =
 2 2 1
--> b = [4,5,3]
b =
 4 5 3
--> c = cross(a,b)
c =
  1 -2  2
--> 

Irrlicht:

#include <stdio.h>
#include <irrlicht.h>
using namespace irr::core;


int _tmain(int argc, _TCHAR* argv[])
{
    vector3df a(2.0f, 2.0f, 1.0f);
    vector3df b(4.0f, 5.0f, 3.0f);

    vector3df c = a.crossProduct(b);

    printf("c = (%f, %f, %f)", c.X, c.Y, c.Z);


    return 0;
}

输出:

c = (1.000000, -2.000000, 2.000000)

DirectX:

#include <stdio.h>
#include <d3dx9.h>

int _tmain(int argc, _TCHAR* argv[])
{
    D3DXVECTOR3 a(2.0f, 2.0f, 1.0f);
    D3DXVECTOR3 b(4.0f, 5.0f, 3.0f);

    D3DXVECTOR3 c;
    D3DXVec3Cross(&c, &a, &b);

    printf("c = (%f, %f, %f)", c.x, c.y, c.z);

    return 0;
}

输出:

c = (1.000000, -2.000000, 2.000000)

这里给出个较为完整的例子是希望大家了解一下Irrlicht这种C++风格的接口及DirectX的C风格接口使用上的不同,这里就不对两种风格的接口提出更多评论了,以防引起口水战。

下一篇预计讲矩阵的计算

参考资料:

1.《DirectX 9.0 3D游戏开发编程基础》 ,(美)Frank D.Luna著,段菲译,清华大学出版社

2.《大学数学》湖南大学数学与计量经济学院组编,高等教育出版社

3.百度百科及wikipedia

原创文章作者保留版权 转载请注明原作者 并给出链接

write by 九天雁翎(JTianLing) -- blog.csdn.net/vagrxie

<script type="text/javascript">var sitebro_tracker_atc_kw = {u:'http://www.sitebot.com.cn/754892/',w:'NzU0ODky',bt:'#804000',bg:'#EEEEDD',fs:1,ca:'#770000',bh:'#f4f4c6',cp:'',l:10,s:1,lang:'zh_CN'};</script><script type="text/javascript" src="http://www.sitebot.com.cn/js/widget_track2/tracker_atc_kw.js"></script><script type="text/javascript"><!-- var sitebro_tracker_atc={u:'http://www.sitebot.com.cn/754892/',w:'NzU0ODky',bt:'#804000',bg:'#EEEEDD',cf:'#ffffff',ca:'#770000',bh:'#DDDDCC',cp:'%E6%9C%AC%E7%AB%99%E7%83%AD%E9%97%A8%E6%96%87%E7%AB%A0',l:10,s:0,lang:'zh_CN'}; // --></script><script src="http://www.sitebot.com.cn/js/widget_track2/tracker_atc.js" type="text/javascript"></script>
分享到:
评论

相关推荐

    3D数学基础 图形与游戏开发

    《3D数学基础:图形与游戏开发》主要介绍了基本的3D数学概念,这对电脑游戏开发人员和编程人员来说尤为重要。作者详尽地讨论了数学理论,并在必要时提供几何说明,帮助读者形成直观的3D感。书中还提供了将理论应用于...

    OpenGL图形变换时的矩阵和向量计算数学库GLM

    在使用OpenGL做图形加载后,根据实际需求往往需要进行平移、旋转、缩放等操作,在三维坐标中,通过矩阵和向量之间的各种数学运算完成以上过程。glm包含了丰富的矩阵、向量计算数学库。在用此数学库之前,需要有一定...

    DirectX9 3D游戏设计入门 英文版+中文版+源代码

    传说中的龙书是也。。。 最好最新的DirectX游戏开发入门书籍 ...Prank Luna是Hero lnteractive的程序员,从事交互式3D图形编程已有八年多。他最早接触DirectX可以追溯到DirectX5发布之时,目前居住在加州的洛杉矶市。

    python的感悟.docx

    python的感悟全文共2页,当前为第1页。...例如numpy库可以处理数学运算中的向量、矩阵等等,pandas库可以用来快速分析数据,matplotlib库可以制作出各种漂亮的图形。这一切都让编程变得更容易。 有趣

    MATLAB程序设计与典型应用(源程序)

    向量及其运算... 32 2.2.6 矩阵的特殊运算... 35 2.2.7 数组的运算... 44 2.2.8 字符串... 47 2.3 MATLAB多项式及其运算... 47 2.3.1 多项式求值... 48 2.3.2 多项式求根... 48 2.3.3 部分分式展开... 49 2.3.4 ...

    计算方法与软件应用

    目前,它是集计算、可视化及编程等功能于一身的一个最流行的数学软件。其特点是: 1、功能强大 它不仅具有强大的数值计算功能,可以处理如:矩阵计算、微积分运算、各种方程的求 解、插值和拟合计算、完成各种统计和...

    [Flash.ActionScript.3.0动画教程

    第15章 3D基础 15.1 第3维和透视 15.1.1 z轴 15.1.2 透视公式 15.2 速度和加速度 15.3 回弹 15.3.1 单个物体回弹 15.3.2 多物体回弹 15.3.3 z轴排序 15.4 重力 15.5 折回 15.6 缓动和弹性 15.6.1 缓动 15.6.2 弹性 ...

    matlab代码sqrt-MATLAB-Basics:MATLAB基础

    Matlab代码sqrt Introduction: Matlab是用于技术计算的高性能语言。 它在易于使用的数学表示法中表达问题和解决方案的用户友好环境中...•算术和逻辑运算,•数学函数,•图形函数,以及•输入/输出运算。 Expressions:

    Foundation Actionscript 3.0 Animation

    第15章3D基础 15.1第3维和透视 15.1.1z轴 15.1.2透视 15.2速度和加速度 15.3回弹 15.3.1单个物体回弹 15.3.2多物体回弹 15.3.3z轴排序 15.4重力.. 15.5折回 15.6缓动和弹性 15.6.1缓动 15.6.2弹性 15.7坐标旋转 7 ...

    javaSE代码实例

    16.1.1 多线程编程的意义 343 16.1.2 定义自己的线程 344 16.1.3 创建线程对象 345 16.1.4 启动线程 347 16.1.5 同时使用多个线程 348 16.2 线程的状态 350 16.3 线程的调度 351 16.3.1 睡眠 351 ...

    Matlab基础及应用教程

    MATLAB 语言基础............................20 2.1 基本概念..............................................20 2.1.1 MATLAB 数据类型.................20 2.1.2 常量与变量..............................21 ...

    数据结构(C++)有关练习题

    内容及步骤: 1、 在前一个实验的基础上,继续增加搜索函数Search(int Info)(如果找到结点,返回指向该结点的指针,如果没有,则返回空指针)和删除函数bool Delete(int Info),如果找到结点,则删除该结点,并...

    程序设计方法.[美]Matthias Felleisen(带书签文字版).pdf

    国外著名高等院校信息科学与技术优秀教材:本书以Scheme语言为基础介,绍计算和程序设计的一般理论,具体讨论了程序设计、基于数据驱动的程序设计基础、程序设计中的抽象问题等。 第一部分 简单数据的处理 18 第1章...

Global site tag (gtag.js) - Google Analytics