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 条评论
登录 后参与评论

相关文章

来自专栏一棹烟波

张正友标定法示例 (含源代码)

博主的博客,以著名的张大牛标定法开始吧! 具体标定原理就不详细说了,资料数不胜数,重点看张正友的原著《A Flexible New Technique for ...

4578
来自专栏大数据文摘

如何运用Python绘制NBA投篮图表

1998
来自专栏生信宝典

AI科研绘图(二):模式图的基本画法

经过上次对基本元素和工具的学习(AI科研绘图(一):零基础入门和基本图形绘制),我们基本上掌握了最常用和基础的工具的用法。

573
来自专栏生信宝典

NGS基础 - FASTQ格式解释和质量评估

FASTQ文件格式和命名 高通量测序之后用于下游分析的数据一般存储在FASTQ文件中。为了节省空间,又不影响下游使用,也一般用gzip压缩的格式。 单端测序每个...

2485
来自专栏数据处理

人大代表数据分析爬取代表数据正则表达式提取需要的数据还可以把上面数据画成饼图民族词云图统计代表姓氏人数姓氏词云

2163
来自专栏Python疯子

Python强大的pyecharts绘画优美图形<三>

支持保存做种格式 对象.render(path='snapshot.html') 对象.render(path='snapshot.png') 对象.re...

953
来自专栏C语言及其他语言

【每日一题】问题 1146: 舍罕王的失算

关注我们 题目描述 相传国际象棋是古印度舍罕王的宰相达依尔发明的.舍罕王十分喜爱象棋,决定让宰相自己选择何种赏赐.这位聪明的宰相指着8*8共64格的象棋说:陛...

27611
来自专栏owent

ECUST 09年 校赛个人赛第三场部分解题报告(A,D,F,I)

首先是VC6.0的algorithm里没有min函数,而我用min做变量名导致CE4次,找了半天才找出来

741
来自专栏数据结构与算法

BZOJ4916: 神犇和蒟蒻(杜教筛)

$$g(1)s(n) = \sum_{i = 1}^n g(i)s(\frac{n}{i}) - \sum_{i = 2}^n g(i)s(\frac{n}{i...

922
来自专栏CreateAMind

互信息论文笔记

https://github.com/topics/mutual-information

1465

扫码关注云+社区