#ThreeJS学习笔记( 二)——导入外部模型

外部模型格式

Threejs支持了许多格式的3D模型导入,包括*.obj、 *.sea、*.3mf 、*.amf、*.sea、*.pmd、*.json等。
这里主要讲解一下obj模型的导入,及将obj文件转成文件更小的json格式导入。

导入obj模型

3Dmax格式转换成obj格式

美术提供的一般为3Dmax项目文件夹,里面包含了.max文件以及贴图图片等资源,用3DMAX打开.max文件可以看到3D模型
如图:

1469608235365.png

  • 点击菜单里的导出,选择obj格式,点击保存后出现选项
  • 导出比例:几何体选项里的输出比例,默认是1.0,我们根据模型的分辨率,以及需要在网页上呈现的分辨率设置一下比例输出即可。简单的说,你直接按1.0比例输出的如果在浏览器看的时候大小差距太大了,可以在这里导出比例设置一下,另外也可以设置mesh的scale,直接缩放。这个模型需要将比例设置为0.1。几何体的其他选项都不需要修改。
  • 材质:点击材质导出,勾选使用材质路径,选择保存材质的路径。格式为jpg,格式设置中可以设置导出jpg的质量,无特殊要求默认即可,并勾选转换位图,关闭材质设置。
  • 点击导出,导出过程有可能会提示找不到材质,设置材质路径为美术提供的文件夹目录即可
  • 最后会得到一个*.obj文件、一个*mtl文件、若干材质图,包括多个不同通道的贴图jpg和法线贴图jpg

1469608270416.png

1469608312275.png

1469608413927.png

1469610128497.png

导出obj后的二次处理

导出后的文件如图所示

1469613142241.png

其中,test.mtl文件定义了test.obj模型与各贴图间的对应关系。
由于示例中的模型是由多个子模型组成的模型组,同时设置了多通道贴图,每一个子模型都对应四张贴图。
打开mtl文件,我们可以看到每个子模型都有一组贴图数据

# 3ds Max Wavefront OBJ Exporter v0.97b - (c)2007 guruware
# 创建的文件:27.07.2016 17:00:50

newmtl JZ2012_jz_l_1  
    Ns 20.0000
    Ni 1.5000
    d 1.0000
    Tr 0.0000
    Tf 1.0000 1.0000 1.0000 
    illum 2
    Ka 0.5882 0.5882 0.5882
    Kd 0.5882 0.5882 0.5882
    Ks 0.4500 0.4500 0.4500
    Ke 0.0000 0.0000 0.0000
    map_Ka JZ2012_jz_d.jpg
    map_Kd JZ2012_jz_d.jpg
    map_Ks JZ2012_jz_s.jpg
    map_Ke JZ2012_jz_e.jpg
    map_bump Map__171_法线凹凸.jpg

newmtl JZ2030_jz_l_1    
......

例如上面例子中关于子模型JZ2012_jz_l_1 贴图描述,其中map_Ka,map_Kd,map_Ks,map_Ke指定了各通道对应的贴图,map_bump则指定了法线贴图:

1469613671197.png

在使用threejs的mtlLoader解析mtl贴图并渲染为模型材质时,threejs仅调用map_Ka通道图片和map_bump法线贴图进行材质渲染。而基于图片数量及文件大小的控制,项目中采用的方法是让设计师将 JZ2012_jz_d.jpg、JZ2012_jz_s.jpg、JZ2012_jz_e.jpg三张图合成并调色后作为map_Ka通道贴图:

1469623501953.png

注:目前未查到threeJS 的mtl_loader是否能自动载入多通道贴图,待研究。
另外由于自动导出的法线贴图名称中带有中文,需要将mtl中map_bump和对应的法向贴图名称改成非中文命名。
在做以上图片处理时,需要同时将图片资源做压缩处理,以免资源加载时间过长。

使用mtlLoader和objLoaer加载obj模型及贴图

加载带mtl材质的obj模型,需要先定义mtl对象并加载mtl之外再加载obj模型。此时需要引用threejs两个js库

    <script src="../js/lib/threejs/MTLLoader.js"></script>
    <script src="../js/lib/threejs/OBJLoader.js"></script>

THREE.MTLLoader()函数说明:

  • mtlLoader.setBaseUrl():设置材质路径
  • mtlLoader.setPath():设置mtl文件所在路径
  • mtlLoader.load(filename,onSuccess(materials ),onProgress(xhr),onError(error)):mtl文件名、 加载成功后回调处理(参数为生成的材质库)、加载过程中回调处理(xhr对象属性可计算出已完成加载百分比)、失败回调处理

THREE.OBJLoader() 函数说明:

  • objLoader.setMaterials( materials ):设置obj使用的材质贴图
  • objLoader.setPath( options.objPath ):设置obj文件所在路径
  • objLoader.load( filename,onSuccess(object ),onProgress(xhr),onError(error)):obj文件名、 加载成功后回调处理(参数为生成的三维对象)、加载过程中回调处理(xhr对象属性可计算出已完成加载百分比)、失败回调处理。

在onSuccess(object ){}回调里我们可以对生成的三维对象做一些处理:对材质进行调色、设置透明度、设置贴图模式等,对设置旋转、缩放、位置摆放、自发光颜色、环境光颜色。
如果obj文件代表的三维对象是由多个子模型构成的模型组合,我们可以调用object.traverse(function(child){})来对每个子模型进行处理。
以下简单封装成一个函数


function createMtlObj(options){
//      options={
//          mtlBaseUrl:"",
//          mtlPath:"",
//          mtlFileName:"",
//          objPath:"",
//          objFileName:"",
//          completeCallback:function(object){  
//          }
//          progress:function(persent){
//              
//          }
//      }
    THREE.Loader.Handlers.add( //.dds$/i, new THREE.DDSLoader() );
    var mtlLoader = new THREE.MTLLoader();
    mtlLoader.setBaseUrl( options.mtlBaseUrl );//设置材质路径
    mtlLoader.setPath( options.mtlPath );//设置mtl文件路径
    mtlLoader.load( options.mtlFileName, function( materials ) {
        materials.preload();
        var objLoader = new THREE.OBJLoader();
        objLoader.setMaterials( materials );//设置三维对象材质库
        objLoader.setPath( options.objPath );//设置obj文件所在目录
        objLoader.load( options.objFileName, function ( object ) {
            
            
            if(typeof options.completeCallback=="function"){
                options.completeCallback(object);
            }
        }, function ( xhr ) {
            if ( xhr.lengthComputable ) {
                var percentComplete = xhr.loaded / xhr.total * 100;
                if(typeof options.progress =="function"){
                    options.progress( Math.round(percentComplete, 2));
                }
                //console.log( Math.round(percentComplete, 2) + '% downloaded' );
            }
        }, function(error){
            
        });

    });
}

调用示例:


createMtlObj({
    mtlBaseUrl:"../resource/haven",
    mtlPath: "../resource/haven",
    mtlFileName:"threejs.mtl",
    objPath:"../resource/haven",
    objFileName:"threejs.obj",
    completeCallback:function(object){
        object.traverse(function(child) { 
            if (child instanceof THREE.Mesh) { 
                child.material.side = THREE.DoubleSide;//设置贴图模式为双面贴图
                child.material.emissive.r=0;//设置rgb通道R通道颜色
                child.material.emissive.g=0.01;//设置rgb通道G通道颜色
                child.material.emissive.b=0.05;//设置rgb通道B通道颜色
                child.material.transparent=true;//材质允许透明
                //child.material.opacity=0;//材质默认透明度                        
                //child.material.shading=THREE.SmoothShading;//平滑渲染
            }
        });
        object.emissive=0x00ffff;//自发光颜色
        object.ambient=0x00ffff;//环境光颜色
//      object.rotation.x= 0;//x轴方向旋转角度
        object.position.y = 0;//位置坐标X
        object.position.z = 0;//位置坐标y
        object.scale.x=1;//缩放级别
        object.scale.y=1;//缩放级别
        object.scale.z=1;//缩放级别
        object.name="haven";//刚体名称
        object.rotation.y=-Math.PI;//初始Y轴方向旋转角度
        scene.add(object);//添加到场景中
    },
    progress:function(persent){
        
        $("#havenloading .progress").css("width",persent+"%");
    }
})

此时如果在之前没有添加光源,在浏览器上看到的可能是一片黑色,通过增加光源可以将物体显现出来。比例添加一个全局光

var ambient = new THREE.AmbientLight( 0xffffff );
scene.add( ambient );

此时在浏览器看到是这样的效果:
1470037673302.png

导入obj模型的后期渲染

如上图看到,我们导入的模型经过调色虽然比3Dmax显示效果好些,但仍然一片灰蒙蒙,丑不拉叽的。所以后期需要适当添加各种光源来渲染出更好的效果。
添加一个平行光做为主光源营造反射光面,并添加三个点光源对暗部适当补光后效果如下:

var directionalLight = new THREE.DirectionalLight( 0xffffff );
directionalLight.position.set( -5, 5, 5).normalize();
scene.add( directionalLight );
var pointlight = new THREE.PointLight(0x63d5ff, 1, 200); 
pointlight.position.set(0, 0, 200);
scene.add( pointlight );                
var pointlight2 = new THREE.PointLight(0xffffff, 1, 200); 
pointlight2.position.set(-200, 200, 200);
scene.add( pointlight2 );
var pointlight3 = new THREE.PointLight(0xffffff, 1.5, 200); 
pointlight3.position.set(-200, 200, 0);
scene.add( pointlight3 );

1470039562664.png

接下再添加一个4000*4000*4000的天空盒子作为背景,并适当调整camera的可视范围,最后显示结果如下:

1470039862826.png

在设置点光源位置时可借助Threejs提供的helper来查看设置的点光源位置,方便调整光源位置

scene.add( new THREE.PointLightHelper( pointlight3 ) );
scene.add( new THREE.PointLightHelper( pointlight2 ) );
scene.add( new THREE.PointLightHelper( pointlight ) );

1470041255056.png

DEMO:

<!DOCTYPE html>
<html>
    <head>
        <meta charset="UTF-8">
        <title></title>
    </head>
    <body>
    <div id="space"></div>  
    <script   src="https://code.jquery.com/jquery-1.12.4.min.js"   integrity="sha256-ZosEbRLbNQzLpnKIkEdrPv7lOy9C27hHQ+Xp8a4MxAQ="   crossorigin="anonymous"></script>
    <script src="../js/lib/threejs/three.js"></script>
    <script src="../js/lib/threejs/MTLLoader.js"></script>
    <script src="../js/lib/threejs/OBJLoader.js"></script>
    <script src="../js/lib/threejs/OrbitControls.js"></script>
    <script>
        

        var container, stats;

        var camera, scene, renderer;

        var mouseX = 0, mouseY = 0;

        var windowHalfX = window.innerWidth / 2;
        var windowHalfY = window.innerHeight / 2;


        init();
        animate();
        var mesh;

        function init() {

            container = document.getElementById("space")
            camera = new THREE.PerspectiveCamera( 45, window.innerWidth / window.innerHeight, 1, 8000 );
            camera.position.set(0, 0, 1500);
            
            scene = new THREE.Scene();

            var ambient = new THREE.AmbientLight( 0xffffff );
            scene.add( ambient );
            
            
            var directionalLight = new THREE.DirectionalLight( 0xffffff );
            directionalLight.position.set( -5, 5, 5).normalize();
            scene.add( directionalLight );

            var pointlight = new THREE.PointLight(0x63d5ff, 1, 200); 
            pointlight.position.set(0, 0, 200);
            scene.add( pointlight );                
            var pointlight2 = new THREE.PointLight(0xffffff, 1, 200); 
            pointlight2.position.set(-200, 200, 200);
            scene.add( pointlight2 );
            var pointlight3 = new THREE.PointLight(0xffffff, 1.5, 200); 
            pointlight3.position.set(-200, 200, 0);
            scene.add( pointlight3 );
            scene.add( new THREE.PointLightHelper( pointlight3 ) );
            scene.add( new THREE.PointLightHelper( pointlight2 ) );
            scene.add( new THREE.PointLightHelper( pointlight ) );
        
            
            var path = "../resource/sky";
            var format = '.jpg';
            var urls = [
                    path + 'px' + format, path + 'nx' + format,
                    path + 'py' + format, path + 'ny' + format,
                    path + 'pz' + format, path + 'nz' + format
                ];
            var skyMaterials = []; 
            for (var i = 0; i < urls.length; ++i) {
                var loader = new THREE.TextureLoader();
                loader.setCrossOrigin( this.crossOrigin );
                var texture = loader.load( urls[i], function(){}, undefined, function(){} );
                
                skyMaterials.push(new THREE.MeshBasicMaterial({
                    //map: THREE.ImageUtils.loadTexture(urls[i], {},function() { }), 
                    map: texture, 
                    overdraw: true,
                    side: THREE.BackSide,
                    //transparent: true,
                    //needsUpdate:true,
                    premultipliedAlpha: true
                    //depthWrite:true,
                    
    //              wireframe:false,
                })
                ); 
                
            } 
            
            var cube = new THREE.Mesh(new THREE.CubeGeometry(4000, 4000,4000), new THREE.MeshFaceMaterial(skyMaterials)); 
            cube.name="sky";
            scene.add(cube);
            
            createMtlObj({
                mtlBaseUrl:"../resource/haven",
                mtlPath: "../resource/haven",
                mtlFileName:"threejs.mtl",
                objPath:"../resource/haven",
                objFileName:"threejs.obj",
                completeCallback:function(object){
                    object.traverse(function(child) { 
                        if (child instanceof THREE.Mesh) { 
                            child.material.side = THREE.DoubleSide;
                            child.material.emissive.r=0;
                            child.material.emissive.g=0.01;
                            child.material.emissive.b=0.05;
                            child.material.transparent=true;
                            //child.material.opacity=0;                     
                            //child.material.shading=THREE.SmoothShading;
                        }
                    });

                    object.emissive=0x00ffff;
                    object.ambient=0x00ffff;
                    //object.rotation.x= 10/180*Math.PI;
                    object.position.y = 0;
                    object.position.z = 0;
                    object.scale.x=1;
                    object.scale.y=1;
                    object.scale.z=1;
                    object.name="haven";
                    object.rotation.y=-Math.PI;
                    scene.add(object);
                },
                progress:function(persent){
                    
                    $("#havenloading .progress").css("width",persent+"%");
                }
            })
            renderer = new THREE.WebGLRenderer();
            renderer.setPixelRatio( window.devicePixelRatio );
            renderer.setSize( window.innerWidth, window.innerHeight );
            container.appendChild( renderer.domElement );
            document.addEventListener( 'mousemove', onDocumentMouseMove, false );
            window.addEventListener( 'resize', onWindowResize, false );
        }
        
        var controls = new THREE.OrbitControls(camera,container);
        //controls.maxPolarAngle=1.5;
        //controls.minPolarAngle=1;
        controls.enableDamping=true;
        controls.enableKeys=false;
        controls.enablePan=false;
        controls.dampingFactor = 0.1;
        controls.rotateSpeed=0.1;
//      controls.enabled = false;
        //controls.minDistance=1000;
        //controls.maxDistance=3000;
        function createMtlObj(options){
        //      options={
        //          mtlBaseUrl:"",
        //          mtlPath:"",
        //          mtlFileName:"",
        //          objPath:"",
        //          objFileName:"",
        //          completeCallback:function(object){  
        //          }
        //          progress:function(persent){
        //              
        //          }
        //      }
                //THREE.Loader.Handlers.add( //.dds$/i, new THREE.DDSLoader() );
            var mtlLoader = new THREE.MTLLoader();
            mtlLoader.setBaseUrl( options.mtlBaseUrl );
            mtlLoader.setPath( options.mtlPath );
            mtlLoader.load( options.mtlFileName, function( materials ) {
                materials.preload();
                var objLoader = new THREE.OBJLoader();
                objLoader.setMaterials( materials );
                objLoader.setPath( options.objPath );
                objLoader.load( options.objFileName, function ( object ) {
                    if(typeof options.completeCallback=="function"){
                        options.completeCallback(object);
                    }
                }, function ( xhr ) {
                    if ( xhr.lengthComputable ) {
                        var percentComplete = xhr.loaded / xhr.total * 100;
                        if(typeof options.progress =="function"){
                            options.progress( Math.round(percentComplete, 2));
                        }
                        //console.log( Math.round(percentComplete, 2) + '% downloaded' );
                    }
                }, function(error){
                    
                } );
        
            });
        }
        function onWindowResize() {
            windowHalfX = window.innerWidth / 2;
            windowHalfY = window.innerHeight / 2;
            camera.aspect = window.innerWidth / window.innerHeight;
            camera.updateProjectionMatrix();
            renderer.setSize( window.innerWidth, window.innerHeight );
        }
        function onDocumentMouseMove( event ) {
            mouseX = ( event.clientX - windowHalfX ) / 2;
            mouseY = ( event.clientY - windowHalfY ) / 2;
        }
        function animate() {
            requestAnimationFrame( animate );
            render();
        }
        function render() {
//              camera.position.x += ( mouseX - camera.position.x ) ;
//              camera.position.y += ( mouseY - camera.position.y ) ;
            camera.lookAt( scene.position );
            renderer.render( scene, camera );

        }
        
    </script>
    </body>
</html>


使用jsonLoader加载3D模型

threejs提供了对json格式的支持,相对obj格式的文件来说要小很多,可以提高模型的加载速度。
将obj文件转为了json格式需要借助一个软件:blender以及THREEJS提供的插件io_three;
软件下载地址:https://www.blender.org/
io_three插件在three代码包里的路径:three.js-master/utils/exporters/blender/addons

安装插件的方法

  • 将io_three 复制到blender安装目录下的addons目录中,如:D:/blender-2.77a-windows64/2.77/scripts/addons
  • 打开blender,点击菜单->用户设置,向下拖动保存用户设置按钮所在的区域,会显示所有的用户设置内容
  • 选择add-ons选项卡,在左侧搜索框输入three ,将右侧出现的结果勾选上,到此则已经启用该插件。

1470192336842.png

转换格式

  • 在blender中新建 一个项目,删除默认添加的正方体等对象
  • 切换回info菜单栏,点击文件->导入->*.obj,选择obj之后,blender中会显示模型
  • 点击文件->导出->Threejs(.json),保存为haven.json,存储到材质所在的路径。导出设置如下:

1470209341377.png

使用JsonLoader载入json

var loader = new THREE.JSONLoader();
loader.load( '../resource/haven/haven.json', function ( geometry, materials ) {
    var object = new THREE.Mesh( geometry, materials[0] );
    object.material.side = THREE.DoubleSide;
    object.material.emissive.r=0;
    object.material.emissive.g=0.01;
    object.material.emissive.b=0.05;
    object.material.transparent=true;
    //object.material.opacity=0;                        
    object.material.shading=THREE.SmoothShading;
    object.position.y = 0;
    object.position.z = 0;
    object.scale.x=1;
    object.scale.y=1;
    object.scale.z=1;
    scene.add(object);
})

这时我们看到的图像是这样的

1470209408071.png

是的,没错,blender只能将当前选中的单个模型导出json文件,我们使用的obj文件是由多个子模型构成的模型组合,需要对每个子模型单独导出为json文件,并在页面中使用jsonLoader单独载入。

来源:http://feg.netease.com/archives/301.html

WEBGL学习网(WebGLStudy.COM)专注提供WebGL 、ThreeJS、BabylonJS等WEB3D开发案例源码下载。
声明信息:
1. 本站部分资源来源于用户上传和网络,如有侵权请邮件联系站长:1218436398@qq.com!我们将尽快处理。
2. 分享目的仅供大家学习和交流,您必须在下载后24小时内删除!
3. 不得使用于非法商业用途,不得违反国家法律。否则后果自负!
4. 本站提供的源码、模板、插件等等其他资源,都不包含技术服务请大家谅解!
5. 如有链接无法下载、失效或广告,请联系管理员处理!
6. 本站资源打赏售价用于赞助本站提供的服务支出(包括不限服务器、网络带宽等费用支出)!
7.欢迎加QQ群学习交流:549297468 ,或者搜索微信公众号:WebGL学习网
WEBGL学习网 » #ThreeJS学习笔记( 二)——导入外部模型

发表评论

提供优质的WebGL、ThreeJS源码

立即查看 了解详情