上节我们完成了林海雪原的场景。
但有一个问题:

这个山坡其实是有高低起伏的,但现在看不出来,都是白色的。
能不能给山坡根据不同的高度来设置不同颜色呢?
可以的。
用前面学过的自定义顶点颜色 geometry.attributes.color 的知识。
我们可以拿到所有顶点的坐标,也就是就可以拿到每个顶点的高度,根据高度来设置这个顶点的颜色就好了。
改下 mountainside.js

const heightArr = [];
for (let i = 0; i < positions.count; i++) {
heightArr.push(positions.getZ(i));
}
heightArr.sort();
const minHeight = heightArr[0];
const maxHeight = heightArr[heightArr.length - 1];
const height = maxHeight - minHeight;
const colorsArr = [];
const color1 = new THREE.Color('green');
const color2 = new THREE.Color('red');
for (let i = 0; i < positions.count; i++) {
const percent = (positions.getZ(i) - minHeight) / height;
const c = color1.clone().lerp(color2, percent);
colorsArr.push(c.r, c.g, c.b);
}
const colors = new Float32Array(colorsArr);
geometry.attributes.color = new THREE.BufferAttribute(colors, 3);
const material = new THREE.MeshLambertMaterial({
// color: new THREE.Color('white'),
vertexColors:true,
// wireframe: true
});
因为旋转之前这个山坡是在 XY 平面的,也就是 Z 坐标是高度。
我们拿到所有顶点的 Z,排一下序。
计算出最低和最高的高度,高度差就是山坡的整体高度。
然后遍历每个顶点,用当前高度减去最低高度,算出百分比,这样就可以实现颜色插值。
用 color.lerp 方法,指定一个开始颜色、一个结束颜色,然后根据高度算出百分比之后,就可以算出这个百分比对应的颜色。
把算出的 colors 数组设置到 geometry.attributes.color。
然后材质里开启 vertextColors 为 true,使用顶点颜色。
这样,就实现了顶点颜色的自定义,并且自定义的颜色是和高度相关的。
看下效果:

可以明显看出来,海拔高的地方是红色,海拔低的地方时绿色,中间高度是渐变的颜色。
然后我们设置山顶白色,山谷灰色就好了:

const color1 = new THREE.Color('#eee');
const color2 = new THREE.Color('white');

现在,海拔高低就一目了然了。
对比之前的:

是不是好多了?
然后我们加一个相机动画,这里让相机做圆周运动:

let angle = 0;
let r = 1000;
function render() {
angle += 0.03;
camera.position.x = r * Math.cos(angle);
camera.position.z = r * Math.sin(angle);
camera.lookAt(0, 0, 0);
renderer.render(scene, camera);
requestAnimationFrame(render);
}
每一帧改变相机的位置,设置半径为 1000,每一帧改变一下角度,用 cos、sin 算出当前的 x、z 来,然后 lookAt 0,0,0
看下效果:

案例代码上传了小册仓库。
总结
这节我们做了一些优化。
通过自定义顶点颜色实现了海拔高度的区分,拿到所有顶点坐标,根据高度用 color.lerp 计算颜色插值,实现了不同高度的顶点颜色不同。
之后又加上了相机的圆周动画,根据角度的 cos、sin 计算相机位置的 x、z,高度 y 保持不变。
这个实战里自定义顶点坐标、自定义顶点颜色都用到了,这些原理性的东西还是很重要的。