3.第一章3小节 场景动画

以下文章即Learn Threejs 第三版英文翻译学习记录,可以到正版书店购买对应书籍。

如果你想在场景种功能使用动画,那第一件要做的事情就是先找到绘制场景的时间间隔(就是已特定的周期渲染场景)。在HTML5 和JavaScript Api 出来之前,使用setInterval方法去处理这件事情,这个方法定义了,我们可以100 毫秒去执行一个方法(就是周期函数,约定某个周期调用某个函数一次),但是这个方法没有考虑到浏览器的一些问题,如果你浏览其他的页面,这个函数依然会按照时间周期执行。而且,setInterval方法不是异步刷新屏幕,它需要更高的CPU使用率、频率、获得较差的性能。

介绍 RequestAnimationFrame

幸运的是,我们可以通过requestAnimationFrame这个函数来解决问题,通过这个函数,你可以按照时间间隔调用,而且不用定义这个时间。这个时间间隔有浏览器定义。你可以在将你的函数传入到这个方法中去处理你的绘制任务,浏览器会尽可能平滑、高效的处理这个绘制任务。

function renderScene() { 

requestAnimationFrame(renderScene); 

renderer.render(scene, camera);

}

(就是一个while循环 / 也类似递归调用)

在这个renderScene 函数中,我们调用了requestAnimationFrame函数,来保证动画的正常运行。你仅需要在创建场景完成后调用一次rendererScene函数来启动函数。

...

document.getElementById("webgl-output").appendChild(renderer.domElement);

renderScene();

如果你运行这个示例,你会发现,和我们之前提供的示例,没有任何区别,因为我们还没有开始设置任何动画。在我们添加动画之前,我们需要添加一个小的插件,它会为我们提供有关动画帧率的相关信息。这个库是Threejs同一个作者开发的,渲染一个小的界面,为我们提供相关信息。

添加这个静态库,我们需要在head里引入,例如:

<script type="text/javascript" src="../../libs/util/Stats.js"></script>

(我在demo中只发现了一个stats.min.js)

唯一需要做的事情就是将这个库初始化,并添加到页面中;

function initStats(type) {

       var stats = new Stats();

       stats.showPanel(type);// 0: fps, 1: ms, 2: mb, 3+: custom

       document.body.appendChild(stats.dom);

      return stats;

  }

(这个我试过,有一个小窗口,能显示fps ms mb,3+什么都没有,setMode和showPanel都可以)

通过函数初始化统计试图信息,并根据传入类型变量显示对应的视图,可以显示每秒渲染帧数(FPS)、渲染需要的时间(MS)、内存分配相关信息(MB), 在我们的初始化函数之前调用initStats函数,例如:

function init(){

var stats = initStats(); ...

}

Tip:说的意思是他将这些工具类的函数放到了util.js中,说你要用的话,就直接引入util.js

<script type="text/javascript" src="../js/util.js"></script>

我们现在唯一要做的事情就是在循环执行的方法中调用stats的update方法,在renderScene 函数中,例如:

function renderScene() {

stats.update();

......

equestAnimationFrame(renderScene);

renderer.render(scene, camera);

}

如果你运行了添加的代码,你将会看到在左上角有一个统计图,如截图所示(略,自己试吧);

开始 立方体 动画

我们写好了requestAnimationFrame和状态提示插件,我们现在找到一个地方写我们的动画代码。在这部分,我们将扩展renderScene方法,通过旋转各个坐标轴来执行立方体旋转动画,我们开始,下边是代码:

function renderScene() { ...

cube.rotation.x += 0.02;

cube.rotation.y += 0.02;

cube.rotation.z += 0.02;

... requestAnimationFrame(renderScene);

renderer.render(scene, camera);

}

看起来很简单,是吧?我们要做的是,在每次renderScene函数调用刷新的时候,将每个坐标轴属性增加0.02,它显示是一个围绕所有轴平滑旋转的立方体。将蓝色的球执行弹性动画也不难。

(https://www.cnblogs.com/jaycethanks/p/12032947.html 这篇文章详细介绍了,球的切面什么的,会解决你做出来的球不圆的问题)

开始 球 动画

弹球动画,我们再次向rebderScene函数中添加了几行代码,例如:

var step=0;

function renderScene() {

...

step+=0.04;

sphere.position.x = 20 + 10*(Math.cos(step));

sphere.position.y = 2 + 10*Math.abs(Math.sin(step)); ...

requestAnimationFrame(renderScene);

renderer.render(scene, camera);

}

在场景中,立方体,我们不断设置了它的rotation属性,球体我们设置了position属性。我们希望球体从一个点完美的运动到场景的另一个点,而且是一个平滑的曲线,下面是我的算法介绍:(图略)


运动轨迹图

可以看到,我们需要修改x轴和y轴的值,数学函数Math.cos,Math.sin 函数可以帮助我们创建一个生成平滑的轨迹可供使用的一个值(就是通过函数的计算能够得到平滑曲线上的点的值),在这里我们不需要知道详细的工作流程。现在,你只需知道 step+=0.4 定义了球体运动的速度,在第8节中,Creating and Loading Advanced Meshes and Geometries (应该是一节内容,就是高级使用技巧),我们可以通过章节了解如何使用这些动画函数,而且会解释这些。下班就是球在空中弹跳的样子:(略 自己做就好了 没什么);

在结束本章之前,我们需要在场景中再添加一些物体,在使用3D场景、动画、颜色和属性的时候,它通常需要一些实验来验证颜色或者是速度这些。如果你能有一个简单的GUI,允许你动态地改变这些属性,那做实验就更容易了。

使用GUI 体验更加

谷歌的几位员工开发了一个dat.GUI库(你可以通过http://code.google.com/p/dat-gui/查看文档),你可以在代码中很容易的使用用户界面组件,在本章的队后一部分,在示例中添加一个用户界面,允许我们执行以下操作:控制弹跳球的速度、控制立方体旋转。

就像我们做统计一样,第一步将开源库添加html的<head>标签中,如:

<script type="text/javascript" src="../../libs/util/dat.gui.js"></script>

接下来需要配置JavaScript对象,使用dat.GUI来保存属性。在JavaScript主方法中,我们添加以下JavaScript对象,例如:

var controls = new function() {

 this.rotationSpeed = 0.02; 

this.bouncingSpeed = 0.03;

}

在这个JavaScript对象中,我们定义了两个属性:this.rotationSpeed 和 this.bouncingSpeed,而且给他们赋值,下一步,我们将这个对象传递到dat.GUI 对象中,并定义这两个属性的范围,如下所示:

var gui = new dat.GUI();

gui.add(controls, 'rotationSpeed', 0, 0.5);

gui.add(controls, 'bouncingSpeed', 0, 0.5);

rotationSpeed 和 bouncingSpeed 属性都是设置了0-0.5区间,现在我们只需要将操作放入到renderScene 渲染循环中。(后边说的比较复杂,意思就是将你之前加入GUI的两个属性,立刻就会生效了)

function renderScene() {

...

cube.rotation.x += controls.rotationSpeed;

 cube.rotation.y += controls.rotationSpeed; 

cube.rotation.z += controls.rotationSpeed;

step += controls.bouncingSpeed;

sphere.position.x = 20 +(10 * (Math.cos(step)));

 sphere.position.y = 2 +(10 * Math.abs(Math.sin(step)));

...

}

现在,我们运行示例 05-control-gui.html,你可以看到一个简单的用户界面,用来控制弹性系数和旋转速度

GUI 控制

(其实咋说呢就是提供了一个GUI用户操作界面,操作控制项,还是比较好用的,新版的需要导入dat.gui.min.js)

我们之前几章写的HTML代码,我们导入TrackballControls.js JavaScriypt文件。在这个文件中,我们可以控制camera 影响渲染结果,我们会在第9章详细介绍Camera的动画和移动。我们只需要添加几行JavaScript代码就可以添加这个组件,初始化球运动的轨迹...(接下来说的意思就是一定要记住将randerer添加到document中)。

document.getElementById("webgl-output").appendChild(renderer.domElement); // add the two lines below

var trackballControls = initTrackballControls(camera, renderer);

var clock = new THREE.Clock();

initTrackballControls 方法在utils.js中已经被定义了,这个我们之前提到过。在接下来的章节种功能,我们会介绍更多细节来说明它是怎么工作的,现在我们只需要更新render方法,例如:

function render() {

trackballControls.update(clock.getDelta());

...

}

现在已经做完了,如果你再次打开05-control-gui.html 示例,你可以移动你的鼠标,或者左击鼠标来移动视角camera,如果你在移动的时候点击 ‘s’键,你可以放大和缩小;如果按下‘d’,你可以拖拽那个场景。(我没找见这个文件)

以后的demo将默认使用这些动画控制组件,一边您能够更容易的查看渲染结果。

如果你在你的浏览器上查看,你可能注意到当你缩放浏览器的大小时,渲染界面不会跟着缩放,在下一个章节中,我们将会添加这个(就是想说下几节中,解决上述问题)。

当浏览器尺寸改变时,渲染结果跟着改变

当浏览器尺寸发生改变的时候,更改camera的操作是很简单的。第一件事情就是注册添加时间监听,如下:

window.addEventListener('resize', onResize, false);

现在,当浏览器i尺寸发生变化时,就会调用onResize方法,下一步我们需要做的是,当onResize方法调用时,我们需要更新camera和渲染,例如:

function onResize() {

camera.aspect = window.innerWidth / window.innerHeight; 

camera.updateProjectionMatrix(); 

renderer.setSize(window.innerWidth, window.innerHeight);

}

对于camera来说,我们需要改变aspect属性,让他始终保持浏览器横纵比;对于renderer,我们需要改变它的渲染尺寸;(接下来说的意思就是:把这些代码封装到一个init方法中,以便我们更好的使用它);

var camera;

  var scene;

  var renderer;

function init() { ...

scene = new THREE.Scene();

camera = new THREE.PerspectiveCamera(45, window.innerWidth /

window.innerHeight, 0.1, 1000); renderer = new THREE.WebGLRenderer(); ...

}

总结

第一章到此结束,在这一节中,我们为您展示了如何设置你的开发环境、如何获取程序代码、如何从提供的示例中开始。您进一步了解到Three.js是如何渲染的,第一步创建THREE.Scene 对象,添加camera、光线light、你想渲染的模型。我们也向您介绍了如何在基础的场景里显示阴影(我就没加出来,估计日后能加出来吧)、显示动画等,最后,我们添加了一些小组件。我们使用dat.GUI 它可以很快速的为我们创建用户界面来控制渲染属性,添加了stats.js(用来观察渲染情况,如内存、刷新频率等)

在下一章节中,进一步了解Three.js的其他功能。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 194,088评论 5 459
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 81,715评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 141,361评论 0 319
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,099评论 1 263
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 60,987评论 4 355
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,063评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,486评论 3 381
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,175评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,440评论 1 290
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,518评论 2 309
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,305评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,190评论 3 312
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,550评论 3 298
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,880评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,152评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,451评论 2 341
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,637评论 2 335

推荐阅读更多精彩内容