miaoYu

3D WebGL 入门之动画

原文链接: codepen.io

这是这是3D入门系列文章的第四部分,如果你还没有看其他部分,最好先看一下:


在之前的文章中,我们学会了几样东西:

  • 一个网格个位置,旋转,和材料

  • 网格顶点位置

  • 相机的位置和旋转

  • 光源的位置和方向

我们可以直接控制这些属性,我们也可以让他们动起来。对于我而言在3D工作中这个部分是最神奇的,有非常多的变量需要变化,潜在的变化组合对于动画的影响无穷无尽。

Anything is possible

任何情况都可能发生!

让Three.js的场景动起来:渲染循环

让你的3D场景动起来的关键是用requestAnimationFrame函数创建一个循环,循环渲染你的场景。换句话说你每次任何的改变都会马上别渲染。允许你自己写js或者用第三方库(tween)来改变属性。

// 渲染循环
function render() {
  requestAnimationFrame(render);
  // 渲染场景
  renderer.render(scene, camera);
};

将一个渐变库(tween.js)合并到你的3D场景中

如果你不熟悉tween.js,可以看我之前写的文章

一个例子:渐变一个20面体的顶点位置。

用我们之前文章讲过的知识创建一个有20面体的场景。

<iframe height='265' scrolling='no' title='icosahedron' src='//codepen.io/rachsmith/embed/KzrarL/?height=265&theme-id=0&default-tab=result&embed-version=2' frameborder='no' allowtransparency='true' allowfullscreen='true' style='width: 100%;'>See the Pen icosahedron by Rachel Smith (@rachsmith) on CodePen. </iframe>

现在我们有一个网格体,它拥有很多顶点。我们将从每个顶点的原始位置随机改变位置。所以首先我们将保存二十面体的原始形状:

  function getOriginalVerticePositions() {
  // 把所有的顶点存到一个数组
  for (var i = 0, l = geometry.vertices.length; i<l; i++) {
    verticePositions.push({x: geometry.vertices[i].x, y: geometry.vertices[i].y});
  } 
}

现在我们有了所有顶点的位置,我可以创建所有顶点的渐变,让他们于运动到它们的新位置。在这里我使用GSAP TweenLite,当然你也可以使用其他的渐变引擎或者第三方的库。

  function getNewVertices() {
  /* 这个函数返回一个数组,数据包含所有顶点从原始位置移动随机距离后新的位置 */
  var newVertices = [];
  for (var i = 0, l = geometry.vertices.length; i<l; i++) {
    newVertices[i] = {
      x: verticePositions[i].x -5 + Math.random()*10,
      y: verticePositions[i].y -5 + Math.random()*10
    }
  }
  return newVertices;
}

function tweenIcosahedron() {
  var newVerticePositions = getNewVertices();
  // 每个顶点渐变到它们的新位置
  for (var i = 0; i < geometry.vertices.length; i++) {
    tweenVertice(i, newVerticePositions);
  }
}

function tweenVertice(i, newVerticePositions) {
  // 设置渐变
  TweenLite.to(geometry.vertices[i], 1, {x: newVerticePositions[i].x, y: newVerticePositions[i].y, ease: Back.easeInOut, onComplete: function() {
    // 再次启动二十面体渐变,现在动画就完成了
    if (i === 0) tweenIcosahedron();
  }});
}

这就创建了一个动态的二十面体!

<iframe height='265' scrolling='no' title='Morphing icosahedron' src='//codepen.io/rachsmith/embed/WwYRmm/?height=265&theme-id=0&default-tab=result&embed-version=2' frameborder='no' allowtransparency='true' allowfullscreen='true' style='width: 100%;'>See the Pen Morphing icosahedron by Rachel Smith (@rachsmith) on CodePen. </iframe>

看起来很酷吧,接下来我们来试试渐变另一个场景中的元素:网格体的旋转。

// 新的渐变函数
function tweenIcosohedron() {
  // 创建一个随机的旋转
  var rotation = {x: Math.random()*3, y: Math.random()*3, z: Math.random()*3};
  // 渐变网格体旋转属性,让它旋转起来
  TweenLite.to(mesh.rotation, 1, {x: rotation.x, y: rotation.y, z: rotation.z, 
    ease: Back.easeInOut, onComplete: tweenIcosohedron});
  var newVerticePositions = getNewVertices();
  for (var i = 0; i < geometry.vertices.length; i++) {
    tweenVertice(i, newVerticePositions);
  }
}

创建了一个更有趣的动画!

<iframe height='265' scrolling='no' title='Spinning morphing icosahedron' src='//codepen.io/rachsmith/embed/RaqKpg/?height=265&theme-id=0&default-tab=result&embed-version=2' frameborder='no' allowtransparency='true' allowfullscreen='true' style='width: 100%;'>See the Pen Spinning morphing icosahedron by Rachel Smith (@rachsmith) on CodePen. </iframe>

如果你可以渐变3D场景中任意的属性组合,你会创建出怎样的3D动画呢?

还有些很棒的效果,比如移动场景中的相机

三维场景中的动画效果可以延伸到电影拍摄中。通过移动你的相机你可以探索你制造的3D空间。

我试着做了一个“太空中的小行星”。

<iframe height='265' scrolling='no' title='space rocks' src='//codepen.io/rachsmith/embed/jqQBdM/?height=265&theme-id=0&default-tab=result&embed-version=2' frameborder='no' allowtransparency='true' allowfullscreen='true' style='width: 100%;'>See the Pen space rocks by Rachel Smith (@rachsmith) on CodePen. </iframe>

这个场景现在是静态的。你知道怎么把它做得更好吗?如果我们身处太空中,在太空中漂浮,看到这些小行星从你身前飞过。

虽然我们没有太空船,但是我们能控制我的的相机,也许我能让动画看起来像我们在小行星中穿梭。

function updateCamPosition() {
    // 根据X或者Y轴旋转我们的相机
    angle += 0.005;
    var z = 100 * Math.cos(angle);
    var y = 100 * Math.sin(angle);
    camera.position.z = z;
    camera.position.y = y;

    /* 旋转相机的角度,没有科学依据,我只是取了Z轴位置的随机百分比*/

    camera.rotation.x = z*0.02;
}

为了让我们的场景更动态,我会让每个小行星动起来。

  Rock.prototype.rotate = function() {
  this.mesh.rotation.x += this.vr.x;
  this.mesh.rotation.y += this.vr.y;
}

我们飞起来了!!

<iframe height='265' scrolling='no' title='flying through some space rocks' src='//codepen.io/rachsmith/embed/EKLVvp/?height=265&theme-id=0&default-tab=result&embed-version=2' frameborder='no' allowtransparency='true' allowfullscreen='true' style='width: 100%;'>See the Pen flying through some space rocks by Rachel Smith (@rachsmith) on CodePen. </iframe>

以上两个例子,只是冰山一角

在未来的文章中我们将探索动画的实用性,比如结合用户输入的3D动画。我希望以上两个例子能够对你的3D动画制作有所启发。

在以后的文章,我们将探索用户输入,实用性,阴影等等。