美文网首页
Threejs 入门并导入外部模型(obj+mtl)

Threejs 入门并导入外部模型(obj+mtl)

作者: heavens丶 | 来源:发表于2021-07-15 17:54 被阅读0次

            目前碰到一个需求,需要把隔壁部门提供的外部模型展现到 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>
    

           合并到项目的截图如下:

    项目截图

           研究完成之后,特此记录,希望可以帮到需要帮助的人,有什么疑问或者遗漏,也可以评论留言。

    相关文章

      网友评论

          本文标题:Threejs 入门并导入外部模型(obj+mtl)

          本文链接:https://www.haomeiwen.com/subject/pnvppltx.html