#ThreeJS学习笔记( 二)——导入外部模型
外部模型格式
Threejs支持了许多格式的3D模型导入,包括*.obj、 *.sea、*.3mf 、*.amf、*.sea、*.pmd、*.json等。
这里主要讲解一下obj模型的导入,及将obj文件转成文件更小的json格式导入。
导入obj模型
3Dmax格式转换成obj格式
美术提供的一般为3Dmax项目文件夹,里面包含了.max文件以及贴图图片等资源,用3DMAX打开.max文件可以看到3D模型
如图:
- 点击菜单里的导出,选择obj格式,点击保存后出现选项
- 导出比例:几何体选项里的输出比例,默认是1.0,我们根据模型的分辨率,以及需要在网页上呈现的分辨率设置一下比例输出即可。简单的说,你直接按1.0比例输出的如果在浏览器看的时候大小差距太大了,可以在这里导出比例设置一下,另外也可以设置mesh的scale,直接缩放。这个模型需要将比例设置为0.1。几何体的其他选项都不需要修改。
- 材质:点击材质导出,勾选使用材质路径,选择保存材质的路径。格式为jpg,格式设置中可以设置导出jpg的质量,无特殊要求默认即可,并勾选转换位图,关闭材质设置。
- 点击导出,导出过程有可能会提示找不到材质,设置材质路径为美术提供的文件夹目录即可
- 最后会得到一个*.obj文件、一个*mtl文件、若干材质图,包括多个不同通道的贴图jpg和法线贴图jpg
导出obj后的二次处理
导出后的文件如图所示
其中,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则指定了法线贴图:
在使用threejs的mtlLoader解析mtl贴图并渲染为模型材质时,threejs仅调用map_Ka通道图片和map_bump法线贴图进行材质渲染。而基于图片数量及文件大小的控制,项目中采用的方法是让设计师将 JZ2012_jz_d.jpg、JZ2012_jz_s.jpg、JZ2012_jz_e.jpg三张图合成并调色后作为map_Ka通道贴图:
注:目前未查到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 );
此时在浏览器看到是这样的效果:
导入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 );
接下再添加一个4000*4000*4000的天空盒子作为背景,并适当调整camera的可视范围,最后显示结果如下:
在设置点光源位置时可借助Threejs提供的helper来查看设置的点光源位置,方便调整光源位置
scene.add( new THREE.PointLightHelper( pointlight3 ) );
scene.add( new THREE.PointLightHelper( pointlight2 ) );
scene.add( new THREE.PointLightHelper( pointlight ) );
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 ,将右侧出现的结果勾选上,到此则已经启用该插件。
转换格式
- 在blender中新建 一个项目,删除默认添加的正方体等对象
- 切换回info菜单栏,点击
文件->导入->*.obj
,选择obj之后,blender中会显示模型- 点击
文件->导出->Threejs(.json)
,保存为haven.json,存储到材质所在的路径。导出设置如下:
使用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);
})
这时我们看到的图像是这样的
是的,没错,blender只能将当前选中的单个模型导出json文件
,我们使用的obj文件是由多个子模型构成的模型组合,需要对每个子模型单独导出为json文件,并在页面中使用jsonLoader单独载入。
手机阅读请扫描下方二维码:
有在线预览地址吗? 我教程走 根本没用啊……
报错
Uncaught TypeError: Cannot read property 'visible' of undefined
at p (three.min.js:134)
at p (three.min.js:134)
at Xd.render (three.min.js:173)
at render (main.js?v=fsd:195)
at animate (main.js?v=fsd:189)
我的代码里没有设置visible属性的,看一下是否你自己的代码里main属性里的animate方法里的脚本是否有多余的设置
咨询一个问题,向blender导入文件的时候,为什么就只导入模型,没有图片。这样模型没有皮肤。很奇怪。
[...]http://feg.netease.com/archives/301.html[...]
test
你好,我是北京网易传媒的,有个3dmax方面的问题请教一下,你们3dmax贴图的材质球是用的默认扫描线渲染器下的材质球吗?但是看你们的mtl文件中的代码,是newmtl,这个应该是mental rey或v-rey渲染器下的名字,我用这两个渲染器下的材质球,导出obj时,贴图导不出来,只能在默认扫描线渲染器下的材质球导出obj才能导出贴图。这是我遇到的问题以及总结http://jingyan.baidu.com/article/fec7a1e5d9e7011190b4e7b6.html,我觉得您是用插件渲染器导出的贴图,求指教,谢谢啦!
找到答案了,已经解决!
3Dmax是由美术同学提供的。具体用哪个渲染器我就不知道了
找到什么答案可以分享么~最近也踩了你说的坑
天啦噜,找到答案了也不说一下,还出来炫耀一下。。。
这个问题,obj本身不带贴图,需要mtl同时导入,另外导出obj+mtl时,如果贴图路径没设置正确,可以用文本编辑器打开mtl文件,把图片路径修改为项目的相对路径。
单独导入obj是没有贴图的。如果只有obj可以用threejs的textureload导入贴图,并在导入obj后, 把obj的mesh.materital设置为导入的贴图。
您好,我导出obj格式后,没有导出贴图文件,mtl文件中也没有贴图路径是为什么呢
你好,请问将单独的obj文件导入threejs中,贴图的时候只会改变obj 的颜色而看不到实际的纹理,可能在那些方面出问题?
我的代码里没有设置visible属性的,看一下是否你自己的代码里main属性里的animate方法里的脚本是否有多余的设置
能否给demo包,新手求老司机领路
直接上threejs.org上找就可以了
加载obj的是这个 demo
https://threejs.org/examples/?q=obj#webgl_loader_obj_mtl
能否给个demo包,新手求老司机带路
直接上threejs.org上找就可以了
加载obj的是这个 demo
https://threejs.org/examples/?q=obj#webgl_loader_obj_mtl
想请教一个问题,three.js 导入 Obj 时,如果 Obj 本身是带有分组的,会把分组下的原件重复添加进去。比如在3D建模中,Room作为一个模型,编组下面有 Desk 对象。那么 three.js 导入 obj 后,Desk 这个对象会被导入两次,生成一个 Desk_1。这个怎么解决?如果不在建模时分组,那么我又怎么样在导入 Obj 之后把 Desk 和 Room 重新编组呢?例如 Room.add(Desk); 这样的形式。希望有时间的话可以回复,谢谢。
你这个room应该只是一个group,你可以只循环mesh添加,或者只添加group,这样就只会导入一次,重新编组,可以var g=new THREE.Group(); g.add(mesh1);g.add(mesh2),这样子,mesh1 和mesh2的position参考系就以g的中心点为原点。
问一下,max2012导出obj包含贴图并转换格式之后,如dds转tga。dds转xxx,导出的贴图都变暗了。用2010的倒是没问题。另外 使用obj导出的贴图不能带通道吗?源贴图是有通道的,导出之后通道就消失了?
我之前也遇过这个问题,后来发现threejs只贴了一个通道的图,最后我只能把所有的通道都合并成一张图,或者使用lightMAP来贴光影图。
用ligthMAP还需要把UV展开,搞2层uv,在原来的uv上贴上默认贴图,在uv2上贴光影图。而obj不支持2UV,只能blender加载包含2uv的模型,然后导出带2uv信息的json,再用threejs导入json。
你好,请问下如果我要加载不同的obj模型,建模出来的obj模型大小位置都不统一,这时候不能把camera的位置写死吧,该如何做到设置camera位置适配不同的模型啊
建议调整模型的position 和scale 来统一,而不调整camera。调camera的位置更难统一视觉效果。。
好厉害呀,学习一下
好文
asd
非常感谢
你好,我最近刚学threejs,在加载了包含贴图的mtl时爆了错误,Handlers.get() has been removed. Use LoadingManager.getHandler() instead.和这个错误Cannot read property 'setCrossOrigin' of undefined,这个错误是什么原因导致的呢?我怀疑是贴图路径的原因,但是路径我已经改为英文的了,还是报这个错误,有哪位大佬能帮我解惑
我也遇到了这个问题 求问怎么解决
1
1
1
1
1