【翻译】基于web创建逼真的3D图形 | CSS技巧

  个人翻译小站:http://www.zcfy.cc/article/creating-photorealistic-3d-graphics-on-the-web-css-tricks-4039.html

  翻译原文链接:https://css-tricks.com/creating-photorealistic-3d-graphics-web/

  在成为一名 web 开发者之前,我从事于视觉设计行业,创造屡获殊荣,电影和电视节目等高端 3D 效果,例如 Tron, The Thing, Resident Evil, 和 Vikings。为了能够创造这些效果我们需要使用高度复杂

的动画软件,例如 Maya3Ds Max 或者 Houdini,包括使用数以百计的机器渲染机器来做长时间的离线渲染。正因为我工作以来一直使用这些工具,以至于我对现在的 web 技术感到很惊讶。我们可以

创造和显示高质量的 3D 内容在浏览器里面,实际上,就是使用 webGL 和 Three.js。这个项目例子是使用这些技术创建的。在他们的网站里, 你可以发现更多 thress.js 的项目。网站:

  使用 three.js 的一些项目

  这些由 three.js 构建的网站表明,3D 可视化在商业,零售,娱乐和广告领域拥有巨大的潜力。

  WebGL 是比较低级的 JavaScript 的 API,它能够通过使用 GPU,来创造并将 3D 的内容显示在浏览器里面。不幸地是,既然 WebGL 是比较低的 API,意味着它更加困难被使用。

你需要编写上百行代码来运行,即使是很简单的任务。three.js,另一方面来说,是一个开放的 JavaScript 库,抽象了 WebGL 的复杂性,同时允许你以更简单的方式,创造实时的 3D 内容。

  在本教程中,我将介绍基础的 three.js 库。在介绍一个新的程序库时,先举一个简单的例子来更好地表达基本原理,但我想进一步说明这一点。同时,我还将建立一个场景,更加愉快和逼真。

我们就开始了一个简单的平面和球面但最终它会看起来像这样:

See the Pen learning-threejs-final by Engin Arslan (@enginarslan) on CodePen.

  临摹是计算机图形学的顶峰,但是,在你的位置下,实现不一定是进程中必要的因素,而是从你的工具箱中进行智能部署。这里有一些技术,你将会在本教程中学到,它将帮助实现照片写实。

  • 颜色,凹凸和粗糙度图。

  • 物理材料

  • 阴影照明

    逼真的 3D 图像 by Ian Spriggs

    您将在这里学习的基本 3D 原理和技术在任何其他 3D 内容创建环境中都是相关的,无论是混合机、Unity、玛雅还是 3ds max。

    这将是一个漫长的教程。如果您是一个喜欢看视频的人,或者您想了解更多的关于 Three.js, 您应该看看我对这个问题培训的视频。来自Lynda.com.

    要求

    当使用 three.js 时,如果你在本地工作,它有助于通过本地服务器服务 HTML 文件,以便加载像外部三维几何图形、图像等应用场景。如果你在寻找一个很容易建立的服务,你可以使用 python 来运行

简单的 HTTP 服务器。python 在很多操作系统上都有安装。您不必担心设置本地开发服务器来执行本教程。相反,您将依赖于数据 URL 来加载像图片这样的资源,以消除设置服务器的开销。使用线上代码

编辑器像CodePen,你可以很轻松的运行你的 Three.js 代码场景。

  本教程假设对前端 JavaScript 以及对前端 Web 开发的知识有一些了解。如果您不习惯使用 JavaScript,但希望轻松上手,您可能需要查看课程 / 书籍。

   [“Coding for Visual Learners: Learning JavaScript with p5.js”](http://www.codingforvisuallearners.com/). ( 免责声明:我是作者)

  让我们开始在 Web 上建立 3D 图形!

  入门

  我已经准备了 prepared a Pen 你可以跟随本教程进行操作。

  您将使用的 HTML 代码将是超级简单的。它只需要有一个 div 元素来承载正在显示 3D 图形的画布。它从 CDN 上加载 Three.js 库(版本 86)。

1
2
<div id="webgl"></div>
`<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/86/three.min.js">`</script>

  为了您的方便,CodeOpen 隐藏了 HTML 结构。如果您在其它在线编辑器或本地站点上构建此场景,那么您的 HTML 将需要类似于下面代码的内容,其中 main.js将是保存 JavaScript 代码的文件。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<!DOCTYPE html>
<html>
<head>
<title>Three.js</title>
<style type="text/css">
html, body {
margin: 0;
padding: 0;
overflow: hidden;
}
</style>
</head>
<body>
<div id="webgl"></div>
`<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/86/three.min.js">`</script>
`<script src="./main.js">`</script>
</body>
</html>

  请注意 html 中的简单 CSS 声明。这就是你在 CodePen 里的 CSS 标签:

1
2
3
4
5
html, body {
  margin: 0;
  padding: 0;
  overflow: hidden;
}

  这是为了确保没有任何的 margin 和 padding 值,可以通过您的浏览器应用,不会出现滚动条,因此可以有图形填充整个屏幕。这就是我们开始构建 3D 图形需要的一切。

第一部分 - Three.js 基本场景

  当我们平常用 three.js 和 3D 来工作时,这里有几个您需要掌握的对象。它们是 场景, 相机 以及 渲染。首先,您需要创造一个场景。您可以把一个场景对象看作是您要处理的每一个 3D 对象的容器。

它代表了您将要创造的 3D 世界。您可以像这样来创造场景对象:

1
`var scene = new THREE.Scene();`

  另外一件事就是当你使用 3D 工作时,你需要相机。想象相机就像眼睛一样,你会看到这个 3D 世界。当使用 2D 可视化时,相机的概念通常是不存在的。你看到什么就是什么。但是在 3D 的世界,

你需要一个相机来定义你的观点,因为你可以从一个场景中看到很多的位置和角度。相机不仅定义了位置,而且还定义了其他的信息,如视野或纵横比。

1
2
3
4
5
6
var camera = new THREE.PerspectiveCamera(
  45, // field of view
  window.innerWidth / window.innerHeight, // aspect ratio
  1, // near clipping plane (beyond which nothing is visible)
  1000 // far clipping plane (beyond which nothing is visible)
);

  相机捕捉场景以显示目的,但我们实际上可以看到任何东西,3D 数据需要转换为 2D 图像。这个过程称为 正在渲染,同时你需要用 渲染器 在 three.js 的场景中来进行渲染。你可以初始化渲染像这样的:

1
`var renderer = new THREE.WebGLRenderer();`

  之后设置渲染器的大小。这将决定输出图像的大小。

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

  为了能够显示你渲染的结果,你需要将渲染器的domelement属性追加到 HTML 内容里面。你会使用空的 div 元素,为此来创造 id 为WebGL的元素。

1
`document.getElementById('webgl').appendChild(renderer.domElement);`

  当做完这一切,你可以视在渲染器上渲染的这种方法,是提供场景和相机作为参数对象。

1
2
3
4
renderer.render(
  scene,
  camera
); 

  为了让代码更简洁,在函数init里面进行调用和执行功能。

1
init();

  也许你现在什么也看不见…除了黑屏。别急,这是正常的。这个场景是正常工作的,但是你没有包含任何的对象再场景里面,你所看到的基本上是空的空间,接下来,你会使用 3D 对象填充这个场景。

看 the Pen learning-threejs-01 by Engin Arslan (@enginarslan) on CodePen.

  向场景中添加对象

  three.js 里面的几何对象是由两部分组成的。一个几何对象决定了一个对象的形状和一个材料决定了其表面质量,一个对象的外形,两者的结合组成了 three.js 里面的网格,形成 3D 对象。

Three.js 允许你创建一些简单的形状,如立方体、球体在一个简单的方式。您可以通过创建一个简单的球体的半径值。

1
`var geo = new THREE.SphereGeometry(1);`

  这里有各种材料可以在几何中使用。材料决定了一个对象对光场景的反应。我们可以用一切材料使物体反光,粗糙,透明等。默认的材料,像由MeshBasicMaterialMeshBasicMaterial创造的 three.js 对象一点不受光场景的影响。这意味着即使你的场景中没有灯光,你的几何图形也将可见。你可以用一个颜色属性,可以为对象设置你想要的颜色meshbasicmaterial hex 值传递一个对象,您现在将使用此材料,但稍后更新它将使您的对象受到场景灯光的影响。你没有任何照明的场景,现在meshbasicmaterial应该是一个好的选择。

1
2
3
var material = new THREE.MeshBasicMaterial({
    color: 0x00ff00
});

  你可以结合几何和材料创建一个网格,这将形成三维物体。

1
`var mesh = new THREE.Mesh(geometry, material);`

  创建一个函数来封装创建球体的代码。在本教程中,您不会创建多个球体,但保持整体整洁还是不错的。

1
2
3
4
5
6
7
8
9
function getSphere(radius) {
  var geometry = new THREE.SphereGeometry(radius);
  var material = new THREE.MeshBasicMaterial({
  color: 0x00ff00
});
var sphere = new THREE.Mesh(geometry, material);
  return sphere;
}
var sphere = getSphere(1);

  然后,您需要将这个新创建的对象添加到场景中以使其可见。

1
`scene.add(sphere);`

  让我们再次检查这个场景,你还会看到这个黑屏。

  看代码 learning-threejs-02 by Engin Arslan (@enginarslan) on CodePen.

  无论何时你往 three.js 里面添加场景对象,对象被放置在场景的中心,在 x、y 和 z 的坐标为 0, 0, 0,就是你什么都看不到的原因,这就意味着你目前的相机和球体处于同一位置。你应该改变

他们中的任何一个的位置,以便能够开始看东西。

  3D 协调

  让我们把照相机 20 个单位移到 z 轴上。这是通过在摄像机上设置 * 位置的属性来实现的。3D 对象有 positionrotation和 scale 属性,可以允许你在 3D 空间里面任意旋转它们。

1
`camera.position.z = 20;`

  你可以移动摄像机以及其他轴。

1
2
3
camera.position.x = 0;
camera.position.y = 5;
camera.position.z = 20;

  相机现在位置更高了,但是球体不再在框架的中心位置了。你需要把相机指向它。为了能够做到这样,你可以调用相机的一个方法称为lookAt。通过相机上的lookAt方法确定哪些点是相机指向的。

在三维空间中的点表示的是向量。所以,你可以通过一个新的Vector3对象,这 lookAt方法能在 0, 0, 0 相机坐标看。

1
`camera.lookAt(new THREE.Vector3(0, 0, 0));`

  球对象现在看起来不是很圆。究其原因,是spheregeometry函数实际上接受两个额外的参数,宽度和高度细分,影响 resolution 表面。增加这些值,平滑的曲面会出现。我将此值设置为 24 的宽度和

高度细分。

1
`var geo = new THREE.SphereGeometry(radius, 24, 24);

  See the Pen learning-threejs-03 by Engin Arslan (@enginarslan) on CodePen.

  现在,您将创建一个简单的平面几何的球体。 PlaneGeometry函数要求有width和 height参数。在 3D 中,2D 对象没有默认的两个侧面,所以你需要将一个side属性传递给材质,使平面几何体的两边呈现。

1
2
3
4
5
6
7
8
9
function getPlane(w, h) {
  var geo = new THREE.PlaneGeometry(w, h);
  var material = new THREE.MeshBasicMaterial({
    color: 0x00ff00,
    side: THREE.DoubleSide,
  });
  var mesh = new THREE.Mesh(geo, material);
  return mesh;
}

  现在你可以往这个平面对象里添加场景。你会注意到,平面几何的初始旋转是平行于 y 轴,但你可能需要它是水平的,因为它作为一个接地平面,你需要记住一件重要的事,那就是 three.js 里的旋转。

它们用 radians 为单位,不用 degrees。弧度旋转 90 度相当于π/ 2数学。

1
2
3
var plane = getPlane(50, 50);
scene.add(plane);
plane.rotation.x = Math.PI/2;

  当你创造一个球对象,它用它的中心点定位。如果你想把它移动到地面上,那么你可以只增加它的’位置’ Y 值的当前半径的数量。但这不会是一个纲领性的做事方式。如果你想让球体停留在平面上,

不管它的半径值是多少,你都应该利用半径值来定位。

1
`sphere.position.y = sphere.geometry.parameters.radius;`

  看 the Pen learning-threejs-04 by Engin Arslan (@enginarslan) on CodePen.

  动画

  你几乎完成了本教程的第一部分。但在我们把它整合起来之前,我想说明一下如何用 three.js 来做动画。three.js 动画中使用的requestanimationframe方法对窗口对象反复执行一个给定的函数。

它有点像一个setInterval函数,但它是绘制浏览器运行性能。创造一个update函数,通过渲染,场景,和相机在那里执行渲染方法,在函数内进行渲染。你将会在内部声明requestAnimationFrame函数

并将更新函数,通过对requestanimationframe函数递归进行回调,这样说明代码比写出来更好。

1
2
3
4
5
6
function update(renderer, scene, camera) {
  renderer.render(scene, camera);
  requestAnimationFrame(function() {
    update(renderer, scene, camera);
  });
}

  在这一点上一切看起来都一样,但核心的区别是,requestanimationframe函数是使场景渲染每秒约 60 帧递归调用的更新函数。这意味着如果你正在 update函数里面执行语句,该语句将以每秒 60 次的速度

执行。让我们添加一个缩放动画球对象。为了从update 函数中选择球体对象,您可以将它作为参数传递,但我们将使用不同的技术。首先,在球对象上设置一个name属性,并给它取一个你喜欢的名字。

1
`sphere.name = 'sphere';` 

  在 update 函数内部,你可以使用它的名字,并且使用 getObjectByName 方法在其父对象上,找到该对象,这个场景。

1
2
3
var sphere = scene.getObjectByName('sphere');
sphere.scale.x += 0.01;
sphere.scale.z += 0.01;  

  通过这段代码,这个球的 X 和 Z 轴在缩放。尽管我们的意图不是创造一个缩放的球。我们正在设置 update函数,以便我们以后可以使用不同的动画。现在你已经看到了它是如何工作的,你可以删除这个缩放动画。

看代码 learning-threejs-05 by Engin Arslan (@enginarslan) on CodePen.

  第二部分 - 在场景中添加现实

  目前,我们正在使用MeshBasicMaterial,即使在场景中没有灯光时,它也会显示出给定的颜色,这会导致一个非常扁平的外观。尽管真实世界的材料不会以这种方式进行工作。真实世界可见的表面,取决于从

表面反射回我们眼睛的光有多少。three.js 有几个不同的材料,提供更好的接近实际表面的行为,其中一个是meshstandardmaterialmeshstandardmaterial是一个基于物理的渲染材料,可以帮助你达到逼真的效果。

这是现代游戏引擎的一种材料,如 Unreal or Unity,是游戏和视觉效果的行业标准。让我们开始在我们对象中使用MeshStandardMaterial,改变其颜色变为白色。

1
2
3
var material = new THREE.MeshStandardMaterial({
  color: 0xffffff,
}); 

  你将会在这个点上再次得到黑色渲染。这是正常的。对象在场景中需要光才能显示。这对于meshbasicmaterial不是必须的,因为它是一个简单的材料,在所有条件下,显示给定的颜色,但其他材料需要与光的相互

作用才可见。让我们创造一个SpotLight函数,你将会用这个函数创造两个聚光灯。

1
2
3
4
5
6
function getSpotLight(color, intensity) {
  var light = new THREE.SpotLight(color, intensity);
  return light;
}
var spotLight_01 = getSpotlight(0xffffff, 1);
scene.add(spotLight_01);

  你也许在这个点上可以看到些事情。为了更好的取景和阴影,光线和相机的位置有点不同。同时创造一个辅助光。

1
2
3
4
5
6
7
8
9
10
11
var spotLight_02 = getSpotlight(0xffffff, 1);
scene.add(spotLight_02);
camera.position.x = 0;
camera.position.y = 6;
camera.position.z = 6;
spotLight_01.position.x = 6;
spotLight_01.position.y = 8;
spotLight_01.position.z = -20;
spotLight_02.position.x = -12;
spotLight_02.position.y = 6;
spotLight_02.position.z = -10;

  做完这些,你场景中将有两个光源,从两个不同的位置照射球体。灯光有助于了解场景的维度。但在这一点上,事情仍然非常不真实,因为照明缺少一个关键的组成部分:阴影!

不幸地是,在 three.js 中渲染阴影不是很简单。这是因为阴影计算上很费成本,我们需要在多个地方激活阴影渲染。首先,你需要告诉渲染器开始渲染阴影:

1
2
var renderer = new THREE.WebGLRenderer();
renderer.shadowMap.enabled = true; 

  然后,你需要告诉光线投射阴影。在 getSpotLight 函数里面进行。

1
`light.castShadow = true;`

  你应该告诉对象投射或者接收阴影。在这种情况下,你将会让球体投射阴影并且用这个平面来接收阴影。

1
2
mesh.castShadow = true;
mesh.receiveShadow = true;

  在设置了这些之后,我们应该开始看场景中的阴影。首先,他们可能效果不是很好。您可以通过设置光影贴图大小来提高阴影的分辨率。

1
2
light.shadow.mapSize.x = 4096;
light.shadow.mapSize.y = 4096;  

  MeshStandardMaterial有很多属性,例如 roughness 和metalness控制表面与光的相互作用。这些属性取值在 0 到 1 之间,它们控制着相应的表面行为。增加平面材料的粗糙度值到 1,使表面看起来更像一个橡胶

作为反射而使其变得模糊。

1
2
3
// material adjustments
var planeMaterial = plane.material;
planeMaterial.roughness = 1;

  尽管如此,在本教程中我们不会将其值设为1。您可以自由试验值,但可以将其设置为 0.65,用于粗糙度和 0.75 的金属。

1
2
planeMaterial.roughness = 0.65;
planeMaterial.metalness = 0.75;

  尽管现在的场景看起来更有希望,但仍然很难称之为现实。事实是,在不使用纹理贴图的情况下,很难在三维中建立光致主义。

看代码 learning-threejs-06 by Engin Arslan (@enginarslan) on CodePen.

  纹理映射

  材质贴图是二维图像,可以在材质上进行映射,以提供表面细节。到目前为止,你只能在表面上得到纯色,但使用纹理贴图,你可以在一个表面上绘制任何你想要的图像。纹理贴图不仅被用来操纵表面的

颜色信息,而且还可以用来操纵表面的其他特性,如反射性、亮度、粗糙度等。纹理可以从照片来源获得,也可以从草稿中绘制。对于一个在 3D 环境中有用的纹理,它应该以某种方式被捕获。图像中有反射或阴影

或图像过于扭曲的图像,不会产生很好的纹理贴图。有几个专门的网站在网上寻找纹理。其中一个是 textures.com,它有一个很好的存档。他们有一些免费的下载选项,但你需要注册才能做到这一点。另一个 3D 材质的

网站是Megascans,它的高分辨率,高质量的环境扫描,高质量的产品质量。

  我使用了一个名为 mb3d.co.uk的网站上的例子。这个网站提供了无缝的、免费的纹理。无缝纹理意味着一种纹理,可以在表面上多次重复,而不会在边缘遇到任何不连续性的情况下发生.这是链接我用的纹理文件。

我已经将图片文件的大小缩小到 512px,并使用一个名为ezgif的在线服务将图像文件转换为数据 URI,以将其作为 JavaScript 代码的一部分,而不是将其作为单独的资产加载。( 提示: 如果要使用该服务,不要将标记包含

在数据中。

  创建一个函数,返回我们生成的数据 URI,这样我们就不必在代码的中间放置一个大的字符串。

1
2
3
4
function getTexture() {
  var data = 'data:image/jpeg;base64,/...'; // paste your data URI inside the quotation marks.
  return data
}

  接下来,你需要加载纹理并将其应用到平面上。为此您将使用 three.js。在加载纹理之后,你会将纹理加载到所需材质的地图属性中,将其作为一种颜色地图在表面上。

1
2
3
var textureLoader = new THREE.TextureLoader();
var texture = textureLoader.load(getTexture());
planeMaterial.map = texture; 

  现在的东西看起来很难看,因为表面的纹理像素化了。这张图片拉伸得太大了,无法覆盖整个表面。你可以做的是让图像重复,而不是缩放,这样就不会像素化了。为此

您需要设置 wrapS and wrapT属性为THREE.RepeatWrapping。重复包装并指定重复值。因为你也会为其他类型的地图做这个 (比如凹凸或粗糙贴图),最好是为它创建一个循环:

1
2
3
4
5
6
7
var repetition = 6;
var textures = ['map']// we will add 'bumpMap' and 'roughnessMap'
textures.forEach((mapName) => {
  planeMaterial[mapName].wrapS = THREE.RepeatWrapping;
  planeMaterial[mapName].wrapT = THREE.RepeatWrapping;
  planeMaterial[mapName].repeat.set(repetition, repetition);
});

  这看起来应该更好。因为你所使用的纹理是无缝的,你不会注意到有重复发生的边缘的任何不连接。

纹理的加载实际上是一个异步操作。这意味着您的 3D 场景是在加载图像文件之前生成的。但是由于您一直在使用 requestAnimationFrame不断地呈现场景,因此在本例中不会造成任何问题。

如果不这样做,则需要使用回调或其他异步方法来管理加载顺序。

  看代码learning-threejs-07 by Engin Arslan (@enginarslan) on CodePen.

  其他纹理地图

  正如上一章所提到的,纹理不仅被用来定义表面的颜色,而且还用来定义它的其他性质。贴图的另一种方式是凹凸贴图。当被用作凹凸贴图时,纹理的亮度值会模拟一个高度的效果。

1
`planeMaterial.bumpMap = texture;`

  凹凸贴图也应该使用与颜色贴图相同的重复配置,所以在textures数组中包含它。

1
`var textures = ['map', 'bumpMap'];`

  使用凹凸贴图,加亮一个像素的值,相应的表面看起来会更高。但是凹凸贴图并没有改变表面,它只是在操纵光与表面的相互作用产生一种不均匀拓扑的错觉。现在的碰撞量看起来有点太大了。

凹凸贴图在使用时效果很好。因此,让我们将 bumpScale 参数更改为更低的值,以获得更细微的效果。

1
`planeMaterial.bumpScale = 0.01;`

  注意,这种纹理在外观上有很大的不同。反射不再完美了,但却被完美地分割了,就像现实生活中那样。另一种可用于标准材料的StandardMaterial是 roughness map。纹理贴图是一个粗糙的贴图,

你可以使用给定图像的亮度值来控制反射的锐度。

1
2
planeMaterial.roughnessMap = texture;
var textures = ['map', 'bumpMap', 'roughnessMap'];

  根据这 three.js 文档,当与 environment map 一起使用时,标准材料的工作效果最好。环境图模拟了一个遥远的环境,反映了场景中的反射面。当你试图模拟物体的反射率时,这真的很有帮助。

在三个环境地图。js 以立方体地图的形式出现。多维数据集映射是一个在多维数据集内映射的场景的全景视图。一个立方体地图由 6 个独立的图像组成,这些图像对应一个立方体的每个面。由于在一个在线

编辑器中加载 6 个模式图像将会有太多的工作,所以在本例中不会实际使用环境映射。但是为了使这个球面对象更加有趣,也可以添加一个粗糙度图。你会使用这个纹理, 但是是 320px*320px 数据 URI。

  创建一个名为getMetalTexture纹理的新函数。

1
2
3
4
function getMetalTexture() {
  var data = 'data:image/jpeg;base64,/...'; // paste your data URI inside the quotation marks.
  return data
}  

  把它应用在球面上,比如bumpMaproughnessMap:

1
2
3
4
5
6
7
var sphereMaterial = sphere.material;
var metalTexture = textureLoader.load(getMetalTexture());
sphereMaterial.bumpMap = metalTexture;
sphereMaterial.roughnessMap = metalTexture;
sphereMaterial.bumpScale = 0.01;
sphereMaterial.roughness = 0.75;
sphereMaterial.metalness = 0.25;

看代码 learning-threejs-08 by Engin Arslan (@enginarslan) on CodePen.

  封装起来

  你几乎完成了,在这里,你只需要做一些小小的调整。可以看到这个场景文件的最终版本 in this Pen.

  给灯光提供一种非白色的颜色。注意,如何实际使用 CSS 颜色值作为字符串来指定颜色:

1
2
var spotLight_01 = getSpotlight('rgb(145, 200, 255)', 1);
var spotLight_02 = getSpotlight('rgb(255, 220, 180)', 1);

  然后在灯光中添加一些微妙的随机动画,为场景添加一些生命。首先,将名称属性分配给灯光,这样您就可以使用getObjectByName方法在 update 函数中找到它们。

1
2
spotLight_01.name = 'spotLight_01';
spotLight_02.name = 'spotLight_02';

  之后,在update 函数里面,使用Math.random()函数创造动画,

1
2
3
4
5
6
var spotLight_01 = scene.getObjectByName('spotLight_01');
spotLight_01.intensity += (Math.random() - 0.5) * 0.15;
spotLight_01.intensity = Math.abs(spotLight_01.intensity);
var spotLight_02 = scene.getObjectByName('spotLight_02');
spotLight_02.intensity += (Math.random() - 0.5) * 0.05;
spotLight_02.intensity = Math.abs(spotLight_02.intensity); 

  作为一个额外的奖励,在场景文件中,我已经包含了OrbitControls脚本。three.js 摄像机,这意味着你可以把你的鼠标拖到场景上与摄像头互动!我也这样做了,这样场景就会随着窗口大小的变化而调整。

为了方便起见,我使用外部脚本实现了这一点。

  看代码 learning-threejs-final by Engin Arslan (@enginarslan) on CodePen.

  现在,这个场景有点接近现实主义了。尽管如此,仍有许多缺失的部分。由于缺少反射和周围的光线,球体球太暗了。地面平面在看的角度看得太平了。球体的轮廓太完美了——它是 CG(计算机图形) 完美的。

照明实际上并不像它所能的那样真实; 它不会随着源的距离而衰减 (失去强度)。你还应该添加粒子效果,相机动画,以及后期处理滤镜,如果你想要这样做的话。但这仍然是一个很好的例子来说明 three.js 的力量。

以及你可以在浏览器中创建的图形质量。有关可以实现使用这个神奇的库的更多信息, 你应该看看我的新课程Lynda.com的项目!

  谢谢你阅读到这里!希望你喜欢这篇文章,如果有任何问题,你可以在 Twitter 上@inspiratory或在我网站上, 随时联系我。