Ziv小威

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

Ziv小威 发表于2015-03-21 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);

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

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

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

Ziv小威 · Javascript · WebGL · 翻译

让美的事情发生

 
comments powered by Disqus