创建、加载高级网格和几何体

1.对象合并

使用THREE.Group对象作为组合对象,调用它的add函数附加其他几何体。如下面的代码,创建了两个几何体sphere、cube,然后调用group的add函数,把两个几何体组合在一起。代码中的redraw函数在最后调用了position.BoundingBox()函数,用来定位组合体对象的位置。在positionBoundingBox函数中调用了setFromeObject并且返回了一个THREE.Box3对象。这里先不介绍这个对象,直接看后面的代码,通过box.max和box.min的两个位置对象,计算出了BoxGeometry的width、height、depth。最后也通过这两个位置对象计算出bboxMesh的position值。

this.redraw = function () {
                scene.remove(group);

                sphere = createMesh(new THREE.SphereGeometry(5, 10, 10));
                cube = createMesh(new THREE.CubeGeometry(6, 6, 6));

                sphere.position.set(controls.spherePosX, controls.spherePosY, controls.spherePosZ);
                cube.position.set(controls.cubePosX, controls.cubePosY, controls.cubePosZ);

                group = new THREE.Group();
                group.add(sphere);
                group.add(cube);

                scene.add(group);
                controls.positionBoundingBox();

                var arrow = new THREE.ArrowHelper(new THREE.Vector3(0, 1, 0), group.position, 10, 0x0000ff);
                scene.add(arrow);
            };

            this.positionBoundingBox = function () {
                scene.remove(bboxMesh);
                var box = setFromObject(group);
                var width = box.max.x - box.min.x;
                var height = box.max.y - box.max.y;
                var depth = box.max.z - box.min.z;

                var bbox = new THREE.BoxGeometry(width, height, depth);
                bboxMesh = new THREE.Mesh(bbox, new THREE.MeshBasicMaterial({
                    color: 0x000000,
                    vertexColors: THREE.VertexColors,
                    wireframeLinewidth: 2,
                    wireframe: true
                }));

                bboxMesh.position.x = ((box.min.x + box.max.x)/2);
                bboxMesh.position.y = ((box.min.y + box.max.y)/2);
                bboxMesh.position.z = ((box.min.z + box.max.z)/2);
            }

接下来再继续看看setFromObject函数的定义,如下:

function setFromObject(object) {
            var box = new THREE.Box3();
            var v1 = new THREE.Vector3();
            object.updateMatrixWorld(true);
            box.makeEmpty();
            object.traverse(function (node) {
                if (node.geometry !== undefined && node.geometry.vertices !== undefined) {
                    var vertices = node.geometry.vertices;
                    for (var i = 0, il = vertices.length; i < il; i++) {
                        v1.copy(vertices[i]);
                        v1.applyMatrix4(node.matrixWorld);
                        box.expandByPoint(v1);
                    }
                }
            });
            return box;
        }

首先生命了一个Box3对象,主要用来计算网格的边界。更新Group对象的世界坐标,然后调用makeEmpty函数清空盒子。接下来遍历object对象的子对象,获取子对象的所有顶点并遍历。把这些顶点全都扩展到box中。这样我们就能获取整个组合体的边界了。

2.将多个网格合并到一个网格

多数情况下使用组可以很容器操作和管理大量网格。但是当对象的数量非常多时,性能就会成为一个瓶颈。在组里每个对象还是独立的,需要对它们分别进行处理和渲染。但通过Geometry的merge函数,可以把多个几何体合并,创建一个联合体。实现代码如下:

this.redraw = function () {
                var toRemove = [];
                scene.traverse(function(e){
                    if(e instanceof THREE.Mesh) toRemove.push(e);
                });

                toRemove.forEach(function(e){
                    scene.remove(e);
                });

                if(controls.combined){
                    var geometry = new THREE.Geometry();
                    for(var i = 0; i < controls.numberOfObjects; i++){
                        var cubeMesh = addcube();
                        cubeMesh.updateMatrix();
                        geometry.merge(cubeMesh.geometry, cubeMesh.matrix);
                    }
                    scene.add(new THREE.Mesh(geometry, cubeMaterial));
                }else{
                    for(var i = 0; i < controls.numberOfObjects; i++){
                        scene.add(controls.addCube());
                    }
                }
            };

代码会根据controls.numberofObjects的值来确定创建多少个几何体。如果controls.combined为true,那么我们就使用Geometry的merge函数把numberofObjects数量的几何体合并到一个联合体中。和和直接创建numberofObjects的几何体相比,性能有很大提升。但缺点是不能分别操作每个几何体了。

3.three.js支持加载的文件格式

Three.js可以读取几种三维文件格式,并从中导入结合体和网格。下面所列的就是Three.js支持的文件格式:

JSON/是three.js自己的json文件格式,你可以用它以生命的方式定义几何体和场景。但不是一种正是格式。它很容易使用,当你想要复用复杂的几何体或场景时非常有用

OBJ和MTL/OBJ是一种简单的三维文件格式。它是使用最广泛的三维文件格式,用来定义对象的几何体。MTL文件常同OBJ文件一起使用,在MTL文件中,对象的材质定义在OBJ文件中

Collada/是一种用来定义Xmzl类文件中数字内容的格式。这也是被广泛使用的格式,差不多所有的三维软件和渲染引擎都支持这种格式

STL/STL是StereoLithography(立体成型术)的缩写,广泛于快速成型。例如三维打印机的模型文件通常是STL文件

CTM/使用openCTM创建的文件格式,用来压缩存储表示三维网络的三角形面片

VTK/是由Visualization Toolkit定义的文件格式,用来指定顶点和面。VTK有两种格式,Three.js支持旧的,即ASCII格式

PDB/这是一种非常特别的格式,由Protein Databank创建,用来定义蛋白质的形状。Three.js可以加载并显示用这种格式描述蛋白质

PLY/该格式全称为多边形文件格式。通常用来保存三维扫描仪的数据

4.保存和加载几何体

几何体对象提供了toJSON函数,可以直接把几何数据持久化为JSOn格式。我们可以直接把toJSON的结果通过localStorage缓存起来。要加载时候直接读取localStrorage获取json字符串。加载可以使用THREE.ObjectLoader对象的parse函数把字符串转换为网格对象。代码如下:

this.save = function () {
                var result = knot.toJSON();
                localStorage.setItem("json", JSON.stringify(result));
            };

            this.load = function () {
                scene.remove(loadedMesh);
                var json = localStorage.getItem("json");

                if(json){
                    var loadedGeometry = JSON.parse(json);
                    var loader = new THREE.ObjectLoader();
                    loadedMesh = loader.parse(loadedGeometry);
                    loadedMesh.position.x = -50;
                    scene.add(loadedMesh);
                }
            }

5.保存和加载场景

想要导出和导入场景,必须要引入SceneExporter.js和SceneLoader.js文件,可以使用THREE.SceneExporter对象的parse函数把scene格式化成json对象。代码如下:

this.exportScene = function(){
                var exporter = new THREE.SceneExporter();
                var sceneJson = JSON.stringify(exporter.parse(scene));
                localStorage.setItem("scene", sceneJson);
            }

下一次我们就可以直接使用保存到localStorage的json数据加载场景。加载场景数据使用THREE.SceneLoader的parse函数。代码如下:

this.importScene = function(){
                var json = (localStorage.getItem("scene"));
                var sceneLoader = new THREE.SceneLoader();
                sceneLoader.parse(JSON.parse(json), function (e) {
                    scene = e.scene;
                }, ‘.‘);
            }

传递给loader的最后一个参数(“.”)是一个URL相对地址。例如,在材质中使用的纹理就可以从这个相对地址中获取。在导入之前我们需要清理场景,直接重新创建一个场景即可。

this.clearScene = function(){
                scene = new THREE.Scene();
            }

6.在blender里使用three.js插件导出json格式模型

Blender中可以安装Three.js导出器插件。插件的具体安装方法这里不做介绍。这里主要介绍如何加载通过blender导出的json模型。加载的代码也很简单,直接使用three.js内部的JSONLoader对象即可。代码如下:

var loader = new THREE.JSONLoader();
        loader.load("../assets/models/misc_chair01.js", function(geometry, mat){
            mesh = new THREE.Mesh(geometry, mat[0]);

            mesh.scale.x = 15;
            mesh.scale.y = 15;
            mesh.scale.z = 15;

            scene.add(mesh);
        }, "../assets/models/");

load函数包含三个参数,第一个指定json格式数据地址,这里是把json数据放到js文件中了。第二个参数是加载后的回调函数,该函数包含geometry和mat两个参数。第三个参数指定了json数据中可以依赖的外部文件(例如一个图片)的相对地址。如果没有外部依赖可以设置为“.”。

7.加载加载OBJ文件而不加载MTL文件

首选需要引入three.js扩展库OBJLoader.js文件,用来加载OBJ文件。加载代码如下所示:

var loader = new THREE.OBJLoader();
        loader.load("../assets/models/pinecone.obj", function(loadedMesh){
            var material = new THREE.MeshLambertMaterial({
                color: 0x5c3a21
            });
            loadedMesh.children.forEach(function(child){
               child.material = material;
                child.geometry.computeFaceNormals();
                child.geometry.computeVertexNormals();

                mesh = loadedMesh;
                mesh.scale.set(100, 100, 100);
                mesh.rotation.x = -0.3;
                scene.add(mesh);
            });
        });

代码先实例化一个OBJLoader对象,然后调用load函数加载OBJ文件。load函数包含参数为load(url, callback),url是OBJ文件路径,当加载完成后会调用第二个回调函数,包含一个参数loadedMesh。loadedMesh是一个组合体,所有需要遍历它下面的每个网格。child就是一个网格,需要设置它的material属性,并分别调用computeFaceNromals和computeVertexNormals函数,计算出几何体的法向量。加载出来的网格可能尺寸比较小,所以需要设置scale放大。

8.同时加载OBJ和MTL

如果需要同时加载几何体和纹理,需要引入三个js文件,它们分别是加载器:OBJLoader、MTLLoader、OBJMTLLoader。首先实例化一个OBJMTLLoader对象,然后调用load函数。load函数包含三个参数,load(objUrl, mtlUrl, callbac)。下面是加载OBJ和TML的代码:

var loader = new THREE.OBJMTLLoader();
        loader.load("../assets/models/butterfly.obj", "../assets/models/butterfly.mtl", function(object){
            var wing1 = object.children[4].children[0];
            var wing2 = object.children[5].children[0];

            wing1.material.opacity = 0.6;
            wing1.material.transparent = true;
            wing1.material.depthTest = false;
            wing1.material.side = THREE.DoubleSide;

            wing2.material.opacity = 0.6;
            wing2.material.transparent = true;
            wing2.material.depthTest = false;
            wing2.material.side = THREE.DoubleSide;

            object.scale.set(140, 140, 140);
            mesh = object;

            scene.add(mesh);

            object.rotation.x = 0.2;
            object.rotation.y = -1.3;
        });

回调函数返回了一个object对象,object本身就是一个网格对象,可以直接附加到场景中。但有时候在加载模式时由于材质兼容性问题,需要手动调整下。上面代码就是获取了两个翅膀wing1和wing2,并设置了一些属性,让翅膀能够正常显示出来。

9.加载Collada模型

Collda模型(文件扩展名是.dae)是另外一种非常通用的、定义场景和模型(以及动画)的文件格式。Collada不仅定义了几何体,有定义了材质。甚至还可以定义光源。

加载Collada和加载OBJ、MTL模型步骤一样。首先需要引入ColladaLoader.js文件。加载代码如下:

var loader = new THREE.ColladaLoader();

        var mesh;
        loader.load("../assets/models/dae/Truck_dae.dae", function(result){
            mesh = result.scene.children[0].children[0].clone();
            //mesh = result.scene.children[0];
            mesh.scale.set(4, 4, 4);
            scene.add(mesh);
        });

首先创建一个ColladaLoader加载器,调用load函数,传入模型文件地址。第二个参数是一个回调函数,result是加载的结果。其中包含了scene属性。这里result.scene.children[0].children[0]是根据实际情况加载了某一个网格对象。具体的加载要根据实际的模型文件而定。例如本例我们只加载了模型文件中的一部卡车。

10.使用外部模型创建粒子系统

这里我们使用PLY外部模型。PLY和其他模型加载相似,首先需要引入加载器PLYLoader.js文件,然后调用load函数加载模型。这里我们主要看下如何创建粒子系统。代码如下:

var loader = new THREE.PLYLoader();
        loader.load("../assets/models/test.ply", function(geometry){
            var material = new THREE.PointCloudMaterial({
                color: 0xffffff,
                size: 0.4,
                opacity: 0.6,
                transparent: true,
                blending: THREE.AdditiveBlending,
                map: generateSprite()
            });
            group = new THREE.PointCloud(geometry, material);
            group.sortParticles = true;
            scene.add(group);
        });

        render();

        function generateSprite(){
            var canvas = document.createElement("canvas");
            canvas.width = 16;
            canvas.height = 16;
            var context = canvas.getContext("2d");
            var gradient = context.createRadialGradient(canvas.width/2, canvas.height/2, 0, canvas.width/2, canvas.height/2, canvas.width/2);
            gradient.addColorStop(0, "rgba(255, 255, 255, 1)");
            gradient.addColorStop(0.2, "rgba(0, 255, 255, 1)");
            gradient.addColorStop(0.4, "rgba(0, 0, 64, 1)");
            gradient.addColorStop(1, "rgba(0, 0, 0, 1)");

            context.fillStyle = gradient;
            context.fillRect(0, 0, canvas.width, canvas.height);

            var texture = new THREE.Texture(canvas);
            texture.needsUpdate = true;
            return texture;
        }

这里我们没有修改加载的结果geometry。显示使用PointCloudMaterial创建粒子材质,材质指定了map属性,map属性调用generateSprite函数,生成一个纹理。generateSprite函数使用canvas画板绘制了一个由内向外渐变的效果,并把这个canvas直接传递给Texture构造函数。最后使用PointCloud创建粒子系统。需要强调的是设置sortParticles为true,这样呈现出来的效果比较好。呈现结果如下:

时间: 2024-12-14 06:58:23

创建、加载高级网格和几何体的相关文章

Three.js开发指南---创建,加载高级网格和几何体(第八章)

本章的主要内容: 一, 通过Three.js自带的功能来组合和合并已有的几何体,创建出新的几何体 二, 从外部资源中加载网格和几何体 1 前面的章节中,我们学习到,一个几何体创建的网格,想使用多个材质的方法: var mesh=THREE.SceneUtils.createMultiMaterialObject(geometry,[material1,,material2]); 看似一个网格中有一个几何体,多个材质,其实该网格拥有与材质数量相对应的几何体,每个几何体都对应一种材质,形成一个网格,

spring框架中多数据源创建加载并且实现动态切换的配置实例代码

原文:spring框架中多数据源创建加载并且实现动态切换的配置实例代码 源代码下载地址:http://www.zuidaima.com/share/1774074130205696.htm 在我们的项目中遇到这样一个问题:我们的项目需要连接多个数据库,而且不同的客户在每次访问中根据需要会去访问不同的数据库.我们以往在spring和hibernate框架中总是配置一个数据源,因而sessionFactory的dataSource属性总是指向这个数据源并且恒定不变,所有DAO在使用sessionFa

Virtualbox 多重加载 高级功能介绍

1. 简介 对于虚拟机,相信对大家来说都不陌生,尤其对一个IT行业的人来讲.让你在现有的操作系统上不需要从新装系统和购买新机器就可以使用其他类型的操作系统.现在主流的虚拟就有VMWare和Virtualbox,virtualbox体系小,也比较稳定,还有一些独到的高级功能比较受用户的青睐. 对于虚拟机中的操作系统,我们大多时候都是用来做实验或者其他用途,但总结起来一句通俗的话,那就是用来折腾的.对于经常使用虚拟机的人来说,可能经常需要将虚拟机中的系统还原到之前的状态,搞不好还需要重新安装.有人会

资源:创建 加载 存储 使用 ---- 热更新

Unity中资源动态加载的几种方式比较 http://blog.csdn.net/leonwei/article/details/18406103 Unity 加载外部资源 http://www.360doc.com/content/14/0323/13/12282510_363016241.shtml

动态创建加载程序集

public class CodeProvider { //动态创建Driver类 private string strStart = "using System;" + "public static class Driver { public static void Main(){ "; private string strEnd = " } }"; /// <summary> /// 根据输入的文本动态编译,返回编译结果 ///

Unity加载模块深度解析(网格篇)

在上一篇 加载模块深度解析(一)中,我们重点讨论了纹理资源的加载性能.这次,我们再来为你揭开其他主流资源的加载效率. 这是侑虎科技第53篇原创文章,欢迎转发分享,未经作者授权请勿转载.同时如果您有任何独到的见解或者发现也欢迎联系我们,一起探讨.(QQ群465082844) 资源加载性能测试代码 与上篇所提出的测试代码一样,我们对于其他资源的加载性能分析同样使用该测试代码.我们将每种资源均制作成一定大小的AssetBundle文件,并逐一通过以下代码在不同设备上进行加载,以期得到不同硬件设备上的资

Android 图片加载框架Universal-Image-Loader源码解析

Universal-Image-Loader(项目地址)可以说是安卓知名图片开源框架中最古老.使用率最高的一个了.一张图片的加载对于安卓应用的开发也许是件简单的事,但是如果要同时加载大量的图片,并且图片用于ListView.GridView.ViewPager等控件,如何防止出现OOM.如何防止图片错位(因为列表的View复用功能).如何更快地加载.如何让客户端程序员用最简单的操作完成本来十分复杂的图片加载工作,成了全世界安卓应用开发程序员心头的一大难题,所幸有了Universal-Image-

Android异步加载

Android异步加载 一.为什么要使用异步加载? 1.Android是单线程模型 2.耗时操作阻碍UI线程 二.异步加载最常用的两种方式 1.多线程.线程池 2.AsyncTask 三.实现ListView图文混排 3-1 实现读取网页中的json数据到ListView中 (图片首先为默认图片) 3.1.1:主布局只有一个ListView和一个listView_item的布局 3.1.2:网页json数据的链接(http://www.imooc.com/api/teacher?type=4&n

控制器的view的加载优先级

拿到控制器后,控制器的view是在什么时候按照什么优先级创建加载的? 1.控制器内部的view是延迟加载 1> 用到时再加载(loadView) 2> 加载完毕后会调用控制器的viewDidLoad方法 也就是说,控制器的view在第一次加载的时候会调用控制器的loadView 方法,需要自定义view的救灾该方法中完成: 如果没有实现loadView方法,那么就会看该控制器是不是通过storyboard创建的,如果是就加载对应storyboard中的view: 如果不是通过加载storybo