LayaAir引擎3D坐标系与矩阵变换
先扯些闲话,(以下闲话只针对小白,老鸟直接跳过)。
当看到了很多开发者在QQ群和社区中提到的问题,百分之八十以上的问题我的内心是崩溃的,感觉到大部分开发者是没有3D基础的,甚至很多连2D基础都没有,很多是因为这项新鲜的3D和VR H5产业吸引了他,然后就跟着官网的示例学习。但我觉得,如果没有3D的基础知识。就算你学会了用各种工具里导出3D资源,又能怎么样呢?即便是了解了一些API可以实现你想要的效果,但不知道3D游戏的原理是什么吗?也很难制作出来一个完整的游戏!所以建议大家在学习LayaAir 3D引擎之前,要先去了解一些3D的基础知识。LayaAir的3D教程文档,未来也尽可能多整理一些初级基础文档,希望大家能认真学习。
3D坐标系
关于3D坐标系,LayaAir 3D与3DMax都是右手坐标系,而unity3D是左手坐标系。我主要讲解laya3d坐标系。
先说轴向问题,看上面右边的图,别看那个诡异的手,伸出你的右手,手面朝着自己,让大拇指指向X轴方向,食指指向Y轴方向,其他的那一堆手指指向Z轴方向,也是指向自己,保持这个姿势,看好自己的右手,凝视它,记住他,这就是LayaAir 3D的坐标系,理解他,以后在3D场景中对物体进行平移旋转操作不要太简单。左手坐标系,同理,但一定要记住,LayaAir3D的坐标系统是右手坐标系,你能理解这个,也算我没白撸这些废话。
然后是单位尺度,LayaAir 3D中的单位是米,而不是像素。3DMax中一般默认的系统单位是米,显示单位是毫米。Unity3D中默认的单位也是米。为什么要跟大家介绍这些东西,看下面就知道了,如果大家用过Layabox的fbxTool工具导出过3D模型,然后放到3D场景中,却发现怎么也显示不出来。那是因为,在3Dmax中看到的模型,感觉虽然不大,但那是以毫米为显示单位的,而系统单位是米,实际的模型是他的1000倍大,因为大多数物体都是单面渲染的,内部你是看不到的,因此放到我们LayaAir 3D场景中,你怎么移动照相机位置,也看不到模型,这就是问题所在,可以试试把模型缩小1000倍,此时即可看到。由于Unity3D中的单位尺度与LayaAir 3D相同,所以就不必以上操作。说句工具上的题外话,制作单个模型时,尽量把模型放在3D坐标系原点,会更利于你的开发!
为了让大家更加深入的了解LayaAir 3D坐标系统,在这里我还要介绍一个简单的概念,矩阵变换,这里不做过深入的介绍,你只需要知道它可以控制3D精灵的旋转,平移,缩放即可;
想要深入了解矩阵和3D空间物体的关系,可以仔细研究下我们引擎的源码,或者学习下webGL相关的知识!下来我会向大家介绍,怎么用LayaAir 3D API对空间物体进行矩阵变换也就是旋转平移缩放。
首先我需要在3d空间中,创造点基本的元素,一个平面,一个方体,一个球体。以下我用LayaAir自带的方法创建,这个环节不做过多介绍。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 | //初始化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))) asMeshSprite3D; 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))) asMeshSprite3D; 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))) asMeshSprite3D; 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))) asMeshSprite3D; 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))) asMeshSprite3D; var material:StandardMaterial = new StandardMaterial(); material.albedo = new Vector4(0.5, 0.5, 0.5, 1); box.meshRender.material = material; |
以上是该程序执行的效果,有一个平面,有一个模拟的LayaAir 3D坐标系,红色的为x轴,绿色的为y轴,蓝色的为z轴,还有一个处于坐标原点的正方体,看到这里,我想大家对laya3d坐标系有了更深的了解!
下来我简单介绍下Transform3d这个类的一些简单属性,看下面的代码和注释。
|
对于没有接触过3D的同学,会对上面解释也许有些模糊,尤其对世界和局部有什么区别?下面将着重介绍介绍Transform3D两个方法,并在实践中向大家具体介绍。
第一步:在原始没进行过任何旋转的基础上进行旋转
|
上图,上边为原始的,下边为绕x轴进行世界/局部旋转后的效果。
第二步:在步骤一的基础上,先进行局部旋转
1 2 | //2.对box,进行绕z轴局部旋转90度, box.transform.rotate( new Vector3(0, 0, 90), true , false ); |
上图,上边为第一步旋转后的,下变为本次局部旋转后的效果,为什么会出现现在的效果呢,因为当第一步进行旋转时,box内部的坐标系跟着同时旋转了,动动脑子,脑补下,进行第一步旋转后,z轴不再朝向你,而是朝向下面。
第三步:在步骤一的基础上,进行世界旋转
1 2 | //3.对box,进行绕z轴世界旋转90度, box.transform.rotate( new Vector3(0, 0, 90), false , false ); |
上图,上边为第一步旋转后的,下边为本次世界旋转后的效果,这个很好理解,就是绕着世界坐标系坐标轴的z轴进行旋转。还有一个小规律,旋转的方向问题,当旋转值为正时,旋转方向为你正对着该旋转轴的正方向的逆时针方向,自己理解。
第四步:在步骤一的基础上,进行局部平移。
1 2 3 4 5 6 7 | /** * 平移变换。 * @param translation 移动距离。 * @param isLocal 是否局部空间。 */ //4.对box,进行x轴方向局部平移1米, box.transform.translate( new Vector3(0, 1, 0), true ); |
上图,上边为第一步旋转后的,下边为本次进行局部移动后的,由于进行了第一步的旋转,box内部的坐标轴y轴朝向的是自己,所以会看到box向自己移动了1米的距离。
第五步:在步骤一的基础上,进行世界平移,到这里,我相信大家心中都会有正确的答案了,上代码的答案。
|
上图,上边为第一步旋转后的,下边为本次进行世界移动后的,box不受自身影响,会向世界坐标系的y轴进行移动1米的距离。有的人或许有疑问,position/localPosition和translate有什么不同,前者是直接设置位置,后者是在当前位置基础上移动相应的距离,旋转同理。
看到这里,相信大家对LayaAir 3D坐标系已经有了进一步的了解,同时也对3d精灵的矩阵变换有了更多的了解,反复阅读本节内容,相信您能在LayaAir引擎的3D场景中熟练的操纵任何一个物体的旋转平移,缩放。