1、光线投射算法刍议孙年芳 湖南娄底职业技术学院 湖南娄底 417000摘要:本文采用光线投射算法来为移动设备创建一个 3D 引擎,这可大大降低算法的复杂度并减少计算量。关键词:光线投射;视野;投射列;焦距我们在通常的移动设备上创建一个 3D 引擎。虽然移动设备上的 3D 游戏差别很大,但是也能创建不错的 3D 场景,可以用来开发各种设备的 3D 游戏。为了实现这个目标,我们将使用一种很简单的技术光线投射(Ray Casting)算法 1 ,该项技术并不需要复杂的算法和高深的数学知识。1 光线投射这项技术从观察者的视点开始,向 3D 空间投射出很多光束,建立在光束是否碰到物体及离视点多远的位置碰到
2、物体这样的基础之上,可以绘制一个相应的点。如果对屏幕上每个象素都这样做,那么最终屏幕的效果就有了 3D 的感觉 2 。2 基本原理2.1 地图光线投射包括把光线投影到世界,还要检查是否发生碰撞,以及碰撞发生的具体位置。问题是,碰撞到的具体是什么呢?答案非常的简单,和 2D 图块引擎一样,需要一个数组来表示场景中的所有图块。当投射光线的时候,检测该数组来判断某个具体的位置是否有墙壁。 产生的地图数组的代码和过去看到的一样,同样是用 World 类来封装这一切。为了限定游戏的范围,可以把数组的边界设置为墙壁,以及在整个场景里加上一些柱子。可以把数组初始化代码放到 World 这个类的构造器里。2.
3、2 视野(FOV)光线投射引擎使用的视角就是玩家对着显示器看场景的视角,也就是所谓的第一人称视角。2D 游戏看到玩家操作的对象,比如一个飞船,用的是第三人称视角。其次,需要确定哪些光线需要投影,以及以什么样的角度。如果把人放到刚才设置好的图块地图的场景里(里面竖着很多柱子的那个) ,并向一定的方向观察,可以肯定仅能一定的区域,这主要取决于视线的方向(除非你的脑袋后面还长着一对眼睛) 。这个所能看到的范围,就叫视野(FOV,Field-Of-View) 。人的眼睛生理上有个 FOV,大概是 100 度。不过可以设置 FOV 为你希望的值,比如150 度,来感觉下它是如何影响视线范围的。如果设置
4、FOV 的值 360 度,那感觉一定很奇妙。 2.3 投射列 对于我们简化的引擎来说,如果光线碰到了某个图块,只要绘制点东西就可以了。因为处理的是 2D 的场景,对于每个竖直的屏幕列,仅仅需要一束光线,这是一个很重要的概念,需要好好理解。为屏幕上每个竖条投射一束光线,而不是屏幕上的每一个象素。每个象素点的宽度,都需要投射一条光线,这称为投射列 3 。 投射每一条光线的时候,都需要查看该光线是否碰到了什么,如果真的碰到了,可以绘制一个竖线,表示你碰撞到的墙。至于该墙的位置和大小,则与光线已经走过的距离有关系。为每一竖直的屏幕列投射一束光线,如果光线穿越了墙壁,则依据光线穿过的距离来绘制一个小条来
5、表示墙壁,这就是光线投射引擎的核心思想。另外还要确定每一个投射列对应的角度。对于一般的手机来说,屏幕是 128*128 的,也就是说,我们有 128 个投射列(casting column) 。2.4 焦距焦距(focal distance,也叫 focal length)指的是观察者和焦点之间的距离。计算它的目的,是因为可以用这个值计算需要缩放的物体的具体大小。根据 FOV 的值和最终效果图的宽度可以计算出焦距。注意这里并不关注最终效果的高度,因为我们处理的只是一个 2D的场景。对于 Nokia S40 系列的手机来说,手机屏幕的宽度都是 128 象素。由于 FOV 已经确定为 60 度,所
6、以依据这两个值,就可以计算出观察者和投影平面之间的距离。如果把 FOV分成两半,就可以利用三角形的基本理论计算出来我们需要的值来。因为我们仅知道这半个 FOV 所对的边的长度,也就是屏幕宽度的一半(即 64) ,还不知道这个角,需要利用TAN 函数来计算这个焦距。由三角函数的定义 TAN(夹角) =对边/ 邻边,可知:邻边=对边/TAN (夹角) 。这样简单地代入对边和夹角的值就可以求出邻边了。2.5 快速数学运算在真正编写引擎代码之前,先来看看如何加速数学运算(就是刚才的三角函数的运算) 。下面的代码其实就是 Word 类里的,采用了查找法来事先算出 0360 的三角函数值(正弦,余弦和正切
7、) 。/0359public static int lookupCosFP = new int360;public static int lookupSinFP = new int360;public static int lookupTanFP = new int360;for (int i = 0; i 360; i+)/ 创建一个预先算好三角函数值(正玄,余玄,正切) 0-359/为每一个被除数都加 0.01,这样做是为了避免以后出现除数为零的情况。lookupCosFPi = MathFP.cos(MathFP.add(MathFP.toFP(“0.01“),Actor.getRadi
8、ansFPFromAngle(i);lookupSinFPi = MathFP.sin(MathFP.add(MathFP.toFP(“0.01“),Actor.getRadiansFPFromAngle(i);lookupTanFPi = MathFP.tan(MathFP.add(MathFP.toFP(“0.01“),Actor.getRadiansFPFromAngle(i);参考文献:1Schroeder W J, Avila L S, Hoffman W. Visualizing with VTK: A tutorial J. Computer Graphics and Applications, IEEE, 2000, 20(5):20-27.2丁庆木, 张虹. 图像体绘制算法的分析与评价 J. 系统仿真学报, 2007, 19(4):897-900.3罗振东, 廖光裕. 计算机图示学原理和方法 M. 上海: 复旦大学出版社, 1993.作者简介:孙年芳 女(1974- )副教授 研究方向:计算机辅助设计通信地址:湖南娄底职业技术学院电子信息工程系邮编:417000 电话:13487993102 Email: