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

three.js 08-03 之 加载和保存对象

2018-02-08 17:52 381 查看
    前面介绍过,通过组合及合并等操作,你可以使用 three.js 提供的基本几何体来创建大型的、复杂的几何体。但是,如果你想创建更加高级的几
4000
何体,那么使用 three.js 所提供的编程方式就不是最好、最有效的方式。幸运的是 three.js 提供了其他多种从外部资源中创建并加载几何体和网格的方式。
    下表所示就是 three.js 支持的几种常用的文件格式:

格式描述
JSONthree.js 有它自己的 JSON 格式,你可以用它以声明的方式定义几何体和场景。但它并不是一种正式的格式。
它很容易使用,当你想要复用复杂的几何体或场景时非常有用
OBJ 和 MTLOBJ 是一种简单的三维文件格式,由 Wavefront 科技公司创立。它是使用最为广泛的三维文件格式,用来定义对象的几何体。
MTL 文件常同 OBJ 文件一起使用,在一个 MTL 文件中,对象的材质定义在 OBJ 文件中
ColladaCollada 是一种用来定义 XML 类文件中数字内容的格式。这也是一种被广泛使用的格式,差不多所有的三维软件和渲染引擎
都支持这种格式
STLSTL 是 STereoLithography(立体成型术)的缩写,广泛用于快速成型。例如三维打印机的模型文件通常都是 STL 文件格式
 three.js 有一个可定制的 STL 导出工具,STLExporter.js,你可以用它将 three.js 中的模型导出成一个 STL 文件
CTMCTM 是由 openCTM 创建的文件格式。可以用来压缩存储表示三维网格的三角形面片
VTKVTK 是由 Visualization Toolkit 定义的文件格式,用来指定顶点和面。VTK 有两种格式,但 three.js 只支持旧有的 ASCII 格式
PDBPDB 是一种非常特别的格式,由 Protein Databank(蛋白质数据银行)创建,用来定义蛋白质的形状。three.js 可以加载并
显示用这种格式描述的蛋白质
PLYPLY 格式的全称是多边形(polygon)文件格式。通常用来保存三维扫描仪的数据
一般来说针对以上每一种文件格式,three.js 都有一个相应的 Loder 来加载其数据。下面我们就来看看第一种 three.js 独有的格式,看看如何通过 three.js 相应的 API 来加载并保存其信息。
    首先来看看如何保存并加载几何体。为了展示保存和加载几何体功能,我们基于 THREE.TorusKnotGeometry 类创建一个环面扭结,然后通过右上角的 Save & Load 菜单中的 Save 按钮来保存当前的几何体。在这个例子中我们使用了 H5 的本地存储 API 来把持久化信息保存到客户端浏览器里,而且以后还可以读取(即使在浏览器关闭并重启之后);保存后,可以通过右上角的 Save & Load 菜单中的 Load 按钮来加载用 Save 保存在本地存储的几何体。以下是本示例的完整代码:
<!DOCTYPE html>
<html>
<head>
<title>示例 08.03 - Load Save JSON Object</title>
<script src="../build/three.js"></script>
<script src="../build/js/controls/OrbitControls.js"></script>
<script src="../build/js/libs/stats.min.js"></script>
<script src="../build/js/libs/dat.gui.min.js"></script>
<script src="../jquery/jquery-3.2.1.min.js"></script>
<style>
body {
/* 设置 margin 为 0,并且 overflow 为 hidden,来完成页面样式 */
margin: 0;
overflow: hidden;
}
/* 统计对象的样式 */
#Stats-output {
position: absolute;
left: 0px;
top: 0px;
}
</style>
</head>
<body>

<!-- 用于 WebGL 输出的 Div -->
<div id="webgl-output"></div>
<!-- 用于统计 FPS 输出的 Div -->
<div id="stats-output"></div>

<!-- 运行 Three.js 示例的 Javascript 代码 -->
<script type="text/javascript">

var scene;
var camera;
var render;
var webglRender;
//var canvasRender;
var controls;
var stats;
var guiParams;

var ambientLight;
var spotLight;
var axesHelper;
//var cameraHelper;

var ground;

$(function() {
initStats();
initRender();
initCamera();
initControls();

scene = new THREE.Scene();

createAxesHelper();
createAmbientLight();
//createSpotLight();
createGuiControls();

// 加入地面
//createGround()
// 创建网格
createMesh();

renderScene();
});

/** 初始化 stats 统计对象 */
function initStats() {
stats = new Stats();
stats.setMode(0); // 0 为监测 FPS;1 为监测渲染时间
$('#stats-output').append(stats.domElement);
return stats;
}

/** 初始化渲染器 */
function initRender() {
webglRender = new THREE.WebGLRenderer( {antialias: true, alpha: true} ); // antialias 抗锯齿
webglRender.setSize(window.innerWidth, window.innerHeight);
webglRender.setClearColor(0x0F0F0F, 1.0); // 0xeeeeee
render = webglRender;
render.shadowMap.enabled = true; // 允许阴影投射

$('#webgl-output')[0].appendChild(render.domElement);
window.addEventListener('resize', onWindowResize, false);
}

/** 初始化相机 */
function initCamera() {
camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 0.1, 2147483647); // 2147483647
camera.position.set(30, 40, 50);
}

/** 初始化鼠标控制器 */
function initControls() {
var target = new THREE.Vector3(0, 0 , 0);
controls = new THREE.OrbitControls(camera, render.domElement);
controls.target = target;
camera.lookAt(target);
}

/** 当浏览器窗口大小变化时触发 */
function onWindowResize() {
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
render.setSize(window.innerWidth, window.innerHeight);
}

/** 渲染场景 */
function renderScene() {
stats.update();
rotateMesh(); // 旋转物体

requestAnimationFrame(renderScene);
render.render(scene, camera);
}

/** 旋转物体 */
var step = 0;
function rotateMesh() {
step += guiParams.rotationSpeed;
scene.traverse(function(mesh) {
if (mesh instanceof THREE.Mesh && mesh != ground) {
//mesh.rotation.x = step;
mesh.rotation.y = step;
//mesh.rotation.z = step;
}
});
}

/** 创建一个坐标轴:X(橙色)、Y(绿色)、Z(蓝色) */
function createAxesHelper() {
axesHelper = new THREE.AxesHelper(60);
scene.add(axesHelper);
}

/** 创建一个 AmbientLight 环境光源 */
function createAmbientLight() {
ambientLight = new THREE.AmbientLight(0x0c0c0c);
scene.add(ambientLight);
}

/** 创建一个 AmbientLight 环境光源 */
function createSpotLight() {
spotLight = new THREE.SpotLight(0xffffff);
spotLight.position.set(-60, 60, -10);
spotLight.castShadow = true;
spotLight.shadow.mapSize.set(2048, 2048); // 必须是 2的幂,默认值为 512
scene.add(spotLight);
//cameraHelper = new THREE.CameraHelper(spotLight.shadow.camera);
//scene.add(cameraHelper);
}

/** 用来保存那些需要修改的变量 */
function createGuiControls() {
guiParams = new function() {
this.rotationSpeed = 0.02;
this.radius = 10;
this.tube = 0.3;
this.tubularSegments = 64;
this.radialSegments = 8;
this.p = 2;
this.q = 3;

this.Save = function() {
if (mesh) {
var geoJSON = mesh.toJSON();
console.log(geoJSON);
localStorage.setItem('geoJSON', JSON.stringify(geoJSON));
}
};
this.Load = function() {
var geoJSON = localStorage.getItem('geoJSON');
if (geoJSON) {
var parsedGeom = JSON.parse(geoJSON);
var loader = new THREE.ObjectLoader();
var mesh = loader.parse(parsedGeom);
mesh.material = new THREE.MeshBasicMaterial( { color: (Math.random() *0xffffff), wireframe: true } );
mesh.position.set(0, 3, 0);
mesh.name = "mesh";
scene.remove(scene.getObjectByName(mesh.name));
scene.add(mesh);
}
};
}
/** 定义 dat.GUI 对象,并绑定 guiParams 的几个属性 */
var gui = new dat.GUI();
var folder = gui.addFolder('Load & Save');
folder.open();
folder.add(guiParams, 'Save');
folder.add(guiParams, 'Load');

folder = gui.addFolder('Mesh');
folder.add(guiParams, 'radius', 0, 40, 1).onChange( function(e) { createMesh(); } );
folder.add(guiParams, 'tube', 0, 40, 0.1).onChange( function(e) { createMesh(); } );
folder.add(guiParams, 'tubularSegments', 0, 400, 1).onChange( function(e) { createMesh(); } );
folder.add(guiParams, 'radialSegments', 1, 20, 1).onChange( function(e) { createMesh(); } );
folder.add(guiParams, 'p', 1, 10, 1).onChange( function(e) { createMesh(); } );
folder.add(guiParams, 'q', 1, 15, 1).onChange( function(e) { createMesh(); } );
}

/** 加入地面 */
function createGround() {
var groundGeom = new THREE.PlaneGeometry(60, 60);
var groundMaterial = new THREE.MeshPhongMaterial( { color: 0xbbbbbb, side: THREE.DoubleSide} ); // 0x777777
ground = new THREE.Mesh(groundGeom, groundMaterial);
ground.receiveShadow = true; // 地面接收阴影
ground.rotation.x = -0.5 * Math.PI;
scene.add(ground);
}

/** 创建网格 */
var material = new THREE.MeshBasicMaterial( { color: 0xbcbcbc, wireframe: true } );
var mesh;
function createMesh() {
var geometry = new THREE.TorusKnotGeometry(
guiParams.radius,
guiParams.tube,
guiParams.tubularSegments,
guiParams.radialSegments,
guiParams.p,
guiParams.q
);

mesh = new THREE.Mesh(geometry, material);
mesh.position.set(0, 3, 0);
mesh.name = "mesh";
scene.remove(scene.getObjectByName(mesh.name));
scene.add(mesh);
}

</script>
</body>
</html>
顺便提一下,在旧有的 three.js 版本中,有专门的一个叫 GeometryExporter.js 的库来导出相应的信息。但是在新版 three.js 中,已经不需要借助任何外部的库就可以支持数据的导出。如本例中所使用的就是 THREE.Mesh 的 toJSON() 函数实现导出的。其实在新版 three.js 中,任何从 THREE.Object3D 类型继承而来的对象都可以通过其 toJSON() 函数来实现 JSON 格式的数据导出。随后就可以通过 THREE.ObjectLoader 对象的 parse() 函数或者 load() 函数反向解析出来。具体用法可以参考本示例中 createGuiControls() 函数中的 Save 及 Load 两个处理函数的代码。
未完待续···
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: