[]LayaAir3D 坐标系统与矩阵变换

        先扯些闲话,(以下闲话只针对小白,老鸟直接跳过)。在laya做3d也有一段时间了,我平时看到了很多开发者在QQ群和社区中提到的问题,当我看到百分之八十以上的问题,我的内心是崩溃的,感觉到大部分开发者是没有3d基础的,甚至很多连2d基础都没有,单纯是因为这项新鲜的产业3D VR H5吸引了他,然后就跟者我们直播,官网的示例学习。但我觉得,这些教学对于你们来说造成了很大的误导,因为他并不适合初学者,就算你学会了用各种工具里导处3d资源,又能怎么样呢?你会看着一些生硬的API实现你想要的效果,但你又知道内部的原理是什么吗?你能做出来游戏吗?并不能!为了让你们真正的熟悉Laya3d,以后我会抽空出一整套从菜鸟到老鸟的教程,让你们真正进入Laya3D的世界。
1.png

        关于3d坐标系,我想大家并不陌生,laya3d是右手坐标系,unity3d是左手坐标系,3dmaxs是右手坐标系。我主要讲解laya3d坐标系。
        先说轴向问题,看上面右边的图,别看那个诡异的手,伸出你的右手,手面朝着自己,让大拇指指向X轴方向,食指指向Y轴方向,其他的那一堆手指指向Z轴方向,也是指向自己,保持这个姿势,看好自己的右手,凝视它,记住他,这就是laya3d的坐标系,理解他,以后在3d场景中对物体进行平移旋转操作不要太简单。左手坐标系,同理,但一定要记住,laya3d的坐标系统是右手坐标系,你能理解这个,也算我没白撸这些废话。
        然后是单位尺度,laya3D中的单位是米,而不是像素。3dmax中一般默认的系统单位是米,显示单位是毫米。unity中默认的单位也是米。为什么要跟大家介绍这些东西,看下面就知道了,如果大家用过laya的fbxTool导出过3d模型,然后放到3d场景中,怎么也显示不出来,因为,你在3dmax中看到的模型虽然不大,但他是以毫米为显示单位的,系统单位是米,实际的模型是他的1000倍大,因为大多数物体都是单面渲染的,内部你是看不到的,因此放到我们laya3d场景中,你怎么移动照相机位置,也看不到模型,这就是问题所在,可以试试把模型缩小1000倍即可,因为unity中的单位尺度与laya3d相同,所以就不必以上操作。说句工具上的题外话,制作单个模型是,尽量把模型放在3d坐标系原点,会更利于你的开发!
        为了让大家更加深入的了解laya3d坐标系统,在这里我还要介绍一个简单的概念,矩阵变换,这里不做过深入的介绍,你只需要知道它可以控制3d精灵的旋转,平移,缩放即可;想要深入了解矩阵和3d空间物体的关系,可以仔细研究下我们引擎的源码,或者学习下webgl相关的知识!下来我会向大家介绍,怎么用laya3d api对空间物体进行矩阵变换也就是旋转平移缩放。
        首先我需要在3d空间中,创造点基本的元素,一个平面,一个方体,一个球体。以下我用laya自带的方法创建,这个环节不做过多介绍。
            //初始化3d画布
Laya3D.init(0, 0, true);

//设置全屏
Laya.stage.scaleMode = Stage.SCALE_FULL;
//显示统计数据
Stat.show();

//给舞台添加laya3d场景
var scene:Scene = Laya.stage.addChild(new Scene()) as Scene;

//初始化照相机
var camera:Camera = scene.addChild(new Camera()) as Camera;
camera.transform.position = new Vector3(0, 3, 3);
camera.transform.rotate(new Vector3( -45, 0, 0), true, false);
camera.addComponent(CameraMoveScript);

//生成平面,其实是一个box,只不过高度很小,可看成一个平面
var plane:MeshSprite3D = scene.addChild(new MeshSprite3D(new BoxMesh(4,4,0.001))) as MeshSprite3D;
var material:StandardMaterial = new StandardMaterial();
material.albedo = new Vector4(1.3, 1.3, 1.3, 1);
material.diffuseTexture = Texture2D.load("res/threeDimen/layabox.png");
plane.meshRender.material = material;

//生成坐标中心,其实是球体
var sphere:MeshSprite3D = scene.addChild(new MeshSprite3D(new SphereMesh(0.05,100,100))) as MeshSprite3D;
var material:StandardMaterial = new StandardMaterial();
material.albedo = new Vector4(0, 0, 0, 1);
sphere.meshRender.material = material;
sphere.transform.position = new Vector3(0, 0, 0);

//模拟x轴,其实是方体
var x:MeshSprite3D = scene.addChild(new MeshSprite3D(new BoxMesh(2,0.03,0.03))) as MeshSprite3D;
var material:StandardMaterial = new StandardMaterial();
material.albedo = new Vector4(1, 0, 0, 1);
x.meshRender.material = material;
x.transform.position = new Vector3(1, 0, 0);

//模拟y轴,其实是方体
var y:MeshSprite3D = scene.addChild(new MeshSprite3D(new BoxMesh(0.03,0.03,2))) as MeshSprite3D;
var material:StandardMaterial = new StandardMaterial();
material.albedo = new Vector4(0, 1, 0, 1);
y.meshRender.material = material;
y.transform.position = new Vector3(0, 0, 0);

//模拟z轴,其实是方体
var z:MeshSprite3D = scene.addChild(new MeshSprite3D(new BoxMesh(0.03,2,0.03))) as MeshSprite3D;
var material:StandardMaterial = new StandardMaterial();
material.albedo = new Vector4(0, 0, 1, 1);
z.meshRender.material = material;
z.transform.position = new Vector3(0, 0, 1);

//生成方体
var box:MeshSprite3D = scene.addChild(new MeshSprite3D(new BoxMesh(0.3,0.3,0.3))) as MeshSprite3D;
var material:StandardMaterial = new StandardMaterial();
material.albedo = new Vector4(0.5, 0.5, 0.5, 1);
box.meshRender.material = material;
2.png

        以上是该程序执行的效果,有一个平面,有一个模拟的laya3d坐标系,红色的为x轴,绿色的为y轴,蓝色的为z轴,还有一个处于坐标原点的正方体,看到这里,我想大家对laya3d坐标系有了更深的了解! 
        下来我简单介绍下Transform3d这个类的一些简单属性,看下面的代码和注释。
			//设置世界位置,会使这个方体在整个世界坐标系的绝对位置为(x,y,z)
box.transform.position = new Vector3(x, y, z);

//设置局部位置,会使这个方体在父节点坐标系的相对位置为(x,y,z)
//如果没有加入别的节点中,则position与localPosition的效果相同
box.transform.localPosition = new Vector3(x, y, z);

//设置世界旋转,会使这个方体根据世界坐标系的坐标轴进行旋转
//Quaternion四元数,常用来表示3d旋转,可以根据各个轴的旋转弧度计算出
//Quaternion.createFromYawPitchRoll(yaw:Number, pitch:Number, roll:Number, out:Quaternion)
box.transform.rotation = new Quaternion(x, y, z, w);

//设置局部旋转,会使这个方体根据自身坐标系的坐标轴进行旋转
//如果没有进行过旋转,则rotation与localRotation效果相同
box.transform.localRotation = new Quaternion(x, y, z, w);

//
设置局部缩放,会使这个方体根据自身坐标轴方向进行缩放(x, y, z)倍
box.transform.localScale = new Vector3(x, y, z);
  
       估计没有接触过3d的同学,会对上面解释有些模糊,尤其对世界和局部有什么区别,没关系,下面我着重介绍介绍Transform3d两个方法,并在实践中向大家具体介绍。
        第一步:在原始没进行过任何旋转的基础上进行旋转
            /**
* 旋转变换。
* @param rotations 旋转幅度。
* @param isLocal 是否局部空间。
* @param isRadian 是否弧度制。
*/
//1.先对box,进行绕x轴世界旋转90度,
//box.transform.rotate(new Vector3(90, 0, 0), false, false);
//或者对box,进行绕x轴局部旋转90度,因为没有被旋转过,世界坐标轴和局部坐标轴相同,效果是一样的.
box.transform.rotate(new Vector3(90, 0, 0), true, false);
3.png

4.png

        上图,上边为原始的,下边为绕x轴进行世界/局部旋转后的效果。

        第二步:在步骤一的基础上,先进行局部旋转      
			//2.对box,进行绕z轴局部旋转90度,
box.transform.rotate(new Vector3(0, 0, 90), true, false);
4.png


5.png

        上图,上边为第一步旋转后的,下变为本次局部旋转后的效果,为什么会出现现在的效果呢,因为当第一步进行旋转时,box内部的坐标系跟着同时旋转了,动动脑子,脑补下,进行第一步旋转后,z轴不再朝向你,而是朝向下面。

        第三步:在步骤一的基础上,进行世界旋转
			//3.对box,进行绕z轴世界旋转90度,
box.transform.rotate(new Vector3(0, 0, 90), false, false);
4.png


6.png

        上图,上边为第一步旋转后的,下边为本次世界旋转后的效果,这个很好理解,就是绕着世界坐标系坐标轴的z轴进行旋转。还有一个小规律,旋转的方向问题,当旋转值为正时,旋转方向为你正对着该旋转轴的正方向的逆时针方向,自己理解。

       第四步:在步骤一的基础上,进行局部平移。
			/**
* 平移变换。
* @param translation 移动距离。
* @param isLocal 是否局部空间。
*/
//4.对box,进行x轴方向局部平移1米,
box.transform.translate(new Vector3(0, 1, 0), true);



4.png


7.png

        上图,上边为第一步旋转后的,下边为本次进行局部移动后的,由于进行了第一步的旋转,box内部的坐标轴y轴朝向的是自己,所以会看到box向自己移动了1米的距离。

        第五步:在步骤一的基础上,进行世界平移,到这里,我相信大家心中都会有正确的答案了,上代码的答案。
			//5.对box,进行x轴方向世界平移1米,
box.transform.translate(new Vector3(0, 1, 0), false);
4.png


8.png

        上图,上边为第一步旋转后的,下边为本次进行世界移动后的,box不受自身影响,会向世界坐标系的y轴进行移动1米的距离。有的人或许有疑问,position/localPosition和translate有什么不同,前者是直接设置位置,后者是在当前位置基础上移动相应的距离,旋转同理
        
 
         看到这里,我相信大家对laya3d坐标系已经有了更深入的了解,同时也对3d精灵的矩阵变换有了更深层次的了解,反复阅读本节内容,相信你能在laya3d场景中熟练的操纵任何一个物体的旋转平移,缩放。
         如果大家对本节内容有任何疑问,请随时跟帖留言;如果大家发现以上有错误的地方,欢迎指出,我们一起学习进步,谢谢。最后提前祝大家圣诞快乐! 
 
已邀请:

cuixueying

赞同来自:

受教,感谢!

layabox

赞同来自:

推荐

fool_tiger

赞同来自:

 请教下
我转X轴的时候用的是 sphere.transform.rotate(vectx,true,false);
转Y轴的时候用的是sphere.transform.rotate(vecty,false,false);
 
一个是局部,一个是世界
我怎么限制x轴转到北极就不转了,欧拉角,同一个位置每次输出的值都不一样,怎么确定呢 

182*****824

赞同来自:

很棒!赞!但是有瑕疵,首先效果图,多数不对,其次进行X轴前进时,写的却是Y轴!
box.transform.translate(new Vector3(0, 1, 0), true);

不过影响不大!!

Six.Sir

赞同来自:

受益匪浅,感谢,请问下 你的一套菜鸟到老鸟的 教程有了吗?

helingen

赞同来自:

当旋转值为正时,旋转方向为你正对着该旋转轴的正方向的逆时针方向——这个说反了吧?

要回复问题请先

商务合作
商务合作