站内搜索: 请输入搜索关键词

当前页面: 开发资料首页Java 专题Java 3D显示3D物体

Java 3D显示3D物体

摘要: Java 3D是对Java的一个用来显示三维图形的扩展。用Java 3D编写的程序可以运行在很多不同类型的计算机或互联网上。
Java 3D是对Java的一个用来显示三维图形的扩展。用Java 3D编写的程序可以运行在很多不同类型的计算机或互联网上。Java 3D类库提供了比其它多数图形库更简单的接口,但仍有足够的能力制作不错的游戏和动画。Java 3D建立在DirectX和OpenGL这些已有的技术上,所以程序运行并不是你所想像的那样慢。并且Java 3D中也可以加入使用诸如TrueSpace和VRML这些3D模型包创建的物件。

  这份教程介绍了Java 3D。其中的例子将指导你通过基本的方法制作3D图像和动画。学习这份教程并不需要你拥有任何3D图形或Java 3D的知识,但如果你对Java语言有一定的了解将会很有帮助。由于大量的术语和数学知识,3D编程看起来相当复杂,但本教程会尽量保持简单。

安装和运行Java 3D

  使用Java 3D所必须的软件可以从Sun Microsystems网站http://java.sun.com免费获得。Sun经常会发布新的版本,所以你最好亲自到他们的站点看看而不是完全依赖这篇文档。你可能需要注册成为"Java Developer Connection"成员才能下载某些文件。

  写作本文时最新版的Java本身 (1.3) 在http://java.sun.com/j2se/而当前版本的Java 3D扩展(1.2.1)在http://java.sun.com/products/java-media/3D/。 Netscape和Internet Explorer都需要下载插件如果你想要使用最新版本的Java和Java 3D,插件可以在http://java.sun.com/products/plugin/找到。(译者:当前JDK版本为1.5,Java 3D稳定版本1.3,1.4开发中)

一旦你安装了Java和Java 3D,你可以使用以下命令编译程序:

java 文件名.java

这样运行程序:

java 文件名

  文件名应该总是和文件中所定义的类名相一致。某些版本的Java 3D会给出关于空图形配置的警告信息,可以忽略。

  以下程序说明了显示3D物体的基本步骤:

  1. 创建一个用来容纳你的场景的虚拟宇宙(Virtual Universe);
  2. 创建一个用来放置一组物体的数据结构;
  3. 向组中添加物体;
  4. 放置观察者(Viewer)使之面对物体;
  5. 将物体组添加至宇宙。

  看Hello3d()构造器,你会看见五行代码分别执行了这些步骤。这个程序显示了一个发光的立方体,观察者直接注视着其中的红色面,所以你实际看到的是一个黑色背景上的红色方块。

import com.sun.j3d.utils.universe.SimpleUniverse;

import com.sun.j3d.utils.geometry.ColorCube;

import com.sun.j3d.utils.geometry.Sphere;

import javax.media.j3d.BranchGroup; 

public class Hello3d {

 

public Hello3d()

{

    SimpleUniverse universe = new SimpleUniverse();

    BranchGroup group = new BranchGroup();

    group.addChild(new ColorCube(0.3));

    universe.getViewingPlatform().setNominalViewingTransform();

    universe.addBranchGraph(group);  

}

public static void main( String[] args ) {   

   new Hello3d();

}

} // end of class Hello3d

  程序开头的import语句使用了Java 3D的多个部分,所以编译运行这个程序是对你是否正确安装了Java 3D的一个很好的测试。 点亮这个世界

  好的,第一个程序是一个很好的开始,但它真是3D吗?如果你认为一个方形还不够3D的资格,那就需要给宇宙添加点灯光。灯光落到物体上产生的明暗可以帮助我们在3D空间中观察图形。

  下一个例子说明了怎样显示一个被红光照亮的球:

import com.sun.j3d.utils.geometry.*;

import com.sun.j3d.utils.universe.*;

import javax.media.j3d.*;

import javax.vecmath.*;

 

public class Ball {

  public Ball() {

    // 创建宇宙

    SimpleUniverse universe = new SimpleUniverse();

 

    // 创建容纳物体的结构

    BranchGroup group = new BranchGroup();

 

    // 创建一个球体并加入到物体组

    Sphere sphere = new Sphere(<?XML:NAMESPACE PREFIX = ST1 />0.5f);

    group.addChild(sphere);

 

    // 创建一个从原点延伸100米的红色光源

    Color3f light1Color = new Color3f(1.8f, 0.1f, 0.1f);

    BoundingSphere bounds =

       new BoundingSphere(new Point3d(0.0,0.0,0.0), 100.0);

    Vector3f light1Direction  = new Vector3f(4.0f, -7.0f, -12.0f);

    DirectionalLight light1

      = new DirectionalLight(light1Color, light1Direction);

    light1.setInfluencingBounds(bounds);

    group.addChild(light1);


 

     // 注视球体

    universe.getViewingPlatform().setNominalViewingTransform();

 

    // 添加物体组到宇宙中

    universe.addBranchGraph(group);

}

  public static void main(String[] args) { new Ball(); }

}

  我们创建的球体是白色的(缺省值), 由于红色的灯光它看起来是红色。因为是定向光源(DirectionalLight),我们必须指定光线照射的距离和方向。在这个例子里,光线由原点照射100米,方向为向右、向下由屏幕向内(由向量定义: 4.0向右,-7.0向下,-12.0向内)。

  你也可以建立一个产生无方向光的环境光源(AmbientLight),或者使用聚光灯(SpotLight)指向场景中某个特定部分。使用强有向光和弱些的环境光组合可以使你的场景有更自然的外观。Java 3D光源不会产生阴影。

  到目前为止,这些例子都在宇宙中间这样一个相同的地方建立物体。在Java 3D中,位置由x,y,z坐标描述。X轴坐标值沿向右方向增长,Y轴向上,Z轴由屏幕向外。图中z,y,z分别用球体,锥体和柱体表示。这被称为“右手”坐标系,因为右手的拇指和前两指可以用来表示三个方向。距离都使用米来表示。

  放置一个物体到场景中,你将从点(0,0,0)开始,然后移动物体到你想要的地方。移动物体被称为“变换(transformation)”,所以你要使用的类是:TransformGroup和Transform3D。(移动物体)你就要先把物体和Transform3D对象加入TransformGroup,再把TransformGroup放入场景中。

<table style="WIDTH: 643px; HEIGHT: 321px" cellSpacing=1 cellPadding=1 width=643 border=1> <tr> <td>步骤</td> <td>示例</td></tr> <tr> <td>1.创建一个变换(transform),一个
变换组(transformGroup)和物体</td> <td>

Transform = new Transform3D();
transformGroup tg  = new TransformGroup();
Cone cone = new Cone(0.5f, 0.5f);

</td></tr> <tr> <td>2.为物体指定位置</td> <td>Vector3f vector = new Vector3f(-.2f,.1f , -.4f);</td></tr> <tr> <td>3.设置Transform移动(变换)物体到指定位置</td> <td>Transform.setTranslation(vector);</td></tr> <tr> <td>4.把Transform加入TransformGroup</td> <td>tg.setTransform(transform);</td></tr> <tr> <td>5.把物体加入TransformGroup</td> <td>tg.addChild(cone);</td></tr></table>

  这个可能看起来有点复杂,但变换组(TransformGroup)允许你把物体聚集在一起并作为一个单位一起移动。举个例子,桌子由作为腿的柱体和作为表面的长方体组成。如果你把桌子的所有部件放入同一个TransformGroup,你就可以使用一个变换来移动整个桌子。

  Transform3D类可以做的远不止指定物体坐标这么多。它的功能包括setScale改变一个物体大小,和rotX、rotY、rotZ使物体绕某个坐标轴旋转(逆时针)。

这个例子在每个坐标轴上显示不同物体。

import com.sun.j3d.utils.geometry.*;

import com.sun.j3d.utils.universe.*;

import javax.media.j3d.*;

import javax.vecmath.*;

 

public class Position {

 

  public Position() {

    SimpleUniverse universe = new SimpleUniverse();

    BranchGroup group = new BranchGroup();

    // X轴由球体组成

    for (float x = <?XML:NAMESPACE PREFIX = ST1 />-1.0f; x <= 1.0f; x = x + 0.1f)

    {

        Sphere sphere = new Sphere(0.05f);

        TransformGroup tg = new TransformGroup();

        Transform3D transform = new Transform3D();

        Vector3f vector = new Vector3f( x, .0f, .0f);

        transform.setTranslation(vector);

        tg.setTransform(transform);

        tg.addChild(sphere);

        group.addChild(tg);

    }   

    // Y轴由锥体组成

    for (float y = -1.0f; y <= 1.0f; y = y + 0.1f)

    {

        TransformGroup tg = new TransformGroup();

        Transform3D transform = new Transform3D();

        Cone cone = new Cone(0.05f, 0.1f);

        Vector3f vector = new Vector3f(.0f, y, .0f);

        transform.setTranslation(vector);

        tg.setTransform(transform);

        tg.addChild(cone);

        group.addChild(tg);

    }     

    // Z轴由柱体组成

    for (float z = -1.0f; z <= 1.0f; z = z+ 0.1f)

    {       

        TransformGroup tg = new TransformGroup();

        Transform3D transform = new Transform3D();

        Cylinder cylinder = new Cylinder(0.05f, 0.1f);

        Vector3f vector = new Vector3f(.0f, .0f, z);

        transform.setTranslation(vector);

        tg.setTransform(transform);

        tg.addChild(cylinder);

        group.addChild(tg);

    }

    

    Color3f light1Color = new Color3f(.1f, 1.4f, .1f); // green light

    BoundingSphere bounds =

        new BoundingSphere(new Point3d(0.0,0.0,0.0), 100.0);

    Vector3f light1Direction  = new Vector3f(4.0f, -7.0f, -12.0f);

    DirectionalLight light1

      = new DirectionalLight(light1Color, light1Direction);

    light1.setInfluencingBounds(bounds);

    group.addChild(light1);

    universe.getViewingPlatform().setNominalViewingTransform();

 

    // 把物体组加入宇宙

    universe.addBranchGraph(group);

}

  public static void main(String[] args) {

    new Position();

  }

}

外观才是一切 

  有很多方法改变你的场景中物体的外观。你能改变它们的颜色,反光度等。你能使用二维图片填涂,或者给它们的表面加上粗糙的纹理。Apperance类包含了进行这些改变的功能。这一节将告诉你如何使用这些功能。

  指定外观的最简单的方式是只指定颜色和描影方法。这适于设置一个简单的单色物体,但想要是物体看起来有真实感,你需要指定一个物体如何在灯光下显示。你可以使用Material对象来做这个。

<table cellSpacing=1 cellPadding=1 width="100%" border=1> <tr> <td>步骤</td> <td>示例</td></tr> <tr> <td>1.建立物体</td> <td>Sphere sphere = new Sphere();</td></tr> <tr> <td>2.建立外观(Appearance)</td> <td>Appearance ap = new Appearance();</td></tr> <tr> <td>3.建立颜色对象(Color)</td> <td>Color3f col = new Color3f(0.0f, 0.0f, 1.0f);</td></tr> <tr> <td>4.创建色彩属性对象(ColorAttributes)</td> <td>

ColoringAttributes ca = new ColoringAttributes
(col, ColoringAttributes.NICEST);

</td></tr> <tr> <td>5.把属性加入外观对象</td> <td>ap.setColoringAttributes(ca);</td></tr> <tr> <td>6.设置物体外观</td> <td>sphere.setAppearance(ap);</td></tr></table>

材质

  Material对象有五个属性允许你指定物体如何显示。(其中)有四种颜色:环境(Ambient),放射(Emissive),漫射(Diffuse),和镜射(Specular)。第五个属性是发光度(Shininess),可以使用一个数字指定。每个颜色属性指定了物体在特定情况下如何发光。

  改变发光度不止影响物体的闪亮程度,还决定它的外观是物体某一区域的小亮光还是一个大范围内的微光。对大多数物体你可以给环境光和漫射光成分指定同一个颜色,给放射光指定黑色(大多数东西都不发光)。如果是一个闪亮的物体,你可以给镜面反射光使用一个稍亮的颜色。例如,一个红色撞球的材质可能会是这样:

// 撞球

//                            环境      放射   漫射   镜射   发光


 

 // Material mat = new Material(red,   black,   red,   white,   <?XML:NAMESPACE PREFIX = ST1 />70f);

  对于一个橡皮球,你可以使用一个黑色或红色的镜面光代替白色使球看起来不那么闪亮。减少发光度从并不会表现得像你所想的那样,白色的反射光会由聚集在一点变为由整个物体发出。

纹理

  材质改变整个图形的外观,但有时最闪亮的物体也会看起来暗淡。通过添加纹理你能够制作出像大理石纹或二维图片包裹物体这样更加有趣的效果。

  纹理装载器(TextureLoader)类允许你装载一个图片作为纹理。图片的尺寸必须是2的幂,例如128像素宽256像素高。当你载入图片后你也可以指定你想要如何使用图片。例如,RGB使用图片的颜色或LUMINANCE使图片显示为黑白。装载纹理后,你可以改变TextureAttributes对象使图片替换下面的物体或修改下面的颜色。 你也可以应用它为帖图或把图片和你选择的颜色混合。

  如果你在使用一个简单物体如球体,那你需要通过设置“原始标记”允许纹理化。这些可以在你创建物体的时候被设置为Primitive.GENERATE_NORMALS + Primitive.GENERATE_TEXTURE_COORDS 。这些开始听起来有些复杂了,这是一个例子。你可以试着改变纹理设置并对比结果。你可以建立三维纹理,使用形体代替平面图片。不幸的是,这些目前还不能很好的跨平台工作。

import com.sun.j3d.utils.geometry.*;

import com.sun.j3d.utils.universe.*;

import com.sun.j3d.utils.image.*;

import javax.media.j3d.*;

import javax.vecmath.*;

import java.awt.Container;

public class PictureBall {

  public PictureBall() {

    // 创建宇宙

    SimpleUniverse universe = new SimpleUniverse();

    // 创建容纳物体的结构

    BranchGroup group = new BranchGroup();

    // 建立颜色

    Color<?XML:NAMESPACE PREFIX = ST1 />3f black = new Color3f(0.0f, 0.0f, 0.0f);

    Color3f white = new Color3f(1.0f, 1.0f, 1.0f);

    Color3f red = new Color3f(0.7f, .15f, .15f);

    // 建立纹理帖图

    TextureLoader loader = new TextureLoader("K:\\3d\\Arizona.jpg",
                     "LUMINANCE", new Container());

    Texture texture = loader.getTexture();

    texture.setBoundaryModeS(Texture.WRAP);

    texture.setBoundaryModeT(Texture.WRAP);

    texture.setBoundaryColor( new Color4f( 0.0f, 1.0f, 0.0f, 0.0f ) );

    // 建立纹理属性

    //可以用REPLACE, BLEND 或 DECAL 代替 MODULATE

    TextureAttributes texAttr = new TextureAttributes();

    texAttr.setTextureMode(TextureAttributes.MODULATE);

    Appearance ap = new Appearance();

    ap.setTexture(texture);

    ap.setTextureAttributes(texAttr);   

    //建立材质

    ap.setMaterial(new Material(red, black, red, black, 1.0f));                     

    // 创建一个球来展示纹理

    int primflags = Primitive.GENERATE_NORMALS +
          Primitive.GENERATE_TEXTURE_COORDS;

    Sphere sphere = new Sphere(0.5f, primflags, ap);

    group.addChild(sphere);

    // 创建灯光

    Color3f light1Color = new Color3f(1f, 1f, 1f);

    BoundingSphere bounds =

        new BoundingSphere(new Point3d(0.0,0.0,0.0), 100.0);

    Vector3f light1Direction  = new Vector3f(4.0f, -7.0f, -12.0f);

    DirectionalLight light1

      = new DirectionalLight(light1Color, light1Direction);

    light1.setInfluencingBounds(bounds);

    group.addChild(light1);   

    AmbientLight ambientLight =

    new AmbientLight(new Color3f(.5f,.5f,.5f));

    ambientLight.setInfluencingBounds(bounds);

    group.addChild(ambientLight);    

    // 注视球体

    universe.getViewingPlatform().setNominalViewingTransform();

    // 把物体组加入宇宙

    universe.addBranchGraph(group);

  }

  public static void main(String[] args) {

    new PictureBall();

  }

}

特效

  看看Java 3D里的AppearanceTest例子,有更多你可以使用的效果。例如你可以使用线框显示物体,显示物体的一角等等。你甚至可以使物体透明,使用以下设置:

TransparencyAttributes t_attr =

  new TransparencyAttributes(TransparencyAttributes.BLENDED,0.5,

    TransparencyAttributes.BLEND_SRC_ALPHA,

    TransparencyAttributes.BLEND_ONE);

ap.setTransparencyAttributes( t_attr );    

现实中很多应用程序混合使用三维、二维元素。这一节会说明如何使Java 3D和程序其它部分结合。Canvas3D

  每个用来绘制三维图形的区域被称为Canvas3D。这是一个包含了你场景中物体的视图的矩形。你可以放置Canvas在你的框架(Frame)中,然后建立一个宇宙显示在Canvas中。

  下面的例子显示如何在一个上下都有标签(Label)的框架中创建Canvas。这个程序既可以作为Applet也可以作为应用程序运行。

import com.sun.j3d.utils.universe.SimpleUniverse;

import com.sun.j3d.utils.geometry.ColorCube;

import javax.media.j3d.BranchGroup;

import javax.media.j3d.Canvas3D;

import java.awt.GraphicsConfiguration;

import java.awt.BorderLayout;

import java.awt.Label;

import java.applet.Applet;

import com.sun.j3d.utils.applet.MainFrame;

public class CanvasDemo extends Applet {

 

    public CanvasDemo()  {

       

        setLayout(new BorderLayout());

        GraphicsConfiguration config =

            SimpleUniverse.getPreferredConfiguration();

        Canvas3D canvas = new Canvas3D(config); 

        add("North",new Label("This is the top"));

        add("Center", canvas);

        add("South",new Label("This is the bottom"));

        BranchGroup contents = new BranchGroup();

        contents.addChild(new ColorCube(0.3));

        SimpleUniverse universe = new SimpleUniverse(canvas);

        universe.getViewingPlatform().setNominalViewingTransform();

        universe.addBranchGraph(contents);  

    }

 

    public static void main( String[] args ) {   

        CanvasDemo demo = new CanvasDemo();

        new MainFrame(demo,400,400);

    }   

}

Java 3D和Swing

  Canvas3D利用了你机器的显示卡增强性能。不幸的是,这说明它不能很好地和Sun的swing用户界面组件混合。这些组件被称为“轻量”。轻量组件会被Canvas3D遮蔽即使想要把它们放在前方。

这是解决这个问题的几个方案:

动画和交互-跳动的小球

  想要制作动画你就要在动画的每一帧间移动物体。你可以使用计时器并且每次经过一个很短的时间移动物体。你也可以用其它方式修改物体,下面的例子缩放了小球使它看起来在每次碰撞时被压扁。为了和用户交互,你可以处理按键或按钮点击或其它组件。有一点需要注意的是你必须通过设置功能属性告诉Java 3D你要移动物体。否则一旦物体被绘制你将不能再移动它们。

objTrans = new TransformGroup();
objTrans.setCapability(TransformGroup.ALLOW_TRANSFORM_WRITE);

  下面的例子组合了这些技术。点击按钮使它启动,小球开始上下跳动,然后你可以按下a或s左右移动小球。

import java.applet.Applet;

import java.awt.*;

import java.awt.event.*;

import java.awt.event.WindowAdapter;

import com.sun.j3d.utils.applet.MainFrame;

import com.sun.j3d.utils.universe.*;

import javax.media.j3d.*;

import javax.vecmath.*;

import com.sun.j3d.utils.geometry.Sphere;

import javax.swing.Timer;

 

public class BouncingBall extends Applet implements ActionListener, KeyListener {

    private Button go = new Button("Go");

    private TransformGroup objTrans;

    private Transform3D trans = new Transform3D();

    private float height=<?XML:NAMESPACE PREFIX = ST1 />0.0f;

    private float sign = 1.0f; // going up or down

    private Timer timer;

    private float xloc=0.0f;

    public BranchGroup createSceneGraph() {

    // 创建分支图的根

    BranchGroup objRoot = new BranchGroup();

    objTrans = new TransformGroup();

    objTrans.setCapability(TransformGroup.ALLOW_TRANSFORM_WRITE);

    objRoot.addChild(objTrans);

 

    // 创建一个简单图形叶子节点,添加到场景图。

    Sphere sphere = new Sphere(0.25f);

    objTrans = new TransformGroup();

    objTrans.setCapability(TransformGroup.ALLOW_TRANSFORM_WRITE);

    Transform3D pos1 = new Transform3D();   

    pos1.setTranslation(new Vector3f(0.0f,0.0f,0.0f));

    objTrans.setTransform(pos1);

    objTrans.addChild(sphere);

    objRoot.addChild(objTrans);

        BoundingSphere bounds =

        new BoundingSphere(new Point3d(0.0,0.0,0.0), 100.0);

    Color3f light1Color = new Color3f(1.0f, 0.0f, 0.2f);

    Vector3f light1Direction  = new Vector3f(4.0f, -7.0f, -12.0f);

    DirectionalLight light1

      = new DirectionalLight(light1Color, light1Direction);

    light1.setInfluencingBounds(bounds);

    objRoot.addChild(light1);

 

     // 设置环境光

    Color3f ambientColor = new Color3f(1.0f, 1.0f, 1.0f);

    AmbientLight ambientLightNode = new AmbientLight(ambientColor);

    ambientLightNode.setInfluencingBounds(bounds);

    objRoot.addChild(ambientLightNode);

 

    return objRoot;

    }

 

    public BouncingBall() {

        setLayout(new BorderLayout());

        GraphicsConfiguration config =

            SimpleUniverse.getPreferredConfiguration();

        Canvas3D c = new Canvas3D(config);

        add("Center", c);

        c.addKeyListener(this);

        timer = new Timer(100,this);

        //timer.start();

        Panel p =new Panel();

        p.add(go);

        add("North",p);

        go.addActionListener(this);

        go.addKeyListener(this);

        // 创建一个简单场景附加到虚拟宇宙

        BranchGroup scene = createSceneGraph();

       

        SimpleUniverse u = new SimpleUniverse(c);

        u.getViewingPlatform().setNominalViewingTransform();

        u.addBranchGraph(scene);

    }

    public void keyPressed(KeyEvent e) {

        //按键被按下时调用    

        if (e.getKeyChar()==


↑返回目录
前一篇: 澄清Java(一)----接口与继承
后一篇: 我独自设计制作的战棋游戏,欢迎大家提建议