很熟悉吧,经常逛掘金的童鞋们,一定见过这张图咯,今天我们就用前端技术还原出里面部分的3d模型的,首先我们分析一下里面的元素
分析
除了基础的场景,背景布,灯光,还有一些文字,贴图和各种材质,我们一步一步的做,一个一个元素的往里加
布景
背景布
颜色使用吸管工具大致取一下,不过会受到灯光的影响
``` js // 创建背景布 function initGround () { const geometry = new THREE.PlaneGeometry(80, 80, 1, 1); const material = new THREE.MeshLambertMaterial({ color: 0x5abe64, // 画布颜色 side: THREE.DoubleSide // 双面可见 }); const plane = new THREE.Mesh(geometry, material); plane.receiveShadow = true // 是否接受阴影 return plane }
const plan = initGround() plan.rotation.x = -0.5 * Math.PI scene.add(plan) ```
第一个元素
白色材质的一个倒圆角的立方体,制作立方体,首先要有顶点信息,有了顶点之后,再连线,通过线条,进行挤压,最终形成一个立方体,带着这个思路,我们首先要做一下顶点
需要8个连接点,还有所有的圆角顶点,圆角顶点所使用的是 椭圆曲线 EllipseCurve
四个椭圆曲线顺序链接就可以形成一个闭环的圆角矩形,说干就干
因为大部分都是这种圆角矩形的结构,所以获取圆角矩形的顶点信息写一个公共的方法getpoint
,
``` js
/* * Perform an A Search on a graph given a start and end node. * @param {R} radius * @param {L} length * @param {S} subsection * @return {points} points */ function getPoint (R, L, S) { const curve1 = new THREE.EllipseCurve( L, L, // ax, aY R, R, // xRadius, yRadius 0, 0.5 * Math.PI, // aStartAngle, aEndAngle false, // aClockwise 0 // aRotation ); const curve2 = new THREE.EllipseCurve( -L, L, // ax, aY R, R, // xRadius, yRadius 0, 0.5 * Math.PI, // aStartAngle, aEndAngle false, // aClockwise 0.5 * Math.PI // aRotation ); const curve3 = new THREE.EllipseCurve( -L, -L, // ax, aY R, R, // xRadius, yRadius 0, 0.5 * Math.PI, // aStartAngle, aEndAngle false, // aClockwise Math.PI // aRotation ); const curve4 = new THREE.EllipseCurve( L, -L, // ax, aY R, R, // xRadius, yRadius 0, 0.5 * Math.PI, // aStartAngle, aEndAngle false, // aClockwise -0.5 * Math.PI // aRotation );
// 创建组合曲线对象CurvePath var CurvePath = new THREE.CurvePath(); // 把多个线条有顺序的插入到CurvePath中 CurvePath.curves.push(curve1, curve2, curve3, curve4); //分段数S var points = CurvePath.getPoints(S); return points } ```
最终导出的是一组二维坐标 vector2
将这组二维坐标通过lineloop
的api进行线的渲染,看一下效果
js var geometry = new THREE.Geometry(); //声明一个几何体对象Geometry // setFromPoints方法从points中提取数据改变几何体的顶点属性vertices geometry.setFromPoints(points); console.log(points) //材质对象 var material = new THREE.LineBasicMaterial({ color: 0x000000 }); //线条模型对象 var line = new THREE.LineLoop(geometry, material); scene.add(line); //线条对象添加到场景中
现在了顶点,形成了线条,那么下面就将顶点使用挤压缓冲几何体(ExtrudeGeometry)
进行挤压,形成一个真正的圆角矩形立方体,
```js
/* * Perform an A Search on a graph given a start and end node. * @param {points} 顶点信息vector2 * @param {option} 挤压的参数 * @param {option.steps} 用于沿着挤出样条的深度细分的点的数量。 * @param {option.depth} 挤出的形状的深度 * @param {option.bevelEnabled} 对挤出的形状应用是否斜角 * @param {option.bevelSize} 斜角与原始形状轮廓之间的延伸距离 * @param {option.bevelOffset} 斜角的分段层数,默认值为3。 * @param {option.bevelSegments} 斜角与原始形状轮廓之间的延伸距离 */ function extrudeGeometry (points, option) { const shape = new THREE.Shape(); // 循环每一个点放在形状 shape.moveTo(points[0].x, points[0].y); for (let i = 1; i < points.length; i++) { shape.lineTo(points[i].x, points[i].y); } // 设置挤压属性 const extrudeSettings = { steps: option.steps || 32, //用于沿着挤出样条的深度细分的点的数量。 depth: option.depth || 0.2,// 挤出的形状的深度 bevelEnabled: !!option.bevelEnabled, // bool,对挤出的形状应用是否斜角 bevelThickness: option.bevelThickness || 0.2, // 设置原始形状上斜角的厚度 bevelSize: option.bevelSize || 0.1, // 斜角与原始形状轮廓之间的延伸距离 bevelOffset: option.bevelOffset || 0.05, bevelSegments: option.bevelSegments || 32 // 斜角的分段层数,默认值为3。 }; // 返回挤压的几何体包含顶点信息等 const geometry = new THREE.ExtrudeGeometry(shape, extrudeSettings); return geometry } ```
第一个模型
制作第一个白色的模型,加材质
```js
// 第一个白色 const firstGeo = extrudeGeometry(points, { steps: 32, depth: 0.2, bevelEnabled: true, bevelThickness: 0.13, bevelSize: 0.1, bevelOffset: 0.1, bevelSegments: 32 }) // 设置材质 const material = whiteMaterialBool(); // 生成模型 const mesh = new THREE.Mesh(firstGeo, material); // 调整位置等信息 mesh.position.y = 0.4 mesh.rotation.x = 0.5 * Math.PI mesh.castShadow = true // 是否产生阴影 mesh.receiveShadow = true // 是否接受阴影 // 将模型加入到场景中 scene.add(mesh);
```
漫反射(反射插件)
同样的原理,把再上面的一层绿色的模型加上去,然后再加一层反光面
html <script src="../../three.js-master\examples\js/objects/Reflector.js"></script>
js function initReflector() { // 反光面 reflector = new THREE.Reflector(new THREE.PlaneGeometry(20, 20), { textureWidth: window.innerWidth * window.devicePixelRatio, textureHeight: window.innerHeight * window.devicePixelRatio, color: 0x81f98d }); reflector.position.y = 1.35; reflector.rotation.x = -0.5 * Math.PI scene.add(reflector); }
镜面效果如果在3D软件可以用漫反射直接做出来,代码敲漫反射有点复杂,直接调用插件,镜面上面放一个模型 试试效果
堆砌元素
先从简单的入手,一层一层往上加,金属的那的外层稍后再加一下,接下来就是文字后面的大的绿色模块
跟底板的绿色是一样的效果 我把下面效果相同的都先堆出来
这里的代码就不铺了 就是各种调参数,可以去源码扒拉扒拉
文字的制作
接下来制作文字部分,没引入字体,直接用ai软件画一个svg导出来,找到一款差不多的字体
导出到svg文件
接下来将文字导入到场景中
```js function loadSvg () { var loader = new THREE.SVGLoader(); loader.load( "./svg/club.svg", function (data) { // 将svg挤出高度 var paths = data.paths; for (var i = 0; i < paths.length; i++) { var path = paths[i]; var shapes = path.toShapes(true); shapes.forEach((shape) => { let extrudeGeo extrudeGeo = new THREE.ExtrudeGeometry(shape, { bevelEnabled: true, depth: 20, steps: 32, bevelSegments: 32, bevelSize: 2, }); extrudeGeo.scale(0.05, 0.05, 0.05); var mesh = new THREE.Mesh(extrudeGeo, greenMaterialBool()); mesh.rotation.x = -Math.PI mesh.position.set(-4.2, 6.2, 8.5) scene.add(mesh) }); } }) }
```
logo及其他部分
导入之后尺寸和方向都不合适 稍微调整一下,再加上上面的logo部分,稍微调整一下灯光,找一下角度,就酱
大致复刻成这样,再有的就是一些耐心,铺设所有的内容,再加上阴影调整细节