热门标签 | HotTags
当前位置:  开发笔记 > 后端 > 正文

(转)从零实现3D图像引擎:(11)苍井空做客讲解3D变换矩阵的推导

1.数学分析上一篇中间在做旋转的时候我直接用了旋转变换矩阵,当时觉得很尴尬,因为之前没说过是怎么产生的该矩阵。1)矩阵和向量的微妙关系如果您还记得向量加法的几何意义,那么不难看懂下

1. 数学分析

 

上一篇中间在做旋转的时候我直接用了旋转变换矩阵,当时觉得很尴尬,因为之前没说过是怎么产生的该矩阵。

1) 矩阵和向量的微妙关系

如果您还记得向量加法的几何意义,那么不难看懂下面的等式:

      [x]    [x]     [0]    [0]          [1]          [0]          [0]

v = [y] = [0] + [y] + [0] = x * [0] + y * [1] + z * [0]

      [z]    [0]     [0]    [z]          [0]          [0]          [1]

我们可以看到一个向量或者说是一个点,可以表示为该向量的各分量与某坐标轴单位向量的乘积之和。

我们设p,q,r分别代表这三个单位向量,则可以转化为这种形式:

v = xp + yq + zr

现在我们把p,q,r一般化,令p = [px py pz], q = [qx qy qz], r = [rx ry rz],我们把它们组成一个矩阵

       [ px py pz ]

M = [ qx qy qz ]

       [ rx  ry  rz ]

然后用向量[x y z]乘以该矩阵,得

[x y z] × M = [(x*px +y*qx + z*rx)  (x*py + y*qy + z*ry)  (x*pz + y*qz + z*rz)] = xp + yq + zr

呵呵,看到M的作用了吗,他把向量[x y z]执行了一次矩阵变换,变成了v。这就是变换矩阵的来源。那他可以做什么呢?下面就是重点了。

我们用坐标轴的单位向量(我们管他叫基向量)来分别乘以M:

[1 0 0] × M = p

[0 1 0] × M = q

[0 0 1] × M = r

哈哈,这个性质太重要了:对于一个坐标轴(上面的[1 0 0] [0 1 0] [0 0 1]都是某个坐标轴的单位向量),我们可以使用一个向量来变换他。比如对于X轴,我们只要给出一个变换向量p,就可以把X轴按p向量进行变换。而且我们可以同时给出p,q,r,同时对X,Y,Z三个轴进行变换。

下面的图非常形象的表示了我们现在可以如何利用这个性质:

我们可以把图中的绿色、红色箭头看作是坐标轴的单位向量,分别对应X轴[1 0]和Y轴[0 1],那么我们对他们进行变换会发生什么?

比如,我们把X轴从向量[1 0]变换到[2 1],把Y轴从[0 1]变换到[-1 2],那么效果如下图:

进行了这个变换,我们发现,空姐的照片不仅被放大了一些,而且还绕Z轴旋转了一些。这就是刚才我们构建的矩阵

M = [ 2  1 ] 的作用

       [-1  2 ]

现在弄清楚变换矩阵和向量的关系了吧,我们可以构建矩阵M,用M的第一行来变换物体坐标系的X轴向量,用M的第二行来变换物体坐标系的Y轴向量,用M的第三行来变换物体的Z轴向量,就好象控制3ds max的操作柄一样。现在我们可以来推导旋转矩阵了。

2) 3D旋转变换矩阵的推导。

我们首先还用一个俯视图来推导,我们顺着Z轴俯视下来,现在让物体绕Z轴旋转,那么物体的X轴控制柄向量和Y轴控制柄向量应该如何变换呢?看图:

因为是绕Z轴旋转,那么X轴和Y轴都应该旋转同一个角度,设为theta。那么对于X轴单位向量(1,0),则旋转theta后,坐标变为 cos(theta), sin(theta)。Y轴单位向量(0,1)在旋转theta后,坐标变为-sin(theta),cos(theta)。那么我们开始整理矩阵:

对于X控制柄,变换向量为:[cos(theta)  sin(theta)  0]

对于Y控制柄,变换向量为:[-sin(theta)  cos(theta)  0]

对于Z控制柄,变换向量为: [        0                0         1]  (因为Z控制柄没有变化,扔为Z轴单位向量)

好了,我们已经求得了某点绕Z轴旋转的变换矩阵了:

M =

[ cos(theta)   sin(theta)   0 ]

[ -sin(theta)   cos(theta)  0 ]

[        0                  0         1 ]

2. 代码实现

根据上面的推导,我们可以同理推出绕X,Y轴的变换矩阵,下面的函数是实现一个综合旋转,可以获得让一个点,绕X,Y,Z同时转的变换矩阵:

void _CPPYIN_3DLib::BuildRotateMatrix(double anglex_du, double angley_du, double anglez_du, MATRIX4X4_PTR m) // 创建同时绕X,Y,Z旋转不同度数的变换矩阵
{
	// 初始化针对绕X,Y,Z轴旋转的3个变换矩阵,将他们都复制为单位矩阵,如果绕某轴不旋转,则单位矩阵不会影响结果
	MATRIX4X4 mx, my, mz;
	memcpy((void *)(&mx), (void *)&MATRIXI_4X4, sizeof(MATRIX4X4));
	memcpy((void *)(&my), (void *)&MATRIXI_4X4, sizeof(MATRIX4X4));
	memcpy((void *)(&mz), (void *)&MATRIXI_4X4, sizeof(MATRIX4X4));

	double sin_theta, cos_theta; // 缓存计算结果,减少CPU开销

	if (anglex_du > EPSILON)
	{
		cos_theta = FastCos(anglex_du);
		sin_theta = FastSin(anglex_du);

		MatrixCreate(&mx, 
						1,    0,         0,         0,
                        0,    cos_theta, sin_theta, 0,
                        0,   -sin_theta, cos_theta, 0,
                        0,    0,         0,         1);
	}

	if (angley_du > EPSILON)
	{
		cos_theta = FastCos(angley_du);
		sin_theta = FastSin(angley_du);

		MatrixCreate(&my,
						cos_theta, 0,  -sin_theta,  0,  
						0,         1,  0,           0,
						sin_theta, 0,  cos_theta,   0,
						0,         0,  0,           1);
	}

	if (anglez_du > EPSILON)
	{
		cos_theta = FastCos(anglez_du);
		sin_theta = FastSin(anglez_du);

		MatrixCreate(&mz,
						cos_theta,  sin_theta,  0,  0,  
						-sin_theta, cos_theta,  0,  0,
                        0,          0,          1,  0,
                        0,          0,          0,  1);
	}

	// 变换矩阵相乘,获得最终变换矩阵
	MATRIX4X4 mtemp;
	MatrixMul(&mx, &my, &mtemp);
	MatrixMul(&mtemp, &mz, m);
}

使用这个函数,对上篇文章的某些函数做了简化,不用每次都手写这些矩阵了,这次的DEMO对上次的金字塔同时执行X和Z轴的变换。

请注意我修改了世界变换与旋转物体的顺序,并且保存旋转后的点的存储位置也不一样了哦。

截图:

3. 代码下载

完整项目源代码下载:>>点击进入下载页<<

4. 补充内容

其实这次只说了旋转矩阵的推导,但通过这个过程,我们知道了向量和矩阵之间的微妙关系,对于其他变换,如:缩放、投影、镜像、切变也都可以利用这种关系来推导出来,这里就不多费口舌了。

一个比较特殊的情况是平移。如果您仔细看了文章就会发现,对X,Y,Z三个坐标轴进行变换并不能达到使物体平移的目的,这也就是为什么要用4D向量来表示和变换了,具体这第4维怎么用,有空我们再研究研究4D齐次坐标的原理吧,有兴趣的朋友可以发链接分享哦。

对于本次来客串的空姐,纯属增加趣味,所有的肖像权、发行权、出版权全归您所有,如果您认为权利被侵犯了,我会立刻更换图片~~

转自:http://blog.csdn.net/cppyin/archive/2011/02/18/6193076.aspx


推荐阅读
  • 如何去除Win7快捷方式的箭头
    本文介绍了如何去除Win7快捷方式的箭头的方法,通过生成一个透明的ico图标并将其命名为Empty.ico,将图标复制到windows目录下,并导入注册表,即可去除箭头。这样做可以改善默认快捷方式的外观,提升桌面整洁度。 ... [详细]
  • 本文介绍了Java工具类库Hutool,该工具包封装了对文件、流、加密解密、转码、正则、线程、XML等JDK方法的封装,并提供了各种Util工具类。同时,还介绍了Hutool的组件,包括动态代理、布隆过滤、缓存、定时任务等功能。该工具包可以简化Java代码,提高开发效率。 ... [详细]
  • 一句话解决高并发的核心原则
    本文介绍了解决高并发的核心原则,即将用户访问请求尽量往前推,避免访问CDN、静态服务器、动态服务器、数据库和存储,从而实现高性能、高并发、高可扩展的网站架构。同时提到了Google的成功案例,以及适用于千万级别PV站和亿级PV网站的架构层次。 ... [详细]
  • 一、Hadoop来历Hadoop的思想来源于Google在做搜索引擎的时候出现一个很大的问题就是这么多网页我如何才能以最快的速度来搜索到,由于这个问题Google发明 ... [详细]
  • 在Docker中,将主机目录挂载到容器中作为volume使用时,常常会遇到文件权限问题。这是因为容器内外的UID不同所导致的。本文介绍了解决这个问题的方法,包括使用gosu和suexec工具以及在Dockerfile中配置volume的权限。通过这些方法,可以避免在使用Docker时出现无写权限的情况。 ... [详细]
  • android listview OnItemClickListener失效原因
    最近在做listview时发现OnItemClickListener失效的问题,经过查找发现是因为button的原因。不仅listitem中存在button会影响OnItemClickListener事件的失效,还会导致单击后listview每个item的背景改变,使得item中的所有有关焦点的事件都失效。本文给出了一个范例来说明这种情况,并提供了解决方法。 ... [详细]
  • Windows下配置PHP5.6的方法及注意事项
    本文介绍了在Windows系统下配置PHP5.6的步骤及注意事项,包括下载PHP5.6、解压并配置IIS、添加模块映射、测试等。同时提供了一些常见问题的解决方法,如下载缺失的msvcr110.dll文件等。通过本文的指导,读者可以轻松地在Windows系统下配置PHP5.6,并解决一些常见的配置问题。 ... [详细]
  • 本文介绍了一些好用的搜索引擎的替代品,包括网盘搜索工具、百度网盘搜索引擎等。同时还介绍了一些笑话大全、GIF笑话图片、动态图等资源的搜索引擎。此外,还推荐了一些迅雷快传搜索和360云盘资源搜索的网盘搜索引擎。 ... [详细]
  • 在重复造轮子的情况下用ProxyServlet反向代理来减少工作量
    像不少公司内部不同团队都会自己研发自己工具产品,当各个产品逐渐成熟,到达了一定的发展瓶颈,同时每个产品都有着自己的入口,用户 ... [详细]
  • 达人评测 酷睿i5 12450h和锐龙r7 5800h选哪个好 i512450h和r75800h对比
    本文介绍了达人评测酷睿i5 12450h和锐龙r7 5800h选哪个好的相关知识,包括两者的基本配置和重要考虑点。希望对你在选择时提供一定的参考价值。 ... [详细]
  • 本文介绍了关于smarty自定义缓存名的解决思路,通过放弃生成缓存,直接生成html的静态页面来提高速度。同时提供了一个参考链接供参考。 ... [详细]
  • Vagrant虚拟化工具的安装和使用教程
    本文介绍了Vagrant虚拟化工具的安装和使用教程。首先介绍了安装virtualBox和Vagrant的步骤。然后详细说明了Vagrant的安装和使用方法,包括如何检查安装是否成功。最后介绍了下载虚拟机镜像的步骤,以及Vagrant镜像网站的相关信息。 ... [详细]
  • 本文介绍了操作系统的定义和功能,包括操作系统的本质、用户界面以及系统调用的分类。同时还介绍了进程和线程的区别,包括进程和线程的定义和作用。 ... [详细]
  • 网卡工作原理及网络知识分享
    本文介绍了网卡的工作原理,包括CSMA/CD、ARP欺骗等网络知识。网卡是负责整台计算机的网络通信,没有它,计算机将成为信息孤岛。文章通过一个对话的形式,生动形象地讲述了网卡的工作原理,并介绍了集线器Hub时代的网络构成。对于想学习网络知识的读者来说,本文是一篇不错的参考资料。 ... [详细]
  • Android自定义控件绘图篇之Paint函数大汇总
    本文介绍了Android自定义控件绘图篇中的Paint函数大汇总,包括重置画笔、设置颜色、设置透明度、设置样式、设置宽度、设置抗锯齿等功能。通过学习这些函数,可以更好地掌握Paint的用法。 ... [详细]
author-avatar
Gvyi_262
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有