本文是此系列两部分中的第 1 部分,介绍了 Mobile 3D Graphics API (JSR 184) 的有关内容。作者将带领您进入 Java 移动设备的 3D 编程世界,并展示了处理光线、摄像机和材质的方法。 在移动设备上玩游戏是一项有趣的消遣。迄今为止,硬件性能已足以满足经典游戏概念的需求,这些游戏确实令人着迷,但图像非常简单。今天,人们开发出大量二维平面动作游戏,其图像更为丰富,弥补了俄罗斯方块和吃豆游戏的单调感。下一步就是迈进 3D 图像的世界。Sony PlayStation Portable 将移动设备能够实现的图像能力展现在世人面前。虽然普通的移动电话在技术上远不及这种特制的游戏机,但由此可以看出整个市场的发展方向。Mobile 3D Graphics API(简称为 M3G)是在 JSR 184(Java 规范请求,Java Specification Request)中定义的,JSR 184 是一项工业成就,用于为支持 Java 程序设计的移动设备提供标准 3D API。 M3G API 大致可分为两部分:快速模式和保留模式。在快速模式下,您渲染的是单独的 3D 对象;而在保留模式下,您需要定义并显示整个 3D 对象世界,包括其外观信息在内。可以将快速模式视为低级的 3D 功能实现方式,保留模式显示 3D 图像的方式更为抽象,令人感觉也更要舒服一些。本文将对快速模式 API 进行介绍。而本系列的第 2 部分将介绍保留模式的使用方法。 M3G 以外的技术 M3G 不是孤独的。HI Corporation 开发的 Mascot Capsule API 在日本国内非常流行,日本三大运营商均以不同形式选用了这项技术,在其他国家也广受欢迎。例如,Sony Ericsson 为手机增加了 M3G 和 HI Corporation 的特定 API。根据应用程序开发人员在 Sony Ericsson 网站上发布的报告,Mascot Capsule 是一种稳定且快速的 3D环境。 JSR 239 也就是 Java Bindings for OpenGL ES,它面向的设备与 M3G 相同。OpenGL ES 是人们熟知的 OpenGL 3D 库的子集,事实上已成为约束设备上本地 3D 实现的标准。JSR 239 定义了一个几乎与 OpenGL ES 的 C 接口相同的 Java API,使现有 OpenGL 内容的移植更为容易。到 2005 年 9 月为止,JSR 239 还依然处于早期的蓝图设计状态。关于它是否会给手机带来深刻的影响,我只能靠推测。尽管 OpenGL ES 与其 API 不兼容,但却对 M3G 的定义产生了一定影响:JSR 184 专家组确保了 MSG 在 OpenGL ES 之上的有效实现。如果您了解 OpenGL,那么就会在 M3G 中看到许多似曾相识的属性。 尽管还有其他可选技术,但 M3G 获得了所有主要电话制造商和运营商的支持。之前我提到过,游戏是最大的吸引力所在,但 M3G 是一种通用 API,您可以将其用于创建各种 3D 内容。未来的几年中,手机将广泛采用 3D API。 您的第一个 3D 对象 在第一个示例中,我们将创建一个如图 1 所示的立方体。 图 1. 示例立方体: a) 有顶点索引的正面图,b) 切割面的侧面视图(正面,侧面) 这个立方体存在于 M3G 定义的右手坐标系中。举起右手、伸出拇指、食指和中指,保持其中任一手指与其他两指均成直角,那么拇指就表示 x 轴、食指表示 y 轴,中指表示 z 轴。试着将拇指和食指摆成图 1a 中的样子,那么您的中指必然指向自己。我在这里使用了 8 个顶点(立方体的顶点)并使立方体的中心与坐标系的原点相重合。 从图 1 中可以看到,拍摄 3D 场景的摄像机朝向 z 轴的负轴方向,正对立方体。摄像机的位置和属性定义了随后将在屏幕上显示的东西。图 1b 展示了同一场景的侧面视图,这样您就可以更容易地看清摄像机究竟能看到 3D 世界中的哪些地方。限制因素之一就是观察角度,这与使用照相机的情况类似:长焦镜头的视野比广角镜头的观察角度要窄得多。因此观察角度决定了您的视野。与真实世界中的情况不同,3D 计算给我们增加了两个视图边界:近切割面和远切割面。观察角度和切割面共同定义了视域。视域中的一切都是可见的,而超出视域范围的一切均不可见。 在清单 1 中,您可以看到 VerticesSample 类,实现了上面提到的所有内容。 清单 1. 显示立方体的示例,第 1 部分:类成员 package m3gsamples1; import javax.microedition.lcdui.*; import javax.microedition.m3g.*; /** * Sample displaying a cube defined by eight vertices, which are connected * by triangles. * * @author Claus Hoefele */ public class VerticesSample extends Canvas implements Sample { /** The cube's vertex positions (x, y, z). */ private static final byte[] VERTEX_POSITIONS = { -1, -1, 1, 1, -1, 1, -1, 1, 1, 1, 1, 1, -1, -1, -1, 1, -1, -1, -1, 1, -1, 1, 1, -1 }; /** Indices that define how to connect the vertices to build * triangles. */ private static int[] TRIANGLE_INDICES = { 0, 1, 2, 3, 7, 1, 5, 4, 7, 6, 2, 4, 0, 1 }; /** The cube's vertex data. */ private VertexBuffer _cubeVertexData; /** The cube's triangles defined as triangle strips. */ private TriangleStripArray _cubeTriangles; /** Graphics singleton used for rendering. */ private Graphics3D _graphics3d; VerticesSample 继承自 Canvas,应该能够直接绘制到屏幕。并且还实现了 Sample,定义它的目的是协助组织本文中的其他源代码示例。VERTEX_POSITIONS 以同样的顺序定义了与图 1a 相同的 8 个顶点。例如,顶点 0 定义为坐标(-1, -1, 1)。由于我将立方体的中心点放在坐标系原点位置处,因此立方体的各边长应为 2 个单位。随后,摄像机的位置和视角可定义一个单位在屏幕上所占的像素数。 仅有顶点位置还不够,您还必须描述出想要建立的几何图形。只能像逐点描图法那样,将顶点用直线连接起来,最终得到所需图形。但 M3G 也带来了一个约束:必须用三角形建立几何图形。任何多边形都可定义为一组三角形的集合,因此三角形在 3D 实现中应用十分广泛。三角形是基本的绘图操作,在此基础上可建立更为抽象的操作。 不幸的是,如果只能使用三角形描述立方体,就需要 6 条边 * 2 个三角形 * 3 个顶点 = 36 个顶点。这么多重复的顶点显然浪费了大量内存。为节约内存,首先应将顶点与其三角形定义分隔开来。TRIANGLE_INDICES 使用 VERTEX_POSITIONS 数组索引定义几何图形,使顶点可重用。然后用三角形带取代三角形,从而减少索引数量。通过使用三角形带,新的三角形可重用最后两个索引。举例来说,三角形带(0,1,2,3)可转换为两个三角形(0,1,2)及(1,2,3)。图 1a 的各角均已标注相应索引数,如果您在图 1a 的 TRIANGLE_INDICES 中遵循这一规则处理,就会发现两个面之间意外地多出了一些三角形。这只是一种用于避免定义某些三角形带的模式。我曾用一个有 14 个立方体索引的三角形带处理过 8 个顶点的情况。 |