O3d 是一套开源的javascript API, 用来制作在浏览器中运行的3D 游戏,当然也可以用来展示商家的产品,做一些3D 模型。这系列教程会用一个个例子来展示如何写一个o3d 程序,展示o3d 所能够做的应用。
巧妇难为无米之炊,首先当然需要一些基本的东西,像o3d 插件,这个可以在google code 中获取,给个链接http://code.google.com/intl/zh-CN/apis/o3d/ ,还有O3d 的库文件,这个我是在下载的demo 中直接拷出来了(在本章最后上传的附件中)所需要的基本上就这些了,到后来要导入模型的话就再说了,至于IDE ,哪个写js 写得顺手就是哪个了,我比较喜欢用aptana 。调试的话个人觉得firebug 就已经很强大了,像设置断点,查看局部变量等都很方便了。
这一章介绍一个最基本的o3d 程序应该有的框架,就像在windows 下要写一个最基本的opengl 程序一样,需要建立一个窗口,处理一些键盘和鼠标的消息,设置回调函数,还有建立一个3D 世界所必须的视口等等,刚开始会做很多像这样的初始化和设置工作,像windows 编程一样,一开始可能会让人感到迷茫,这么多繁杂的东西,都不知道是用来干嘛的,为什么不能调用一个函数就搞定一切呢,其实如果搞不懂的话,可以先完全不管这些,正如这章的标题,这只是个框架,你可以从教程中把demo 下下来,作为程序框架,然后在上面改就可以了。随着学习的深入,再回过头去看看,便会逐渐理解那些东西了。
好了,现在开始写一个最基本的o3d 程序吧。
首先,这个毕竟是在浏览器中运行的,所以一个基本的HTML 结构是必须的,当然还要包含一些基本的js 库文件
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
"http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="content-type" content="text/html; charset=UTF-8">
<title>my first o3d application</title>
<script type="text/javascript" src="o3djs/base.js"></script>
<script type="text/javascript">
// 下面包含一些实用库
o3djs.require('o3djs.util');
o3djs.require('o3djs.math');
o3djs.require('o3djs.rendergraph');
o3djs.require('o3djs.primitives') ;
o3djs.require('o3djs.effect');
o3d 有一系列的库来帮助你简化一些繁琐的活,而你要调用其中的函数的话,只需要调用 o3djs.require('o3djs. ... ') 来包含所需要的类库。比如o3djs.math 中有许多数学运算的方法,进行矩阵的转换等等。
接下来就要设置这个o3d 开始的地方吧(其实就是当页面加载完后回调的函数,一般js 程序都要做的一件事)
// 当页面加载完后调用init() 函数
// 当页面被关掉后调用uninit() 函数
window.onload = init;
window.onunload = uninit;
在具体实现这两个函数前, 先把接下来要用的一些全局变量定义好
var g_o3d ;
var g_math ;
var g_client ;
var g_pack ;
var g_finished = false ; // for selenium testing
然后就要定义init ()函数了,这个函数其实就做了一件事,那就是再调用 o3djs.util.makeClients() 函数, 这个函数是已经实现了的,它用来查找在整个html 文档中是否有id 用o3d 开头的元素(像o3d ,o3d_elem,o3d_lang 都是可以的),然后再这个元素里面开辟一块区域( 一个object) ,这个就相当于windows 的窗口了,以后操作都是在这里面进行的,所以这个函数所做的其实跟windows 编程中的建立窗口差不多。
你也可以手工操作在html 中插入一个div
<!-- Start of O3D plugin -->
<div id="o3d" style="width: 600px; height: 600px;"></div>
<!-- End of O3D plugin -->
不过makeClients 毕竟是自动化的,它会在开始检测你的浏览器是否装了o3d 插件,你的插件是否过时了等等(很婆妈啊。。。。),然后据说这个函数能够帮你的o3d 应用加速。
扯了这么多,下面是这个很简洁的init 函数
function init () {
o3djs . util . makeClients ( initStep2 )
}
makeclients 的参数initStep2 是这个函数执行完毕后的回调函数。也就是说这个函数执行完后就执行initStep2 函数了。
如果说Init 函数是用来建立窗口的话,initStep2 函数就是用来初始化视口等这些一个3D 世界必须的东西。这些都可以说是套路吧,你程序不管怎么写,这些操作是必须的。
下面一步步来看initStep2 这个函数:
首先当然是定义这个函数了,clientElements 作为穿进去的参数,是makeClients 在找到id 是o3d 开头的元素后所建立的各个object 。
function initStep2(clientElements) {
接下来就要用到上面定义的各个全局变量了
var o3dElement = clientElements [ 0 ];
g_client = o3dElement . client ;
g_o3d = o3dElement . o3d ;
g_math = o3djs . math ;
o3dElement 是在HTML 中的一个元素,是DOM 的一部分(即是那个object )
g_client 是 整个o3d 应用的入口点,
g_o3d 是o3d 的命名空间,
g_math 是整个数学库的命名空间。
下面要创建一个包(pack) 来管理所有的对象(object) 和这些对象的生存时间,到后面讲到创建的模型,效果和材质时都是一个个对象。而这些对象就都由这个包来管理了
g_pack = g_client.createPack();
接下来创建一个渲染图(renderGraph) ,用库函数renderGraph.createBasicView() 产生一个标准的渲染图。这个渲染图有两个绘图列表,一个用于绘制非透明材质的元素(性能绘图列表),另一个用于绘制透明材质元素(透明效果绘图列表)
var viewInfo = o3djs . rendergraph . createBasicView (
g_pack ,
g_client . root ,
g_client . renderGraphRoot );
然后就是建立绘制背景(drawContext) 。绘制背景定义了投影方式(让一个3d 世界显示在平面的显示器中需要用到投影的知识了,其实就是把3 维坐标系中各个点的坐标乘以一个矩阵,然后就得到一个2 维坐标系的坐标,在游戏编程中基本上用到的只有透视投影),还有照相机的位置,这个照相机只是个比较形象的概念,跟真实世界中相似,3D 世界中的东西最后都是投到这个照相机上,而坐在电脑前的玩家也通过这个照相机看到了一个接近真实的世界。
// 建立一个基本的透视投影
viewInfo . drawContext . projection = g_math . matrix4 . perspective (
g_math . degToRad ( 30 ),
g_client . width / g_client . height ,
1 , // 近切面的Z 轴坐标.
5000 ); // 远切面的Z 轴坐标.
// 设置照相机
viewInfo . drawContext . view = g_math . matrix4 . lookAt ([ 0 , 1 , 5 ], // eye
[ 0 , 0 , 0 ], // target
[ 0 , 1 , 0 ]); // up
lookAt 方法有三个参数,第一个是照相机的位置坐标,第二个是照相机朝向的位置,第三个是照相机朝上位置的向量,比如[0,1,0] 就是正朝上。
好了,现在该基本的绘制背景已经设好了,照相机也准备好了,对准了适当的位置,现在就要在照相机前面的这个世界中放入你自己喜欢的东西了。这个要涉及到具体的模型,材质,纹理贴图等等细节了。这章暂不讨论。
到这里这章本该差不多结束了,除了在最后加个 unload() 然后把html 中的body 里的东西填一下。但是只是这样的话看不出有什么效果,这个是让人很不爽的,写了这么大堆东西连行不行都不知道,于是至少得在里面加一个东西,看看有什么效果啊。
下面这段代码就是用来创造一个球。
var effect = g_pack.createObject('Effect');
var material = g_pack.createObject('Material');
// Set the material's drawList.
material.drawList = viewInfo.performanceDrawList;
material.effect = effect;
var shape = o3djs.primitives.createSphere(g_pack, material, 0.5, 20, 20);
// Create a new transform and parent the Shape under it.
g_cubeTransform = g_pack.createObject('Transform');
g_cubeTransform.addShape(shape);
// Parent the cube's transform to the client root.
g_cubeTransform.parent = g_client.root;
加一个结束的标志
g_finished = true ; // for selenium testing.
}
程序结束调用的uninit() 函数,
function uninit() {
if (g_client) {
g_client.cleanup();
}
}
</script>
</head>
到这里还只是写了Html 中的head ,然后在body 中加入上文提到的id 是o3d 。。。的元素
<body>
<div id="o3d" style="width: 600px; height: 600px;"></div>
</body>
</html>
到这里一个简单的o3d 程序就完成了。