VRML多媒体编程及自定义节点的应用

一. VRML多媒体编程

1. 形体生成

方法1: 利用VRML的基本形体节点

方法2: 利用VRML的复杂形体节点

方法3: 利用Inline节点调用另外一个VRML文件

方法4: 利用其它三维绘图工具(如3D MAX、RHINO)生成并输出成VRML文件

2. 灯光的生成

在一个局部坐标系中设置光源,根据具体的光源,给出光照半径、角度、方向

使用的节点有:

PointLight,主要参数有:radius、color
DirectionalLight,主要参数有:direction、color

SpotLight,主要参数有:radius、direction、beamWidth、cutOfAngle

3. 文字的生成

使用的节点有:

Text,主要参数有:string、fontStyle
FontStyle,主要参数有:size、justify、horizontal

4. 图片处理
目的:在形体上贴图。
使用的节点有:

ImageTexture,主要参数有:url
TextureCoordinate,主要参数有:point

TextureTransform,主要参数有:rotation、scale、translation

5. 像素图的生成

使用的节点有

PixelTexture,主要参数有:image
TextureCoordinate,主要参数有:point

TextureTransform,主要参数有:rotation、scale、translation

6. 影像的应用
使用的节点有

MovieTexture,主要参数有:url
7. 声音的生成
使用的节点有

Sound,主要参数有:location、maxBack、minBack、maxFront、minFront、source
AudioClip,主要参数有:url

二. VRML自定义节点的应用
1. PROTO使用步骤
作用:在程序中给出一个自定义节点。
假设的一个问题,想定义一个板凳节点BENCH,如何编程?

步骤1:

写出一段程序,放在程序的第一行之后,注意,有一对方扩弧和一对花扩弧

PROTO BENCH [ ] { }
步骤2:
在方扩弧里面给出编程时可能要改变的参数内容,如想改变板凳腿颜色和板凳腿的尺寸,则在方扩弧里面给出下面的内容:

[ field SFColor legcolor 1 0 0
field SFVec3f legsize 0.4 1.2 1
]

步骤3:

在花扩弧里面给出用几个Transform节点语句定义的板凳内容,颜色用IS语句替代

{
Transform{ … }
}
步骤4:
在程序中用BENCH节点定义所需要的板凳内容,例如下面定义了两个板凳,一个用缺省参数,一个改变了板凳腿的颜色:

Transform{
translation -5 0 0
children BENCH {}
}
Transform{
translation 5 0 0
children BENCH {legcolor 0 0 1}
}
2. EXTERNPROTO使用步骤
作用:使用自己或他人定义的PROTO节点
步骤1:

了解所使用的自定义节点的具体内容

步骤2:

在调用程序中给出一个EXTERNPROTO语句,它由三个部分组成:名称、一对方扩弧、一对双引号。方扩弧里面给出被调用的PROTO语句的内容,但要去掉初始值;双引号中给出被调用文件的位置,它可以在电脑上,也可以在网络中。例如:

EXTERNPROTO BENCH [… ]

VRML2.0十四个关键字

VRML2.0有十四个关键字,它们不能作为自定义的域名,节点名和对象名。
(1)DEF:给后续的节点命名,这个名字就是节点名,典型格式为:
DEF 节点名 节点
(2)USE: 引用DEF定义的节点名,典型格式为:
USE 节点名
(3)TRUE:表示“真”,“1”,“是”等,用于给SFBool域赋值。
(4)FALSE:表示“假”,“0”,“否”等,用于给SFBool域赋值。
(5)NULL:表示空值,用于给SFNode域赋空值。
(6)PROTO:用于声明自定义节点的原型,典型格式为:
PROTO 节点名称 [
域的自定义(包括其缺省值)
事件的自定义
]{
执行体
}
(7)EXTERNPROTO:用于预解释引用的外部定义节点的原型,典型格式为:
EXTERNPROTO 节点名称 {
域的自定义(不包括其缺省值)
事件的自定义
]
外部节点的资源定位。
其中域、事件的类型和名称必须与引用的外部节点中的定义一样。外部节点的资源定位格式为:
“URL/URN”或[“URL/URN”,”URL/URN”,···]。
资源定位可以为URL或URN格式,当使用”URL/URN”或[“URL/URN”,”URL/URN”,···]的数组形式时,浏览器使用数组中第一个正确寻获的资源。资源的形式也可以写为:
URL/URN # externProtytypeName
externProtytypeName为引用的外部节点的名称,这时候,可以使用与外部节点定义的名称不同的节点名称。
(8)ROUTE:构成事件通路。典型格式为:
ROUTE fromNode.fromEvent TO toNode.toEvent
fromNode 为发出事件的节点的名称。
fromEvent 为事件输出的名称。
toNode 为接受事件的节点的名称。
toEvent 为事件输入的名称。
(9)TO:见ROUTE的说明。
(10)eventIn:定义事件输入,典型格式为:
eventIn 数值类型 事件名。
(11)eventOut:定义事件输出,典型格式为:
eventOut 数值类型 事件名。
(12)field:定义私有域,典型格式为:
field 数值类型 域名 缺省值。
(13)exposedField:定义公共域,典型格式为:
exposedField 数值类型 域名 缺省值。
(14)IS:用于原型声明中,把自定义的域和事件与执行体中节点的域和事件等同起来。典型格式为:
执行体中节点的域或事件 IS 自定义的域或事件

VRML域的说明

有两类常见的域,一类只包含单值(所谓单值,可以是一个单独的数,也可以是定义一个向量或颜色的几个数,甚至可以是定义一幅图像的一组数)另外一类包含多个单值。单值类型的域,名称以“SF”开始;多值类型的域,名称以“MF”开始。
在VRML文件中,表示多值域的方法是:一系列用逗号和空格间隔开的单值,整个用方括号括起来。如果一个多值域,不包含任何值,则只标出方括号(“[]”),其中不填任何数。如果一个多值域,恰好只包含一个数,可以不写括号,直接写该值。例如,可以表示一个多值域,其中只包含一个单独的整数1,下列两种方式均属有效:

      [1]
本说明依次介绍两种域的值。单值类型的域按英文字母的顺序给出;多值域按相应的单值域配合列出。事件的类型与域的类型是完全一致的。每一个事件类型都具有一个相关的初始值,在类型描述中有所规定。当脚本试图读取一个尚未发送的输出事件的数值时,该初始值被返回。
SFBool
一个SFBool域只含有一个Bool值。TRUE和FALSE(不带引号也不带任何标点符号)是SFBool域仅有的两个有效值。
SFBool输出事件的初始值是FALSE。
注意:一般的程序设计重用1和0表示True和False值,在VRML中不允许。

SFColor和MFColor
SFColor域是只有一个颜色值的单值域。SFColor值和RGB值一样,由一组三个浮点数组成。每个数都是从0.0–1.0,极值包括在内,分别表示构成颜色的红绿蓝三个分量。
MFColor域是一个多值域,包含任意数量的RGB颜色值。例如:
[ 1.0 0.0 0.0,0 1 0,0 0 1]
表示三种颜色红绿蓝的组成。
SFColor域的输出事件的初始值是(0,0,0)。而MFColor域的输出事件的初始值是[]。

SFFloat和MFFloat
一个SFFloat域含有一个ANSI C格式的单精度浮点数。
一个MFFloat域含有零个或多个ANSI C格式的单精度浮点数。即允许空白,不赋任何值。
SFFloat域输出事件的初始值为0.0。MFFloat域输出事件的初始值为[]。

SFImage
一个SFImage域含有非压缩的二维彩色图像或灰度图像。
一个SFImage域,首先列出三个整数值,前两个表示图片的宽度和高度,第三个整数表示构成图像格式的元素个数(1–4),随后,按(宽度x高度)的格式列出一组16进制数,数与数之间以空格分隔,每一个16进制数表示图像中一个单独的像素。
图像格式的元素个数表示这张图像是灰度图还是彩色图,以及是否包括透明像素或半透明像素。
单元素图像中的每一个像素用一个16进制的字节表示,所表示的是一个像素的亮度。例如:0XFF表示最高亮度(白色),而0X00表示最低亮度(黑色)。
双元素图像用两个字节表示一个像素。第一个字节表示亮度,第二个字节表示透明度。表示透明度时,字节为0xFF表示完全透明,而0x00表示不透明。所以0x40C0表示1/4亮度(暗灰)和3/4透明度。
三元素图像的每个像素有三个字节表示,每个字节表示像素颜色中红绿蓝分量(所以0xFF0000表示红色)。
四元素图像是在红绿蓝三色的值之外再加一个表示透明度的字节(所以0x0000FF80表示办透明的蓝色)。和双元素图像一样,透明度字节为0xFF表示完全透明,而0x00表示完全不透明。
为了提高可读性,最好把所有的16进制字节都写全,包括前导0。然而,写出每个字节有时是不必要的。例如可以把一个三元素图像的蓝色像速写成0xFF而不是0x0000FF。
像素的排列规定从左到右、从底到顶的顺序。第一个16进制数描述一个图像最左下角的像素,最后一个则描述右上角的像素。
例:1 2 1 0XFF 0X00
一个像素宽,两个像素高的灰度图像,底部像素是白的,顶部像素是黑的。
2 4 3 0XFF0000 0X00FF00 0 0 0 0 0XFFFFFF 0XFFFF00
两个像素宽,四个像素高的RBG图像,左下角像素是红色,右下角像素是绿色,中间两行是黑色,左上角像素是白色,右上角像素是黄色。
在任何脚本节点或原型内都可以使用这种类型的域,但是,使用的具体地点只能在PiexlTexture(像素纹理)节点。
SFImage域的输出事件的初始值为(0,0,0)。

SFInt32和MFInt32
一个SFInt32域含有一个32位整数。一个SFInt32值是由一个十进制或十六进制(以OX开头)格式的整数构成。
一个 MFInt32域是多值域,由任意数量的以逗号或空格分隔的整数组成。例如:
[17,-0xE20,-518820]
SFInt32域的输出事件的初始值为0,MFInt32域的输出事件的初始值为[]。

SFNode和MFNode
一个SFNode域含有一个单节点,必须按标准节点句法写成。
一个MFNode域包含任意数量的节点。例:
[Transform{translation 1 0 0},
DEF PANDORA box{},
USE PANDORA]
一个SFNode允许包含一个关键字NULL,此时,表示它不包含任何节点。
注意:一个组或一个变换的children域也就是列出一组节点的MFNode域。
把SFNode域放入一个脚本节点,就使节点的脚本可以直接存取列在SFNode域的节点,而不需要一个ROUTE语句。
SFNode域的输出事件的初始值为NULL,MFNode域的输出事件的初始值为[]。

SFRotation和MFRotation
SFRotation域规定一个绕任意轴的任意角度的旋转。SFRotation值含有四个浮点数,各数之间以空格分隔。前三个数表示旋转轴(从原点到给定点的向量);第四个数表示围绕上述轴旋转多少弧度。例:绕y轴旋转180o表示为
0 1 0 3.1416
MFRotation域可包含任意数量的这类旋转值。
注意:视点的旋转是从缺省的视点方向开始的,该方向是从(0,0,10)沿-z轴观察。
SFRotation域的输出事件的初始值为(0 0 1 0),MFRotation域的输出事件的初始值为[]。

SFString和MFString
SFString域包含一串字符,各字符遵照UTF-8字符编码标准(ASCII是UTF-8的子集,可以由于SFString域)SFString值含有双引号括起来的UTF-8 octets字符串。任何字符(包括“#”和换行符)都可在双引号中出现。
为了在字符中使用双引号,在它之前加一个反斜杠“\”。为了在字符串中使用反斜杠,连续打两个反斜杠“\\”。例如:
“One,Two,Three,123.”
He asked, \”Who is #1?\””
MFString域含有零个或多个单值,每个单值都和SFString值的格式一样。
SFString域的输出事件的初始值为“”,MFString域的输出事件的初始值为[]。

SFTime和MFTime
SFTime域含有一个单独的时间值。每个时间值是一个ANSI C格式的双精度浮点数,表示的是从1970年1月1日(GMT,格林威治平均时)子夜开始计时,延续当前时间的秒数。
MFTime域包含任意数量的时间值。
SFTime域的输出事件的初始值为-1,MFTime域的输出事件的初始值为[]。

SFVec2f和MFVec2f
SFVec2f域定义了一个二维向量。SFVec2f的值是两个由空格分隔的浮点数。
MFVec2f域是多值域,包含任意数量的二维向量值。例如:
[0 0,1.2 3.4,98.6 -4e1]
SFVec2f域的输出事件的初始值为(0 0),MFVec2f域的输出事件的初始值为[]。

SFVec3f和MFVec3f
SFVec3f域定义了一个三维空间的向量。一个SFVec3f值包含三个浮点数,数与数之间以空格分隔。该值表示从原点到所给定点的向量。
MFVec3f域包含任意数量的三维向量值。例如:
[0 0 0,1.2 3.4 5.6,98.6 -461 451]
SFVec3f域的输出事件的初始值为(0 0 0),MFVec3f域的输出事件的初始值为[]。

VRML与JAVA

1.VRML的概念
  VRML是虚拟现实建模语言 (Virtual Reality Modeling Language)的简称,是一种基于文本的通用语言,是一种在网络上使用的描述三维环境的场景描述语言,是HTML的3D(三维)模拟。它定义了3D应用中大多数常见概念,如光源、视点、动画、雾化、材质属性、纹理映射等。VRML语言的诞生,尤其是新的VRML2.0标准,被称为第二代Web语言,它改变了原来WWW上单调、平面的缺点,将人的行动作为浏览的主体,所有的表现都将随操作者行为而改变。

2.VRML的发展历史
  VRML的历史可以上溯到1994年,当时在日内瓦的第一次WWW年会上,一些与会者提出为创建三维网络的界面必须有一种通用的描述性语言,用于在 WWW上超级链接——类似于超文本描述语言(HTML),于是诞生了虚拟现实描述语言(Virtual Reality Makeup Language)。稍后,改为虚拟现实建模语言 (Virtual Reality Modeling Language),用于反映图像的本质。现在世界上有一个管理 VRML 的团体 ,简称为 VRML结构组织 (VGA),VGA为网络的三维功能建立了标准。1995年5月,VRML1.0规范正式出台,但此规范缺少对一些关键特性如动作、交互和行为的支持,为此,急需对它进行修订。1996年8月,在新奥尔良举行的SIGGRAPH’96会议上公布了VRML2.0规范的第一版,经ISO JTCI/ SC2 4同意,这一版被作为14772号委员会草案公布,并且于1997年4月提交审议。该草案又称为VRML 97(遵循ISO把发布年号写入名字的命名约定)。从此之后,许多公司纷纷推出了自己的 VRML浏览器,如 Netscape公司 Live3D,IE内嵌的Microsoft VRML 2.0 Viewer,Apple公司的 Quick Time VR等等。

3.VRML产品的技术特点
  (1)低带宽、开放的标准、跨平台 VRML采用“可执行的代码”技术,有效克服了网络带宽造成的瓶颈。开发人员利用VRML可以不必在介质中传输内容大得惊人的图像和动画文件,而只需传输用VRML描述的小型指令性数据集。在客户端利用本地计算机的强大计算及三维图形功能便可把接收的瘦型文件转换成极富感染力的可视化数据。 由于Web应用框架是由开放的标准 (如HTTP,HTML,VRML等)组成,这些标准相对于硬件平台和操作系统平台是中性的。从开发者的角度来说,开放的标准意味着平台自由性。开发者能在Web应用框架上采用许多开发工具编写应用程序,并在许多硬件平台、操作系统下部署这些应用程序。
  (2)从LAN到Internet的可扩展性好 由于VRML和其他Web程序有着共同的网络通信协议TCP/IP,因而基于Web的虚拟产品技术既能在小型局域网上应用 ,又能在大型广域网乃至Internet上应用。
  (3)性能/价格比高 Web使用统一的通用客户端程序(即Web浏览器)处理显示来自不同服务器的数据,而这些服务器可以不同的协议、不同的格式传递文件。由于Web应用框架包含了开放标准,客户端程序可以使用HTTP协议访问HTML文件或Java Applet,使用FTP协议下载文件,使用SMTP或POP协议收发Email,通过VRML文件访问3D影象,以及访问许多其他的网络资源。这个通用的客户端程序消除了现在的C/S应用中往往需要不同客户端软件所带来的隐含费用支出。通用的客户端程序还使用户能够以一致的方式与所有基于Web的应用交互,减少了技术支持和培训费用。所有这些因素使得基于 Web的虚拟产品技术具有良好的性能价格比。

⒋VRML的作用原理
  VRML文件是以扩展名为wrl结尾的一种用来描述几何形体的ASCII文本文件,它不需要任何编译,直接由浏览器解释执行。当用户打开VRML文件时,系统首先装入一个内嵌的VRML浏览器,该浏览器将VRML语言中的信息解释成空间中目标的几何形体描述,如长方体,球体,不规则的其他三维物等等,同时它将提供实时显示,一秒显示多次,这样在用户的计算机上就会有一个活动场景的感觉。

1. 场景图结构
  VRML 2.0使用场景图(Scene Graph)数据结构来建立3D实境,这是以SGI(Silicon Graphics Inc.)的Open Inventor 3D工具包为基础的一种数据格式,其基本单元被称为节点(Node),场景图规定了节点之间的等级关系与嵌套关系。VRML2.0总共定义有54个节点,它大致可以分为如下几类 :
·造型节点。用于表示各种基本的几何体和用于任意几何体的线框图和面框图。
·属性节点。用于定义相关对象的颜色、材质、纹理以及摄像机组、灯光组、视点、背景等。
·组节点。用于将节点分组 ,把相关节点组合成为同一个对象。
·感应节点。用于感知用户的输入或动作 ,以触发相应的动作。
·其它节点。包括移动和旋转动作 ,脚本(Script)节点、超链接节点等。
  VRML2.0使用符合右手规则的三维坐标系统,原点在屏幕中心,它也是使用这一坐标系统的各种几何体的中心。所以,当根据构图要求,某个几何体的中心不在屏幕中心时(事实上,几何体的中心落在屏幕中心的情形是很少见的),就需要移动坐标系统,即使用Transform节点,在该节点内定义的几何体都使用该节点中所定义的平移后的坐标系统。可以想见,在构造一个较为复杂的场景时,这种坐标平移将是相当频繁的。
2. 事件驱动机制和传感器
  由面向对象编程技术可以知道,对象之间的相互作用是通过一个对象向另一个对象发出某个操作消息(message)而实现的,VRML也是这样。它在两个要传递消息的节点之间创造(或称为绑定)一个路由(或称为路径)。于是,第一个节点就可以通过路由传递消息给第二个节点。这样的消息称为事件(event),它包含的是一个值,即节点内需要传递的某个域值。当一个节点接受到某个事件时,它将根据新得到的域值,去进行一次数据更新,从而可能引发某个动作。通过绑定多个节点,就可以创建出复杂的线路 ,从而使创造出的世界充满动感和交互性。
  传感器(Sensor,或称检测器)是VRML中提供交互能力和动态行为的基元。一个具有动态能力的节点需包含传感器。VRML共提供7种传感器节点,即Cylinder Sensor、Plane Sensor、Time Sensor、Touch Sensor、 Visibility Sensor、Proximity Sensor。它们提供了用户与虚拟世界中的物体进行交互的机制:根据时钟或者用户的动作,它们可以产生一个相应的事件,这事件沿着事先设定好的路由传递下去,从而使得虚拟世界对用户做出反应 ,实现交互。

3. 动画的实现和内插器
  VRML的动画比普通动画更吸引人,因为当用户在一个虚拟世界中漫游时,他可以从各种角度来观察动画,而要完成一个复杂的从所有视点来看都成功的动画是很困难的。VRML动画的另一个特点是,在播放动画时没有一个固定的帧速率,如果动画是由Time Sensor驱动的,它就可以确保动画在固定的Cycle Interval时间内完成,这样做的好处是可以使动画与声音或其它时间固定的媒体相同步。
  内插器(Interpolator,或称插补器)是实现动画的重要手段之一。通过一个被启动的时钟节点(Time Sensor),不停地向内插器提供时间消息。内插器一旦接受到一个时间消息,就会结合其内的关键时刻列表,从自己的关键值表中 ,通过线形插值的算法,得到一个当前时候的关键值,并经过路由送至Transform节点的相应域,从而实现动画。(例:声波图)
  实现动画的另一种方法是使用描述符script,它是用于VRML 2.0各个节点的一种描述语言,是Java描述符语言的子集,其语法和Java script有相似之处。

  VRML以其文件体积小、实时着色、全方位的观看效果等优势而逐渐成为新一代的网络三维技术,在建筑、天文、机械制图等应用得如火如荼。但是,在Web课件中,VRML作品还依然停留在作为一种展示型的课件的阶段上,只是纯粹的给人观看,缺少动感和交互。目前,VRML技术在Web课件上用得最多的是在化学上,主要是展示化学分子的分子结构,这样的例子在K12(http://www.k12.com.cn/)、课堂教学课件站(http://spschool.myrice.com/)、123工作室(http://mz123.myrice.com/kjku)很多。究其原因是它自身还不是很完善,还存在着许多不足之处,只要表现在:
1.浏览器的控制比较困难
  用过的人都知道,几乎所有的VRML浏览器都是利用鼠标的拖动或键盘箭头来改变视角的方式来观看VRML世界的,没有比较精确的控制手段,因而很容易一拖都不知道拖到哪里去,定位很困难。
2.VRML的与外界的通信能力比较差
  VRML几乎不与HTML文件进行通信,仅仅是作为一个插件嵌入到网页中去,VRML的数据传不到HTML中,HTML也没法读取VRML的数据。
3.VRML与用户的交互界面比较弱
  VRML的交互还仅仅限于鼠标单击及移动,没提供其它友好的用户界面了。最糟糕的是它没提供用户输入数据的功能,这对于一般的用户,特别是课件是使用者来说是极大的不便!教学课件的必须交互手段之一是可以根据学习者输入的数据作出不同的响应,而VRML在这方面就显得无能为力了,这大大影响它在网络课件中的发展。
  幸运的是,VRML的Script节点的URL域可以与JAVA接口,为其扩展提供了可能性。我们可以借助JAVA的强大功能大大延伸VRML的功能,使得它可以在网络课件发挥得更好。

 1991年,SUN MicroSystem公司的JameGosling、BillJoe等人,为在电视、控制烤面包箱等家用消费类电子产品上进行交互式操作而开发了一个名为Oak的软件(即一种橡树的名字),但当时并没有引起人们的注意,直到1994年下半年,Internet的迅猛发展,环球信息网WWW的快速增长,促进了Java语言研制的进展,才使得它逐渐成为Internet上受欢迎的开发与编程语言。JAVA语言兼具解释型语言(如BASIC语言)和编译型语言(如C语言)两者的优点,具有执行效率高、程序的可移植性以及安全性好的特点。
1. JAVA语言的优点
  ☆是一种专门为网络应用而开发的语言;
  ☆以C++为蓝图,真正面向对象;
  ☆取消了指针概念,由计算机自动管理内存,因而安全可靠;
  ☆结构标准且移植性高,具跨平台性;
  ☆具有很高的可扩展性;
2. JAVA语言的类(Class)和包(Package)
  类是有所有相似对象的状态变量与行为构成的模板(template)或原型(prototype)。
  包(Package)由一组类(class)和界面(interface)组成。它是管理大型名字空间,避免名字冲突的工具。每一个类和界面的名字都包含在某个包中,通常在程序中由 import 导入。JAVA自带了许多类包,我们可以直接利用类包所提供的功能而不必重新从底层写起,大大的减少了编程的工作量,提高了编程效率。
    语言基础类  java.lang
    实用工具类  java.util
    输入输出类  java.io
    AWT类库    java.awt
    Swing类库   java.swing
    网络类库   Sockets、Telnet、ftp、NNtp、WWW
    浏览器类库  Applet、Audio、Links、Style、Properties

 JAVA 3D是一个用于开发三维图形的API,它从高层次为开发者提供对三维实体的创建、操纵和着色,使开发工作变得极为简单。同时,JAVA 3D的低级API是依赖于现有的三维图形系统的,如Direct 3D、OpenGL、QuickDraw 3D和XGL等,它可以帮助我们:
  ☆生成简单或复杂的形体(也可以直接调用现有的三维形体)
  ☆使形体具有颜色、透明效果、贴图。
  ☆可以在三维环境中生成灯光、移动灯光。
  ☆可以具有行为(Behavior)的处理判断能力(键盘、鼠标、定时等)
  ☆可以生成雾、背景、声音等。
  ☆可以使形体变形、移动、生成三维动画。
  ☆可以编写非常复杂的应用程序,用于各种领域如VR
  JAVA 3D的这种体系结构既可以使其开发的程序“到处运行”,又使其能充分利用系统的三维特性。就因为JAVA 3D拥有如此的强大的三维能力,使得它在网络世界,特别是在游戏中能大展姿彩。但是,由于其要求复杂的编程要求与慢如蜗牛的运行速度,使得其发展大打折扣。

 基于Web上的课件从以前的简单文字显示、图文并茂、声画俱全到视频的运用,一直到现在的三维立体技术,这是一个发展的历程,也是一个必然的趋势。声音、动画能集中学习者的注意力,三维立体的世界更能引起学习者的莫大兴趣。化学的原子结构、生物的解剖图、物理的运动、立体几何的图形变换等的网络课件用VRML技术表现就最好不过了。再配合JAVA的强大控制能力,VRML世界带来的视觉、听觉上的感官效果令学习者能在网络上感受到现实生活的学习环境,甚至还能感受到真实学习环境所不能达到的效果。以下是一些基于JAVA与VRML技术的三维立体Web课件。
※旋转几何体
  抽象的空间想象、错综复杂的线线面面,立体几何对学生来说是历来是最头疼的。在本课件里,你可以看到一个菱形绕着轴心旋转是怎么样子的。基于该思想,你可以设计出其它形状图形(如三角形、矩形、圆形等)的旋转、变换过程。学习者可以从720度全方位的角度看到这个过程。本课件利用了JVAVA的createVrmlFromString()函数接口即时生成大量的VRML形体,再旋转组合而成。
※氢氧成水
  在这里,你可以看到氢分子跟氧分子是怎么结合成水分子的。通过简单的修改,我们可以轻易的做出其它原子、分子的动态结合图。
※活动地球仪
  在这里,你可以象在真实环境一样拨动地球仪了。本课件设计成可以从各个角度一度一度地转动地球仪。虽然在VRML浏览器里可以随意从不同角度观看物体,但它不能精确数值。在本课件里,我们可以通过旋转地球仪的角度、方向来学习经度、纬度、时差等地理知识。本课件通过坐标变换Transform的rotation来旋转物体的。

  我们以“旋转几何体”为例子,讲解一下VRML与JAVA的接口技术。由于VRML只有方体、圆锥体、圆柱体、球等几种基本的形体,因而,要建立复杂的几何体,只能利用基本形体重叠的方式来建立。本例子演示的效果是一个菱形绕着它的中心轴旋转一周,因此,我们采用JAVA动态生成的方式来制作。每生成一个菱形就绕着中心轴旋转 1 度,循环180次(一周)就可以达到我们所要求的效果了。下面是VRML文件的代码,十分简单。
#VRML V2.0 utf8

Background {skyColor 1 1 1}
DEF OBJ Transform {
  children [
    DEF TS TouchSensor {}
    DEF shape Transform {
          rotation 0 0 1 0.785398
          children [
            Shape {
              appearance Appearance {
                material Material {
                  diffuseColor 0.5 0.2 0.7
                }
              }
              geometry Box {size 5 5 0.2}
            }
          ]
    }
  ]
}
DEF Rot Script{
    url “Rotation.class”
    eventIn SFBool touched
    eventOut MFNode RotIt
}

ROUTE TS.isActive TO Rot.touched
ROUTE Rot.RotIt TO OBJ.addChildren

  利用URL连接“Rotation.class”,变量touched作为事件的传入口,变量RotIt作为事件的出口。当接触传感器TS检测到鼠标被按下后,“ROUTE TS.isActive TO Rot.touched”使touched的值变为真。下面的JAVA程序被加载执行。
//Rotation.java
import java.util.*;
import vrml.*;
import vrml.node.*;
import vrml.field.*;
//线程接口
public class Rotation extends Script implements Runnable{
  MFNode RotIt;
  int k;
  float R=1.0f;
  Thread clockThread =null;
//初始化,取得RotIt的值
  public void initialize(){
    RotIt = (MFNode)getEventOut(“RotIt”);
  }
  //生成新的线程,循环180次
  public void run(){
    for(k=0;k<181;k++){
      R=(float)k/180*3.1415926f;
      rotation();
        try{Thread.sleep(10);} //暂停10毫秒
          catch(InterruptedException e){}
        }
    }
  //开始线程
  public void start(){
    if(clockThread ==null){
      clockThread =new Thread(this);
      clockThread.start();
    }
  }
  //停止线程
  public void stop(){
    try{
      clockThread.stop();
      clockThread = null;
    }catch (Exception e){}
  }

  //动态增加节点
  public void rotation() {
      String Rotation1 =
        ”Transform {” +
        ”  rotation 0 1 0 “;
      String Rotation2 =
        ”  children [” +
        ”Transform {” +
        ”  rotation 0 0 1 0.785398″ +
        ”  children [” +
        ”    Shape {” +
        ”      appearance Appearance {” +
        ”        material Material {” +
        ”          diffuseColor 0.5 0.2 0.7″ +
        ”        }” +
        ”      }” +
        ”      geometry Box {size 5 5 0.2}” +
        ”    }” +
        ”  ]” +
        ”}]” +
        ”}”;
      //取得VRML浏览器的句柄,产生一个新的节点
      Browser browser = getBrowser();
      BaseNode baseNodes[];
      //利用createVrmlFromString接口函数生成新的形体并检测异常
      try{
        baseNodes = browser.createVrmlFromString(Rotation1 + R +
                             Rotation2);
        //设置新的RotTt值并传送回VRML中
        if(null != baseNodes) {
          RotIt.setValue(baseNodes);
        }
      } catch (Exception ex) {
        ex.printStackTrace() ;
      }
      
    }
 //接收事件,取得touched变量的值
  public void processEvent(Event ev) {
   if(ev.getName().equals(“touched”)){
    start();
    }
  }
}
  VRML接收到JAVA传回的值后触发OBJ节点新增加一个子节点,从而形成我们所看到的效果。

在3D Studio MAX 中制作VRML文件

VRML是第二代Web中的基础技术,它将今天的以页面为中心的模式转换到一个令人激动的三维世界。如果你会用3DS MAX,那么利用Kinetix公司的VRML插件制作自己的第二代Web页面将是一件轻松愉快的事情。

Kinetix公司的VRML插件为3DS MAX提供了输出VRML(*.wrl)文件格式的功能,这种文件可以在任何的VRML浏览器浏览,使用此插件建立和测试一个VRML场景首先需要安装以下软件:

.3DS MAX和3DS MAX VRML插件(网址为www.ktx.com);

.Netscape Navigator Gold。

通过3DS MAX建立一个VRML文件需要按照下列步骤:

1)在3DS MAX中创建物体组成场景;

2)在Create面板中,访问Helpers工具;

3)使用VRML-Helper工具为场景增加动作Actions和触发器Triggers,同时对场景作相应

的改动;

4)选择File菜单中的Export项,把场景以VRML格式文件输出;

5)使用Netscape Navigator浏览和测试。

如果3DS MAX VRML插件安装正确,当你点击Create面板中的Helpers按钮,将会看到Helpers列表框里出现了VRML项;选择VRML项,展卷栏中出现了VRML,VRML Inline,LOD三个按钮。VRML项为在场景中的物体定义动作和触发点提供访问工具。要在场景中建立一个VRML物体,首先按下VRML按钮,然后按住鼠标左键同时拖动鼠标到场景中的任何位置,此时就会出现一个VRML物体。在此之后,你就可以使用Modify面板中的工具对其进行修改。在修改VRML物体时有两个展卷栏,一个是Actions动作展卷栏和Triggers触发器展卷栏。

当你点击VRML按钮时,Actions动作展卷栏就会随之出现。在这个展卷栏中的工具允许你定义一个动作,而触发这个动作由Triggers触发器展卷栏中的项指定。通常是你先选择一个动作,然后选择击发这个动作的触发器,最后再来调整场景中触发机制的参数。例如:你想建立一个物体,使在浏览过程中到达这个物体的某个范围内时,物体就开始运动。那么,要指定这样一个事件,你可以首先在Actions动作展卷栏里选择Animate动画选项,然后在Triggers触发器展卷栏中选择Proximity附近选项,最后来修改Proximity选项具体的值,从而确定范围的大小。

在Actions展卷栏中共有四个选项:

Hyperwire:这个选项主要是用于初始化一个动作,将指定的击发信息作为事件存储。

Hyperlink Jump:这个选项用于指定一个触发点,击发这个点之后可以跳转到另一个URL页面。指定URL可以选择Bookmarks书签选项,通过它从对话框中选取一个URL值,也可以直接在URL栏中输入。URL可以为任何的HTML或VRML页面,如果为VRML页面,就至少要在Camera栏中为这个场景设置一个摄像机。为了向用户提示跳转的有关信息,还应该在Description栏中输入描述此次跳转的文本。

Set Camera:这个选项用于产生切换到另一个摄像机的动作。通过Camera栏选择你想要切换到的摄像机的名称。当触发点有效的时候,你就可以从指定的摄像机角度观察场景。这就是说你至少要在场景中建立一台摄像机。如果你在场景中只建立了一台摄像机,那么你只有从系统缺省的摄像机切换到它。如果你一台摄像机也没有设立,那么这一栏无效。类似于Hyperlink Jump ,Set Camera中也存在Description栏用于输入描述信息。

Animate:这个选项用于指定物体在被击发后产出相应的运动。通过改变Star/Stop项决定动画开始或者结束。通过Pick Object选择场景中的物体,你可以选择多个物体,但是你不能选择有父子关系的物体,这在VRML中不支持。因此,你不能击发一个组中的某个物体,因为编了组的物体是这个组的孩子。

在Triggers触发器展卷栏中包括如下几项:

Mouse Click:当用户按下链接的物体时击发动作。在Navigator浏览器中,当鼠标移动到一个有Mouse-Click鼠标点击定义的触发器上时会由平时的斜箭头形变为手掌形。这个定义了鼠标点击的VRML物体必须链接到另一个物体上。

Proximity:当摄像机移动到一个球形范围内时击发动作。你既可以将触发器链接到一个物体上,也可以在场景中随意放置。选择此项后会出现三条蓝色的环组成一个球形,你可以改变球半径的大小。

Aligned Bbox:当摄像机移动到一个有限界的方盒子中时产生动作。这种触发器主要在产生诸如进入一个房间的事件时特别有用,在利用这一种触发器时应该给方盒子赋予足够的深度,使摄像机能够自如的转动。选择此项后会出现一个紫红色的方盒子,你可以调整盒子的长宽高。

Line of Sight:当摄像机移动到能看到指定物体时击发动作。这种触发器常用于武器的瞄准。注意不管物体是否被前面的物体遮挡住,它都是有效的。选择此项后会出现一个黄色的圆锥,你可以选择See或See and Seen选项。See选项是一种缺省的视线,就是说当物体只要是在摄像机视线的范围内时,触发器是有效的;而See and Seen选项是说物体不但要在摄像机范围内而且这个物体能够被看见才有效。

Angle:设置视线承受的角度。

除VRML项外,还有VRML Inline项和LOD项,VRML Inline项允许你指定另外的一个VRML文件插入到当前的场景中,构成一个虚构的物体。修改Helper Size项用于改变虚构物体的大小,你同样可以对这个物体进行移动、旋转、放缩之类的操作。Insert URL栏用于输入你想插入虚构物体的位置。而在LOD项中允许你指定一系列物体,使其具有不同的清晰程度。

下面介绍一个具体的VRML文件制作过程:

1)在3DS MAX中打开文件Vhome0.max。你将会看到一个有家具和挂图的房间,其中两幅挂图已经赋予了Hyperlink Jumps,场景中用两条蓝色的直线表示。

2)在Create面板中,按下Helpers按钮,在物体列表下拉菜单中选择VRML项,然后按下VRML按钮。

3)在靠近画有茶壶的挂图旁边任意一处,按下并拖动鼠标用来建立一个VRML物体。

4)给这个物体取名为GoToTeapot,紧接着在工具条按下Select and Link按钮。

5)将GoToTeapot与墙上的茶壶挂图链接在一起。

6)转到Modify面板观察GoToTeapot的参数,在Actions展卷栏中选择Hyperlink Jump项,在Triggers展卷栏中选择Mouse Click项。

7)在Hyperlink jump展卷栏的URL栏中输入vteapot.wrl。(此时输入的是一个相对路径而不是一个绝对路径,因为vteapot.wrl和vhome0.wrl在同一个目录下。)

8)在Description项中输入”Go to teapot room”,这样当光标移动到茶壶挂图的时候就会出现这条信息。

9)在File菜单中选择Export项输出该文件,不改变输出的对话框中的值并将文件输出到存放vhome0.wrl等存放VRML文件的目录之下。到此为止,你就可用浏览器测试你的页面效果了。如果以上操作正确的话,点击茶壶挂图后就会跳转到一个展示茶壶的VRML页面。

另外,由于目前的硬件和软件的限制,那么为了制作快速、精制的页面,要注意场景中的物体不宜过于复杂,如物体边数限制在5000到10000之内,物体的材质贴图尽量用GIF或JPEG格式,动画变形只使用常见的放缩、旋转、移动等等。

第二代WWW标准语言VRML

—-VRML的英文全称为Virtual Reality Modeling Language,即虚拟现实建模语言,它是第二代WWW的标准语言。今天,随着计算机和多媒体技术的逐渐成熟,以及Internet和个人电脑的日益普及,VRML正逐渐深入我们每个人的生活。为较好地理解VRML,我们先介绍一下虚拟现实和第二代WWW。

—-第二代WWW

—-一、虚拟现实和VR系统

—-虚拟现实(Virtual Reality,简称VR)诞生于90年代初,目前尚处在一个研究、发展的阶段,至今并没有一个完整的、系统的定义。通常认为,虚拟现实是一项新的技术,它是以计算机技术为核心,综合使用了各种最新技术,融合视、听、触觉为一体的模仿现实的三维空间再现技术。通过虚拟现实系统,使用者借助一定的设备(头盔式显示器、数据手套等),利用其自然技能与之交互,达到身临其境的体验和沉浸感受。

—-将虚拟现实技术应用于计算机系统中,就形成我们常说的VR系统,即虚拟现实系统。VR系统一般应具备以下的特征:(1)以计算机系统为中心的一种计算机应用系统。(2)展现三维多媒体空间。(3)对象的交互性。(4)使用者的访问不以VR系统作者的设定为转移。

—-由以上介绍可以看出,VR系统是在多媒体系统基础上发展起来的新型计算机应用系统,是多媒体系统在三维时空领域的拓展,它是多媒体系统的一个子集。

—-二、第二代WWW系统

—-虚拟现实最重要的应用就是第二代WWW系统1。

—-我们知道,第一代WWW是基于HTML的超文本系统,系统的基本组成单元是Web页面,Web页面的所有对象都是二维的,即使部分对象具有三维的视觉效果,如按钮、划线等。

—-第二代WWW则是在第一代WWW基础上发展起来的,它保留了第一代WWW的基本框架,引入了虚拟现实技术,使得其实质发生了根本性的改变,第二代WWW是基于虚拟现实建模语言VRML的超媒体2VR系统。

—-在第二代WWW中,系统的基本组成单元是Web3D页面,一个或多个相关的Web3D页面组成虚拟世界(VirtualWorld,也称虚拟境界),虚拟世界和现实世界一样,建立在一个与时间相关的x-y-z三维坐标系统中,这个世界的所有组成对象都是三维的,可以提供一种更加自然的访问和人机交互。概括起来,第二代WWW的特点是:分布式、三维、交互性、模拟现实和多媒体集成。

—-三、第二代WWW的框架结构

—-第二代WWW框架上沿用第一代的Client/Server模式。客户端是VRML浏览器,它负责管理用户端资源,向服务器发出服务请求,定位和解释获取的VRML资源;服务器则负责管理和维护共享资源,响应客户机的请求,提供VRML文档及图像、音频、视频等相关资源。具体地说,该框架从结构上可以分为三层:

—-(1)用户接口层即客户端的VRML浏览器。

—-(2)超媒体抽象机由VRML文档和HTTP协议所组成,它用于处理和解释各种用户接口层的对象。第二代WWW保留了HTTP协议,也就保留了我们在第一代WWW上做过的所有工作。

—-(3)虚拟世界库由Internet上所有VRML服务器共同构成。

—-值得一提的是为了解决三维图形穿越网络所需要的高带宽,第二代WWW借助浏览器所在客户机的图形子系统来动态构造虚拟世界,这样,网络上传输的仅仅是含有建模信息的VRML文档,传输的瓶颈解决了,对客户机的系统性能要求却大幅提高——访问第二代WWW的最低配置应该是:Pentium100MHzCPU、16MB内存和1MB显示缓存的VGA显卡。

—-VRML语言

—-一、什么是VRML语言

—-按照Web3D协会的定义,VRML语言是一种用于在Internet上构筑3D多媒体和共享虚拟世界的开放式语言标准。它具有以下4个要点:

—-1.开放式标准

—-VRML的最新国际标准是ISO/IEC-14772-1:1997。能够被ISO接受这一点就足以说明该标准所处的地位和现实意义,现在,ISO已把它作为未来标准发展的重要模型加以开发和研究。

—-2.3D多媒体

—-在成为国际标准以前,VRML已经是CAD、动画制作以及3D建模软件等领域数据共享和数据发布的事实上的标准——这些软件有的直接输出VRML格式的文档,有的提供实用工具或插件实现这种变换;在一些最新的多媒体标准中,如:MPEG-4、Java3D等,也都包含或者涉及到VRML规范。

—-3.共享虚拟世界

—-VRML先驱们最早的动机之一就是能够在一个虚拟空间中工作和交谈,这个空间就是著名的Cyberspace。通过VRML97,我们已经可以部分地实现这一构想。

—-4.基于Internet

—-与以往的3D应用不同,VRML规范考虑的第一件事就是通过Internet共享3D实体和场景。实际上,VRML规范本身就是通过Internet探讨、研究和发布的。

—-二、VRML的发展

—-VRML的前身是SGI公司OpenInventer系统使用的一种文档格式,后经MarkPesce、TonyParisi和GavinBell等人的改进和努力,最终发展成第二代WWW的标准语言。

—-到目前为止,VRML标准的发展已经历了三代。

—-1.VRML1.0

—-1994年10月公布。VRML1.0非常简单,它只定义了36种节点类型,涉及的对象也只有静态对象,而没有声音、动画等动态对象。实际上,由于VRML1.0近似HTML的“3D版本”,因此,当时称VRML为虚拟现实标记语言(VirsualRealityMarkupLanguage)。现在,VRML1.0已基本被淘汰。

—-VRML1.0的标志:文档的第一行是“#VRMLV1.0ascii”。

—-2.VRML2.0

—-1996年8月公布。VRML2.0名义上是VRML1.0的修订版,但两者从内容到文档结构上都很不相同。在VRML2.0中,节点类型扩展为54种,支持的对象包括动态和静态两类。这时的VRML语言,已经完全脱离HTML的影响,被正式命名为:虚拟现实建模语言(VirsualRealityModelingLanguage)。

—-VRML2.0的标志:文档的第一行是“#VRMLV2.0utf8”。

—-3.VRML97

—-VRML1.0和VRML2.0并不是真正的国际标准。直到1997年12月,VRML才被国际标准化组织ISO和国际电子工业协会IEC正式接纳为国际标准,国际标准号ISO/IEC14772-1:1997,习惯上称为VRML97。

—-VRML97是在VRML2.0的基础上,进行了少量功能性调整而形成的,对用户而言,两者完全一样。可以认为,VRML97是VRML2.0的国际正式名称。

—-VRML97的标志与VRML2.0一样:文档的第一行是“#VRMLV2.0utf8”。

—-除了以上3个版本外,预计1999年中期将公布VRML99,这将是一个崭新的更有建设性的VRML版本。

—-三、Web3D协会(Web3DConsortium)

—-VRML标准是由一个称为Web3D协会的组织来管理和发布的。

—-Web3D协会的前身是VRML协会,它创建于1996年,是一个非盈利性的开放式贸易协会,主要负责有关VRML标准的研究、定义和推广工作。

—-协会下设工作组(WorkingGroup),工作组按自愿原则组成,分别从事某个技术专题方面的研究,如:KeyboardInputWG、CompressedBinaryFormatWG、DatabaseWG等,目前这样的工作组共有17个。

—-VRML的基本概念

—-一、节点

—-使用VRML语言编写的文档称为VRML文档(或VRML程序),其扩展名为.wrl。VRML文档的格式有两种:utf8文本格式和二进制格式,目前我们所用的都是文本格式。每个VRML文档是一个Web3D页面,VRML虚拟世界是由一个或多个VRML文档所共同展现。不同的VRML文档之间通过超链接组织在一起,共同构成了遍布全球的非线性超媒体系统。

—-节点(Node)是VRML文档基本的组成单元,它描述对象某一方面的特征:如形状、材质等。VRML虚拟世界的对象往往由一组具有一定层次结构关系的节点来构造。节点具有节点名、节点类型、域、事件接口和实现五个组成部分。节点可以用DEF语句命名,用USE语句引用。

—-节点类型可分为基本类型和用户自定义类型两大类,基本类型由系统提供,自定义类型由用户在基本类型的基础上通过原型机制构造,它们都是对虚拟世界的某些共性的提炼,如:Appearance节点描述实体的外观、Material节点描述对象的材质……域、事件接口和实现通常包含在节点类型的定义中,并在构造节点时被使用。域描述节点的静态固有属性,域可以有多个,每个域都必须具有一个域值;事件接口描述节点的动态交互属性,它提供节点与外界的通信接口,事件接口也可以有多个。接口类型有事件入口和事件出口两种:事件入口(eventIn)是节点的逻辑接收器,它负责监听和接收外界事件;事件出口(eventOut)是节点的逻辑发送器,它负责向外界发送节点产生的事件。实现则是域和事件接口机制的实现代码,它一般由系统提供。需要提醒注意的是事件具有事件值和时间戳属性,事件值即该控制信息本身,时间戳则标识事件发生或传送的时间。

—-每个域(或事件)都具有一个数据类型,用于描述数据的结构、值域、个数等属性。数据类型按照包含的数据个数分为单值型和多值型两类,单值型以SF打头,只能包含一个数据;多值型以MF打头,可包含一个数据列表。

—-二、场景图(SceneGraph)

—-场景图由节点按一定的层次关系组成,它用于构造虚拟世界的主体——各种静态和动态对象。

—-在场景图层次模型中,上下层节点之间存在两种关系:包容关系和父子关系。

—-节点的包容关系是指后代节点作为祖先节点的一个属性域而存在,如Appearance节点,它只能用于Shape节点的appearance域中,参见下文例3。

—-而在父子关系中,子节点并不直接出现在父节点的属性域中,它们集中在父节点的MFNode类型的子域内,依次排列。父节点必须由群节点担任,VRML97的群节点有8个:Anchor、Billboard、Collision、Group、Inline、LOD、Switch、Transform。(未完待续)

表1第二代WWW与第一代WWW的几个区别比较项目第一代WWW第二代WWW
冲浪方式Web页面虚拟世界
通信和交往电子邮件或文字交谈可含各种表情的语言或文字交谈
共享资源文档文档、音频、视频、3D空间
时间相关性无关相关
MUD游戏文字角色扮演游戏真人三维立体游戏

表2域和事件的数据类型
单值型名称多值型名称基类型说明
SFBoolMFBool逻辑型值只有2个:TRUE和FALSE。
SFFloatMFFloat浮点数每个数据成员都是一个浮点数。
SFInt32MFInt3232位整数每个数据成员都是一个32位的整数,该整数可以使用十进制或十六进制表示,使用十六进制时应在数字前加0x,如:0xFF00。
SFStringMFString字符串每个数据成员都是一个utf8字符串,定义时应使用双引号(”)包围字符串。
SFVec2fMFVec2f2D向量每个数据成员都是一个2D向量,2D向量用一对浮点数表示。
SFVec3fMFVec3f3D向量每个数据成员都是一个3D向量,3D向量用3个浮点数表示。
SFTimeMFTime浮点数每个数据成员都是一个表示时间的浮点数,该值等于从1970年1月1日0:00到此刻的秒数。
SFRotationMFRotation每个数据成员都由4个浮点数所组成:3个限定旋转轴的x、y、z坐标和1个旋转角度。
SFNodeMFNode节点每个数据成员都是一个节点。
SFColorMFColor每个数据成员都是一种颜色,颜色由3个分别表示红、绿、兰的0~1间的浮点数所组成,如:001表示蓝色。
SFImage定义二维像素图像。它由三部分组成:(1)最前面两组整数,分别表示二维图像的宽度和高度,单位是像素(pixel);(2)接着的一个整数描述像素的性质:1表示灰度,2表示灰度透明,3表示RGB彩色,4表示RGB彩色透明;(3)最后是若干组十六进制或十进制的整数,分别描述每个像素的颜色、强度和透明度。

—-注释

—-1严格地说,第二代WWW还不是一个完全的VR系统,但它已经具有了VR系统的基本特征,并且正随着虚拟技术的发展而迅速地成熟起来,所以本文姑且称之为VR系统。

—-2超媒体是多媒体技术与超文本结合的结果。在超文本系统中,将节点拓展为多媒体演播单元就成为超媒体系统。超媒体系统与超文本系统的最大区别是其时间特性:超文本系统具有时间无关性,它的链、锚的特性都是时间无关的;而超媒体系统是时间相关的,它的音频、动态图像节点都具有时间相关性。

BS Contact VRML 各个版本特性

德国 Bitmanagement,致力开发Web3D/VRML/X3D和交互MPEG-4市场。

现在 Bitmanagement 正式推出的 BS Contact 浏览器,在场景版权保护、场景视觉效果、大场景浏览速度都取得了很大的发展。从 BS Contact VRML 6.1 开始提供对 ISO 标准 X3D (X3D/XML编码)的支持。BS Contact VRML/X3D 6.2 开始支持硬件的 Direct3D 9 的高级特效,Bitmanagement 正在参与 X3D 工作组的程序化着色标准的开发。Bitmanagement 还积极的参与多用户 MPEG-4 浏览器的开发。最新 物理系统的支持也在开发中。

Contact 7.108 特性

PhysX 物理引擎☆☆☆☆
多用户支持☆☆☆☆
脚本和界面中支持UTF-8 和 Unicode☆☆☆☆
支持中文文字显示为纹理☆☆☆☆
base64 编码图片加密 ☆☆☆☆
cookie 读取与设置☆
Flash 动态纹理和 Flash 交互链接支持☆
64 位版支持超过 2G 的内存读写 ☆☆☆☆
COLLADA 格式支持
物体级别的自动 LOD

Contact 7.04 特性

组渲染节点,可用于后期特效处理的 ☆☆☆☆☆☆☆
实时渲染到纹理节点更新,并可支持 HDR 格式和多渲染目标 ☆☆☆
粒子系统支持自定义发射器形状 ☆
支持 DirectX 合声和变声特效

Contact 7.0 特性

JPEG2000 小波压缩纹理格式支持
几何对象阻隔优化
自定义透视/轴侧视角 ☆
高分辨率静像渲染 ☆
视频渲染输出 ☆☆
实时视频输入支持 ☆☆☆☆☆☆☆
立体纹理支持
元数据节点 ☆
数据类型扩展
CAD 组件扩展

Contact 6.2 特性

Mozilla FireFox 支持
DirectX 9 支持,支持 DirectX 9 FX 特效文件和 HLSL(高级着色语言)☆☆☆☆☆☆☆
DirectX 9 图形驱动下支持 TGA、HDR 图像格式
支持高级程序化着色引擎(Shader)节点,支持提议中的 X3D 程序化着色标准 ☆☆☆☆☆☆
支持 OpenGL 图形驱动下的红蓝立体显示,另外还提供支持 OpenGL 四方立体显示加速的特别版本的 BS Contact Stereo。☆☆☆
支持专门的组节点调节 Z-buffer 顺序
支持 X3D 事件工具节点
支持由场景来指定浏览器的设置
截图工具 ☆

Contact 6.1 特性

场景内容加密 ☆☆☆☆☆
X3D支持 X3D/XML编码支持,支持XML DOM 整合 ☆☆☆
BSP树、四叉树、八叉树等大场景优化节点 ☆☆
Composite textures 合成纹理支持透明度
兼容 Mozilla、Opera、Netscape
Internet Explorer 下的CAB包自动安装 ☆
Internet Explorer 安全补丁兼容性修正

Contact 6 特性

增强了多纹理的支持,支持凹凸贴图
整合 H-Anim 2001角色动画系统支持、顶点变形优化
过程化纹理用于支持自动生成水等动态纹理
DSS压缩纹理格式支持
多显示支持
支持Quake类的操控方式
支持游戏杆和3D眼镜
透明网页镶入模式及在三维场景镶入浏览器模式

Contact 5.1 特性

实时阴影投射

Contact 5.0 特性

底层图形管线控制节点,支持直接Z缓存、模板缓存的读写和多遍绘制 ☆☆☆☆☆☆☆
环境反射和金属效果
多纹理支持
动态3D视点纹理支持
MPEG-4 层
MPEG-4 二维图形扩展
大场景内存优化
自由变形
真实雾化
纹理化文本
拖放事件支持
键盘鼠标支持
EAI 及 EAI 扩展

VRML的基础教程

VRML创作工具很多是“所见即所得”式的,通过图形界面可以方便地创作虚拟境界,但VRML不仅仅是普通的三维设计,尽管这些工具很容易上手,却往往屏蔽掉了VRML标准的具体细节,因为如果想深入掌握VRML,还需要全面了解节点、域、检测器等技术细节,而达成此目的的最好方法就是用编写文本文件的方式创作VRML境界。本教程提供了六个典型例子,这些例子并不复杂,也不精彩,但涵盖了VRML的关键内容。

在开始创作之前,应作好下面的准备。

文本编辑器 随便你喜欢的文本编辑器,如Win95下的NotePad,Dos下的Edit等等。

VRML浏览器 若用的Web浏览器是Netscape4.0一下版本,可下载CosmoPlayer(http://cosmo.sgi.com);若用的是Netscape4.0或更高版本,则已内置CosmoPlayer2.0,只是安装Netscape时请注意是否选中了相应选项;若用的是Internet Explore4.0,则有可能已经内置了VRML2.0浏览器,判断是否内置的方法很简单,就是看它能否打开VRML文件(*.wrl,*.wrz),如果不行,可以从http://www.microsoft.com/vrml/下载VRML浏览器插件,对于IE3.x,还需要下载一些辅助插件。当然在开始之前应基本熟悉VRML浏览器的操作方法。

硬件 VRML和硬件平台无关,只要能提供VRML浏览器。在下面的教程中,我们假定硬件平台是微机,输出设备是图形窗口,输入设备为鼠标器和键盘。当然,如果有更先进的虚拟现实设备和支持它的VRML浏览软件效果会更好。对于我们将要创作的境界,微机就足够了。

资料 本站就是最全面的资料,遇到新概念时可查阅本站相关资料。

第一节 “Hello,World!”

按照惯例,我们以”Hello,World!”作为我们的第一个虚拟境界,它由立方体、圆锥和球体组成,你可能已经注意到,VRML的标志正是由这三个几何形状构成的。输入的第一行文字是:

#VRML V2.0 utf8

这是VRML文件的标志,所有2.0版本的VRML文件都以这行文字打头,VRML97是由VRML2.0版修订而成的,符合VRML97规范的VRML文件也以这行文字打头。其中“#”表示这是一个注释。而utf8表示此文件采用的是utf8编码方案,这在标准中有详细说明。

先加入一个Group节点(组节点):

Group {

组节点的花括号之内的所有内容视为一个整体,利用组节点可以把虚拟场景组织成条理清晰的树形分支结构。下面定义组节点的children域(孩子域):

children [

在children后的方括号内定义Group节点的所有孩子对象,第一个孩子是一个Shape节点(形态节点),它描述一个几何形状及其颜色等特征:

Shape {

在Shape 节点内定义一个几何体Box(方盒节点):

geometry Box {}

注意我们没有为Box定义任何域,这意味着它的尺寸和坐标位置等特性取缺省值(单位立方体)。随后补齐各右括号:

}

]

}

至此,我们已经成功地制作了第一个虚拟境界,把它保存为Hello World.wrl,下面是完整的文件:

#VRML V2.0 utf8

Group {

children [

Shape {

geometry Box {}

}

]

}

用浏览器打开这个文件,你会看到一个灰色的立方体,尽管不太好看,但你还是可以通过改变视点位置从不同方位观察它,初步体验“三维交互”的感觉。

下面定义立方体的外观,这只需改变Shape节点的appearance域(外观),appearance 域是一个Appearance 节点,此Appearance节点的material域(材质)定义为一个Material 节点:

appearance Appearance {

material Material {}

}

这样,上面的Shape节点变成了:

Shape {

appearance Appearance {

material Material {}

}

geometry Box {}

}

这是定义几何造型的基本格式。现在立方体还是灰色的,这是因为其中的Material节点采用的还是缺省值,下面修改它的diffuseColor域(漫射色),VRML的颜色说明采用的是RGB颜色模型,所以要定义红色的立方体,漫射色应该是{1 0 0},三个数字依次表示红色、绿色和蓝色,取值范围都是0到1:

material Material {diffuseColor 1 0 0 }

现在我们生成了第二个场景,完整的代码是:

#VRML V2.0 utf8

Group {

children [

Shape {

appearance Appearance {

material Material { diffuseColor 1 0 0 }

}

geometry Box {}

}

]

}

在这个场景中,红色的立方体位于屏幕的中心,它的中心坐标为{0 0 0 }。若想把它移动一个位置,可以通过为它外套一个Transform(变换节点)来实现:

Transform {

translation 5 0 0

children [

Shape {

appearance Appearance {

material Material {}

}

geometry Box {}

}

]

}

在VRML中,Transform节点除了可以引进平移、旋转和缩放变换以外,其作用和Group节点的作用一样。把Transform 节点的translation域(平移)设置为5 0 0,意味着Transform节点所在的坐标系相对于其上层坐标系向右平移(即x轴方向)5个单位,在其它两个方向不移动,VRML的距离单位是米,5个单位相当于5米。我们第三个场景的完整代码是:

#VRML V2.0 utf8

Group {

children [

Transform {

translation 5 0 0

children [

Shape {

appearance Appearance {

material Material { diffuseColor 1 0 0 }

}

geometry Box {}

}

]

}

]

}

接下来我们把方块所在的Transform节点复制三份,并把各自包含的几何形状依次定义为方块、球体和圆锥:

Group {

children [

Transform {

translation 5 0 0

children [

Shape { …. geometry Box {} }

]

}

Transform {

translation 0 0 0

children [

Shape { … geometry Sphere {} }

]

}

Transform {

translation -5 0 0

children [

Shape { … geometry Cone {} }

]

}

]#end of Group children

}

你可能已经感觉到,VRML文件中有许多括号(花括号“{}”和方括号“[]”),所以务请注意括号的配对,建议采用本教程的缩进风格。注意上面的VRML文件中三个Transform节点的平移量是不同的,因而三个几何体的位置也就不同。另外,还可以修改三个几何体的颜色:球面Sphere为绿色(0 1 0),圆锥为蓝色( 0 1 0 )。最后,为了以后引用方便,分别给这三个Transform 节点指定一个名称:

DEF box Transform {…}

DEF sphere Transform {…}

DEF cone Transform {…}

这个VRML场景的完整代码是:

#VRML V2.0 utf8

Group {

children [

DEF box Tranform {

translation 5 0 0

children [

Shape {

appearance Appearance {

material Material { diffuseColor 1 0 0 }

}

geometry Box {}

}

]

}

DEF sphere Transform {

translation 0 0 0

children [

Shape {

appearance Appearance {

material Material { diffuseColor 0 1 0 }

}

geometry Sphere {}

}

]

}

DEF cone Transform {

translation -5 0 0

children [

Shape {

appearnance Appearance {

material Material { diffuseColor 0 0 1 }

}

geometry Cone { }

}

]

}

]# end of Group children

}

把此文件保存为helloworld.wrl,用VRML浏览器打开这个文件,通过调整视点从多个方位浏览自己的作品。

小结:在这一节,我们创建了第一个虚拟境界,涉及到如何用几何体构建境界,以及如何设定几何体的颜色与材质。尽管这个由方块、圆锥和球体组成的场景图比较简单,但已经反映了VRML的基本功能。当然,除了可用鼠标改变视点外,这还只是一个静态世界,在下一节,我们将引进VRML的动态特征。

第二节 增加交互能力

上一节我们学习了用几何体建立虚拟境界以及为几何体赋予色彩和材质的方法,这样建立的虚拟境界是静态的。这一节我们将使一个几何体(为了更具一般性,下面我们称之为对象)能够根据用户动作做出反应,即交互能力,这是VRML2.0最突出的特征。

1。检测器

在VRML中,检测器(Sensor)节点是交互能力的基础。检测器节点共九种。在场景图中,检测器节点一般是以其它节点的子节点的身份而存在的,它的父节点称为可触发节点,触发条件和时机由检测器节点类型确定。

接触检测器( TouchSensor)是最常用的检测器之一,最典型的应用例子是开关。其它检测器将在后续教程中陆续介绍。这里我们定义一个开关节点lightSwitch(这是一个组节点),并定义一个接触检测器作为它的子节点:

DEF lightSwitch Group {

children [

各几何造型子节点…

DEF touchSensor TouchSensor {}

]

}

这样开关节点lightSwitch就是一个可触发节点。当然,检测器存在的理由是它被触发时能够引起某种变化,所以在更深入讨论开关节点之前,我们先讨论一下场景变化。 2.视点

最常见的变化是视点的变化,在我们的第一个境界中你可能已经体验到视点变化:当你拖动鼠标或按动箭头键时(按照VRML术语,称为航行),虚拟境界就会旋转或缩放,这实际上是在调整你的视点位置或视角。在虚拟场景的重要位置可以定义视点节点(ViewPoint),它们是境界作者给用户推荐的上佳观赏方位,在CosmoPlayer浏览器中,用户就可以通过鼠标右键选择作者推荐的各个视点。这里我们定义两个视点节点:

DEF view1 Viewpoint {

position 0 0 20

description “View1”

}

DEF view2 Viewpoint {

position 5 0 20

description “view2”

}

我们的潜在目的是使用户可以通过触发开关节点来切换视点。现在先研究一下这两个视点节点,其中的坐标表示视点在场景中的位置,坐标的单位是米,这在前面已经提到过,视点的名称将会在浏览器菜单中提示出来供用户选择。把上述视点说明加入helloworld.wrl中(放在Group节点之前),并把其中的方块节点修改成可触发节点:

DEF box Tranform {

children [

Shape { …. Box …}

DEF touchBox TouchSensor {}

]

}

把修改过的文件另存为“touchme.wrl”。

3。事件传递

下面我们把触发(用鼠标箭头按动方块)和场景变化(视点切换)这两件事情联系起来,在场景图中,除节点构成的层次体系外,还有一个“事件体系”,事件体系由相互通讯的节点组成。能够接收事件的节点都应具有事件入口(eventIn),如果它要接收多种类型的事件(称为入事件),它就应该具有多个事件入口,也就是说,事件入口象节点的域一样是有类型的。同样,发送事件的节点应有事件出口(eventOut),事件出口也是有类型的。例如ViewPoint节点就有一个事件入口set_bind,当向此事件送入一个值“TRUE”(即所谓的入事件)时,该viewpoint节点成为当前视点。又如,接触检测器TouchSensor有一个事件出口isActive,当受到用户触发后它就从此出口送出一个“TRUE”(即所谓的出事件),补充一句,在下一个事件发送之前,此事件一直保存在事件出口中(作为记录)。

事件出口和事件入口通过路径相连,这就是VRML文件中除节点以外的另一基本组成部分:ROUTE 语句。ROUTE语句把事件出口和事件入口联系在一起,从而构成事件体系。在这里,我们是把接触检测器touchBox的事件出口isActive连接到视点节点view2的事件入口set_bind:

ROTUE touchBox.isActive TO view2.set_bind

好了!现在我们得到的VRML文件是:

#VRML V2.0 utf8

DEF view1 Viewpoint {

position 0 0 20

description “view1”

}

DEF view2 Viewpoint {

position 5 0 20

description “view2”

}

Group {

children [

DEF box Transform {

translation 5 0 0

children [

Shape {

appearance Appearance {

material Material { diffuseColor 1 0 0}

}

geometry Box {}

}

DEF touchBox TouchSensor {}

]

}

DEF sphere Transform {

translation 0 0 0

children [

Shape {

appearance Appearance {

material Material { diffuseColor 0 1 0}

}

geometry Sphere {}

}

]

}

DEF cone Transform {

translation -5 0 0

children [

Shape {

appearance Appearance {

material Material { diffuseColor 0 0 1 }

}

geometry Cone {}

}

]

}

] #end of Group children

}

ROUTE touchBox.isActive TO view2.set_bind

把这个文件调入浏览器,然后把鼠标指向方块并按下左钮(先别松开!),可以看到视点已经变为view2,内部的机制我们已经很清楚:左钮按下时方块节点的接触检测器被触发,接着接触检测器从事件出口isActive送出一个事件“TRUE”,这个事件通过路由进入视点节点view2的事件入口set_bind,view2收到“TRUE”后成为当前视点,所以在我们眼前场景发生了变化。

现在松开左钮,可以看到场景恢复到原来方位,这种功能称为视点回跳,其原因是松开左钮后接触检测器向view2发送了一个“FASLE”事件,这样view2当前的地位被解除,原来的视点成为系统视点栈的栈顶节点(即当前视点),详细说明可参见标准中对视点节点的专门论述。如果我们不想视点回跳,就想停留在view2视点,那该怎么办呢?这种非系统缺省功能要自己来定义。

4。 利用脚本编写自定义行为 在VRML中,利用Script节点(脚本节点)定义用户自定义行为,所谓定义即用脚本描述语言(Scripting Language)编写脚本的过程。VRML97支持的脚本描述语言目前有两种:Java和EMCAScript(这是JavaScript标准化后的名称),关于这两种语言本身,请参考相应参考书,VRML97标准中定义了它们和VRML的接口方法。应提请注意的是:VRML是基于节点的语言,所以脚本也是封装在Script这个特殊节点中的。这里我们不过多讨论脚本描述语言的细节,主要讨论把脚本集成到VRML文件中的方法。

上面我们曾把接触检测器touchBox 和视点view2直接通过路径连接起来,现在要定义我们指定的行为,就需要在二者之间插入一个脚本节点,也就是让路径绕个弯: ROUTE touchBox.isActive TO touchScript.touchBoxIsActive

ROUTE touchScript.bindView2 TO view2.set_bind

其中的脚本节点touchScript有一个事件人口touchBoxIsActive和一个事件出口bind_View2,前者接收来自接触检测器touchBox的事件,然后经自己的脚本处理后,把结果发送给视点节点view2:

DEF touchScript Script {

eventIn SFBool touchBoxIsActive

eventOut SFBool bindView2

url”javescript:

function touchBoxIsActive(active) {

bindView2= TRUE;

}”

}

关于这个Script节点,请注意一下几点:(1)它的事件入口touchBoxIsActive和事件出口bindView2是自定义的,其它VRML节点的域和事件都是固定的。(2)事件入口touchBoxIsActive(即入事件)和事件出口bindView2(即出事件)的类型都是SFBool(单值布尔型),touchBox的事件出口isActive和view2的事件入口set_bind的类型也是相同的。(3)“url”是脚本节点的一个域,可以直接包含脚本,也可以包含一个或多个用URL地址指示的脚本,若有多个地址,则按照先后次序获取第一个可得到的脚本。(4)脚本是以函数(function)的形式给出的,函数名touchBoxIsActive 与事件入口的名称相同,这是和ECMAScript语言的接口约定,表示相应事件入口收到事件后调用此函数进行处理。

5.事件流程与小结

下面我们整理一下事件流程:

(1)用户在方块上按下鼠标左键。

(2)接触检测器发出一个“TRUE”事件。

(3)此事件进入脚本节点touchScript的事件入口touchBoxIsActive.

(4)调用脚本函数touchBoxIsActive(注意函数并没有判断入事件的值)。

(5)函数向touchScript的事件出口bindView2发送一个“TRUE”事件(还可以进行其它判断或执行其它事件)。

(6)view2节点收到“TRUE”事件,成为当前视点。按照VRML约定,“认为”上述事件是同时发生的,也就是这些事件的时间戳相同。

(7)若用户松开鼠标左键,则接触检测器发出一个“FALSE”事件,此事件同样引起脚本函数调用并发送“TRUE”事件,所以view2仍然保持为当前视点。

本节的完整代码是:

#VRML V2.0 utf8

DEF view1 Viewpoint {

position 0 0 20

description “view1”

}

DEF view2 Viewpoint {

position 5 0 20

description “view2”

}

Group {

children [

DEF box Transform {

translation 5 0 0

children [

Shape {

appearance Appearance {

material Material { diffuseColor 1 0 0 }

}

geometry Box {}

}

DEF touchBox TouchSensor {}

]

}

DEF sphere Transform {

translation 0 0 0

children [

Shape {

appearance Appearance {

material Material { diffuseColor 0 1 0}

}

geometry Sphere {}

}

]

}

DEF cone Tranform {

transltion -5 0 0

children [

Shape {

appearance Appearance {

material Material { diffuseColor 0 0 1 }

}

geometry Cone {}

}

]

}

] #end of Group children

}

DEF touchScript Script {

eventIn SFBool touchBoxIsActive

eventOut SFBool bindView2

url “javascript :

function touchBoxIsActive (active) {

bindView2 = TRUE;

}”

}

ROUTE touchBox.isActive TO touchScript.touchBoxIsActive

ROUTE touchScript.bindView2 TO view2.set_bind

小结:本节建立的虚拟境界并不复杂,但涉及到了VRML2.0最基础性的功能和概念:利用检测器产生事件、利用路由传递事件以及利用脚本编写自定义行为,掌握了这些内容也就掌握了VRML2.0的核心。在后面的几节中,我们将探索一些专题性的有趣功能,而本节是基础,因而必须透彻理解。

第三节 邻近检测器

本节讨论邻近检测器(proximitySensor),当用户进入或离开邻近检测器所划定的区域时就会触发它。正如你在标准中可以查到的那样,ProximitySensor节点定义为:

ProximitySensor {

exposedField SFVec3f center 0 0 0

exposedField SFVec3f size 0 0 0

exposedField SFBool enabled TRUE

eventOut SFBool isActive

eventOut SFVec3f position_changed

eventOut SFRotation orientation_changed

eventOut SFTime enterTime

eventOut SFTime exitTime

}

这里稍作介绍。ProximitySensor节点共有三个外露域(exposedField)和五个出事件(eventOut).出事件我们已经熟悉,是节点状态发生改变时用来通知其它节点的,这里的出事件isActive 用于ProximitySensor通报自己已被激活。enterTime和exitTime通报用户(代表用户的用户化身或指示器)进入和退出ProximitySensor检测区的时刻。若用户已在检测器之内,则当用户的位置或方位发生变化时,送出position_changed和orientation_changed出事件这五个出事件联合起来,就定义了邻近检测器的功能。外露域则集域(Field)、入事件(eventIn)和出事件(eventOut)三者的功能于一身,也就是说,它既象域一样描述了节点的当前状态,又可以作为入事件由其它节点修改这种状态,并作为出事件把这种改变通知其它节点。这里的enabled外露域是布尔型的,用于ProximitySensor的启用和停用,center和size定义形为长方体的邻近检测区。

我们的出发点是第一节中建造的境界helloworld,它是由方块、球体和圆柱这三个物体构成的静态世界,现在在球体周围增加一个邻近检测区:

DEF sphere Transform {

translation 0 0 0

children [

Shape {….}

DEF comeClose ProximitySensor {

center 0 0 0

size 4 4 4

}

]

}

ProximitySensor的名字为comeCloser,邻近区的中心和球体的球心重合,形状为正方体,边长为4米,是球体直径的两倍。当用户走进球体时就会触发这个邻近检测器,检测器发出isActive事件,我们把这个事件出口通过路由指向Script节点(用来绑定视点2):

DEF comeCloserScript Script {

eventIn SFBool enterProximitySensorIsActive

eventOut SFBool bindView2

url ” javascript :

function enterProximitySensorIsActive (active) {

bindView2=TRUE;

} “

}

随后,我们在邻近检测器的出事件isActive和脚本节点comeCloserScript的入事件enterProximitySensorIsActive之间建立路由,后者收到事件后执行函数enterProximitySensroIsActive,函数发出bindView2出事件,这个出事件通过路由连接到视点节点View2:

ROUTE comeCloser.isActive TO comeCloserScript.enterProximitySensorIsActive

ROUTE comeCloserScript.bindView2 TO view2.set_bind

也就是说,一旦用户进入邻近区,境界的当前视点将转换成View2.这个由两个视点、三个物体、一个邻近检测器和一个脚本节点组成的境界的完整代码如下:

#VRML V2.0 utf8

DEF view1 Viewpoint {

position 0 0 20

description “view1”

}

DEF view2 Viewpoint {

position 0 0 20

description “view2”

}

Group {

children [

DEF box Transform {

translation 5 0 0

children [

Shape {

appearance Appearance {

material Material { diffuseColor 1 0 0 }

}

geometry Box {}

}

]

}

DEF sphere Transform {

translation 0 0 0

children [

Shape {

appearance Appearance {

material Material { diffuseColor 0 1 0 }

}

geometry Sphere {}

}

DEF comeCloser ProximitrySensor {

center 0 0 0

size 4 4 4

}

]

}

DEF cone Transform {

translation -5 0 0

children [

Shape {

appearance Appearance {

material Material { diffuseColor 0 0 1}

}

geometry Cone {}

}

]

}

]#end of Group children

}

DEF comeCloserScript Script {

eventIn SFBool enterProximitySensorIsActive

eventOut SFBool bindView2

url “javascript :

function enterProximitySensorIsActive(active) {

bindView2=TRUE;

}”

}

ROUTE comeCloser.isActive TO comeCloserScript.enterProximitySensorIsActive

ROUTE comeCloserScript.bindView2 TO view2.set_bind

启动VRML浏览器进入境界,面向球体一直走过去,当你刚刚感到靠近球体时,会突然感到自己后退了一大步(或者说物体跳到前方更远的地方),这表明邻近检测器已经检测到你的靠近,它把这件事通知脚本节点,脚本节点把视点View2绑定成当前视点,从而使你感到视点突然改变。

再稍稍修改一下邻近检测器,把它的中心位置向右移了2米:

DEF comeCloser ProximitySensor {

center 2 0 0

size 4 4 4

}

这样你就可以从左边(方块那一边)走进球体(视点不跳),但不能从右边(圆锥那一边)走近它(视点跳转)。

总之,ProximitySensor能够检测用户是否进入或离开检测器指定的空间区域,典型用法是当用户走进房间时开启灯光,当用户离开时关闭灯光,从而建立功能丰富的“智能”空间。

第四节 连续动画

在第二节中我们已经使用过接触检测器,当我们把鼠标指针放到方块(这个几何节点包含接触检测器)上面时,指针形状发生变化,这意味着我们已经进入检测区,如果按下鼠标左钮,则按照我们的定义,当前视点会发生变化。

这一节仍然制作这样一个对接触有反应的方块,只是接触后它会连续不断地转动,动画行为可以用时间检测器(TimeSensor)驱动,而不断变化的旋转值可用脚本节点或朝向插补器(orientationInterpolator)给出。

1。接触检测器

作为开始的基本代码是:

#VRML V2.0 utf8

DEF cube Transform {

rotation 1 1 1 0

children [

Shape {

appearance Appearance {

material Material { diffuseColor 1 0 0 }

}

geometry Box {}

}

DEF TouchS TouchSensor {}

]

}

DEF revolver Script {

eventIn SFBool startRevolving

eventOut SFRotation revolve

field SFFloat angle 0

url “javascript :

function startRevolving () {

revolve[0]=1;

revolve[1]=1;

revolve[2]=1;

revolve[3]=angle;

angle+=0.1;

}”

}

ROUTE TouchS.isOver TO revolver.startRevolving

ROUTE revolver.revolve TO cube.set_rotation

其中,方块cube包含两个子节点,前者定义了它的形态(红色的单位立方体),后者把它定义成接触检测器。注意,cube的类型是Transform节点,它的rotation 域是外露域,指定本组相对于上层坐标系的旋转值,这里指定的初始值是“1 1 1 0 ”,其中前三个数值定义旋转轴,最后一个值定义旋转角。由于它是外露域,因而可以通过入事件(名为set_rotation)进行修改,下面定义的动态行为就是这样实现的。

Script节点revolver的核心是内联的ECMAScript脚本函数。它给定一个不断变化的旋转值。当鼠标指针移动到方块之上时,接触检测器发出isOver,和第一节中采用的isActive事件不同,isOver只有在鼠标左钮按下时才会发出。isOver事件通过路由传递给脚本节点的事件入口startRevolving,从而启动函数startRevolving,函数将一个新的旋转值发往事件出口revolve,这个旋转值通过路由进入cube的外露域rotation,修改了方块的旋转角,引起它的朝向变化。鼠标指针在cube上面的每次方位变化都引起isOver事件发送一次,从而导致方块旋转一次。

2。时间检测器

为了使方块能够连续旋转,需要引进等间隔连续发送的时间序列,这正是时间检测器的用武之地。时间检测器随着时间推移不断产生事件,可用于多种目的,包括: a. 驱动连续性的仿真和动画

b. 控制周期性的活动(如每分钟一次)

c. 初始化单独事件,如报警钟

下面是我们要用的时间检测器和修改后的路由关系:

DEF ticker TimeSensor {

cleInterval 0.1

loop TRUE

enabled FALSE

}

ROUTE TouchS.isOver TO ticker.set_enabled

ROUTE ticker.cycleTime TO revolver.startRevolving

ROUTE revolver.revolve TO cube.set_rotation

enabled用于启用和停用时间检测器,开始时它处于停用状态,以后由接触检测器的isOver事件修改这一状态。启用的时间检测器每隔0.1秒送出一个cycleTime事件,并用它来触发revolver的startRevolving事件,注意,cycleTime事件的类型为SFTime,而路由两端事件的类型必须匹配,所以尽管这里我们不关心这个事件表示的具体时刻,还是把revolver的startRevolving事件类型也改为SFTime.这样,revolver的函数startRevolving()就会每0.1秒调用一次,从而驱动方块连续旋转。完整的代码是:

#VRML V2.0 utf8

DEF cube Transform {

rotation 1 1 1 0

children [

Shape {

appearance Appearance {

material Material { diffuseColor 1 0 0 }

}

geoemtry Box {}

}

DEF TouchS TouchSensor {}

]

}

DEF revolver Script {

eventIn SFTime startRevolving

eventOut SFRotation revolve

field SFFloat angle 0

url “vrmlscript :

function startRevolving () {

revolve[0]=1;

revolve[1]=1;

revolve[2]=1;

revolve[3]=angle;

angle+=0.1;

}”

}

DEF ticker TimeSensor {

cycleInterval 0.1

loop TRUE

enabled FALSE

}

ROUTE TouchS.isOver TO ticker.set_enabled

ROUTE ticker.cycleTime TO revolver.startRevolving

ROUTE revolver.revolve TO cube.set_rotation

上述脚本节点的功能比较简单,只是不断送出调整的旋转值,它是关键帧动画的一种。由于关键帧动画十分常用,故VRML专门定义了插补器节点来实现它。

3。 朝向插补器

插补器节点可认为是VRML内置的脚本节点,它们执行简单的动态计算,通常和时间检测器或者能够使对象产生动作的节点结合在一起使用,生成线性关键帧动画。插补器节点实际上是一个由关键点和对应关键值定义的分段线形函数。根据插值类型的不同,VRML共定义六个插补器节点:ColorInterpolator(颜色插补器)、CoordinateInterpolator(坐标插补器)、NormalInterpolator(法线插补器)、OrientationInterpolator(朝向插补器)、positionInterpolator(位置插补器)、ScalarInterpolator(标量插补器)。

所有插补器的域和事件都是类似的:

eventIn SFFloat set_fruction

exposedField MFFloat key […]

exposedField MF<type> keyValue […..]

eventOut [S|M]F<type> value_changed

关键值域keyValue的类型决定了插补器的类型(例如,OrientationInterpolator的keyValue域的类型是MFFloat).入事件set_fraction接收SFFloat型的事件,插补器随即根据它进行插值,并通过出事件value_changed送出插值结果。

这里我们把时间检测器的fraction_changed事件作为插补器的输入,这个事件是一个[0,1]区间的值,每个时间步都送出一次,表示当前周期内已过去的时间相对于整个周期的比例,是插补器常用的输入源之一。与此对应,我们把插补器关键帧的取值也定义在[0,1]范围内。与0和1这两个关键帧对应的关键值的旋转轴是相同的,只是旋转角度不同(0,3.14159),这样方位插补器输出的旋转值的旋转轴固定不变,旋转角从0递增到3.14159,然后不断重复。

#VRML V2.0 utf8

DEF cube Transform {

rotation 1 1 1 0

children [

Shape {

appearance Appearance {

material Material { diffuseColor 1 0 0 }

}

geometry Box {}

}

DEF TouchS TouchSensor {}

]

}

DEF revolver orientationInterpolator {

key [0,1]

keyValue [ 0.5 0.5 0.5 0,0.5 0.5 0.5 3.14149]

}

DEF ticker TimeSensor {

cycleInterval 2

loop TRUE

enabled FALSE

}

ROUTE TouchS.isOver TO ticker.set_enabled

ROUTE ticker.fraction_changed TO revolver.set_fraction

ROUTE revolver.value_changed TO cube.set_rotation

小结:本节实现连续动画,动画由接触检测器启动,由时间检测器驱动,动画本身比较简单,就是不断地旋转。产生不断变化的旋转值的方法有两种:自己编写脚本,或者利用插补器节点。

第五节 动态修改场景图

场景图是描述境界结构的基本概念,节点是构成场景图的基本单元。组节点是能够包含字节点的节点,组节点本身还可作为其它组节点的子节点,从而形成层次性体系结构。VRML中的组节点包含Anchor(锚)、 Billboard(布告牌)、 Collision(碰撞)、Group (组)、Inline (内联)、LOD(细节层次)、 Switch(开关)、Transform(变换)共八种,除Inline、LOD、Switch这几个具有特殊功能外,它们都定义了入事件addChildren 和removeChildren ,前者用于向组节点的子节点域children 中增加新的子节点,后者用于从中删除子节点,这样就可以动态修改场景图的结构。

下面是我们这一节要建立的境界,开始的时候球体位于左边红色方块的内部,在按动底部的绿色方块后,球体进入右边蓝色方块之内。

首先定义三个方块:

#VRML V2.0 utf8

Viewpoint { position 0 0 15 }

DEF leftBox Transform {

translation -5 0 0

children [

Shape {

appearance Appearance {

material Material { diffuseColor 1 0 0 }

}

geometry Box {}

}

]

}

DEF rightBox Transform {

translation 5 0 0

children [

Shape {

appearance Appearance {

material Material { diffuseColor 0 0 1 }

}

geometry Box {}

}

]

}

DEF onoff Transform {

translation 0 -5 0

children [

Shape {

appearance Appearance {

material Material { diffuseColor 0 1 0 }

}

geometry Box {}

}

]

}

其中左边的方块为红色,右边的方块为蓝色,下边的方块为绿色,都是Transform类型,三者都位于场景图的最高层,都是场景图的根节点,都包含一个Box几何体作为子节点。下面为红方块增加一个球体子节点:

DEF leftBox Transform {

translation -5 0 0

children [

Shape {

appearance Appearance {

material Material { diffuseColor 1 0 0 }

}

geometry Box {}

}

DEF SphereChild Shape {

appearance Appearance {

material Material { diffuseColor 1 0 1 }

}

geometry Sphere { radius 1.2 }

}

]

}

为了以后引用方便,这里还为球体子节点起了名字:SphereChild .为了让用户能够增删这个儿子,把绿方块定义成接触检测器:

DEF onoff Transform {

translation 0 -5 0

children [

Shape {

appearance Appearance {

material Material {diffuseColor 0 1 0 }

}

geometry Box {}

}

DEF TS TouchSensor {}

]

}

子节点增删的具体任务由Script节点来完成:

DEF S Script {

eventIn SFBool isActive

eventOut MFNode child

field MFNode testNode USE SphereChild

 

url”javascript :

function isActive (value) {

if (value)child = testNode;

}”

}

注意它的出事件child的类型是MFNode,也就是说通过这个事件送出的是节点。节点S的testNode域是对球体SphereChild引用(USE语句),引用不复制该节点,而是把同一节点再次插入场景图,从而导致SphereChild拥有多个父亲,所以场景图仅仅是层次结构,而不是树形结构。加上下面的路由语句,建立事件联系:

ROUTE TS.isActive TO S.isActive

ROUTE S.child TO leftBox.removeChildren

ROUTE S.child TO rightBox.addChildren

接触检测器TS的激活事件isActive连接到脚本节点S的isActive,这样用户一旦按动绿方块,就会启动脚本节点的事件处理函数isActive(),此函数把testNode节点(即球体节点SphereChild )送至出事件S.child.根据路由,左边红方块的事件入口leftBox.removeChildren 收到此事件,按照removeChildren的语义,球体节点SphereChild从leftBox的子节点列表中删除。与此同时,右边蓝方块的事件入口rightBox.addChildren也收到S.child出事件,根据addChildren的语义,球体节点SphereChild加入 rightBox的子节点列表。通过这个过程,球体节点SphereChild的父节点从leftBox更换成rightBox.

第六节 扩充节点类型

VRML提供了54种节点类型,称为内部节点类型。然而实际应用种可能要求新的节点类型,原型(prototype)是VRML实现节点类型扩充的基本机制。新节点类型是根据已定义的(内部的或原型的)节点类型定义的,一旦定义,原型节点类型就可以象内部节点类型一样在场景图中实例化。原型可以在当前文件中定义并使用,也可以在其它文件中定义,即外部原型,外部原型提供了一种使节点类型能够跨越网络的机制。本节的原型例子取自VRML97标准,它定义的是一个桌子类型,这个原型为:

#VRML V2.0 utf8

PROTO TwoColor Table [ field SFColor legColor 0.8 0.4 0.7

field SFColor topColor 0.6 0.6 0.1 ]

}

Transform {

children [

Transform {

translation 0.0 0.6 0.0

children [

Transform {

appearance Appearance {

material Material { diffuseColor IS topColor }

}

geometry Box { size 1.2 0.2 1.2 }

}

}

Transform {

translation -0.5 0 -0.5

children [

DEF Leg Shape {

appearance Appearance { diffuseColor IS legColor }

}

geometry Cylinder { height 1 radius 0.1 }

}

]

}

Transform { #另一条桌腿

translation 0.5 0 -0.5

children USE Leg

}

Transform { #另一条桌腿

translation -0.5 0 0.5

children USE Leg

}

Transform { #另一条桌腿

translation 0.5 0 0.5

children USE Leg

}

]#根节点Transform的儿子结束

}#根Transform 结束

}#原型结束

原型语句PROTO分为原型接口声明和原型定义两部分、接口声明包括原型的入事件和出事件的类型和名称,以及原型的域的类型、名称和缺省值。这里的接口声明为:

PROTO TwoColorTable [ field SFColor legColor 0.8 0.4 0.7

field SFColor topColor 0.6 0.6 0.1 ]

这个原型的类型名称为“TwoColorTable”(双色桌),它有两个域:legColor(桌腿色)和topColor(桌面色)。作为节点类型,TwoColorTable的用法和其它内部节点类型一样,例如下面的语句定义一个TwoColorTable类型的节点,它的桌腿为红色,桌面为绿色:

TwoColor Table {

legColor 1 0 0 topColor 0 1 0

}

接口声明之后是原型的主体,称为原型定义。原型定义实际上是一个场景图,由一个或多个根节点、嵌入的PROTO语句和ROUTE语句构成,其中的第一个节点类型确定原型实例在VRML文件中的使用方法。例如,如果原型定义中的第一个节点是Material节点,则只要可以使用Material节点的地方,原型实例都可以使用。原型定义中定义的其它节点及其附带的场景图都不进入原型实例所在的变换层系,但可以被原型定义中的ROUTE语句或Script节点引用。TwoColorTable原型中的第一个节点是Transform组节点,它决定了TwoColorTable型节点在场景图中的方法,在场景图中添加一个TwoColorTable型节点,相当于增加Transform.

原型定义中节点的域、入事件、出事件可以通过IS语句和接口声明中的域、入事件、出事件建立关联,关联实际上相当于把原型定义中的这些域和事件公开作为原型的域和事件。关联的基本规则是域和域、入事件和入事件、出事件和出事件对应关联,原型定义中的外露域可以和接口声明中的域、入事件、出事件或外露域关联。本例中的关联有两个:桌面diffuseColor 域和接口声明中的topColor域,桌腿的diffuseColor域和接口声明中的legColor域之间。也就是说,TwoColorTable型节点中的topColor和legColor值实际上分别确定了桌面和桌腿的漫反射色diffuseColor.

第七节 结束语

本教程创建了六个典型VRML境界,介绍了VRML的主要功能。这些例子的侧重点在于VRML的交互式特征,而不是营造境界的造型特征,后者可参见一般的三维图形工具,把这二者结合起来,可以创建更加精彩的交互式3D境界。本章根据需要介绍了部分VRML节点的基本用法,以后将对VRML节点进行分类和较为全面的评论。

当然,VRML功能十分丰富,要成为VRML专家,一方面需要研读VRML97标准,以求全面深入的掌握和应用,另一方面,要经常研读成功的作品,获取创作灵感。