使用 ThreeJS 在浏览器中展示 3D 物件
这是一篇介绍如何在浏览器中展示洗面奶3D 物件的文章。
前言
这篇文章仅仅是向你介绍应对下面这种的场景的方法:如果有人突然跟你说,你的皮肤挺不错的耶,你用的什么洗面奶,我也想买一个,你会怎么回答呢?手头又没法拿出你的洗面奶给他看,光凭言语无法准确地形容出你的洗面奶到底长什么样。作为前端的程序猿,我会想,这种时候如果可以有一个链接让对方自己去看一下把玩一下,对方势必会对你的洗面奶高 Bigger有更深刻的认识。本文的目的,就是向屏幕对面的程序猿介绍这种高效搞笑的方式。
走进 3D 的世界
在页面里面放几个图片是完全无法满足让观看者自己看自己把玩的要求的。所以这里我们将会使用 3D 的形式来展现我们的洗面奶。老话说得好,同样是山,却有横看成岭侧成峰的不同。比起 2D 渲染,3D 渲染多了一个维度,由于透视效果,物体遮挡、光照角度、光的反射折射等的存在,观看者在不同的角度观看会得到不同的结果。
在浏览器里面,CSS3 提供了 3D 变换的相关属性,但对于光照相关的需求却是无能为力。而使用 Canvas 进行绘制的话,如果不依赖封装好的图形库,进行图形变换又是相当麻烦的事情。就算是在 CSS 中一个简单的 2D 旋转或者是放大,在 Canvas 的像素操作中,我们还需要通过矩阵计算才能知道变换后每个像素的位置。正因如此,图形库出现了。
说到图形库,我们不得不提到 OpenGL 。OpenGL 是一个跨平台的图形编程规范,定义了 2D 与 3D 绘制中所需要各种接口,进行图形绘制中所需要的变换,纹理映射,光照等。OpenGL 定义的接口很多是为硬件加速设计的。有了各大硬件厂商的支持,OpenGL 的渲染效率比起软件渲染高了不止一点点。同时,OpenGL 不局限于某平台或者是语言,它只是一个关于图形渲染的规范,对外提供关于图形渲染的各种接口,所以有许多的语言绑定,而在浏览器中用到的是 Javascript 的绑定 WebGL。
ThreeJS
ThreeJS 是一款在浏览器中进行 3D 绘制的 Javascript 库,为使用 canvas 绘制,WebGL 渲染等图形操作提供了简便的 API。到底有多简便?在 PixiJS 等 2D 绘制库中,我们需要场景+物件+贴图来搞定一张图,而在 ThreeJS 中我们只需要在这基础上额外添加适当光照与一台照相机而已,下面是一个最简单的 Demo,绘制了一个旋转的绿色立方体。
var scene = new THREE.Scene();
/*创建场景*/
var camera = new THREE.PerspectiveCamera( 75, window.innerWidth/window.innerHeight, 0.1, 1000 );
/*创建照相机*/
var renderer = new THREE.WebGLRenderer();
/*创建渲染器*/
renderer.setSize( window.innerWidth, window.innerHeight );
document.body.appendChild( renderer.domElement );
var geometry = new THREE.BoxGeometry( 1, 1, 1 );
/*创建几何模型*/
var material = new THREE.MeshBasicMaterial( { color: 0x00ff00 } );
/*创建材质*/
var cube = new THREE.Mesh( geometry, material );
/*几何模型与材质合成为物件*/
scene.add( cube );
/*将物件添加入场景*/
camera.position.z = 5;
var render = function () {
requestAnimationFrame( render );
cube.rotation.x += 0.1;
cube.rotation.y += 0.1;
renderer.render(scene, camera);
/*渲染出在相机 camera 中看到的 scene 场景*/
};
render();
画出来就像这样子:
ThreeJS 中提供了少量基础的几何模型,如长方体(Box3),球体(Sphere) 等,但面对我们要实现的洗面奶还是太小儿科了。这是不是说明我们的洗面奶没办法做了?文章都写到这里了,办法肯定是有的。ThreeJS 提供了加载外部模型的模块(Loader),可以加载外部的 Obj,json 等格式的模型。另外,ThreeJS 的 Github 仓库 中还提供了在 3ds Max、Blender 等 3D 绘制软件中导出模型的工具。是的,我们就可以用别的 3D 建模软件建模再导出成 ThreeJS 所需要的格式了。
动手
我们这里使用的是建模工具是 Blender。我们需要先拍下物体的三视图作为建模的参考。导入 Blender 后,依据三视图,我们很快就可以建出洗面奶的模型。
模型的样子
在 Blender 中加上 ThreeJS 的插件之后,我们可以在 Blender 的文件菜单中见到 Export/Three.js(.json) 选项。点击之后,选择导出的目录,然后记得在左下角勾上我们要导出的元素。在这个例子中,我们需要导出的是 Scene,也就是场景本身。
在 ThreeJS 中进行导入的操作也十分简单。ThreeJS 中提供了许多种类的 Loader,分别针对不同的使用需求。比如,JSONLoader 针对的是.json 格式的模型,OBJLoader 针对的是.obj 格式的模型等等。翻阅网上资料的时候还可以看到 SceneLoader 的踪影,这就是用来加载整个场景的.json 格式文件的。可是在 ThreeJS 的新版本中,SceneLoader 已经被废弃,取而代之的是更为牛叉更为智能的 ObjectLoader。ObjectLoader 可以判断导出的模型到底是什么种类,从而将它们转化为 ThreeJS 中的对应对象便于开发者使用。
在这个例子中,我们导出的.json 文件中包含的是场景本身。所以,除了需要添加部分 ObjectLoader 的代码,其余部分的代码甚至比上面那个例子还要简单:
var scene,
camera,
renderer,
loader,
wid,
hei,
animate;
animate = function () {
renderer.render(scene, camera);
requestAnimationFrame(animate);
};
wid = window.innerWidth;
hei = window.innerHeight;
camera = new THREE.PerspectiveCamera(45, wid / hei, 1, 1000);
/*创建照相机*/
camera.position.set(10, 10, 10);
camera.lookAt(new THREE.Vector3(-1, -1, -1));
renderer = new THREE.WebGLRenderer();
renderer.setSize(wid, hei);
loader = new THREE.ObjectLoader();
/*创建 ObjectLoader*/
document.body.appendChild(renderer.domElement);
loader.load('objs/object.json', function (_sce) {
scene = _sce;
scene.add(camera);
animate();
});
/*加载 Scene,成功后执行回调开始动画*/
浏览器中一看,却不太对劲。形状对了,可是颜色呢?高端黑的洗面奶怎么就变成这么山寨的颜色了,而且每次刷新都变颜色。活了二十几年,小便表示还真没见到过这样的洗面奶。
出现这种情况,毫无疑问是材质的问题。原来我们在建模软件中还没给洗面奶加过材质,所以 ThreeJS 加载完我们的.json 文件后,发现只有模型却没有材质,就给模型加上了一个随机颜色的材质。解决办法也很简单,在 ThreeJS 中手动贴上贴图就好了。而更简单且有效的办法是,在建模软件中上好贴图再一起导出。
回到 Blender 中,将洗面奶的表面进行 UV 展开后导出展开图后,我们新建一张图片,将我们要贴的图放到展开图上的相应位置,再回到 Blender 中将这张新的图片设为瓶身的材质。渲染一下,检查到效果无误后,将模型导出。这次要记得将左下角的 Materials 勾上,另外还需要勾上 texture 的复选框。
将这次导出的模型放到先前的目录下,我们会发现,浏览器中并没有出现想象中的场景。在控制台中可以看到,由于我们没有将贴图一起放到一个目录下,贴图加载失败了。按照要求放好后,我们会更惊奇地发现,浏览器中除了一片黑,什么都没有。这是为什么呢?
神说要有光,所以就有了光。
在 3D 的世界中,光是非常重要的存在。我们之所以能看见物体,都是因为有光进入了我们的眼球。除去本身会发光的物体,我们能看见的其他的物体,都是因为这些物体将外界的光反射后进入我们眼球了。换句话说,没有光的话我们就什么都看不到。
在 ThreeJS 中也是如此。如果没有光,我们就看不到自发光以外的材质,视野中将是一片黑。在 ThreeJS 中,光照也有很多种:全局光照(AmbientLight),有向光(DirectionalLight),点光源(PointLight) 等。在这个例子中,为了 360 观看整个物体,我们添加一个全局光照。
var light = new THREE.AmbientLight( 0xffffff );
scene.add( light );
再刷新一下,就可以看见我们的洗面奶了。大功告成!
小结
进行 3D 建模,将物体在浏览器中展示,目前已经有了不少的应用,有的公司在宣传新产品的时候会使用上这样的技术,让消费者可以在浏览器中亲自把玩产品,观察产品的每个小角落,起到了不错的效果。同样的技术并不只是在展示商品的时候能用上。将适当的全景图贴在立方体的内表面,用户视角置于立方体中心点的话,还可以让用户有置身其境的感觉,可以上下左右转动视角观察一个地点周围的景象,做出街景的效果。可以说,浏览器中的 3D 技术将会有越来越多的用武之地。
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论