您的位置:首页 > Web前端 > JavaScript

Three.js的轻量级封装框架Sim.js解析(1)

2016-03-13 13:17 363 查看
这段时间在看《WebGL入门指南》,其中使用的Three.js的轻量级封装框架,本来使用Three.js框架就有点困难,还要使用Three.js的框架,OMG~~~

发牢骚归发牢骚,不看是不会懂的。初看书本上的Sim.js写出的代码,第一感觉,这都写的是啥啊。的确,要看懂这些,要求对js的面向对象的思想有一定的了解。下面是本人现学现卖总结的有关js面向对象的知识,仅供参考:

http://blog.csdn.net/birdflyto206/article/category/6121645

下面是我阅读Sim.js源码所做的笔记,由于在初步涉猎webgl,js学得也不是很好,难免会有错误的地方,请各位看官不吝指教。

如果直接读Sim.js的源码,肯定云里雾里的,所以我们对照书上第三章的第一个例子,来看Sim.js框架到底是如何运行的。

//-------------earth-basic.js---------------------
//该例子是画一个地球。首先是抽象出一个EarthApp类,该类继承Sim.App类
EarthApp = function()
{
Sim.App.call(this);//调用父类的构造函数
}
// Subclass Sim.App
EarthApp.prototype = new Sim.App();//继承Sim.App的函数原型

//---------------------------Sim.js-----------------------------------
//现在我们看看Sim.App的定义
// Sim.App - application class (singleton)
Sim.App = function()
{
//Sim.App也是继承Sim.Publisher类的,该类负责管理框架中的事件订阅/发布。该例子中没有用到事件,这个后面再讲。
Sim.Publisher.call(this);

//初始化一个Three.js场景中的基本对象
this.renderer = null;
this.scene = null;
this.camera = null;
//全局app中要渲染的物体的集合
this.objects = [];
}
//让Sim.App.prototype指向一个Sim.Publisher对象,也就达到继承Sim.Publisher原型中所有方法和属性的目。这时js中实现继承的一种方式。
Sim.App.prototype = new Sim.Publisher;

//小结:声明一个EarthApp实例,实际主要完成的操作就是声明一些Three.js场景中render、scene、camera等变量。

//-------------earth-basic.js---------------
//自定义EarthApp的初始化方法
EarthApp.prototype.init = function(param)
{
//调用父类的init方法完成scene、render、camera的初始化
//注意:这里的this传递进去很重要,这是js中实现继承的关键。
Sim.App.prototype.init.call(this, param);

// 创建一个Earth对象,由于我们是画一个地球,根据OOP的思想,将其抽象为一个Earth对象,该对象继承Sim.js框架中的Sim.Object对象
var earth = new Earth();
//调用Earth对象的init方法
earth.init();
//将earth对象的实例添加上到EarthApp中
this.addObject(earth);
}

//---------------------------Sim.js-----------------------------------
//Sim.App的init方法
//因为自定义的EarthApp调用父类的init方法传递的是其代表其本身的this,所以,父类init方法中的所有this相关的操作都被映射为EarthApp的操作。说得简单点,现在Sim.App中的this代表的是EarthApp。这样EarthApp就可以不会吹灰之力继承父类init方法中的所有操作,而不必自己再实现一遍,这就是Sim.js框架的目的所在。
//现在我们看看,Sim.App的init方法中干了些什么。
Sim.App.prototype.init = function(param)
{
//传递的param是一个json对象
param = param || {};
var container = param.container;
var canvas = param.canvas;

// 初始化在Sim.App构造函数中定义的一些场景元素
var renderer = new THREE.WebGLRenderer( { antialias: true, canvas: canvas } );
renderer.setSize(container.offsetWidth, container.offsetHeight);
container.appendChild( renderer.domElement );

// Create a new Three.js scene
var scene = new THREE.Scene();
scene.add( new THREE.AmbientLight( 0x505050 ) );
//为scene动态添加一个属性data,指向EarthApp(子类对象),这个还不知道是干什么的,后面再说。
scene.data = this;

// 初始化相机
camera = new THREE.PerspectiveCamera( 45, container.offsetWidth / container.offsetHeight, 1, 10000 );
camera.position.set( 0, 0, 3.3333 );

scene.add(camera);

// 创建一个Object3D对象,Object3D对象是Sim.Object对象的一个属性,这个类会在后面讲到。该属性一般存储的是一个将要绘制的对象,一般是一个Mesh。例如本例中,Object3D对象就是一个球体和材质组合的Mesh对象。该属性是通过setObject3D函数完成的。
var root = new THREE.Object3D();
scene.add(root);

// 创建一个工具类,用于完成在Three.js鼠标拾取对象。
var projector = new THREE.Projector();

// 保存一些变量到全局对象的属性中,为了方便在任何地方能够访问到。
this.container = container;
this.renderer = renderer;
this.scene = scene;
this.camera = camera;
this.projector = projector;
this.root = root;

// 设置事件相关的句柄。这里先跳过。
this.initMouse();
this.initKeyboard();
this.addDomHandlers();
}

//小结:earth-basic.js中这段代码主要完成的任务就是初始化一些Three.js场景中的基本元素,如scene、camera、render等。

//下面看看自定义的Earth对象
//---------earth-basic.js------------------
// Custom Earth class
Earth = function()
{
Sim.Object.call(this);//继承Sim.Object
}
Earth.prototype = new Sim.Object();

//这里Earth的init方法没有调用父类的init方法,为什么呢?
//我们看下Sim.js的源码,发现Sim.Object的init方法为空
Earth.prototype.init = function()
{
//下面是Three.js中的操作
// Create our Earth with nice texture
var earthmap = "../images/earth_surface_2048.jpg";
var geometry = new THREE.SphereGeometry(1, 32, 32);
var texture = THREE.ImageUtils.loadTexture(earthmap);
var material = new THREE.MeshBasicMaterial( { map: texture } );
var mesh = new THREE.Mesh( geometry, material );

// Let's work in the tilt
mesh.rotation.x = Earth.TILT;

// 将Mesh对象作为Object3D传递给Sim.js框架
// 这里的this指的是什么?
// 对,就是Earth类,而Earth对象我们并没有定义setObject3D方法啊?对,这个方法是继承父类Sim.Object的,我们看看父类中的setObject3D方法,其实就是mesh对象赋值给Sim.Object类的Object3D属性。
this.setObject3D(mesh);
}

Earth.prototype.update = function()
{
// "I feel the Earth move..."
this.object3D.rotation.y += Earth.ROTATION_Y;//这里的Object3D就是Mesh对象,因为前面已经 this.setObject3D(mesh); 结合Sim.js源码
}

Earth.ROTATION_Y = 0.0025;
Earth.TILT = 0.41;

//--------Sim.js---------
//此案例中Object3D对象传递进来的就是一个包含了纹理的地球Mesh对象
Sim.Object.prototype.setObject3D = function(object3D)
{
object3D.data = this;
this.object3D = object3D;
}

//然后在主页面中是这么调用框架的
$(document).ready(
function() {
var container = document.getElementById("container");
var app = new EarthApp();//自定义的EarthApp对象,表示整个应用程序
app.init({ container: container });
app.run();
}
);
//看看app.run()方法
Sim.App.prototype.run = function()
{
this.update();//首先调用刷新界面的方法
this.renderer.render( this.scene, this.camera );//Three.js中的render方法
var that = this;//保存Sim.App的引用。
requestAnimationFrame(function() { that.run(); });
}

Sim.App.prototype.update = function()
{
var i, len;
len = this.objects.length;
//循环当前场景中的所有的物体,让其全部重绘。重绘的方法是子类自己实现的。
//该类中Earth对象的update方法就是不断的更改其绕y轴旋转的角度,以达到旋转动画的目的。
for (i = 0; i < len; i++)
{
this.objects[i].update();//每个自定义的Object对象必须有update方法,可以在该方法中完成旋转动画等。
}
}


ok,至此为止,我们分析了第一个例子的运行原理。总结如下:

1.首先抽象出整个应用程序类EarthApp,继承自Sim.App,主要完成Three.js中一些变量的初始化操作,像声明render、scene、camera等,还有一些事件的订阅和发布,本例中没有用到这些 。还有就是初始化当前场景中需要渲染的物体,根据面向对象编程的思想,把这些要渲染的物体都封装在一个对象中,本例中的对象就是Earth类,该类的初始化就是在EarthApp的init方法中完成的。

2.然后抽象出自己要渲染的对象类Earth,这个对象类是一个容器,其中可以包含很多要渲染的物体,当然本例中只包含了一个贴了纹理的地球。该类的init方法的主要是定义要画的物体,本例中画的地球的几何和纹理以及包装成要画的Mesh等操作都是在该方法中完成的。

3.然后是关键的一步,就是循环重绘。Sim.js框架是通过调用Sim.App的run方法开启的,首先调用自身的update方法,当前这步是为了让物体进行简单的动画,比如绕坐标轴进行旋转。真正的循环重绘操作是通过requestAnimationFrame完成的,不断的调用run方法,run方法中调用Three.js中的render(scene,camera)方法,这样整个程序就运行起来了。

4.面向对象思想是门艺术!!!
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  javascript