Three.js camera初探——转场动画实现

作者介绍:郭诗雅,华南理工大学计算机科学与技术硕士,目前就职于腾讯ISUX(社交用户体验设计部) 担任UI开发工程师。

背景

首先简单介绍一下three.jsthree.js是用javascript写的基于webGL的第三方3D库,通过它可以在网页中进行3D建模,结合上TweenMax.js动画库,在网页中实现3D动画效果就变得很简单了。

这是three.js建模的简单流程图例:

用通俗的话来讲,首先可以用js创建各种形状的几何体,或者从外部导入建好的模型文件,然后为几何体添加材质(纹理、颜色等),就组成了一个网格模型mesh,我们可以创建很多的模型,塞进场景容器里,还要为场景加入灯光,通过“打光”照相机才能拍到场景里面的物体,当要拍摄动态场景时,就需要照相机不断的拍摄然后通过渲染器不停地渲染到屏幕上(渲染循环),最后屏幕展现的就是一个3D动态场景。

既然是通过照相机去拍摄场景,让我们在屏幕上可以看到,那么移动照相机用不同的角度拍摄这个世界,自然就可以看到不一样的世界了。

three.js中的camera

three.js中的camera分为两种,一种是正投影相机,一种是透视投影相机,两种的大致区别是:

(图片来自webGL中文网)

在透视投影下,同样大小的物体,在屏幕下远处的物体会比近处的物体小,而正投影下两者是一样大小的。

在本例demo中,我们用的是透视投影,实例化代码如下:

camera = new THREE.PerspectiveCamera(45, width / height, 1, 10000);

四个参数分别表示视角,看到的窗口宽度和高度的比例,能看到的最近处的距离,和能看到的最远处的距离。视角越大,拍摄到的场景范围就越大,能看到的物体就显得越小。

接着便是camera位置的设置,在初始化camera时,一般都会先设置好它的3个属性,position/up/lookAt.以demo为例讲解一下这三个属性。在这之前,我们先了解一下three.js的坐标系,使用的是右手坐标系,如下图所示:

就是这么有气质的手势~~大拇指指向x轴正方向,食指指向y轴正方向,中指指向z轴正方向。

再回来看看camera的初始化代码:

camera.position.x = 0;
camera.position.y = 0;
camera.position.z = 500;
camera.up.x = 0;
camera.up.y = 1;
camera.up.z = 0;
camera.lookAt({x : 0 ,y : 0, z : 0});
  1. camera.position: 顾名思义,就是camera在坐标系中的所在位置,处于z轴正方向上离原点500处。
  2. camera.up:比较难理解,指的是camera“头顶”的方向和向量(0,1,0)同向,即和y轴正方向同向。画个图理解一下:
  1. camera.lookAt: 指的就是camera面向哪里,这里是直接面向原点。

另外,camera.up设置的方向必须与camera位置和lookAt的连线不能平行,否则拍不到场景,这个大家自己可以摆摆手势体会一下~

demo实现过程-计算

1.准备过程

在初始化three.js后,创建我们需要的物体,在这里我随机创建了几个正方体,它们的大小和位置都是随机的,面向屏幕的一面加载了一张图片纹理,作为正面,如下图所示:

如果从y轴正方向往y轴负方向看,示意图大致是这样子的(蓝色代表正方体,有粗线加箭头的一面代表正方体有图片的正面,黑色的圆柱体代表照相机,箭头指明拍摄方向):

接下来便是动画过程,需要注意的是,接下来的都是物体只绕y方向旋转,x/z方向只做位移,这样就把一个三维空间的运动转化为二维空间了~~

2.将照相机移到y轴上,旋转正方体和照相机使之正对,如下图所示:

在初始化时,我们便记录了正方体的坐标值(x,y,z),正方体从面向屏幕到面向y轴要旋转多少角度,我这里用了初中数学方法——反三角函数算出。如下图所示分别为四个象限物体需要旋转的角度值。

旋转了正方体后,照相机只要和正方体旋转同样的角度,并坐标中的y值移到和正方体同向,就可以拍摄到正方体正面了。

  1. 因为之前所说的正方体是大小不一的,需要移动照相机使得照相机和正方体的距离与正方体的大小保持一定的比例,这样看到每个正方体的大小才是一致的。所以需要计算照相机的位移,如下图:

我们已知正方体的边长a,假设照相机与正方体中心点的距离设置为3a,利用相似三角形边长等比例的原理得出照相机的该移到的地方(x2,z2)坐标,公式如下:

  1. 为了让正方体具备随机摆放的感觉,还可以让正方体再绕y方向随机转动一定角度,照相机再绕正方体的中心点旋转到正对正方体正面的位置:

计算方法如下:

如上图所示,照相机原来是在正方体中心和原点的连线上的坐标(x,z),绕正方体的中心点(rx,rz)旋转随机角度θ后得到的(x',z')的计算公式如下:

以上的角度计算过程,都可以用js中强大的Math函数搞定,包括Math.atan()\math.cos()等等,具体代码就不贴了。

demo实现过程-动画

上面过程中的第2、3步,直接调用TweenMax.js的动画库,控制camera.position/camera.rotation就可以了。但是第4步由于不能想css一样直接设定transform-origin为正方体中心点,所以必须自己实现。例如控制位移:


TweenMax.to(
    camera.position,  //指明控制的属性
    2, //动画时间
    {x:x2,y: mesh[index].position.y,z:z2,delay:0,ease:Cubic.easeIn} //需要移动的距离
);

这里用到的是requestAnimationFrame()来实现,通过一点点地改变camera的旋转和位置的移动,直到达到角度θ。

var rate;  //旋转频率
function cameraRoAni(){
    var ani;
    //当物体的旋转角度大于0
    if (rotateMesh[index-1] > 0) {
        rotateRate = rotateRate + rate;
        if(rotateRate <= rotateMesh[index-1]){
            var newX = (camera.position.x - mesh[index-1].position.x)*Math.cos(-rate) - (camera.position.z - mesh[index-1].position.z)*Math.sin(-rate) +  mesh[index-1].position.x;
            var newZ = (camera.position.x - mesh[index-1].position.x)*Math.sin(-rate) + (camera.position.z - mesh[index-1].position.z)*Math.cos(-rate) +  mesh[index-1].position.z;
            camera.position.x = newX;
            camera.position.z = newZ;
            camera.rotation.y += rate;
            ani = requestAnimationFrame(cameraRoAni);
        }else{
            cancelAnimationFrame(ani);
        }
    //当物体的旋转角度小于0
    }else{
        ... //代码和大于0时相似
        }else{
            cancelAnimationFrame(ani);
        }
    }
    if (rate>0.01) {
        rate = rate - 0.0001;
    }
}

总结

通过只改变three.js里的camera,就可以做很多3D动效啦~~~

贴上demo地址:https://kiroroyoyo.github.io/threejsexample/camera/test-camera.html(有些时候角度和正方体的随机性太强控制不住它寄几,欢迎各位大大提出指正和优化!!)

相关推荐

【腾讯TMQ】再不建模你就out了

【腾讯TMQ】基于模型的自动化测试工具——GraphWalker

原创声明,本文系作者授权云+社区-专栏发表,未经许可,不得转载。

如有侵权,请联系 yunjia_community@tencent.com 删除。

编辑于

我来说两句

3 条评论
登录 后参与评论

相关文章

来自专栏编程微刊

移动开发之css3实现背景几种渐变效果

1532
来自专栏宋凯伦的技术小栈

Android - 开发页面需了解的dip,sp,px知识,以及它们的转换

工作中,时常会有任务要求开发新页面,这时一般的流程是产品经理确定要开发的页面和功能,然后设计师提供设计稿,之后由我们开发人员完成开发工作。 通常,设计师提供的设...

1848
来自专栏郭诗雅的专栏

极坐标系下的曲线

在数学中,极坐标系(英语:Polar coordinate system)是一个二维坐标系统。该坐标系统中任意位置可由一个夹角和一段相对原点—极点的距离...

2464
来自专栏Young Dreamer

针对iPhone的pt、Android的dp、HTML的css像素与dpr、设计尺寸和物理像素的浅分析

  最近被一朋友问到:css中设置一DOM的height:65px,请问显示的高度是否和Android的65dp的元素等高?脑子里瞬间闪现了一堆的概念,如dpr...

1905
来自专栏量子位

三分钟训练眼球追踪术,AI就知道你在盯着哪个妹子 | TensorFlow.js代码

如果有个眼球追踪AI,加上人脸识别,或许就能在被老板盯上的瞬间,进入奋力工作模式。

372
来自专栏数据小魔方

R语言可视化——REmapH(中心热度图)

今天是REmap系列的最后一篇——REmapH函数。 这个函数的特色是可以做中心辐射的热力图,这种热力图在气象、人口密度、海拔测绘领域有诸多运用,当然也可以上当...

5286
来自专栏欧阳大哥的轮子

地图开发知识之-投影坐标

由于地球是一个赤道略宽两极略扁的不规则的梨形球体,表面是一个不可展平的曲面,而地图通常是二维平面,因此在地图制图时首先要考虑把曲面转化成平面。然而,从几何意义上...

703
来自专栏coding for love

CSS入门6-盒模型

(注1:如果有问题欢迎留言探讨,一起学习!转载请注明出处,喜欢可以点个赞哦!) (注2:更多内容请查看我的目录。)

421
来自专栏前端桃园

像素相关概念:PPI、DPI、设备像素、独立像素

1312
来自专栏技术总结

iOS进阶之CAEmitterLayer

2398

扫码关注云+社区