3dmax用FBX格式把模型导出并导入unity时需要注意的问题。
目录
二、轴心位置问题
2.1、一个物体
2.2、两个物体
2.3 分部分导出
三、角度问题
3.1、问题研究
3.2、解决方案操作
四、旋转操作
4.1、简单旋转情况
4.2、复杂旋转情况
4.3、不同视图创建的物体
4.4、小结
4.5、开门问题(研究过程,可以不用看,避免混乱)
4.5.1 (我的)创建门的过程:
4.5.2 【偏移:局部】
4.5.3 【偏移:世界】
4.5.4 开门结论
五、缩放操作
七、结论
3dmax和unity的坐标单位
没有修改单位的情况下,默认m为单位。
放到Unity中,结果能够和Unity里面的(1,1,1)大小的Cube重合
把3dmax的单位改成mm,结果就是1000mm的立方体和Unity里面1个单位的立方体是重合的。
说明Unity里面的1个单位就是3dmax里面的1m。
上面的问题也算是轴心的角度问题,这里讨论轴心位置问题。
创建一个小球,坐标设置成(0,0,0)
导入unity中,结果,坐标也是(0,0,0)。
修改物体坐标,再次导入unity中,物体有个初始坐标了,拖出来到Hierarchy里面,会有个坐标在物体上,但是修改物体坐标为(0,0,0),模型能够显示在unity的原点位置,这种情况问题不大。
大部分模型拿来放到unity中也不是用它自己的初始化位置,而是会给他一个位置(无论手动添加到场景中还是代码设置)。
-------------------------------------
接下来要修改轴的位置了。
在上面的基础上,在unity中,创建一个子物体,坐标是(0,0,0),这个子物体的位置就和3dmax导入的模型重合在一起的。
3dmax中修改轴的位置,移动到物体外面。
导入uinty,能看到轴心在小球物体外面了,而且P5这个对象有坐标。
这个坐标到底是什么呢?
可以看到这个代表unity原点的点,在3dmax的小球的里面,小球的位置是在(0,0,0)没错的,但是P5这个对象的坐标则是(0,0,-6)的。
假如把P5的坐标设置为(0,0,0)的话,小球就不在(0,0,0)上了。
创建一个(0,0,0)相对坐标的子物体,
子物体会出现在轴心位置
结论:一个物体,在unity里面的坐标值指的是轴心的坐标。
注意,其实在unity里面点击物体看到的坐标轴不一定就是模型的轴心,这里是1个物体,情况简单。
多个物体时,Unity会自动算一个轴心显示的,一般是几个模型的中心,这个显示轴心不一定就是模型的轴心。而相对坐标(0,0,0)的子物体则一定就是轴心的位置。
导入unity中
在unity中看到的和3dmax中一样的。
假如我把Sphere001的坐标从修改为(0,0,0),改成(0,10,0)。
导入unity中也是一样的
但是,这种情况其实是有问题的。
用小球看不出来,假如我创建一个大楼。
随便建,不用考虑大楼在世界坐标中的位置。
导入unity后
大楼就偏了。
为什么说偏了呢?因为轴心偏了。问题和前面一个物体是手动把轴心拖出来是一样的。
这里明明P6坐标是(0,0,0),大楼就是不在(0,0,0),不在下面正方形(假设是广场)的中心。
这个大楼不是整个世界,是一个大场景中的一部分,比如一个广场上的某一座大楼。Unity中的整个场景不会是全部用一个max文件创建的,会有多个max文件,多个人员,创建一个世界中的不同部分,然后合并组织成一个完整的场景的。
比如大楼原本计划在unity场景的(0,0,0)的,上面的导出模型的情况下,根本没有方便的操作去把大楼模型放到(0,0,0)的,因为它的轴心在模型的外面。
3dmax导出的模型的轴心必须在模型的中心,才能在场景组织时和其他模型能配合起来。
如何操作:3dmax中创建包含全部模型的顶层组,并把该组坐标设置为(0,0,0)。
unity中好使用了。
--------------------------------
这里其实还有的小问题,大楼跑到地下去了。
在3dmax中把组的轴心拖到地板中心
进入unity中,则模型P8,拖动出来就有个初始坐标了(0,-3.4,0)。
把坐标改成(0,0,0),则:
所以我们要求的模型轴心有两种:物体中心或者底面中心。
两种都可以,设备一般要求物体中心,大楼等建筑物体中心和地面中心都行,大楼等物体中心的情况下,代码上也是能统一处理的。
---------------------------------------------------------------------
其实这里就不是一定要把顶层组设置(0,0,0)了,和最前面的一个物体的情况一样。大不了有个初始坐标,使用是设置一下坐标就好了。
没有顶层组的情况下,3dmax导出的模型的轴心是原来3dmax中的世界坐标的(0,0,0)的位置。有顶层组的情况下,顶层组作为一个物体考虑,模型的轴心就是组的轴心,一般也就是组下面的模型的中心。
--------------------------------------------------------------------------------------------------------
我们项目一般是一个园区建模,拿到的模型需要将园区的地形和建筑分别导出给我们,我们再在unity里面搭建成完整的场景。
对于这种整体和部分的情况下的位置问题在这里研究一下。
创建一个3dmax场景模拟一下。
【偏移:世界】旋转90度,导入unity
坐标从3dmax里面的(100,200,300)变成了unity里面的(-100,300,-200),这个先不管。
接下来把各个部分单独导出。
有两种方式
1.选择子物体(分组)保存为max文件,打开max文件再导出fbx。
打开导出的文件,发现会多一个园区。
2.选择子物体(分组),导出子物体为fbx。
结果来看,两种方式都可以。看3dmax的分工合作方式。这块我还不了解,作为一个软件开发人员。(如果我来分配建模工作,首先位置以地形为主,先把地形建好,其他建筑正确摆放到地形上,再导出。)
要注意的是,因为前面导出fbx导出时先【“按递归方式打开”,选择全部物体,旋转轴】了,如果现在选择一个组,导出选定对象或者保存选定对象时,只会导出这个组,组里面的东西都没有。这个还挺不习惯的。
需要做的操作是先关闭组,再打开组,这样子选择的子物体(组)时,其实就是把子物体(组)里面的物体也都包括了。
//todo:其实按我设想,应该可以二次开发,做个自动将顶级组(如:园区)下面的一级子组(如:地形等)自动导出成fbx的工具。
-------------------------------------------------------
本来想说可能导出后部分的坐标可能有问题,所以这部分放在这里。但拖到unity里面,能保持建筑在正确的位置上的。
不过能注意到,每个子物体多了一层。其实这点从前面的子物体的max文件也能看到的,明明选择导出的是地形,结果保存的max打开里面多了个园区。
正确操作是,这种情况下,不要顶级分组了,就只有各个部分的分组(这样也不要做前面提到的打开操作了,不过有”按递归方式打开“的话,还是要先关闭了)。
各个分组导选定对象,并导入unity。
参考:【3dsmax】导入Unity3D需要注意的轴问题
先创建一个Demo场景,能辨认出朝向的。
放入unity里面结果
模型对象角度是(0,0,0)的,子物体对象角度是(-90,0,0)的,不行。
需要注意的是,前面导出的高级设置轴转换,无论是Y向上还是Z向上结果是一样的。
------------------------------------------
加个顶级分组再导出
这种情况就变成模型对象(根对象)是(-90,0,0),子物体是(0,0,0)了。
多加几层分组也是一样的:
1.在没有顶级分组的情况下,导入Unity中,根物体的角度是(0,0,0),第一级子物体(或分组)的角度是(-90,0,0),后续子物体(或分组)的角度是(0,0,0)
2.在有顶级分组的情况下,根物体角度是(-90,0,0),下面的子物体角度都是(0,0,0)
旋转轴:在3dmax中选中物体,切换到层次,仅影响轴。
在没有顶级分组情况下:
红色的轴是X轴,绿色的是Y轴,蓝色的是Z轴,默认是Z轴向上的。
进入旋转操作,注意要选中角度捕捉,设置90度。
旋转90°
注意,这时虽然看起来全选了,实际上选择就只有顶级的4个分组和物体,旋转的也是这4个的轴。导入Unity中,后上面的4个对于与第2级物体了,是(0,0,0)的,而第3级变成了(-90,0,0),4级则还是(0,0,0)了。
就算按ctr+a,或者全部框选,看起来像选中所有的物体,旋转所有物体的轴。
再导出FBX导入Unity,发现还是有些是(-90,0,0)。
正确操作是,先全选,然后选择按递归方式打开,然后再全选(这样才能选中全部物体),再旋转轴。
导出到Unity中后,所有的层级的物体都是(0,0,0)了。
---------------------------------------------------------------------------
在有顶级分组的情况下,假如旋转顶级分组本身的轴,导入Unity中则会是2级物体是(-90,0,0),其他都是(0,0,0)
正确操作还是和上面一样,要选择按递归方式打开,再旋转全部轴。
绕z轴(向上)旋转:
绕y轴旋转
旋转物体时,轴也是一起旋转的(这里有先把轴居中了)
在3dmax里面是沿着y轴旋转30度,到unity里面就变成了(-60,-90,90)。
假如这时按导出FBX要旋转轴90度的操作,因为按全面的教程操作是会把全部旋转的。但是有个问题,旋转的参考坐标系,默认是世界,这时的旋转的圈和轴方向就不一致了。
沿着世界坐标的x轴,旋转90度,结果在unity里面角度就是(0,-30,0)了。
把角度改成(0,0,0)的话,水平方向转了一下,垂直方向的角度还是斜的。
也就是说这时,这种倾斜的状态就是物体的原始角度(0,0,0)状态了。
参考坐标系改为局部(只有这个坐标系旋转控制的圈是和物体的轴方向一致的),沿局部坐标x轴旋转90度,导出后坐标是(0,0,30)。
这时把角度改成(0,0,0)物体就回正了。
从逻辑上讲,这种状态更符合直观逻辑。
沿y轴旋转10度
沿z轴旋转20度
沿x轴旋转30度
结果:
还是和上面一样的,“局部”坐标系,旋转90度,导出fbx。到unity里面。
把Box001的角度调整为(0,0,0),就又回正了。
3dmax还有个问题,在不同视图下创建的物体的轴方向(“局部”坐标系下)是不同的。
发现:
1.顶、前、左创建的物体,在对应视图下都是x轴向右,y轴向上,z轴向里。
2.正交和顶视图创建的物体的轴方向一致。
(成顶级组+局部轴旋转后)导出到unity里面,正交和顶角度是(0,0,0),前和左各有角度。
这里说明一下为什么要注意物体的角度,或者说是物体的轴的角度。
模型提供给unity后,不是静止的,在unity中会用脚本对物体进行操作,比如向某个方向移动,如向上移动。
假如角度不一致,用相同的脚本,实现的效果就会很怪。
public class Move : MonoBehaviour
{
//private Vector3 speed=new Vector3(0,1,0);
// Update is called once per frame
void Update()
{
Vector3 speed = new Vector3(0, 1, 0);
transform.Translate(speed);//正交和顶视图向上移动,前和左向左移动。
//transform.localPosition += transform.up;//正交和顶视图向上移动,前和左向左移动。
//transform.localPosition += speed;//全部物体朝上移动
}
}
public class Rotate : MonoBehaviour
{
void Update()
{
Vector3 speed = new Vector3(0, 1, 0);
transform.Rotate(speed);//正交和顶视图一起旋转,前和左向一起旋转。
//transform.localEulerAngles += transform.up;//正交和顶视图一起旋转,前和左向各自旋转。
//transform.localEulerAngles += speed;//全部物体一起旋转
}
}
对于前面这种简单方向移动的情况,在3dmax中把所有物体的轴改成(90,0,0),导入unity中,所有物体的轴方向就一致,都是(0,0,0)了。
注意这里的坐标系是“世界”了,如果选局部的话,没个物体的绝对角度都是(0,0,0)了。
参考:3dmax参考坐标系怎么设置。
发现我们建模人员没有改过参考坐标系的设置,从头到尾都是用”视图“。视图一定程度上相当于”世界“了。
结合前面的,导出fbx,旋转轴有3种操作:
4.4.1、”世界“坐标系,轴角度【绝对:世界】设置为(90,0,0)。
导入到unity后所有物体的角度就都是(0,0,0)。
4.4.2、"世界"坐标系,轴角度【偏移:世界】设置为(90,0,0),这个和手动绕x轴旋转90度的操作是一样的。
导入unity后,物体的角度会发生变化,参考前面的【4.1】部分的情况。
4.4.3、“局部"坐标系,x轴偏移旋转90度(或者手动绕x轴旋转90度),导入unity后,物体角度保存3dmax中的角度。
这种情况下的旋转轴90度操作只是为了消除3dmax导入unity造成的(-90,0,0)角度的偏移,物体本身还是有角度的。
参考前面的【4.1】部分的情况。
对于
对于门这种模型,特别是双开门,两个门板在创建时会有可能用了对称的方式复制出来,这样的话,它们的轴也是对称的。
1.在透视视图创建一个长方体A。
2.修改长方体的轴,移动并对其到一边的中心。要开启轴约束
3.镜像一个长方体B,复制方式
4.将B和A对其
5.另外创建一个门框
6.创建其他东西,并分组
采用【4.4.3】局部坐标系偏移旋转90度的方式,结果很奇怪
旋转过程中,发现通过镜像方式创建出来的物体在[仅影响轴]的状态下跟着轴一起旋转了:
这个说实话,很奇怪,非常奇怪。
镜像自身,不复制,也是会变成旋转轴旋转物体。
我原本是想推荐“【偏移:局部】“的方式旋转的,因为通过【绝对:世界】方式设置轴角度,会导致在unity中所有的轴都是(0,0,0),那样的话门的轴就不对称了。具体看下面的4.5.2。
本来打算先不用镜像的操作,手动复制一个物体并旋转物体和轴的方式,使两个门的轴对称。
居然做不到!
这里的轴对称是指
用几何体方式镜像出来的物体不会出现旋转轴旋转问题的问题
问题是这样镜像出来的东西轴不对称,保存和原来的物体的轴一样的角度
在4.5.1的过程创建的模型的基础上用"偏移:世界"([4.4.2])的方式旋转(90,0,0)度
旋转后
导入unity后DoorLeft是(0,0,0),DoorRight是(-180,0,0)
给Door1添加开门脚本
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class DoorOpen : MonoBehaviour
{
public Vector3 speed = new Vector3(0, 1, 0);
public Space space = Space.Self;
public Transform Left;
public Transform Right;
void Start()
{
if (Left == null)
{
Left = transform.Find("DoorLeft");
}
if(Left)
print("left:" + Left.up);
if (Right == null)
{
Right = transform.Find("DoorRight");
}
if (Right)
print("right:" + Right.up);
}
void Update()
{
//transform.Translate(transform.forward);
if(Left)
Left.Rotate(speed, space);
if(Right)
Right.Rotate(speed, space);//默认就是Space.Self
//Left.Translate(speed,Space.World);
//Right.Translate(speed, Space.World);
}
}
speed是(0,1,0),其实也是旋转角度,space是Self。
效果:
再改一下,左侧相对完整的一个开门脚本
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class DoorOpen : MonoBehaviour
{
public Vector3 speed = new Vector3(0, 1, 0);
public Space space = Space.Self;
public Transform Left;
public Transform Right;
public float angleMax = 90;
void Start()
{
if (Left == null)
{
Left = transform.Find("DoorLeft");
}
if(Left)
print("left:" + Left.up);
if (Right == null)
{
Right = transform.Find("DoorRight");
}
if (Right)
print("right:" + Right.up);
}
private int direction = 1;
public bool loop = true;
public bool isMove = true;
public Vector3 currentAngle;
void Update()
{
if (isMove == false) return;
currentAngle += speed * direction;//因为左边是0->90,右边是180->90;需要另外记录一下旋转角度
if (Left)
{
Left.Rotate(speed * direction, space);
}
if (Right)
{
Right.Rotate(speed * direction, space); //默认就是Space.Self
}
if (currentAngle.y > angleMax || currentAngle.y < 0)
{
if (loop)
{
direction *= -1; //旋转一下角度
}
else
{
isMove = false;//停止运行
}
}
}
}
效果:
用【偏移:世界】方式旋转轴导出的门模型,可以用脚本方便的制作开门动画。有些建模人员可能从来就没操作过局部视图,导出前的旋转使用默认的”视图“参考坐标系就好了。
对于缩放操作,简单的一个物体,缩放为200%,导出到unity里面后就是(2,2,2)了
之所以需要导出给unity的模型不要有缩放比例,即Scale要为(1,1,1),是因为在Unity里面会对模型进行一下操作。
虽然我们在计算一个物体的大小时是有考虑Scale参数的,但是还有其他子物体的操作情况,总之,设置为(1,1,1)是最保险的。
现在的问题是在3dmax里面有缩放需求的情况下,如何导入unity后Scale为(1,1,1)。
1.附加
附加到一个没有缩放的物体上。
物体A比例是200%,物体B比例是100%。
选择物体B,转换成可编辑多边形,点击附加,选中物体A,结果A就不见了,合并到物体B里面了。
同时物体B的轴、比例都不变,合并后的物体以B为准,A的200%的缩放也就不存在了。
附加后物体A就不存在的,对于需要操作物体A的情况来说,可能不大方便。
因为最终实际的模型是A,B只是为了消除缩放的影响,B物体可以是一个半径为0的球,且居中到物体A的中心,通过对齐方式。
2.加组
给有缩放的一个或者相关的多个物体加上组,结果组上面的缩放则是100%。
物体上的缩放是不变,不过这种情况下一般也就操作组,物体仅仅是作为一个子部件存在了。
3.缩放子元素
通过转换成可编辑多边形,选中所有点,缩放点的方向来改变物体的大小。
修改点后物体的比例还是100%的。
发现,简单的整体模型,Box,无论用点线面缩放都行的。
但是需要注意的是像茶壶这种的有多个部分组成的模型,如果不是用点缩放,用线、面等缩放,各个部分会独立缩放,导致整体就怪怪的了。
而用点缩放,则结果正常。
旋转操作也可以通过这几种方式消除对角度的影响
1.Unity里面的1个单位就是3dmax里面的1m。
2.max文件导出前文件中必须有一个顶层组,有且只有一个。
3.必须选择“按递归方式打开”,再旋转“全部”模型的轴的角度,旋转成Y轴(绿色)向上,注意这时的参考坐标系为视图或者世界(默认就是视图的)。
4.整个园区的各个部分的导出时不需要顶级组,只需要选中各个部分,并导出选中物体就行(参考前面2.3)。
备注:老版本(2012)的3dmax没有“按递归方式打开”,则必须升级max版本,因为人工打开的话,很容易出错,工作量也大。
鉴于导出fbx给unity前需要做这些操作,而后续可能还需要在原来基础上编辑修改模型。可以在改完模型的分组,建立顶级分组后,旋转轴之前,另存为一个专门用于导出fbx的max文件。在该max文件中做修改并导出为FBX文件,导出后这个文件就没用了。修改模型还是原来的文件,每次导出fbx都要再做一次旋转轴操作。
不将旋转轴的操作放到另一个max文件中的话,可能会导致轴混乱,比如,在旋转好轴的模型的基础上,再添加其他模型,新的模型的轴和前面的模型的方向就不一致了。下次导出fbx时必须人工区分开来两种轴的物体。
//todo:这些操作(组、递归打开、旋转轴、导出选定对象)用二次开发方式自动实现