目前碰到一个需求,需要把隔壁部门提供的外部模型展现到 WEB 上,并且可旋转和移动视角查看。作为一个从未接触过 WEBGL 的菜鸟来说,一顿谷歌搜出来常用的四个:Threejs、Babylonjs、Scenejs、Layaboxjs,最后由于英文不好和使用人群的原因,选择了第一个。
决定好了之后,第一步先引入CDN(JQ可以不管):
<script type="text/javascript" src="https://cdn.bootcdn.net/ajax/libs/jquery/3.3.1/jquery.js"></script>
<script type="text/javascript" src="https://cdn.bootcdn.net/ajax/libs/three.js/r128/three.min.js"></script>
引入完毕之后,先进行初始化:
<script type="text/javascript">
var container = window.document.getElementById("content_box"); // 需要插入 canvas 的标签
var width = container.offsetWidth;
var height = container.offsetHeight;
var scene = new THREE.Scene(); // 创建场景
var camera = new THREE.PerspectiveCamera(45, width / height, 1, 1700); // 创建相机
var controls = null; // 控制器
// 设置相机位置
camera.position.x = 100;
camera.position.y = 160;
camera.position.z = 330;
var renderer = new THREE.WebGLRenderer(); // 渲染
renderer.shadowMap.enabled = true; // 开启阴影效果
renderer.setClearColor('#F77C0D', 1); // 设置背景颜色
renderer.setPixelRatio(window.devicePixelRatio); // 设置显示比例
renderer.setSize(width, height); // 设置渲染大小
container.appendChild(renderer.domElement);
render();
// 执行渲染操作,指定场景、相机作为参数
function render() {
renderer.render(scene, camera);
}
</script>
如果 content_box
变成橙色,那说明代码执行成功了。
Threejs是不能直接导入外部模型的,需要先引入响应的扩展插件:
<script type="text/javascript" src="https://www.wjceo.com/lib/js/loaders/MTLLoader.js"></script>
<script type="text/javascript" src="https://www.wjceo.com/lib/js/loaders/OBJLoader.js"></script>
然后就可以加载外部模型了:
<script type="text/javascript">
var objLoader = new THREE.OBJLoader();
var mtlLoader = new THREE.MTLLoader();
loadMTL();
loadOBJ();
// 加载MTL文件(如果不需要材质,可直接执行loadOBJ(),否则必须优先加载材质)
function loadMTL() {
mtlLoader.load('./piers.mtl', function (materials) {
// OBJ模型会和MaterialCreator包含的材质相对应
materials.preload();
objLoader.setMaterials(materials);
loadOBJ(materials);
}, function () {
console.log('import MTL success!');
}, function (err) {
console.log('MTL error!', err);
});
}
// 加载OBJ文件
function loadOBJ() {
// 如果没有材质文件,系统自动设置Phong网格材质
objLoader.load('./piers.obj', function (obj) {
// 初始化模型坐标值(根据需要自行调整)
obj.position.x = 0;
obj.position.y = 0;
obj.position.z = -40;
// 设置模型缩放比例
obj.scale.set(0.7, 0.7, 0.7);
// 把模型添加到场景里面
scene.add(obj);
setTimeout(render, 400);
}, function () {
console.log('import OBJ success!');
}, function (err) {
console.log('OBJ error!', err);
});
}
</script>
执行后如果没有成功加载的,有可能遇到了以下问题:
⑴、请求跨域。类似请求接口一样,加载外部模型也必须要有环境的支持,可以通过 webpack、nginx代理、把代码放在服务器端、用 Chrome 插件(Web Server)等等来解决,具体就不详述了,不会的自行上网查资料。
⑵、MTL材质贴图路径问题。如果MTL文件带有贴图或者其它材质,可能会产生路径错误的问题,查看控制台会看到贴图文件的 404 错误,这就是因为路径匹配不上的原因。生成MTL文件的时候,外部材质的路径一般是根据你的电脑所配置的,就像这样:
MTL材质贴图路径问题
解决方法很简单,其它不动,只用修改引用材质的路径就可以了(这里我是直接放到同级目录),和 HTML 引入 JS 一样,唯一不同的就是左斜杠(/)换成右斜杠(\)罢了。
map_Ka .\concrete.jpg
map_Kd .\concrete.jpg
友情提示,如果是使用了 webpack 或者脚手架工具的话(如vue-cli
),得先看设置的 baseURL 在哪,因为在 webpack 环境里,有时候路径指向的并不是针对于当前文件的相对路径。
⑶、MTL 材质透明度问题(巨坑)。如果上述两步解决了还是看不到模型,那很有可能是 MTL 对象的透明度为 0 的原因。Theeejs 某些情况下加载完 MTL 文件后,MTL 对象里面的 opacity 属性默认居然是 0(至今没找到原因,知道的大佬可以在评论区解释下,感谢),所以需要我们手动设置一下:
<script type="text/javascript">
// 加载MTL文件
function loadMTL() {
mtlLoader.load('./piers.mtl', function (materials) {
materials.preload();
objLoader.setMaterials(materials);
// 遍历所有材质对象,把透明度统一改为1
var materialsDetail = materials.materials;
for (var item in materialsDetail) {
materialsDetail[item].opacity = 1
}
loadOBJ(materials);
}, function () {
console.log('import MTL success!');
}, function (err) {
console.log('MTL error!', err);
});
}
</script>
⑷、外部模型坐标偏离原点问题(巨坑)。如果仍然没看到模型,很有可能给你模型的人把模型偏移了原点很远很远,通过代码貌似设置不了(也可能是没找到办法),我第一次拿到模型的时候,模型距离原点有几十万,当时百思不得其解,最后还是叫群里的大佬帮我看了下模型才发现问题........
到此,应该可以正常的展现模型了。
桥墩模型(无光)
但为啥是黑色的呢?其实,我们肉眼所能看见的一切,都是光的反射,没有光,我们能看到的也只是一片漆黑。在这里也一样。
所以需要添加光源,才能看到实际的样子:
<script type="text/javascript">
// 加载OBJ文件
function loadOBJ() {
objLoader.load('./piers.obj', function (obj) {
obj.position.x = 0;
obj.position.y = 0;
obj.position.z = -40;
obj.scale.set(0.7, 0.7, 0.7);
scene.add(obj);
// 添加环境光源(可根据具体情况添加不同的光源)
var ambient = new THREE.AmbientLight(0xffffff, 0.8);
scene.add(ambient);
setTimeout(render, 400);
}, function () {
console.log('import OBJ success!');
}, function (err) {
console.log('OBJ error!', err);
});
}
</script>
添加完成之后,就能看见差不多的效果了,为啥说差不多呢,如果只是这样基本的添加光源是无法百分百还原的,因为我这边需求没这么高,所以就没在光源上面浪费太多时间。
桥墩模型(有光)
最后的最后,加上轨道控制器,就是所谓的移动旋转视角:
<script type="text/javascript" src="https://raw.githubusercontent.com/fibo/three-orbitcontrols/master/OrbitControls.js"></script>
<script type="text/javascript">
controls = new THREE.OrbitControls(camera,renderer.domElement); // 创建控件对象
controls.addEventListener('change', render); // 监听鼠标、键盘事件
</script>
至此,Threejs 导入外部模型已经全部完成,效果如下。
最终效果完整代码如下:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style type="text/css">
html, body, #content_box{margin: 0;padding: 0;width: 100%;height: 100%;}
#content_box{top: 0;bottom: 0;right: 0;left: 0;z-index: 4;}
</style>
</head>
<body>
<div id="content_box"></div>
</body>
<script type="text/javascript" src="https://cdn.bootcdn.net/ajax/libs/jquery/3.3.1/jquery.js"></script>
<script type="text/javascript" src="https://cdn.bootcdn.net/ajax/libs/three.js/r128/three.min.js"></script>
<script type="text/javascript" src="https://www.wjceo.com/lib/js/loaders/MTLLoader.js"></script>
<script type="text/javascript" src="https://www.wjceo.com/lib/js/loaders/OBJLoader.js"></script>
<script type="text/javascript" src="https://raw.githubusercontent.com/fibo/three-orbitcontrols/master/OrbitControls.js"></script>
<script type="text/javascript">
var container = document.getElementById("content_box"); // 需要插入 canvas 的标签
var width = container.offsetWidth;
var height = container.offsetHeight;
var scene = new THREE.Scene(); // 创建场景
var camera = new THREE.PerspectiveCamera(45, width / height, 1, 1700); // 创建相机
// 设置相机位置
camera.position.x = 100;
camera.position.y = 160;
camera.position.z = 330;
var objLoader = new THREE.OBJLoader();
var mtlLoader = new THREE.MTLLoader();
loadMTL();
loadOBJ();
var renderer = new THREE.WebGLRenderer(); // 渲染
renderer.shadowMap.enabled = true; // 开启阴影效果
renderer.setClearColor('#F77C0D', 1); // 设置背景颜色
renderer.setPixelRatio(window.devicePixelRatio); // 设置显示比例
renderer.setSize(width, height); // 设置渲染大小
container.appendChild(renderer.domElement);
var controls = new THREE.OrbitControls(camera,renderer.domElement); // 创建控件对象
controls.addEventListener('change', render); // 监听鼠标、键盘事件
// 执行渲染操作,指定场景、相机作为参数
function render() {
console.log('render');
renderer.render(scene, camera);
}
// 加载MTL文件
function loadMTL() {
mtlLoader.load('./piers.mtl', function (materials) {
// OBJ模型会和MaterialCreator包含的材质相对应
materials.preload();
objLoader.setMaterials(materials);
var materialsDetail = materials.materials;
for (var item in materialsDetail) {
materialsDetail[item].opacity = 1
}
loadOBJ(materials);
}, function () {
console.log('import MTL success!');
}, function (err) {
console.log('MTL error!', err);
});
}
// 加载OBJ文件
function loadOBJ() {
// 如果没有材质文件,系统自动设置Phong网格材质
objLoader.load('./piers.obj', function (obj) {
// 初始化模型坐标值
obj.position.x = 0;
obj.position.y = 0;
obj.position.z = -40;
// 设置模型缩放比例
obj.scale.set(0.7, 0.7, 0.7);
// 把模型添加到场景里面
scene.add(obj);
var ambient = new THREE.AmbientLight(0xffffff, 0.8);
scene.add(ambient);
var directional = new THREE.DirectionalLight(0xffffff, 0.8);
directional.position.set(5, 10, 7);
setTimeout(render, 400);
}, function () {
console.log('import OBJ success!');
}, function (err) {
console.log('OBJ error!', err);
});
}
</script>
</html>
网友评论