我的订单|我的收藏|我的商城|帮助中心|返回首页
虚拟现实新闻>VR>行业资讯>培训教程

Java3D实现浮动小球游戏

文章来源:第三维度 作者: 发布时间:2012年03月22日 点击数: 字号:

    来源:第三维度
    编译:朱先忠

     Java3D API是Sun定义的用于实现3D显示的接口。3D技术是底层的显示技术,Java3D提供了基于Java的上层接口。Java3D把OpenGL和DirectX这些底层技术包装在Java接口中。这种全新的设计使3D技术变得不再繁琐并且可以加入到J2SE、J2EE的整套架构,这些特性保证了Java3D技术强大的扩展性。

  本文将用一个Java3D游戏来介绍Checkers3D以及如何使用它创建一个场景,该场景包含深绿色和蓝色间隔的平铺表面,其轴向是沿着x和z轴,一个蓝色的背景和一个浮动的可以从两个不同的方向照亮的球体。用户(观察者)能通过移动鼠标移动该场景。

  图1中左边的快照显示出程序起始的视图;右边的显示出用户移动一点后的场景图。

Java3D实现浮动小球游戏    Java3D实现浮动小球游戏
图1.起始和后来的视图

  Checkers3D游戏展示了Java3D编程中许多共同之处及一些技巧。例如,3D场景的显示是使用Java3D Canvas3D类完成的-这个类必须与Java的Swing组件集成到一起。所有的Java3D应用程序要求一个场景图,而Checkers3D展示了怎样添加基本的形状、光源(环境光和有向光)和背景。该场景图用作文档的一种可视化形式,并且借助于Daniel Selman的Java3dTree包可以容易地生成其信息的一种文本版本。

  地板和球利用了Java3D的QuadArray、Text2D和球体几何体类。地板是在一个QuadArray中的一系列的四边形;而标签是利用Text2D对象沿着地板的主轴放置的。球体的实现将向用户展示怎样着色,点亮和放置一个3D形状。用户从一种视图来观察该3D世界。你将看到如何在初始化过程中确定球体的位置,以及如何在执行期间通过使用Java3D的OrbitBehavior类来移动该球体的。

  一、 Checkers3D的类图

  图2中的类图显示该Checkers3D应用程序的所有的公共和私有数据及方法。

Java3D实现浮动小球游戏
图2.Checkers3D的类图

  Checkers3D是该应用程序最顶层的JFrame。WrapCheckers3D是一拥有场景图的JPanel,该场景图可经由一Canvas3D对象来观看。 CheckerFloor创建地板子图(如瓷砖,轴,等等),这里相同颜色的瓷砖是用一个ColoredTiles对象描述的。

  二、 集成Java3D和Swing

  Checkers3D是一个JFrame-如果必要的话,可以把GUI控件,例如Swing文本域和按钮等放置到它上面。在本文的实例中,我创建了一个WrapCheckers3D(一个JPanel)的实例并把它放到一个BorderLayout的中央:

      c.setLayout( new BorderLayout( ) );
      WrapCheckers3D w3d = new WrapCheckers3D( );//3D画布的面板
      c.add(w3d, BorderLayout.CENTER);

  在该场景上的Canvas3D视图是在WrapCheckers3D中创建的:

      public WrapCheckers3D( ){
      setLayout( new BorderLayout( ) ); //另外的初始化代码

      GraphicsConfiguration config =SimpleUniverse.getPreferredConfiguration( );
      Canvas3D canvas3D = new Canvas3D(config);
      add("Center", canvas3D);  //另外的初始化代码
      }

  当使用Canvas3D时必须小心,因为它是一个轻量级的GUI元素(在一个OS生成的窗口之上的薄层)。重量级的组件无法容易地与轻量级的Swing控件相结合;这些控件大部分由Java生成。如果把Canvas3D对象嵌入到Jpanel中就可以避免这些问题;那么该面板就可以安全地与基于Swing构建的应用程序的其它部分集成到一起。

  提示 在j3d.org(http://www.j3d.org/tutorials/quick_fix/swing.html)上有关于把Canvas3D和Swing相结合的详细讨论。

  与前面的章节中的应用程序相比,这里没有更新/绘制动画循环。这是不必要的,因为Java3D包含它自己的机制来监视场景变化并且初始化着色。下面是该算法的伪码形式:

      while(true){
      处理用户输入;
      if (存在请求) break;
      执行行为;
      if (场景图发生变化)
      遍历场景图并着色;
      }

  行为是一些场景图结点。它们包含能够影响图中其它部分的代码,例如移动形状或改变灯光。它们可以用于监控图形,从而把细节信息传递到应用程序中的非3D部分。

  有关细节可能要比这个伪代码中所建议的更为复杂,例如,Java3D使用多线程来执行并行遍历和着色。然而,了解一下这个过程的大致思想将有助于你理解本文后面的代码。

    三、 创建场景图

  这个场景图是通过WrapCheckers3D的构造器创建的:

      public WrapCheckers3D( ){
       //初始化代码
       GraphicsConfiguration config =SimpleUniverse.getPreferredConfiguration( );
       Canvas3D canvas3D = new Canvas3D(config);
       add("Center", canvas3D);
       canvas3D.setFocusable(true); //聚焦画布
       canvas3D.requestFocus( );
       su = new SimpleUniverse(canvas3D);
       createSceneGraph( );
       initUserPosition( ); //设置用户的观察点
       orbitControls(canvas3D); //控制移动观察点
       su.addBranchGraph( sceneBG );
      }

  该Canvas3D对象被从getPreferredConfiguration()中得到的配置初始化;这个方法查询有关硬件的着色信息。一些老式的Java3D程序并不初始化一个GraphicsConfiguration对象,它们使用null作为到Canvas3D构造器的参数。这是一种不好的编程方法。

  聚焦于canvas3D会使得键盘输入事件被发送到场景图中的行为中。行为经常是通过键的按下与释放来激活的,但是它们也可以由定时器、帧变化和由Java3D内部生成的事件来触发。在Checkers3D中不存在任何行为,所以没有必要聚焦。我把这相应的几行代码保留下来,因为它们在我们后面将要讨论的几乎每种其它程序中都要使用。

  SimpleUniverse对象创建一标准视图分支图和场景图的VirtualUniverse及Locale结点。createSceneGraph()方法设置灯光、天空背景、地板及浮动的球体;initUserPosition()和orbitControls()负责处理观察者问题。在该方法的最后,结果BranchGroup被添加到该场景图上:

      private void createSceneGraph( ){
       sceneBG = new BranchGroup();
       bounds = new BoundingSphere(new Point3d(0,0,0), BOUNDSIZE);
       lightScene( ); //添加灯
       addBackground( ); //添加天空
       sceneBG.addChild( new CheckerFloor( ).getBG( ) );//添加地板
       floatingSphere( ); //添加浮动的球体
       sceneBG.compile( ); //修改场景
      } //createSceneGraph()结束

  各种方法把子图添加到sceneBG上以构建内容分支图。一旦该图被终结化并允许Java3D对它进行优化,sceneBG就被编译。这种优化包含生成图、重分组和组合结点。例如,一串包含不同平移的TransformGroup结点可能组合到单个的结点中。另一种可能是把所有的形状用相同的外观属性分组,这样它们可以更快地着色。

  边界是一个全局的BoundingSphere,用来指定对于灯光、背景和OrbitBehavior对象等环境结点的影响。边界球体放置在场景的中央并影响BOUNDSIZE个单位半径内的一切。边界盒和边界多面体在Java3D中都是可用的。

  在WrapCheckers3D( )执行最后的场景图显示在图3中。

  其中的"Floor Branch"结点是我的发明,用来隐藏一些细节直到最后。图3中没有显示的是场景图的视图分支部分。

  四、 点亮场景

  一个环境灯光和两个有向灯光被通过lightScene()方法添加到该场景上。一个环境灯光可以达到世界中的每个角落并同等程度地照亮一切。

      Color3f white = new Color3f(1.0f, 1.0f, 1.0f);
      //设置环境灯光
      AmbientLight ambientLightNode = new AmbientLight(white);
      ambientLightNode.setInfluencingBounds(bounds);
      sceneBG.addChild(ambientLightNode);

  这里环境光源是沿着边界创建的并且被添加到场景中。Color3f()函数中使用了红/绿/蓝色,范围为0.0f~1.0f。

  有向灯光模仿了具有一定距离的灯光的效果,从一个特定方向来照亮物体的表面。其与环境光的主要区别在于,它需要一个有关的矢量。

      Vector3f light1Direction=new Vector3f(-1.0f,-1.0f,-1.0f);
      //左面,下面,后面
      DirectionalLight light1 = new DirectionalLight(white, light1Direction);
      light1.setInfluencingBounds(bounds);
      sceneBG.addChild(light1);

Java3D实现浮动小球游戏
图3.Checkers3D中的部分场景图

  方向是连接点(0,0,0)和点(-1,-1,-1)的矢量;而灯光可以被想象成是方向与该矢量一致的多条平等线。

  点光源和斑点光源是其它形式的Java3D光。点光源位置在空间上,其方向朝各个方向发出。斑点光源是聚集的点光源,它指向一个特定方向。

  一个场景的背景可以用一个固定的颜色(如下)、一静态的图像或一个纹理贴图几何体例如一个球体来指定:

      Background back = new Background( );
      back.setApplicationBounds( bounds );
      back.setColor(0.17f, 0.65f, 0.92f); //天空颜色
      sceneBG.addChild( back );

    五、 浮动的球体

  球体是一个工具类,来自于Java3D的com.sun.j3d.utils.geometry包,这是一个Primitive类的子类,而Primitive类是一个Group结点-它有一个Shape3D子结点(见图3)。它的几何体在一个Java3D TriangleStripArray中相邻-它指定球体是一个相连接的三角形的数据。我不必调整这个几何体,但是该球体的外观和位置确实需要改变。

  Appearance结点是一个包含大量参考信息-包括色彩、线、点、多边形、着色、透明度和材质属性-的容器。

  ColouringAttributes修正一个形状的颜色并且不受场景灯光的影响。对于一个要求颜色和光相交互的形状来说,需要使用Material组件。要使光影响一个形状的颜色,必须满足三个条件:

  ·该形状的几何体必须包括法线。

  ·该形状的Appearance结点必须有一个Material组件。

  ·该Material组件必须用setLightingEnable()启动了灯光效果。

  工具球体类能自动地创建法线,因此第一个条件很容易满足。

  六、 给球体加上颜色

  Java3D Material组件控制一个形状当被不同的灯光点亮时展示什么颜色:

      Material mat = new Material(ambientColor, emissiveColor,
      diffuseColor, specularColor, shininess);

  ambientColor参数指定当被环境光点亮时形状的颜色:这使得对象具有一个统一的颜色。emissiveColor代表形状产生的颜色;这个参数经常被置为黑色(等于off)。diffuseColor是对象点亮时的颜色,其亮度依赖于光柱与形状的表面形成的角度的大小。

  提示: 散射和环境颜色常被设置为相同色,这与真实世界中的大多数物体被颜色点亮时的方式相匹配。

  specularColor参数与形状与它的发光区的反射程度相关。这个值与亮度参数结合在一起。

  提示: 镜面光的颜色常被设置为白色,这与真实世界中的由大多数物体生成的镜面光的颜色相匹配。

  在Checkers3D中,有两个有向光源-它们在浮动球体的顶部创建两个闪亮的光环(见图1)。地板瓦还没有点亮,因为它们的颜色是用形状的几何体来设置的(见后面)。

  在floatingSphere()中管理球体的外观的代码如下:

      Color3f black = new Color3f(0.0f, 0.0f, 0.0f);
      Color3f blue = new Color3f(0.3f, 0.3f, 0.8f);
      Color3f specular = new Color3f(0.9f, 0.9f, 0.9f); //近乎白色
      Material blueMat= new Material(blue, black, blue, specular, 25.0f);
      blueMat.setLightingEnable(true);
      Appearance blueApp = new Appearance( );
      blueApp.setMaterial(blueMat);

  七、 放置球体

  放置一个形状几乎总是一直通过把它的场景图结点放到一个TransformGroup(见图3中的球体Group)的下方来实现的。可以用一个TransformGroup来放置、旋转和缩放放在它下面的结点,这里变换是用Java3D Transform3D对象来定义的:

      Transform3D t3d = new Transform3D();
      t3d.set( new Vector3f(0,4,0)); //放在(0,4,0)
      TransformGroup tg = new TransformGroup(t3d);
      tg.addChild(new Sphere(2.0f, blueApp));
      //设置球体的半径和外观
      //并缺省地设置其法线
      sceneBG.addChild(tg);

  这个set()方法把球体的中心放在(0,4,0)并且重置任何以前的旋转或缩放。set()可以用来在重置其它变换的同时实现缩放和旋转。方法setTranslation(),setScale()和setRotation()仅影响给定的变换。

  不象其它一些3D绘图包,Java3D中的y轴在垂直方向上,而地面是由XZ平面定义的,如图4所示。

  在Checkers3D中球体的位置被设置为(0,4,0),这把它的中心放置到XZ平面上方4个单位的位置。

Java3D实现浮动小球游戏
图4.在Java3D中的轴向

  • 暂无资料
  • 暂无资料
  • 暂无资料
  • 暂无资料
  • 暂无资料