本文是此系列两部分中的第 1 部分,介绍了 Mobile 3D Graphics API (JSR 184) 的有关内容。作者将带领您进入 Java 移动设备的 3D 编程世界,并展示了处理光线、摄像机和材质的方法。
// Set default color for cube.
_cubeVertexData.setDefaultColor(COLOR_0);
// Create the triangles that define the cube; the indices point to
// vertices in VERTEX_POSITIONS.
_cubeTriangles = new TriangleStripArray(TRIANGLE_INDICES,
TRIANGLE_LENGTHS);
// Create a camera with perspective projection.
Camera camera = new Camera();
float aspect = (float) getWidth() / (float) getHeight();
camera.setPerspective(30.0f, aspect, 1.0f, 1000.0f);
Transform cameraTransform = new Transform();
cameraTransform.postTranslate(0.0f, 0.0f, 10.0f);
_graphics3d.setCamera(camera, cameraTransform);
// Rotate the cube so we can see three sides.
_cubeTransform = new Transform();
_cubeTransform.postRotate(20.0f, 1.0f, 0.0f, 0.0f);
_cubeTransform.postRotate(45.0f, 0.0f, 1.0f, 0.0f);
// Define an appearance object and set the polygon mode.
_cubeAppearance = new Appearance();
_polygonMode = new PolygonMode();
_isPerspectiveCorrectionEnabled = false;
_cubeAppearance.setPolygonMode(_polygonMode);
try
{
// Load image for texture and assign it to the appearance. The
// default values are: WRAP_REPEAT, FILTER_BASE_LEVEL/
// FILTER_NEAREST, and FUNC_MODULATE.
Image2D image2D = (Image2D) Loader.load(TEXTURE_FILE)[0];
_cubeTexture = new Texture2D(image2D);
_cubeTexture.setBlending(Texture2D.FUNC_DECAL);
// Index 0 is used because we have only one texture.
_cubeAppearance.setTexture(0, _cubeTexture);
}
catch (Exception e)
{
System.out.println("Error loading image " + TEXTURE_FILE);
e.printStackTrace();
}
}
在 init() 中,向 VertexBuffer 增加了定义为类的静态成员的纹理坐标。与在照明示例中的情况类似,我为立方体的每个面都使用了 4 个向量,以将各顶点映射到纹理的一个角。注意,我使用了比例 2.0 作为 _cubeVertexData.setTexCoords() 的第三个参数。这也就告诉 M3G 将所有纹理坐标都乘以此值。实际上,纹理仅使用了立方体面的四分之一。这样做的目的是展示 M3G 的加强和环绕特性。如果是加强,那么仅在左上角绘制;如果是环绕,那么纹理图案将填充满整个面。
纹理是用 Loader.load() 载入的,并指派给 Texture2D 对象。您还应使用 MIDP 的 Image.createImage(),但如果您想从 Java Archive(JAR)文件中读取纹理,那么 Loader 类是最快的方式。所得到的 Texture2D 对象随后被设置为立方体外观的纹理。
在进行纹理化处理时,您可能依然希望使用通过照明获得或直接指派给顶点的颜色。出于此方面的考虑,M3G 提供了各种混色功能,可通过调用 _cubeTexture.setBlending() 来设置。在 init() 中,我使用了 Texture2D.FUNC_DECAL,它将根据 α 值将纹理与基本顶点颜色相混合。图 10 中纹理图像的灰色位透明度为 60%。这里没有设置顶点颜色或使用照明,而是使用 _cubeVertexData.setDefaultColor() 为立方体设置了一个默认颜色,这也就意味着立方体中的所有三角形都将使用同样的颜色。通过混色,您也可以在各纹理上使用多重纹理,从而获得更丰富的效果。
我还内置了一个可选的 M3G 特性。如照明部分中所示,渲染的质量取决于您所使用的三角形数量 —— 顶点之间的距离越小,插值效果就越好。这对于纹理来说也是成立的。高质量对于纹理而言就意味着纹理在不失真的情况下映射。如图 10 所示的纹理是有缺陷的,因为其中包含直线,显然会发生失真的情况。MSG 提供了一种在处理能力方面代价低廉的方法来解决这一问题。可用 PolygonMode.setPerspectiveCorrectionEnable() 设置可选的透视修正标志,如清单 11 所示。
清单 11. 使用纹理,第 2 部分:交互式更改透视修正、环绕模式及混色
/**
* Checks whether perspective correction is supported.
*
* @return true if perspective correction is supported, false otherwise.
*/
protected boolean isPerspectiveCorrectionSupported()
{
Hashtable properties = Graphics3D.getProperties();
Boolean supportPerspectiveCorrection =
(Boolean) properties.get("supportPerspectiveCorrection");
return supportPerspectiveCorrection.booleanValue();
}
/**
* Handles key presses.
*
* @param keyCode key code.
*/
protected void keyPressed(int keyCode)
{
switch (getGameAction(keyCode))
{
case LEFT:
_cubeTransform.postRotate(-10.0f, 0.0f, 1.0f, 0.0f);
break;
case RIGHT:
_cubeTransform.postRotate(10.0f, 0.0f, 1.0f, 0.0f);
break;
case FIRE:
init();
break;
case GAME_A:
if (isPerspectiveCorrectionSupported())
{
_isPerspectiveCorrectionEnabled = !_isPerspectiveCorrectionEnabled;
_polygonMode.setPerspectiveCorrectionEnable(
_isPerspectiveCorrectionEnabled);
}
break;
case GAME_B:
if (_cubeTexture.getWrappingS() == Texture2D.WRAP_CLAMP)
{
_cubeTexture.setWrapping(Texture2D.WRAP_REPEAT,
Texture2D.WRAP_REPEAT);
}
else
{
_cubeTexture.setWrapping(Texture2D.WRAP_CLAMP,
Texture2D.WRAP_CLAMP);
}
break;
case GAME_C:
if (_cubeVertexData.getDefaultColor() == COLOR_0)
{
_cubeVertexData.setDefaultColor(COLOR_1);
}
else
{
_cubeVertexData.setDefaultColor(COLOR_0);
}
break;
// no default
}
repaint();
}
在示例中,isPerspectiveCorrectionSupported() 用于检查部件是否支持透视修正。如果支持,您可在 keyPressed() 中交互地切换标志的开关状态。这里还增加了一个更改纹理映射到立方体的方式(加强或重复)的选项以及一个更改混色的选项。对混色的更改示范了可以容易地将颜色与纹理相混合以获得更丰富的效果。在 TexturesSample.java 中可以看到完整的示例。
图 11 展示了使用不同选项的纹理映射效果。
图 11. 纹理:a) 无透视修正;b) 有透视修正;c) 加强而非平铺;d) 用绿色代替兰色进行混色
结束语 本文中介绍了大量基础知识,包括使用顶点数据创建立方体、使用摄像机为立方体照相、在立方体上应用光线和材质、利用纹理创建具有真实感的立方体的方法等详细信息。给出了许多立方体作为示例。
在论证概念时,立方体是一种极好的示例,但它并不是复杂的 3D 设计的里程碑。在介绍过程中,我从游戏的角度强调了 3D 图像。如果像示例那样通过手工组合顶点数据,那么设计一个复杂的游戏世界将成为一项令人望而却步的工作。您需要一种方法,通过建模工具来设计 3D 场景,并将数据导入程序。
导入模型后,必须再寻求一种组织数据的方法。如果使用 VertexBuffer 方法,您必须记住所有的转换以及对象之间的关系。比如说,上臂与下臂相连,而下臂应该与手相连。您必须对应地安置手臂与手。M3G 提供的一种场景图形 API —— 保留模式 —— 简化了此类任务,通过保留模式您可以为全部对象及其属性建模。在本系列的第 2 部分中将就此进行详细论述。