利用 javascript 和 WebGL 绘制地球 【翻译】

原翻译:利用 javascript 和 WebGL 绘制地球 【翻译】

在我们所有已知的 HTML5API 中,WebGL可能是最有意思的一个,利用这个 API 我们能够在浏览器中创造出炫酷 3D 场景的能力。本文将完整的向你展示一些炫酷是如何实现的。

需要特别指出的是,这篇教程我们将会构建一个地球行星模型,这个模型可以像一个兴奋的人一样环绕的旋转,另外,它可能使我们可以获得一些其他程序员的称赞,好吧,就这么多了。

准备

这篇教程我们将会用到一个令人着迷的WebGL插件:three.js. 这个插件跟 JQuery 有点像,不过它是针对WebGL的,它将很多复杂的原生 API 访问接口进行了抽象,从而让我们可以更轻易的利用WebGL的特性。

在 HTML 中,我们的可以通过正常的 script 标签引入这个插件,如下:

<script src="//cdnjs.cloudflare.com/ajax/libs/three.js/r63/three.min.js"></script>

此处我们引用的是 CDN 版本,如果你有必要,可以使用本地的方法引入。然后我们需要确保WebGL有东西可以在上面进行渲染。这里我们有个灵活的做法:我们可以直接将一般的 Div 或者 Canvas 写到 HTML 中,或者我们可以另外用 JS 创建并且追加 canvas 元素到 Dom 里后再去渲染。这里我们采用第一种容易点的方法,如下:

<body>
    <div id="container"></div>
    <script src="earth.js"></script>
</body>

添加完script标签连接到 Dom 中后,我们的 HTML 部分差不多就完成了。

下一步

Tree.js 本身是趋向把东西做的非常接近真实的 3D 桌面程序的。我们拥有一个场景,有一些东西现场直播,然后通过摄像机去浏览,然后有些灯光,特效,渲染在整场景上面,当然他们自身也全都是 3D 对象。这个场景的元素列表似乎有点吓人,在我们的earth.js文件里,所有的这些元素都可以当成形状变量,Javascript文件如下:

var scene,
    camera,
    light,
    renderer,
    earthObject;

var WIDTH = window.innerWidth - 30,
    HEIGHT = window.innerHeight - 30;

var angle = 45,
    aspect = WIDTH / HEIGHT,
    near = 0.1,
    far = 3000;

有些额外的变量也定义在这里了,WIDTH,HEIGHT 变量用来获取我们画布的宽与高,下面的其他变量之后将会用来设置我们相机的位置。对于几乎所有的 3D 对象来说,所有这些元素都是共通的,无论是平台还是环境,所以在这里我们习惯性的将这些家伙写到一起。然而利用 Three.js 我们可以轻松的实现,我们将看看所有这些元素是如何在同一时刻融合到项目中的。

环境

首先,我们需要启用新变量并初始化他们,从而使我们的地球模型可以展示的更炫。我们可以先设置每个处理环境因素的变量:

var container = document.getElementById('container');

camera = new THREE.PerspectiveCamera(angle, aspect, near, far);
camera.position.set(0, 0, 0);
scene = new THREE.Scene();

light = new THREE.SpotLight(0xFFFFFF, 1, 0, Math.PI / 2, 1);
light.position.set(4000, 4000, 1500);
light.target.position.set (1000, 3800, 1000);

下面是针对上面代码执行情况的描述:

  • 在我们的 HTMl 中抓取container对象
  • 我们用之前声明的变量设置camera对象(更多关于cameras是如何在 3D 中工作的信息,可以点击这里
  • 通过position.set方法设置camera的位置, 这个方法需要携带一个维度(x, y, z)参数对象, 可能你已经想到了, 我们将会使用这个 camera 去定位我们的 3D 对象,本教程中的 3D 对象就是我们的地球模型。
  • 接下来设置我们的 light 对象。如果没有 light 对象做渲染的话,那么整个模型出来的效果将会是一片漆黑,所以我们必须细心的注意这一步骤。Three.js 的SpotLight object对象拥有与我们的 camera 对象大致相同的参数,只不过这个对象的第一个参数 colour 必须为十六进制的值,然后剩余的其他参数与 camera 基本相同。
  • 最后,我们需要设置我们的画布对象 renderer.另外一个需要确保的点则是:我们需要提前将画布对象完整的渲染到了屏幕上,再次强调,如果没有完成这一步,那么整个画布将会什么也看不见,一片漆黑。我们给画布添加了去锯齿效果,并且将这个效果作为 Dom 元素添加到我们的原始容器中。

现在我们需要通过将整个地球粘贴在网上一样的方式来构建自身整个模型。代码如下:

var earthGeo = new THREE.SphereGeometry (30, 40, 400),
earthMat = new THREE.MeshPhongMaterial();

var earthMesh = new THREE.Mesh(earthGeo, earthMat); earthMesh.position.set(-100, 0, 0);
earthMesh.rotation.y=5;

scene.add(earthMesh);

在这里我们创建了一个网状(Mesh)对象,这个网状是一种可以被用来装扮并看起来像地球形状的对象,然后给这个对象添加一些几何结构,外观包装,或者一些有质感的材料来包裹这个网状体。我们同样会将这个对象设置适当的在位置,与其他参数对象一样,我们会并且将 Mesh 对象添加到我们的场景(scene)中。

如下有个样例。这里面有些额外的渲染效果,稍后我们将会讲解。这个样例看起来离我们想要的越来越近了。

蓝色星球

接下来有趣的部分是给这个家伙制作皮肤。首先我们将会使用一张漫反射贴图,它会让这个家伙看起来更像个地图。你可以像下面的方式一样添加:

// diffuse map
earthMat.map = THREE.ImageUtils.loadTexture('images/earthmap1k.jpg');

如果你想要质感更好些的话,你可以尝试用这个图片,或者你可以去 google 搜索一张你想要的图片都行。高分辨率的图都可以。

现在这个模型看起来没那么糟糕了,但是我们仍然可以通过引用一点地形描绘的方式,使整个模型看起来更真实些。这个地球有一些高山,为了确保区分太阳系的其他星球,我们需要使用凹凸地图(bump map), 在 3D 模型中, 凹凸地图是黑白图,使用鲜明的白色去凸显图像凹凸不平的部分(例如我们示例中的:山脉)。

// bump map
earthMat.bumpMap = THREE.ImageUtils.loadTexture('images/elev_bump_16ka.jpg');
earthMat.bumpScale = 8;

使用上面的图片我们差不多达到了效果,再次强调,使用过 Google 搜索“Earth bump map”会获得大量的选择,但是如果你感觉都不好的话,你可以点击这个连接。 运行了以上的代码,我们将会看到如下效果:

让它转起来

接下来剩余的事情就是我们给这个地球模型添加一些动画效果,为此,我们需要两个新的方法,我们命名为render()animate()

function animate() {
   requestAnimationFrame(animate);
   render();
}

我们的animate()方法并不是很复杂,通过自身递归连续的调用requestAnimationFrame()方法,anmiate()会请求我们的render()方法,让我们看看render()方法的代码:

function render() {
   var clock = new THREE.Clock(),
       delta = clock.getDelta();

   earthMesh.rotation.y += rotationSpeed * delta;
   renderer.render(scene, camera);
}

我们看看上面的代码做了些什么工作。每次render()方法被请求,它便会让地球模型在 y 轴上缓缓的转动起来(此处你可以选择设置任意的转动次数,我们在这里利用getDelta()方法构建一个时钟对象来控制转动次数,当然你可以不使用这种方法)。然后render()方法会执行清理画布操作,这是防止画布乱掉很重要的步骤,最后它会渲染我们的场景(以及场景对象中的其他所有对象)和我们的 camera 对象。

最后

当然,拥有拖拽操作会让我们的地球模型的体验更好,OrbitControls.js是一个可以为我们地球模型提供鼠标驱动旋转效果能力的脚本,并且它为我们的平流层里添加一些星星或者云作为地球模型的背景同样也并不困难,如果你并不嫌麻烦的话,你甚至可以利用 WebGL 的着色器(shaders)为你的星球添加一个平流层。

运行代码,你可以看到一个样例,在 CodePen 中最终的 Demo 如下:

通过按住鼠标拖动和滚动鼠标滑轮来查看效果(或者点击此处demo

结尾

WebGL 和 Three.js 变得越发的具有挑战性,因为他们偶尔会要求我们要像 3D 艺术家一样,利用场景,画布,camera 去完成我们的工作,最终的结果就是做出了一些加令人印象深刻的东西。如果你专注于在这个技术上的话,你可以通过在浏览器中使用 3D 特性创造出一些有趣的可能性。如果坚持它,相信不久你就很可能获取一些非凡的成绩。

原文地址:Building the Earth with WebGL and JavaScript


最后修改于 2015-03-22