前言
在上篇文章《lil-gui调试开发3D效果》中,给立方体材质开启线框模式,可以发现立方体的每个面都是由 2 个三角平面构成。
three.js 中最基础的面单位是就是 1 个三角形。创建一个物体需要确定该物体的几何形状和材质,本篇文章来介绍 Three.js 的几何体相关基础知识。
顶点创建平面
Three.js 提供了一个几何形状的基类API,BufferGeometry
,官方文档查看这里。
三角平面
js
const plane_geometry = new THREE.BufferGeometry();
const vertices = new Float32Array([-1, -1, 0, 1, -1, 0, 1, 1, 0]);
plane_geometry.setAttribute("position", new THREE.BufferAttribute(vertices, 3));
const plane_material = new THREE.MeshBasicMaterial({
color: "#2fa295",
});
const plane = new THREE.Mesh(plane_geometry, plane_material);
scene.add(plane);
以上代码中,通过设置顶点的方式创建一个三角平面。新建一个基础几何形状plane_geometry
,设置它的position
顶点坐标位置,通过BufferAttribute
确定使用上面设置的Float32Array
坐标数组,每 3 个确定为一个坐标。
页面效果如下:
以上创建的三角形平面的坐标顺序是逆时针,那如果换成顺时针呢?
只需要修改vertices
,
ini
const vertices = new Float32Array([-1, -1, 0, 1, 1, 0, 1, -1, 0]);
页面效果如下:
正面视角看不见三角平面,当反转视角时才能看到。这说明了 Three.js 中平面是分正反的,当逆时针创建是正面,顺时针创建是反面。
正方形平面
1 个正方形平面是由 2 个三角形平面组成。
js
const plane_geometry = new THREE.BufferGeometry();
const vertices = new Float32Array([
-1, -1, 0, 1, -1, 0, 1, 1, 0, 1, 1, 0, -1, 1, 0, -1, -1, 0,
]);
plane_geometry.setAttribute("position", new THREE.BufferAttribute(vertices, 3));
const plane_material = new THREE.MeshBasicMaterial({
color: "#2fa295",
wireframe: true,
});
const plane = new THREE.Mesh(plane_geometry, plane_material);
scene.add(plane);
页面效果如下:
这里需要注意,存在一个问题。在控制台输出plane
,可以看出当前平面的顶点个数count
是 6,这和我们认知的一个正方形平面 4 个顶点不符。这是因为 2 个三角形的顶点(-1,-1,0)
,(1,1,0)
没有共用。
这就需要采用索引方式创建,复用可共用的顶点坐标。
索引创建平面
js
const plane_geometry = new THREE.BufferGeometry();
const vertices = new Float32Array([-1, -1, 0, 1, -1, 0, 1, 1, 0, -1, 1, 0]);
plane_geometry.setAttribute("position", new THREE.BufferAttribute(vertices, 3));
const indices = new Uint16Array([0, 1, 2, 2, 3, 0]);
plane_geometry.setIndex(new THREE.BufferAttribute(indices, 1));
const plane_material = new THREE.MeshBasicMaterial({
color: "#2fa295",
wireframe: true,
});
const plane = new THREE.Mesh(plane_geometry, plane_material);
scene.add(plane);
以上代码中,仍然是创建一个顶点数组,数组中每 3 个为一组设置几何体的坐标位置,通过Uint16Array
创建索引数组,其中[0,1,2]
第 0 个索引位置的坐标位置(-1,-1,0)
,第 1 个索引位置的坐标位置(1,-1,0)
,第 2 个索引位置的坐标位置(1,1,0)
,这 3 个坐标位置创建 1 个三角平面。以此类推,[2,3,0]
创建第 2 个三角平面。
页面效果如下:
再次打印plane
,控制台输出结果如下:当前平面的顶点个数count
是 4
多材质
根据顶点创建平面,1 个基础平面是三角形平面,1 个正方形平面需要 2 个三角形平面组成,那一个正方体 6 个平面是需要 12 个三角形平面组成,每个平面都是可以设置不同的材质。
举个栗子,设置 1 个正方形平面中 2 种不同材质的三角形平面。
js
const plane_geometry = new THREE.BufferGeometry();
const vertices = new Float32Array([-1, -1, 0, 1, -1, 0, 1, 1, 0, -1, 1, 0]);
plane_geometry.setAttribute("position", new THREE.BufferAttribute(vertices, 3));
const indices = new Uint16Array([0, 1, 2, 2, 3, 0]);
plane_geometry.setIndex(new THREE.BufferAttribute(indices, 1));
plane_geometry.addGroup(0, 3, 0);
plane_geometry.addGroup(3, 3, 1);
const plane_material = new THREE.MeshBasicMaterial({
color: "#FF0000",
});
const plane_material1 = new THREE.MeshBasicMaterial({
color: "#0000FF",
});
const plane = new THREE.Mesh(plane_geometry, [plane_material, plane_material1]);
scene.add(plane);
以上代码中,通过addGroup
方法设置不同顶点分组的采用不同的材质,该方法的具体使用说明参见官方文档。第 1 个参数是索引数组的开始下标,第 2 个参数是几个顶点,第 3 个参数是采用第几个材质,因此addGroup(0, 3, 0)
表示从索引数组[0, 1, 2, 2, 3, 0]
下标 0 的位置开始,选择 3 个顶点坐标作为一组使用第 0 个材质plane_material
。
页面效果如下:
再举个栗子,正方体 6 个平面颜色不同。
js
const geometry = new THREE.BoxGeometry(1, 1, 1);
function rainbowMaterial(colors) {
let _arr = [];
colors.forEach((color) => {
_arr.push(new THREE.MeshBasicMaterial({ color }));
});
return _arr;
}
const cube = new THREE.Mesh(
geometry,
rainbowMaterial([
"#FF0000",
"#FF7F00",
"#FFFF00",
"#00FF00",
"#00FFFF",
"#0000FF",
"#8B00FF",
])
);
页面效果如下:
常见几何体
以上通过顶点创建平面,而 1 个物体是由多个面组成的,使用的是BufferGeometry
这个 API 显得特别麻烦,好在 Three.js 已经封装了不同的几何体 API 供用户使用,不同几何体的具体的使用查看官方文档。
例如,创建一个平面。
js
const geometry = new THREE.PlaneGeometry( 1, 1 );
const material = new THREE.MeshBasicMaterial( {color: 0xffff00});
const plane = new THREE.Mesh( geometry, material );
scene.add( plane );
单从代码量上可以看出,Three.js 提供的这些封装的几何体API,更加方便。
在官方文档中,选择你需要使用的几何体,可以直接在示例中通过 GUI 调试,对于开发更关注它提供的构造函数选项。例如:
最后
注:本文首发微信公众号【前端一起学】,里面有持续更新的Vue源码实战专栏,Electron实战,Three.js入门教程等,还有更多前端基础知识超详细总结,欢迎关注。