3D机房前端学习笔记

本文源代码地址:http://download.csdn.net/detail/jamesplur/9797919

实现任务:能够在前端网页中嵌入3D机房模型并实现基本的交互


3D基本原理

为了实现3D图像的动态展示有三个要点:

  1. 建立三维场景

    三维场景包括三维模型、光源、相机三个部分构成。

    从最简单的情形来说,三维模型由网格(mesh)和材质(material)组成。网格则一般由若干个图元(一般是空间中的三角形)拼接而成。网格表面还要考虑本身的纹理和对光源的反射情况。所以3D模型的数据主要是顶点信息和网格表面材质信息。

    光源就是在空间中人为添加光照信息,例如方向,强度,光色等。没有光源,渲染出来的3D场景将一片漆黑。

    相机相当于场景的一个观察点。相机在建立时就要确定,视角,远平面,近平面,纵横比四个参数。根据这些参数可以确定一个可视区域。3D模型必须在可视区域(view frustum)内。否则将不会被屏幕捕捉。

3D机房前端学习笔记_第1张图片

  1. 将场景以2D的形式显示

    要想将场景以2D的形式显示出来,实际就是将空间中的点映射为平面的像素值。这个过程需要考虑两个关系:

    模型和相机之间的关系:这个变换关系由两个矩阵控制,其中一个矩阵表示了相机所在位置和方向,另一个矩阵控制了从3D到2D的投影参数。模型和相机的关系决定空间中的某一点是否会被渲染。

    光源和材质的关系: 光源和材质的关系决定这一点的像素值会是多少。着色器将会结合该点的材质(包括对光的反射率)和光照强度,判断该点的颜色。

  2. 当3D数据发生改变时,可以近乎无延迟的对2D图像更新渲染

    上述复杂的映射过程,实际是交给着色器来完成的,着色器往往是一种基于gpu的程序(所谓硬件加速)。保证了渲染的低延迟。

浏览器对3D的支持

WEBGL标准的实现,使利用浏览器进行3D绘图成为可能
这里引用wiki百科的一段介绍

WebGL (Web Graphics Library) is a JavaScript API for rendering 3D graphics within any compatible web browser without the use of plug-ins.[2] WebGL is integrated completely into all the web standards of the browser allowing GPU accelerated usage of physics and image processing and effects as part of the web page canvas. WebGL elements can be mixed with other HTML elements and composited with other parts of the page or page background.[3] WebGL programs consist of control code written in JavaScript and shader code that is written in OpenGL Shading Language (GLSL), a language similar to C or C++, and is executed on a computer’s graphics processing unit (GPU). WebGL is designed and maintained by the non-profit Khronos Group.[4]

总结一下要点:

  • 部分浏览器支持(火狐、谷歌),不需要插件
  • OPENGL的web版,提供了js接口,可将3d场景直接绘制到页面元素中(canvas元素)
  • 提供了硬件加速手段,也就是之前所提的着色器,这段着色器由GLSL语言所编写,可以操控显卡使网页的显示更加流畅

实际上利用WEBGL接口进行开发是非常非常吃力的,具体有多吃力可以参考附件代码。
可以看到编写一个旋转的正方体就用了三百行代码。这是开发所不允许的。

VIZI开发引擎简介

提高开发效率有两个要点:

  • 选用一个合适的3D开发引擎:
    由于目前3D开发还没有一个功能全面的的而且被广泛兼容的(兼容这一点更加重要)框架,所以很多公司都给出了自己的解决方案,大多都是收费的。在免费的框架中,three.js是其中最为出色的,它合理的封装了WEBGL的主要部件,使得初学者学习3D开发变成了可能。tween.js则是three.js有力的补充,利用tween.js可以实现一些简单的补间动画。

  • 利用模型软件进行建模:
    用js进行建模可真的是想多了。最合理的方式是利用3Dmaxs或者maya进行建模和一些复杂动画的编辑。再导出到合适的格式中去。这些格式包括obj,dae等。其中dae是功能最为强大的通用场景格式文件,包含了相机、光源、几何体、材质、动画等信息。three.js中提供了这些文件的解析器,将会把这些文件解析为json格式,再进一步转化为three.scene对象。

    这里注意:
    WebGL默认情况下不允许使用本机上的纹理、模型文件的。若想加载外部模型和纹理文件,
    由于同源策略的安全限制从文件系统加载文件会因为安全异常而失败。不过有两个办法可以解决:
    降低浏览器的安全级别
    在本机上建立一个服务器,把外部文件放到该服务器作为网络文件访问   
    

所谓开发引擎,便是将接口进行高度的封装抽象,以实现快速的开发。

Vizi是由美国tony parisi编写的3d开发引擎,该引擎基于three.js和tween.js,可以快速实现3d开发所需的基本功能。之后给出实例也是基于tony parisi所编写的例程。

这里简要介绍下VIZI的类所完成的工作:

Viewer类:

在实例化viewer时,会将viewer的容器(一个块状dom元素)传递给它作为显示3d内容的画布。

viewerrun()方法会初始化渲染器(一个封装了webgl着色器的类)并开启循环。

viewerreplacescene()方法会将读取到的场景数据加载到画布中,如果场景数据没有相机和光源的话,该函数还会创建默认的光源和相机。

Object类:

vizi中模型存储的基本数据类型

loader类:

loaderloadscene()方法会根据加载文件的类型自动选择three.js的加载器

picker类:

picker对象可以添加到模型对象的属性中去,并可由addEventListener()方法添加交互事件,如鼠标经过,鼠标点击等

Component类:

component对象可以添加到模型对象的属性中去,并通过tween.js为对象添加简单的补间动画

附录程序使用文档

这里利用vizi引擎,开发了3D机房的一个演示界面

3d机房的模型文件是由maya导出的dae文件,导出插件为opencollada(不知道为什么,3dsmax导出的dae文件是不能用的)

该程序将vizi的一些类都封装起来,提供了所需要的一些接口函数和回调函数。下面是程序详细解释:

1. 文件结构

  • 3d 是模型测试页面
  • Src中存放3d机房的源文件3d.js
  • Libs中存放相关库文件
  • Images中存放图片
  • Css中存放样式表

2. 建立机房

首先建立一个容器:

    

然后实例化threedroom类,并将container元素作为参数传递。其余的参数为可能用到的回调函数

var container = document.getElementById("container");
    threedroom = new ThreeDroom({ container : container,
            loadCallback : onLoadComplete,
            loadProgressCallback : onLoadProgress,
            mouseOverCallback : onMouseOver,
            mouseOutCallback : onMouseOut,
            mouseUpCallback : onMouseUp,
         });

    threedroom.go();

threedroom.go()函数实例化了viewer类,加载了场景文件,并开始了渲染循环

ThreeDroom.prototype.go = function() {
    this.viewer = new Vizi.Viewer({ container : this.container, showGrid : true});
    this.loadURL(ThreeDroom.URL);
    this.viewer.run();
}

其中loadURL为加载模型的函数,同时也为添加了两个监听事件,当加载完成之后会调用onLoadComplete()这个函数(onLoadProgress负责显示加载的进度)

ThreeDroom.prototype.loadURL = function(url) {

    var that = this;

    var loader = new Vizi.Loader;
    loader.addEventListener("loaded", function(data) { that.onLoadComplete(data, loadStartTime); }); 
    loader.addEventListener("progress", function(progress) { that.onLoadProgress(progress); }); 

    var loadStartTime = Date.now();
    loader.loadScene(url);  
}

3. 添加鼠标交互

onLoadComplete()除了将场景数据交给viewer之外,还为场景中的对象添加了交互事件:

ThreeDroom.prototype.onLoadComplete = function(data, loadStartTime)
{
     var that = this;
    ThreeDroom.Scene = data.scene;
     this.viewer.replaceScene(data);
    ThreeDroom.Scene.map(/^bottom_Mesh(.*)/,function(o){
         console.log("found node!");
         var highlight=new Vizi.HighlightBehavior({highlightColor:0x88eeff});
         o.addComponent(highlight);
         var picker = new Vizi.Picker;
         picker.addEventListener("mouseover", function (event) {
             that.onMouseOver("over", event);
             if(!that.warningstatus)
             {highlight.on();}
         });
         picker.addEventListener("mouseout", function (event) {
             that.onMouseOut("out", event);
              if(!that.warningstatus)
              {highlight.off();}
         });
         picker.addEventListener("mouseup", function (event) {
             that.onMouseUp("up", event);
         });
         o.addComponent(picker);
     });

首先,添加场景数据之后,所有的3d对象数据都存储在ThreeDroom.Scene中。

map()是一个遍历函数,它会找到对象名称符合正则表达式的对象,并添加鼠标的交互事件。

 ThreeDroom.Scene.map(/这里添加正则表达式/,function(这里添加响应事件))

鼠标交互事件会触发对应的响应函数,这个响应函数定义在3d.js里:

ThreeDroom.prototype.onMouseOver = function(what, event) {
    if (this.mouseOverCallback)
    this.mouseOverCallback(what, event);

}

ThreeDroom.prototype.onMouseOut = function(what, event) {
    if (this.mouseOutCallback)
        this.mouseOutCallback(what, event);
}

ThreeDroom.prototype.onMouseUp = function(what, event) {
    if (this.mouseUpCallback)
        this.mouseUpCallback(what, event);
}

该函数的作用是回调用主页面的处理函数,应在此添加事件
其中可通过what参数来区分是由那个元素触发了事件,event是jquery所定义的事件标记

function onMouseOver(what, event) {
    console.log("Mouse over");
    alert("在这里填充响应事件!");
}

function onMouseOut(what, event) {
    console.log("Mouse out");
    alert("在这里填充响应事件!");

}
function onMouseUp(what, event) {
    console.log("Mouse up");
    alert("在这里填充响应事件!");
}

总结来说,要为添加鼠标交互事件,首先要在onloadcomplete函数中,为指定的3d对象添加picker

然后在页面中的回调函数中利用what参数分辨是哪个元素产生的事件

4. 添加报警动画

警报动画的接口函数为warningon与warningoff,参数为target。也就是所需要添加警报动画的对象的名称的正则表达式,warningon代表开启警报,warningoff代表关闭警报。需要注意只有有材质的子对象才可以添加报警动画。组对象是不能添加的。

function warningon() {
   threedroom.warningon(/^bottom_Mesh(.*)/);
}

function warningoff() {
   threedroom.warningoff(/^bottom_Mesh(.*)/);
}

你可能感兴趣的:(前端开发)