随着人们对用户体验越来越重视,Web开发已经不满足于2D效果的实现,而把目标放到了更加炫酷的3D效果上。Three.js是用于实现web端3D效果的JS库,它的出现让3D应用开发更简单,本文将通过Three.js的介绍及示例带我们走进3D的奇妙世界。
1.2 WebGL
1.3 OpenGL
1.4 Canvas
利用Three.JS可以制作出很多酷炫的3D动画,并且Three.js还可以通过鼠标、键盘、拖拽等事件形成交互,在页面上增加一些3D动画和3D交互可以产生更好的用户体验。
在Three.js中,有了场景(scene)、相机(camera)和渲染器(renderer) 这3个组件才能将物体渲染到网页中去。
1)场景
场景是一个容器,可以看做摄影的房间,在房间中可以布置背景、摆放拍摄的物品、添加灯光设备等。
2)相机
相机是用来拍摄的工具,通过控制相机的位置和方向可以获取不同角度的图像。
3)渲染器
渲染器利用场景和相机进行渲染,渲染过程好比摄影师拍摄图像,如果只渲染一次就是静态的图像,如果连续渲染就能得到动态的画面。在JS中可以使用requestAnimationFrame实现高效的连续渲染。
3.1 常用相机
1)透视相机
透视相机模拟的效果与人眼看到的景象最接近,在3D场景中也使用得最普遍,这种相机最大的特点就是近大远小,同样大小的物体离相机近的在画面上显得大,离相机远的物体在画面上显得小。透视相机的视锥体如上图左侧所示,从近端面到远端面构成的区域内的物体才能显示在图像上。
透视相机构造器
PerspectiveCamera( fov : Number, aspect : Number, near : Number, far : Number )
fov — 摄像机视锥体垂直视野角度
aspect — 摄像机视锥体长宽比
near — 摄像机视锥体近端面
far — 摄像机视锥体远端面
2)正交相机
使用正交相机时无论物体距离相机远或者近,在最终渲染的图片中物体的大小都保持不变。正交相机的视锥体如上图右侧所示,和透视相机一样,从近端面到远端面构成的区域内的物体才能显示在图像上。
正交相机构造器
OrthographicCamera( left : Number, right : Number, top : Number, bottom : Number, near : Number, far : Number )
left — 摄像机视锥体左侧面
right — 摄像机视锥体右侧面
top — 摄像机视锥体上侧面
bottom — 摄像机视锥体下侧面
near — 摄像机视锥体近端面
far — 摄像机视锥体远端面
3.2 坐标系
在场景中,可以放物品、相机、灯光,这些东西放置到什么位置就需要使用坐标系。Three.JS使用右手坐标系,这源于OpenGL默认情况下,也是右手坐标系。从初中、高中到大学的课堂上,教材中所涉及的几何基本都是右手坐标系。
上图右侧就是右手坐标系,五指并拢手指放平,指尖指向x轴的正方向,然后把四个手指垂直弯曲大拇指分开,并拢的四指指向y轴的正方向,大拇指指向的就是Z轴的正方向。
在Three.JS中提供了坐标轴工具(THREE.AxesHelper),在场景中添加坐标轴后,画面会出现3条垂直相交的直线,红色表示x轴,绿色表示y轴,蓝色表示z轴(如下图所示)。
3.3 示例代码
/* 场景 */
var scene = new THREE.Scene();
scene.add(new THREE.AxesHelper(10)); // 添加坐标轴辅助线
/* 几何体 */
// 这是自定义的创建几何体方法,如何创建几何体后续会介绍
var kleinGeom = createKleinGeom();
scene.add(kleinGeom); // 场景中添加几何体
/* 相机 */
var camera = new THREE.PerspectiveCamera(45, width/height, 1, 100);
camera.position.set(5,10,25); // 设置相机的位置
camera.lookAt(new THREE.Vector3(0, 0, 0)); // 相机看向原点
/* 渲染器 */
var renderer = new THREE.WebGLRenderer({antialias:true});
renderer.setSize(width, height);
// 将canvas元素添加到body
document.body.appendChild(renderer.domElement);
// 进行渲染
renderer.render(scene, camera);
var geometry = new THREE.Geometry();
// 添加8个顶点
geometry.vertices.push(new THREE.Vector3(1, 1, 1));
geometry.vertices.push(new THREE.Vector3(1, 1, -1));
geometry.vertices.push(new THREE.Vector3(1, -1, 1));
geometry.vertices.push(new THREE.Vector3(1, -1, -1));
geometry.vertices.push(new THREE.Vector3(-1, 1, -1));
geometry.vertices.push(new THREE.Vector3(-1, 1, 1));
geometry.vertices.push(new THREE.Vector3(-1, -1, -1));
geometry.vertices.push(new THREE.Vector3(-1, -1, 1));
// 添加12个三角形的面
geometry.faces.push(new THREE.Face3(0, 2, 1));
geometry.faces.push(new THREE.Face3(2, 3, 1));
geometry.faces.push(new THREE.Face3(4, 6, 5));
geometry.faces.push(new THREE.Face3(6, 7, 5));
geometry.faces.push(new THREE.Face3(4, 5, 1));
geometry.faces.push(new THREE.Face3(5, 0, 1));
geometry.faces.push(new THREE.Face3(7, 6, 2));
geometry.faces.push(new THREE.Face3(6, 3, 2));
geometry.faces.push(new THREE.Face3(5, 7, 0));
geometry.faces.push(new THREE.Face3(7, 2, 0));
geometry.faces.push(new THREE.Face3(1, 3, 4));
geometry.faces.push(new THREE.Face3(3, 6, 4));
4.1 正面和反面
创建几何体时通过指定几何体的顶点和三角形的面确定了几何体的形状,另外还需要给几何体添加皮肤才能实现物体的效果,材质就像物体的皮肤,决定了物体的质感。
常见的材质有如下几种:
-
基础材质:以简单着色方式来绘制几何体的材质,不受光照影响。 -
深度材质:按深度绘制几何体的材质。深度基于相机远近端面,离近端面越近就越白,离远端面越近就越黑。 -
法向量材质:把法向量映射到RGB颜色的材质。 -
Lambert材质:是一种需要光源的材质,非光泽表面的材质,没有镜面高光,适用于石膏等表面粗糙的物体。 -
Phong材质:也是一种需要光源的材质,具有镜面高光的光泽表面的材质,适用于金属、漆面等反光的物体。 -
材质捕获:使用存储了光照和反射等信息的贴图,然后利用法线方向进行采样。优点是可以用很低的消耗来实现很多特殊风格的效果;缺点是仅对于固定相机视角的情况较好。
前面提到的光敏材质(Lambert材质和Phong材质)需要使用光源来渲染出3D效果,在使用时需要将创建的光源添加到场景中,否则无法产生光照效果。下面介绍一下常用的光源及特点。
6.1 点光源
6.2 平行光
6.3 聚光灯
6.4 环境光
7.1 普通纹理贴图
/* 创建地球 */
function createGeom() {
// 球体
var geom = new THREE.SphereGeometry(1, 64, 64);
// 纹理
var loader = new THREE.TextureLoader();
var texture = loader.load('./earth.jpg');
// 材质
var material = new THREE.MeshLambertMaterial({
map: texture
});
var earth = new THREE.Mesh(geom, material);
return earth;
}
7.2 反面贴图实现全景视图
/* 创建反面贴图的球形 */
// 球体
var geom = new THREE.SphereGeometry(500, 64, 64);
// 纹理
var loader = new THREE.TextureLoader();
var texture = loader.load('./panorama.jpg');
// 材质
var material = new THREE.MeshBasicMaterial({
map: texture,
side: THREE.BackSide
});
var panorama = new THREE.Mesh(geom, material);
7.3 凹凸纹理贴图
// 纹理加载器
var loader = new THREE.TextureLoader();
// 纹理
var texture = loader.load( './stone.jpg');
// 凹凸纹理
var bumpTexture = loader.load( './stone-bump.jpg');
// 材质
var material = new THREE.MeshPhongMaterial( {
map: texture,
bumpMap: bumpTexture
} );
7.4 法线纹理贴图
// 纹理
var texture = loader.load( './metal.jpg');
// 法线纹理
var normalTexture = loader.load( './metal-normal.jpg');
var material = new THREE.MeshPhongMaterial( {
map: texture,
normalMap: normalTexture
} );
7.5 环境贴图
/* 立方相机 */
var cubeCamera = new THREE.CubeCamera( 1, 10000, 128 );
/* 材质 */
var material = new THREE.MeshBasicMaterial( {
envMap: cubeCamera.renderTarget.texture
});
/* 镜面反光的球体 */
var geom = new THREE.SphereBufferGeometry( 10, 32, 16 );
var ball = new THREE.Mesh( geom, material );
// 将立方相机添加到球体
ball.add( cubeCamera );
scene.add( ball );
// 立方相机生成环境纹理前将反光小球隐藏
ball.visible = false;
// 更新立方相机,生成环境纹理
cubeCamera.update( renderer, scene );
balls.visible = true;
// 渲染
renderer.render(scene, camera);
Three.JS已经内置了很多常用的几何体,如:球体、立方体、圆柱体等等,但是在实际使用中往往需要用到一些特殊形状的几何体,这时可以使用3D建模软件制作出3D模型,导出obj、json、gltf等格式的文件,然后再加载到Three.JS渲染出效果。
// .mtl材质文件加载器
var mtlLoader = new THREE.MTLLoader();
// .obj几何体文件加载器
var objLoader = new THREE.OBJLoader();
mtlLoader.load('./chair.mtl', function (materials) {
objLoader.setMaterials(materials)
.load('./chair.obj', function (obj) {
scene.add(obj);
…
});
});
文章来源:宜信技术学院 & 宜信支付结算团队技术分享第6期-支付结算部支付研发团队前端研发高级工程师-刘琳《Three.js - 走进3D的奇妙世界》
分享者:宜信支付结算部支付研发团队前端研发高级工程师-刘琳
原文首发于支付结算团队公众号「野指针」
⭐️⭐️⭐️欢迎加入“宜信技术交流群”。进群方式:请加小助手微信(微信号:creditease_tech)。
◆ ◆ ◆ ◆ ◆
如需转载请与小助手(微信号:creditease_tech)联系。发现文章有错误、对内容有疑问,都可以通过关注宜信技术学院微信公众号(CE_TECH),在后台留言给我们。我们每周会挑选出一位热心小伙伴,送上一份精美的小礼品。快来扫码关注我们吧!
⏬点击“阅读原文”查看更多技术干货
本文分享自微信公众号 - 宜信技术学院(CE_TECH)。
如有侵权,请联系 support@oschina.cn 删除。
本文参与“OSC源创计划”,欢迎正在阅读的你也加入,一起分享。
来源:oschina
链接:https://my.oschina.net/u/4007037/blog/4387584