专栏首页郭诗雅的专栏Three.js camera初探——转场动画实现
原创

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 删除。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • 极坐标系下的曲线

    在简单的图形和动画轨迹上,我们可以换一种实现思维,例如通过函数来实现。

    郭诗雅
  • css+js实现左右滑动卡片组件

    最近的一个活动页面需要做一个可以左右滑动的抽签效果,故通过用css的transform属性和js结合来模拟可以无限滚动的效果。

    郭诗雅
  • Three.js 粒子系统学习小记:礼花效果实现

    在3D建模过程中,当我们需要创建很多细小的物体时,并不会一个个地创建这些物体,而是通过创建粒子,粒子可以模拟很多效果,例如烟花、火焰、雨滴、雪花、云朵等等。Th...

    郭诗雅
  • Android中为图标加上数字--用于未读短信数提醒,待更新应用数提醒等

    在我们开发一些如短消息、应用商店等应用时,会考虑在短消息的图标上加上未读短信的数量,在应用商店上加上可以升级的应用数量,这样不占太大空间还能达到提示的目的。

    飞雪无情
  • Python笔记(十四):操作excel openpyxl模块

    free赖权华
  • 分表与分库使用场景以及设计方式

    场景:对于大型的互联网应用来说,数据库单表的记录行数可能达到千万级甚至是亿级,并且数据库面临着极高的并发访问。采用Master-Slave复制模式的...

    三哥
  • 分表与分库使用场景以及设计方式

    场景:对于大型的互联网应用来说,数据库单表的记录行数可能达到千万级甚至是亿级,并且数据库面临着极高的并发访问。采用Master-Slave复制模式的...

    三哥
  • 机器学习大神 Bengio 最新论文发布,专注 RNN 优化难题

    Bengio 在新作中提出了 RNN 优化的新概念 fraternal dropout,通过最小化使用不同的 dropout mask 的同一个 RNN 的预测...

    AI科技大本营
  • 使用Python调用Nessus 接口实现自动化扫描

    之前在项目中需要接入nessus扫描器,研究了一下nessus的api,现在将自己的成果分享出来。 Nessus提供了丰富的二次开发接口,无论是接入其他系统还...

    Masimaro
  • 重磅 | 机器学习大神Bengio最新论文发布,专注RNN优化难题,将在NIPS提出新概念fraternal dropout

    编译 | Troy·Chang、爱心心、reason_W 校对 | reason_W 下个月机器学习领域的顶会Nips就要在大洋彼岸开幕啦,那么这次的Nips又...

    AI科技大本营

扫码关注云+社区

领取腾讯云代金券