本文是此系列两部分中的第 1 部分,介绍了 Mobile 3D Graphics API (JSR 184) 的有关内容。作者将带领您进入 Java 移动设备的 3D 编程世界,并展示了处理光线、摄像机和材质的方法。
作者:builder.com.cn 2007年8月24日
关键字:
图 3 还指出了索引的定义顺序。例如,(0,1,2)按逆时针方向定义第 1 个三角形的顶点,而第二个三角形为(1,2,3)是按照顺时针方向定义的。这就叫做多边形环绕。可以利用它来确定哪个面在前,哪个面在后。从正前方查看立方体时,您总是会认为自己看的仅仅是外部,但如果盒子能打开呢?您一定也想到里边去看看。立方体的每一面都有正反两面。默认地,逆时针方向表示正面。
但这里还有一个小小的问题:如图 3 所示,三角形带中的环绕在每个后续三角形处都会发生变化。按惯例,由三角形带中的第一个三角形定义其环绕。当我将一个三角形带环绕在清单 1 实现的整个立方体上时,首先从一个逆时针方向环绕的三角形(0,1,2)开始。通过这样的方式,也就隐式地将立方体的外部定义为正面,而将内部作为背面。根据具体的需求,您可以要求 M3G 仅渲染正面、仅渲染背面或同时渲染两面。如果立方体有一个半掩的盖子,您同时可看到其正面和背面,此时同时渲染两面的操作非常有用。如果可能,您应该禁用那些看不到的面,这样可以提高渲染速度。将三角形排除在渲染操作之外的方法称为背景拣出。
清单 4 示范了使用顶点颜色的方法。
清单 4. 各顶点都有颜色的立方体,第 1 部分:初始化顶点颜色
/** The cube's vertex colors (R, G, B). */
private static final byte[] VERTEX_COLORS = {
0, (byte) 255, 0, 0, (byte) 255, (byte) 255,
(byte) 255, 0, 0, (byte) 255, 0, (byte) 255,
(byte) 255, (byte) 255, 0, (byte) 255, (byte) 255, (byte) 255,
0, 0, (byte) 128, 0, 0, (byte) 255,
};
/**
* Initializes the sample.
*/
protected void init()
{
// Get the singleton for 3D rendering.
_graphics3d = Graphics3D.getInstance();
// Create vertex data.
_cubeVertexData = new VertexBuffer();
VertexArray vertexPositions =
new VertexArray(VERTEX_POSITIONS.length/3, 3, 1);
vertexPositions.set(0, VERTEX_POSITIONS.length/3, VERTEX_POSITIONS);
_cubeVertexData.setPositions(vertexPositions, 1.0f, null);
VertexArray vertexColors =
new VertexArray(VERTEX_COLORS.length/3, 3, 1);
vertexColors.set(0, VERTEX_COLORS.length/3, VERTEX_COLORS);
_cubeVertexData.setColors(vertexColors);
// Create the triangles that define the cube; the indices point to
// vertices in VERTEX_POSITIONS.
_cubeTriangles = new TriangleStripArray(TRIANGLE_INDICES,
new int[] {TRIANGLE_INDICES.length});
// Define an appearance object and set the polygon mode. The
// default values are: SHADE_SMOOTH, CULL_BACK, and WINDING_CCW.
_cubeAppearance = new Appearance();
_polygonMode = new PolygonMode();
_cubeAppearance.setPolygonMode(_polygonMode);
// 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);
}
/**
* Renders the sample on the screen.
*
* @param graphics the graphics object to draw on.
*/
protected void paint(Graphics graphics)
{
_graphics3d.bindTarget(graphics);
_graphics3d.clear(null);
_graphics3d.render(_cubeVertexData, _cubeTriangles,
_cubeAppearance, null);
_graphics3d.releaseTarget();
drawMenu(graphics);
}
在类成员部分的 VERTEX_COLORS 中定义了各顶点颜色。将颜色放在 init()中全新的 VertexArray 内,并通过调用 setColors() 将其指派给 VertexBuffer。在这段代码中还初始化了一个名为 _cubeAppearance 的 Appearance 对象,_graphics3d.render() 使用该对象来更改立方体外观。PolygonMode 是 _cubeAppearance 的一部分,其中包含更改多边形级属性(包括显示哪些面)的方法。为交互地更改这些属性,我还在代码中增加了一个 keyPressed() 方法,如清单 5 所示。
清单 5. 各顶点都有颜色的立方体,第 2 部分:处理按键事件
/**
* Handles key presses.
*
* @param keyCode key code.
*/
protected void keyPressed(int keyCode)
{
switch (getGameAction(keyCode))
{
case FIRE:
init();
break;
case GAME_A:
if (_polygonMode.getShading() == PolygonMode.SHADE_FLAT)
{
_polygonMode.setShading(PolygonMode.SHADE_SMOOTH);
}
else
{
_polygonMode.setShading(PolygonMode.SHADE_FLAT);
}
break;
case GAME_B:
if (_polygonMode.getCulling() == PolygonMode.CULL_BACK)
{
_polygonMode.setCulling(PolygonMode.CULL_FRONT);
}
else
{
_polygonMode.setCulling(PolygonMode.CULL_BACK);
}
break;
case GAME_C:
if (_polygonMode.getWinding() == PolygonMode.WINDING_CCW)
{
_polygonMode.setWinding(PolygonMode.WINDING_CW);
}
else
{
_polygonMode.setWinding(PolygonMode.WINDING_CCW);
}
break;
// no default
}
repaint();
}