3D Three.js初探 + 广告业务的关联思考

2,667 阅读9分钟
随着5G大浪潮越演越烈,越发越感觉到VR、裸眼3D、全息投影等技术体验离我们普通人的生活越来越近。作为广告部门的一只前端程序猿,也在思考Three.js在业务中能有什么用。好在之前在大学做项目的时候接触过OpenGL,渲染原理基本上都差不多,在初步学习完之后,列出一些自己的初步思考。这篇文章的前半部分会简单介绍一下Three.js这个技术的基本原理,以及入门知识;而后半部分会聊一下自己认为目前业务中的可能应用。感兴趣的朋友们可以留言,我们一起探索哟。


技术背景:
-什么是Three.js
首先来聊一下什么是Three.js。Three.js 是一款运行在浏览器中的 3D 引擎,你可以用它创建各种三维场景,包括了摄影机、光影、材质等各种对象。简单地来说,three.js的出现,使我们这些小前端们可以快速上手搭起3D程序,而非跟WebGL原生的API死磕到底。通过Three.js, 可以创建三维图形;在三维场景中生成动画、移动物体;在物体上渲染纹理和材质;从三维建模软件中加载图形等。


-浏览器的支持性
因为Three.js其实是一个基于WebGL的JavaScript的库。所以只要是支持WebGL的浏览器,就可以运行Three.js,如下图所示(详细信息请参阅 caniuse.com/#feat=webgl)。基本上,Three.js可在任何流行的浏览器上运行,除了IE的大部分版本,自IE11开始,微软才开始支持WebGL。



-Three.js的原理
Three.js主要有三大组件,场景(Scene), 相机(Camera)和渲染器(Render)。在场景里我们可以定义各种物体和灯光,通过相机我们可以调整观察者的视角;而渲染器是根据相机和场景的相对位置和关系,渲染出来在那种条件下,可以看到的景象。这一过程,其实可以比作拍电影,先布置好场景,然后确定好相机的位置,可以看到的景象也就应运而生了。



-Three.js调用
如果想在项目中使用Three.js,加载库的方式也很简单。
可以将three.js通过script tag加到HTML里,也可以直接通过NPM,直接以模块方式在项目的javascript文件中调用。

<!-- index.html -->
<script src="path/to/three.js"></script>

npm install three --save  // or use yarn add three.js alternatively
import * as THREE from 'three';

如果想要把3D渲染的场景在HTML中展示,我们就需要制定一个简单的<div>元素,并为其制定一个唯一的ID作为钩子。这个<div id="scene-container"/>被称为"divider element",它不会告诉浏览器会其内部会怎样,或者会显示什么,只是起到将周围其它元素与其内部隔开而已。scene-container这个ID,会被当做钩子与相关的js代码建立关联,告诉js/app.js哪里去放置渲染后的3D场景。

<body>
  <h1>广告示例</h1>
  <div id="scene-container"> // This div will hold our scene
  </div>
  <script src="js/app.js"></script>
</body>function init() {
  container = document.querySelector('#scene-container'); // where to drawn  
  scene = new THREE.Scene();
  createCamera();
  createControls();
  createLights();
  createMeshes();
  createRenderer();
  renderer.setAnimationLoop(() => {
    update();
    render();
  });
}

当app.js拿到钩子的位置后,

container = document.querySelector('#scene-container')

就会生成在其内部添加一个<canvas>元素。所有的three.js WebGL场景都会被画到这个<canvas/>上。three.js会把控制权交给底层的WebGL,WebGL会调用显卡(graphics card)来生成图片或一些列图片(如果有动画效果的话),最后这些渲染结果会被在canvas上展示。



-Three.js基本组件

如果我们想要生成下图这个简单的demo,应该怎么来做呢?在上文中,我们提到了Three.js主要有三大组件,场景(Scene), 相机(Camera)和渲染器(Render)。在这里会以这个简单的demo情形为例子,讲解一下从代码细节上我们怎么能够实现这一渲染过程。基本要做的就是布置好场景,创建物体,放好摄像机,开拍!




首先需要做的是创建一个场景,并给场景设置一个背景色。因为本人是个女孩子,就设置成我所偏爱的紫色了。

// create a Scene
scene = new THREE.Scene();
scene.background = new THREE.Color('#9370DB');
有了场景之后,我们就可以往里面增添物体啦!如果需要往场景里添加物体,就调用
scene.add( object )

反之,如果不想要某个物体了(做动画效果时),就用

scene.remove( object )

有了场景,就往里面加物体了,其实物体可以被理解为我们3D场景中的演员。3D计算机图形中,物体通常是用Mesh来生成的。Mesh中又包括geometry,也就是需要声明的物体的形状;以及material,即物体的材质。通过下面的代码,我们制造出来一个长宽高分别为2单位长度的正方体,给其附上了橙色的一般材质,并把它加入了场景中。

// create a geometry - here is a box
const geometry = new THREE.BoxBufferGeometry(2, 2, 2);
// create a orange Standard material
const material = new THREE.MeshStandardMaterial({ color: '#FF6347' });
// create a Mesh containing the geometry and material
mesh = new THREE.Mesh(geometry, material);
// add the mesh to the scene object
scene.add(mesh);

在有了场景和演员之后,就要开始拍大片了。那么,相机需要放在什么位置,其视角又应该如何设定呢?相机配置的参数稍微有点复杂,我们慢慢来看。

首先需要明确的是,为了在2D的浏览器上展示出人眼感觉的3D图像,我们的相机就要模拟人眼的成像原理。这里使用的是PerspectiveCamera,透视相机,基本成像原理用大白话来说,就是“近大远小”。透视原理的相机,需要4个参数,来定义其能看到范围(专业名称叫viewing frustum)。这四个参数分别为:fov, aspect, near, far。near是定义从相机多近的位置开始渲染场景,far是指相机能看到多远,这两个截面内的物体,才属于相机的可视范围。


fov是field of view的缩写,定义了视场的角度。aspect是渲染输出结果的横向长度和纵向长度的比值。由于这个例子会选用整个窗口作为输出界面,就用窗口的长宽比。这个长宽比决定了水平视场和垂直视场的比例关系。


// set up the options for a perspective camera
const fov = 35; // fov = Field Of View
const aspect = container.clientWidth / container.clientHeight;
const near = 0.1;
const far = 100;
camera = new THREE.PerspectiveCamera(fov, aspect, near, far);
// every object is initially created at ( 0, 0, 0 )
// move the camera back a bit so that we can view the scene
camera.position.set(0, 0, 10);

由于在上面添加物体的时候,我们并未声明具体位置,所以物体就被放置在了默认的位置(0,0,0)。所以这里,我们需要把相机的位置稍微往后一点,这样才能看到物体。


之后,我们可以再增加一个灯光,实现光照效果。灯光有很多效果可以选择,我们这里选用了最简单的光照效果,DirectionalLight。该效果是模拟单一光源的,从某个位置向四处发射光线,例如太阳。

// Create a directional light
const light = new THREE.DirectionalLight(0xffffff, 3.0);
// move the light back and up a bit
light.position.set(10, 10, 10);
// remember to add the light to the scene
scene.add(light);

在有了场景,物体,灯光后,就需要renderer来生成2D图像了,并将其插入canvas元素内。当调用WebGLRenderer,会自动生成一个HTML <canvas/>。这里需要用renderer.setSize()来定义canvas画布的大小。在最初,这个canvas会被存在renderer.domElement里。之后采用浏览器内置的方法appendChild,将其插入到上文提到的HTML的钩子元素<div id="scene-container"/>中。

container = document.querySelector('#scene-container');
......
// create a WebGLRenderer and set its width and height
renderer = new THREE.WebGLRenderer({ antialias: true });
renderer.setSize(container.clientWidth, container.clientHeight);
renderer.setPixelRatio(window.devicePixelRatio);
// add the automatically created canvas element to the page
container.appendChild(renderer.domElement);

之后要做的就是,告诉renderer我们所建立的场景和相机,让其渲染产生图像。

renderer.render( scene, camera );

代码写到这里,我们得到了这样的一个静态2D图像。


为了达到3D动态效果,我们需要让物体或相机动起来。这里,我们用比较简单的方法,让正方体运动起来。在animate函数不停地调用自己,利用requestAnimationFrame来处理调用频率。让正方体在每次函数调用时,在X,Y,Z方向都旋转一点点。并且每次都重新调用渲染函数,生成新的图像。

这些连续变化的图片连起来,就形成了我们想要的动画效果。

function animate() {
  // call animate recursively
  requestAnimationFrame(animate);
  // increase the mesh rotation each frame        
  mesh.rotation.z += 0.01;
  mesh.rotation.x += 0.01;
  mesh.rotation.y += 0.01;
  renderer.render(scene, camera);
}



-Three.js广告业务中的应用(个人瞎想...原创哦!!!)

在长篇介绍完Three.js之后,想必小伙伴们已经对它有了初步的了解。那么如何把自己业余时间的学习,与业务联系在一起呢?在此,发挥脑洞的时刻到了。由于本人是广告部门的,所以就初步地瞎想了一些可能用到的场景,如果小伙伴们有更cool的想法,欢迎一起讨论。

下面是自己初步做的一些小demo,欢迎轻拍~~~

1. 3D banner 广告 - 可以把banner广告立体化,用户可以从多角度来改变视角观赏巨幅banner,就有种大街上广告牌的感觉。


2. 3D交互式广告 - 可以把广告牌放置在一个3D空间里,然后用户探索式地去发现广告

此处简单地做了个例子,用户驾驶着可以飞的骑车,在浩瀚的太空中环游,并浏览广告


3. 3D动画广告 - 适合用在一些官网首页,用于效果宣传




4. 3D文字广告 - 通过3D变化 增强观看者对文字关键字的记忆



总的来说,我觉得Three.js在广告业务中的应用可以总结为两个方向(原创):

1)2D广告的图片直接依附于3D空间展示 - 因为2D空间是3D空间的一个子集,那么目前比较流行的2D图片广告,其实可以作为3D空间的一个面,依附于3D空间里。后续的开发基本就是增加各种物体与这个面的交互;

2)将广告创意直接涉及到3D界面里 - 让3D场景中的物体表达出广告的信息,例如后两种广告。更复杂的一点设想是,打造一个3D场景,例如城市街景,然后以游戏探索的方式,把广告加入进去。

以上就是我在业余时间初步学习Three.js并基于业务的一些想法,因为也是刚刚利用业余时间接触这个技术捏,如果有不足的地方,欢迎指出!如果有感兴趣的小伙伴,欢迎点赞留言我,我们一起探索!