很熟悉吧,经常逛掘金的童鞋们,一定见过这张图咯,今天我们就用前端技术还原出里面部分的3d模型的,首先我们分析一下里面的元素
分析
除了基础的场景,背景布,灯光,还有一些文字,贴图和各种材质,我们一步一步的做,一个一个元素的往里加
布景
背景布
颜色使用吸管工具大致取一下,不过会受到灯光的影响
// 创建背景布
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,
/**
* 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进行线的渲染,看一下效果
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)进行挤压,形成一个真正的圆角矩形立方体,
/**
* 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
}
第一个模型
制作第一个白色的模型,加材质
// 第一个白色
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);
漫反射(反射插件)
同样的原理,把再上面的一层绿色的模型加上去,然后再加一层反光面
<script src="../../three.js-master\examples\js/objects/Reflector.js"></script>
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文件
接下来将文字导入到场景中
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部分,稍微调整一下灯光,找一下角度,就酱
大致复刻成这样,再有的就是一些耐心,铺设所有的内容,再加上阴影调整细节