Graphics 版 (精华区)

发信人: seaman (翩翩少年), 信区: Graphics
标  题: JAVA3D学习系列(15)--动画的生成(上)
发信站: 哈工大紫丁香 (Tue Sep 28 19:16:09 1999), 转信

          JAVA3D学习系列(15)--动画的生成(上)


     汕头大学机电系  张杰(jzhang@mailserv.stu.edu.cn)



    1997年底正式成为国际标准的VRML2.0是我们编写简单
三维应用程序的一个非常合适的计算机语言,利用它,我们可以
非常方便地编写三维动画、交互式的三维游戏,当然,只能
是一些较为简单的三维动画及交互式三维游戏。而利用JAVA3D
则可以编写出较为复杂的三维动画及交互式三维游戏。
    前面我们介绍了JAVA3D的形体生成等方面的知识,JAVA3D
三维应用程序最吸引人之处是它可以用来制作三维动画、三维
游戏等。为了掌握JAVA3D三维动画、三维游戏的编程,我们首先
要掌握三维形体在空间的运动控制。
    VRML语言有6个内插器节点和7个传感器节点,同样地,在
JAVA3D中,也有和VRML内插器节点类似的Interpolator对象,它
们和另一个Alpha类结合在一起,可以编写出各种类型的三维动画
程序。
    下面我们就介绍这些对象及其应用方法。


一. Alpha对象
     Alpha类的常数有:
       public static final int INCREASING_ENABLE
       public static final int DECREASING_ENABLE
     它的构造函数有:
       public Alpha()
此时,它的缺省值为:
       loopCount                     -1 
       mode                          INCREASING_ENABLE 
       triggerTime                   0 
       phaseDelayDuration            0 
       increasingAlphaDuration       1000 
       increasingAlphaRampDuration   0 
       alphaAtOneDuration            0 
       decreasingAlphaDuration       0 
       decreasingAlphaRampDuration   0 
       alphaAtZeroDuration           0 

     Alpha的其它构造函数有:
       public Alpha(int loopCount, 
                    long increasingAlphaDuration)

       public Alpha(int loopCount, 
                    long triggerTime, 
                    long  phaseDelayDuration, 
                    long increasingAlphaDuration, 
                    long  increasingAlphaRampDuration,
                    long  alphaAtOneDuration)
 
       public Alpha(int loopCount,
                    int mode, 
                    long triggerTime, 
                    long  phaseDelayDuration, 
                    long increasingAlphaDuration, 
                    long  increasingAlphaRampDuration,
                    long  alphaAtOneDuration, 
                    long  decreasingAlphaDuration, 
                    long  decreasingAlphaRampDuration, 
                    long  alphaAtZeroDuration)

    Alpha的方法有:
      public float value()
      public float value(long atTime)

      public void setStartTime(long startTime)
      public long getStartTime()

      public void setLoopCount(int loopCount)
      public int getLoopCount()

      public void setMode(int mode)
      public int getMode()
     
      public void setTriggerTime(long triggerTime)
      public long getTriggerTime()

      public void setPhaseDelayDuration(long phaseDelayDuration)
      public long getPhaseDelayDuration()

      public void setIncreasingAlphaDuration(long 
            increasingAlphaDuration)
      public long getIncreasingAlphaDuration()

      public void setIncreasingAlphaRampDuration(long 
            increasingAlphaRampDuration)
      public long getIncreasingAlphaRampDuration()

      public void setAlphaAtOneDuration(long alphaAtOneDuration)
      public long getAlphaAtOneDuration()

      public void setDecreasingAlphaDuration(long 
            decreasingAlphaDuration)
      public long getDecreasingAlphaDuration()

      public void setDecreasingAlphaRampDuration(long 
            decreasingAlphaRampDuration)
      public long getDecreasingAlphaRampDuration()

      public void setAlphaAtZeroDuration(long alphaAtZeroDuration)
      public long getAlphaAtZeroDuration()

      public boolean finished()

    从VRML的各个内插器节点的定义来看,我们可以看到,每一个
内插器节点都有一个key字段和一个keyValue字段。key字段定义
一个数组,数值从0到1。
    所有的内插器节点在使用时都需要与TimeSensor(时间传感器)
配合使用。TimeSensor把给定的时间周期归一化,虽然时间的周期
cycleInterval 有给定的秒数,但计算机内部将其处理成从0.0到
1.0,即起始时间为0.0,终止时间为1.0,假如一个时间周期为
20(单位均为秒),则第6秒的归一化结果是0.3,第10秒的归一化
结果是0.5,第20秒的归一化结果是1.0。
    JAVA3D里的Alpha类和VRML的key字段的功能类似,它也是用于
输出归一化的时间,不过它能够处理更加复杂的时间安排问题。
    Alpha可以输出从0到1之间的数值给特定的内插对象,当Alpha
输出的数值为0时,对应的特定内插对象的值为最小;当Alpha输出
的数值为1时,对应的特定内插对象的值为最大;当Alpha输出的数
值为0到1之间的数值时,对应的特定内插对象生成和Alpha成相同
比例的数值。假设某一时刻Alpha输出的数值为0.2,则对应的特定
内插对象的当前值为最小值加上最大最小值之差乘以0.2。

           (当前值-最小值)/(最大值-最小值)=0.2

    JAVA3D里的各种Interpolator对象和VRML的内插节点的作用
比较类似,可用来旋转形体、移动形体的坐标、变化形体的颜色等。


    假设我们要让一个形体在规定的时间内按照指定的方式运动,
我们首先要给出时间段的大小,还要指出时间是否要循环。这些内容
都是由Alpha类来完成的。

    我们在前面已多次用到Alpha类,基本上我们用它及一个旋转内
插器RotationInterpolator来使形体绕着其所在的局部坐标系不停
地旋转。下面是我们眼熟的语句,语句的作用是让形体以4秒为一周
期,不停地绕着某一个轴旋转(Y轴):
      Alpha rotationAlpha = new Alpha(-1, 
                                      Alpha.INCREASING_ENABLE,
                                      0, 0,
                                      4000, 0, 0,
                                      0, 0, 0);
      RotationInterpolator rotator =
            new RotationInterpolator(rotationAlpha, obj, temp,
                                  0.0f, (float) Math.PI*2.0f); 
    从中我们可以看到,Alpha构造函数中:

    loopCount表示循环的次数,例如如果我们将前面介绍的Ex4_03.java
的loopCount设为10,则小丑会旋转10周,然后停止。不过在一些运行速度
慢的计算机上会出现少几次的情况,如作者本人使用的一台机器是
Pentium 586/100的计算机,当loopCount为10时,只看到小丑旋转了7次,
因为程序从开始运行到出现画面,需要一段时间。loopCount为-1时表示
无限循环。我们可以利用下面两个方法设置或获得loopCount:
        public void setLoopCount(int loopCount)
        public int getLoopCount()

    mode表示Alpha每一周期的运行方式,一共有三种:INCREASING_ENABLE、
DECREASING_ENABLE、INCREASING_ENABLE|DECREASING_ENABLE。第一种
方式表示Alpha的数值从0到1,相对应的内插对象的数值从最小到
最大;第二种方式表示Alpah的数值从1到0,相对应的内插对象的数值
从最大到最小;第三种方式表示Alpah的数值从0到1,然后从1到0,
相对应的内插对象的数值从最小到最大,然后又从最大到最小。
我们前面用到的Alpha,它每一个周期的数值,都是从0到1,对应的内插
对象的数值,在每一个循环周期里,都是从最小到最大。我们可以
利用下面两个方法,设置或获得mode:
        public void setMode(int Mode)
        public int getMode()

    triggerTime表示Alpha起作用的时间,它可由外部来触发,它和后面
的7个参数的单位均为毫秒。

    triggerTime参数后面还有7个类型为long的数,我们可以设置
这些数值,来达到我们的各种编程目的。

    前面我们介绍了VRML中各内插器节点key字段的作用,在VRML中,
key的值只能从0到1。而在JAVA3D中,Alpha输出的值不仅可以从0到1,
还可以从1到0,甚至可以先从0到1,再从1到0。
    VRML程序中各内插器节点的keyValue字段则根据key给定的数值,
输出相应的数值,计算机在keyValue各个数之间进行线性插值。而
JAVA3D的Alpha则不仅可以对各内插对象的数值进行线性插值,而且
可以利用increasingAlphaRampDuration、decreasingAlphaRampDuration
这两个参数进行非线性插值,插值曲线可以从SUN公司所提供的JAVA3D的
API中看出,这时又分两种情况:Ramp大于1/2、Ramp小于1/2。
    接下来我们将通过多个JAVA3D动画程序的编制,介绍Alpha的各个参数。
JAVA3D动画编程时经常使用的10个内插器对象,它们分别是:
        PositionInterpolator
        RotationInterpolator
        ColorInterpolator
        ScaleInterpolator
        SwitchValueInterpolator
        TransparencyInterpolator
        PositionPathInterpolator
        RotPosPathInterpolator
        RotPosScalePathInterpolator
        RotationPathInterpolator
    上面这10个对象都是Interpolator对象的子类,为此,我们
先来看一下Interpolator对象。


二. Interpolator对象
    Interpolator对象有一个常量:
        protected WakeupCriterion defaultWakeupCriterion

    Interpolator对象的构造函数有:
        public Interpolator()
        public Interpolator(Alpha alpha)
 
    Interpolator有下面一些方法:
        public void setAlpha(Alpha alpha)
        public Alpha getAlpha()
        public void setEnable(boolean state)
        public boolean getEnable()
        public void initialize()

三. PositionInterpolator对象
    为了编写一个彩色立方体在空间的运动,我们用到了JAVA3D所提供
的PositionInterpolator对象。
    PositionInterpolator的构造函数有两个:
    public PositionInterpolator(Alpha alpha, 
                                TransformGroup target)
    public PositionInterpolator(Alpha alpha, 
                                TransformGroup target, 
                                Transform3D axisOfTranslation, 
                                float startPosition, 
                                float  endPosition)

    PositionInterpolator的方法有:
        public void setStartPosition(float position)
        public float getStartPosition()

        public void setEndPosition(float position)
        public float getEndPosition()

        public void setTarget(TransformGroup target)
        public TransformGroup getTarget()

        public void setAxisOfTranslation(Transform3D axis)
        public Transform3D getAxisOfTranslation()

        public void processStimulus(Enumeration criteria)

    利用这个对象,我们可以在给定的时间内,使某一个局部坐标系
在两个点之间按照Alpha提供的方式移动位置,它的作用类似于VRML
的PositionInterpolator节点,两者的名字完全一样。

    我们先编写一个VRML程序,使一个形体沿着X轴线左右移动,其实,
形体并没有移动,移动的只是形体所在的局部坐标系,源程序Pos.wrl
如下,注意,它的观察点坐标位于(0 ,0 , 10),左右移动一周的时间
为20秒,左右移动的范围是(-4 , 0 , 0 )到( 4 , 0 , 0):
#VRML V2.0 utf8
DEF T Transform{
  children Shape{
    appearance Appearance{material Material{diffuseColor 1 0 0}}
    geometry Box{}
  }
}

Background{skyColor 1 1 1}

DEF TS TimeSensor{
   loop TRUE
   cycleInterval 20}

DEF PI PositionInterpolator{
  key [ 0 .5 1]
  keyValue[-4 0 0, 4 0 0 ,-4 0 0]
  }

ROUTE TS.fraction TO PI.fraction
ROUTE PI.value TO T.translation

#end of Pos.wrl
--------------------------------------------------------
    我们再利用PositionInterpolator对象编写类似的JAVA3D应用程序,
同样,Pos.java的观察点坐标也位于(0 , 0 , 10 ):
//Pos.java

import com.sun.j3d.utils.geometry.ColorCube;
import java.applet.Applet;
import java.awt.BorderLayout;
import com.sun.j3d.utils.applet.MainFrame;
import com.sun.j3d.utils.universe.*;
import javax.media.j3d.*;
import javax.vecmath.*;

public class Pos extends Applet {
    private BranchGroup createSceneGraph( ) {
        BranchGroup objRoot = new BranchGroup();
        BoundingSphere bound=new BoundingSphere(
           new Point3d(0.0,0.0,0.0),50.);

        Transform3D tr=new Transform3D();
        TransformGroup group=new TransformGroup(tr);
        group.setCapability(TransformGroup.ALLOW_TRANSFORM_WRITE);
        group.addChild(new ColorCube());

        Alpha xtranAlpha = new Alpha(-1,
             Alpha.INCREASING_ENABLE|Alpha.DECREASING_ENABLE,
             0, 0,10000, 0, 0,10000, 0, 0);
        PositionInterpolator tran =
            new PositionInterpolator(xtranAlpha, group, tr,           
                                     -4.f,4.f);
        tran.setSchedulingBounds(bound);
        group.addChild(tran);

        Color3f bgColor = new Color3f(1.0f, 1.0f, 1.0f);
        Background bg = new Background(bgColor);
        bg.setApplicationBounds(bound);
        objRoot.addChild(bg);

        objRoot.addChild(group);
        return objRoot;
    }

    public Pos() {
        setLayout(new BorderLayout());
        Canvas3D c = new Canvas3D(null);
        add("Center", c);
          ViewPlatform        viewPlatform;
          Viewer viewer = new Viewer(c);
          Vector3d viewpoint = new Vector3d(0.0, 0.0, 10.0);   
                                  //初始观察点位置
       Transform3D t = new Transform3D();
       t.set(viewpoint);
          ViewingPlatform v = new ViewingPlatform( );
          v.getViewPlatformTransform().setTransform(t);

        BranchGroup scene = createSceneGraph();
       SimpleUniverse u = new SimpleUniverse( v, viewer);
           
       u.getViewingPlatform();
       u.addBranchGraph(scene);
    }

    public static void main(String[] args) {
        new MainFrame(new Pos(), 400,400);
    }
}

//end of Pos.java

    运行Pos.java后我们发现,JAVA3D、VRML程序运行效果基本一致。
    
1. PositionInterpolator构造函书里的参数
    我们来看一下Pos.java里定义的PositionInterpolator对象实例
tran里的各个参数。
    xtranAlpha表示局部坐标系的变化方式是由xtranAlpha这个Alpha
定义的。
    group给出了受影响的局部坐标系的名称。
    tr给出了PositionInterpolator运动变化的方向,缺省时为沿X轴。
    -4.f,4.f给出了局部坐标系移动的范围,表示坐标系的原点在
(-4 , 0 , 0 ) 和 ( 4 , 0 , 0 )之间移动。
    假如我们给出的是PositionInterpolator的第一个构造函数,则局部
坐标系沿着Z轴,在(-1, 0, 0)和(1, 0, 0)范围内按照Alpha给出的方式
移动。
    但在1.1版本的JAVA3D API Specification里,写的是沿Z轴移动,而
程序运行结果为沿着X轴,不知为何?

2. xtranAlpha里各参数的含义
    -1表示无限循环
    Alpha.INCREASING_ENABLE|Alpha.DECREASING_ENABLE表示在一个
周期里面,局部坐标系统的位置从(-4, 0 , 0)移到(4 , 0 , 0),再从
(4 , 0 , 0)回到(-4 , 0 , 0),即从最小值到最大值,再从最大值到
最小值。
    头一个10000表示局部坐标系从最小值移到最大值所用的时间,为
10秒。
    第二个10000表示局部坐标系从最大值移到最小值所用的时间,也
为10秒,因而一个周期的时间为20秒。

3. 坐标轴的改变。
    当然,我们可以改变局部坐标系移动的坐标轴,例如,当我们想
让局部坐标系沿着Y轴移动时,我们就可以在
        Transform3D tr=new Transform3D();
    后面加上一句:
        tr.rotZ(Math.PI/2) ;
    如果我们加的是: 
        tr.rotY(Math.PI/2) ;
    则形体沿着Z轴移动。
    后面我们在详细介绍Transform3D时还将详细介绍坐标系的变换
方法。

四. Alpha构造函数后7个参数的含义
    我们再次给出Alpha类的构造函数:
        public Alpha(int loopCount, int mode, long triggerTime, 
            long  phaseDelayDuration, 
            long increasingAlphaDuration, 
            long  increasingAlphaRampDuration, 
            long  alphaAtOneDuration, 
            long  decreasingAlphaDuration, 
            long  decreasingAlphaRampDuration, 
            long  alphaAtZeroDuration)
    Alpha构造函数里的头三个参数我们已经介绍过了,我们在这里介绍
它的后7个参数,它们用于定义各内插对象的周期变化方式。
    Alpha将每一个周期分外5个时段:
        起始延迟时段
        上升时段
        高位时段
        下降时段
        低位时段

                   ------
                /         \
               /           \
              /             \
       -------               -------


    每个周期的时间长度为这5个时段时间长度之和。
    每一个Alpha都必须给出这5个时段,当然,其中的任何数都可以为0,
因而我们可以获得各种各样的Alpha类型。下面我们给出部分类型:

1. 只有上升时段的类型
       /   /   /
      /   /   /
     /   /   /
    /   /   /
    这种类型的Alpha输出的数值从最小升到最大,然后又从最小升到最大。
    我们将前面的Pos.java改成这种类型时,生成的Alpha应为:
      Alpha xtranAlpha = new Alpha(-1,
                          Alpha.INCREASING_ENABLE,
                          0, 0,20000, 0, 0,0, 0, 0);
    它使形体从左移到右,再将形体跳回到左边。

2. 只有下降时段的类型
   \   \   \
    \   \   \
     \   \   \
      \   \   \
    这种类型的Alpha输出的数值从最大降到最小,然后又从最大降到最小。
    我们将前面的Pos.java改成这种类型时,生成的Alpha应为:
      Alpha xtranAlpha = new Alpha(-1,
                          Alpha.DECREASING_ENABLE,
                          0, 0,0, 0, 0,20000, 0, 0);
    它使形体从右移到左,再将形体跳回到右边。

3. 上升加高位时段的类型
     ---    ---
    /      /
   /      /
  /      /
 /      /
   这种类型的Alpha输出的数值从最小升到最大,暂停一段时间,
然后又从最小升到最大。
    我们将前面的Pos.java改成这种类型时,生成的Alpha应为:
      Alpha xtranAlpha = new Alpha(-1,
                          Alpha.INCREASING_ENABLE,
                          0, 0,10000, 0, 10000, 0,0, 0);

    它使形体从左移到右,暂停一段时间,再将形体跳回到左边。

4. 下降加低位时段
\       \
 \       \
  \       \
   ----    ----
    这种类型的Alpha输出的数值从最大降到最小,暂停一段时间,
然后又从最大降到最小。
    我们将前面的Pos.java改成这种类型时,生成的Alpha应为:
      Alpha xtranAlpha = new Alpha(-1,
                          Alpha.DECREASING_ENABLE,
                          0, 0,0, 0, 0, 10000,0, 10000);

    它使形体从右移到左,再将形体跳回到右边。

5. 暂停、上升加高位时段的类型
         ----      
        /           
       /           
      /           
     /           
----         
   这种类型的Alpha输出的数值一开始先暂停,然后从最小升到最
大,再暂停一段时间。不过要注意的是,这种类型不能用于
循环方式。
    我们将前面的Pos.java改成这种类型时,生成的Alpha应为:
      Alpha xtranAlpha = new Alpha(1,
                          Alpha.INCREASING_ENABLE,
                          0, 5000,10000, 0, 5000, 0,0, 0);

    它使形体暂停一段时间,从左移到右,再暂停一段时间。

6. 暂停、下降加暂停时段的类型
----
    \
     \
      \
       ------
   这种类型的Alpha输出的数值一开始先暂停,然后从最大升到最
小,再暂停一段时间。不过要注意的是,这种类型也不能用于
循环方式。
    我们将前面的Pos.java改成这种类型时,生成的Alpha应为:
      Alpha xtranAlpha = new Alpha(1,
                          Alpha.DECREASING_ENABLE,
                          0, 0,0, 0, 5000, 10000,0, 5000);
    它使形体暂停一段时间,从右移到左,再暂停一段时间。

7. 5种时段均有的类型
           -----
         /       \
        /         \
       /           \
  -----             -----
    此种类型的Alpha输出的数值一开始先暂停,然后从最小升到
最大,再暂停一段时间,然后下降到最小,最后再暂停一段时间。
不过要注意的是,这种类型也不能用于循环方式。
   我们将前面的Pos.java改成这种类型时,生成的Alpha应为:
     Alpha xtranAlpha = new Alpha(1,
        Alpha.DECREASING_ENABLE|Alpha.INCREASING_ENABLE,
        0, 3000,5000, 0, 3000, 5000,0, 3000);

8. 上升、暂停、下降、暂停类型
        ----            ----
       /    \          /    \
      /      \        /      \
     /        \------/        \-----
    本类型和第7种很相似,但没有上升前的暂停阶段,因而可以
用于循环方式。
    我们将前面的Pos.java改成这种类型时,生成的Alpha应为:
      Alpha xtranAlpha = new Alpha(-1,
          Alpha.DECREASING_ENABLE|Alpha.INCREASING_ENABLE,
          0, 0,5000, 0, 5000, 5000,0, 5000);

9. Alpha的加速
    前面介绍的8种类型,可以用于各种场合,如开关门等。但它们
均有一个问题,即形体从静止到运动的变化是跳跃性的,变化时的
加速度为无穷大。为此,JAVA3D还提供了两个参数,解决这个问题,
这就是Alpha里的increasingAlphaRampDuration和
decreasingAlphaRampDuration,它们有两种应用方式:一是设置为
大于或等于0.5,一是设置为小于0.5。
    当它们的值为第一种情况时,计算机自动将其转成0.5,使Alpha
产生的加速度在头一半时间是均匀加速的,在后一半时间里是均匀
减速的,因而在头一半时间里,加速度是正数,在后一半时间里,
加速度是相同的负数(请参考JAVA3D的API说明)。
    第一种情况在某一时段的速度图:
            /\
           /  \
          /    \
         /      \
    下面我们给出这样一个Alpha的例子:
     Alpha xtranAlpha = new Alpha(-1,
          Alpha.DECREASING_ENABLE|Alpha.INCREASING_ENABLE,
          0, 0,5000, (long)0.5, 5000, 5000,(long)0.5, 5000);

    当这两个数小于0.5时,Alpha只在时间段的两头产生加速度,
中间的加速度为0,因而中间的速度保持不变。
   下面我们给出这样一个Alpha的例子:
     Alpha xtranAlpha = new Alpha(-1,
          Alpha.DECREASING_ENABLE|Alpha.INCREASING_ENABLE,
          0, 0,5000, (long)0.2, 5000, 5000,(long)0.2, 5000);
    第二种情况在某一时段的速度图:
           --------
          /        \
         /          \
        /            \

--
※ 来源:·BBS 水木清华站 bbs.net.tsinghua.edu.cn·[FROM: 202.192.158.102]

--
☆ 来源:.哈工大紫丁香 bbs.hit.edu.cn.[FROM: sunsoft.bbs@bbs.net.]
[百宝箱] [返回首页] [上级目录] [根目录] [返回顶部] [刷新] [返回]
Powered by KBS BBS 2.0 (http://dev.kcn.cn)
页面执行时间:211.562毫秒