我们学过了 geometry 的很多属性:
position:定义顶点位置,可以实现任意形状
uv:定义顶点 uv 坐标,可以对颜色贴图做裁切
normal:自定义顶点法线方向,配合可反射光线的材质可以实现反光
这节再来学一个属性 gemetry.attributes.color 自定义顶点颜色。
它可以用来实现各种渐变色。
我们来试一下:
npx create-vite geometry-color

进入项目,安装依赖:
npm install
npm install --save three
npm install --save-dev @types/three
改下 src/main.js
import './style.css';
import * as THREE from 'three';
import {
OrbitControls
} from 'three/addons/controls/OrbitControls.js';
import mesh from './mesh.js';
const scene = new THREE.Scene();
scene.add(mesh);
const width = window.innerWidth;
const height = window.innerHeight;
const helper = new THREE.AxesHelper(500);
scene.add(helper);
const camera = new THREE.PerspectiveCamera(60, width / height, 0.1, 10000);
camera.position.set(300, 300, 500);
camera.lookAt(0, 0, 0);
const renderer = new THREE.WebGLRenderer();
renderer.setSize(width, height)
function render() {
renderer.render(scene, camera);
requestAnimationFrame(render);
}
render();
document.body.append(renderer.domElement);
const controls = new OrbitControls(camera, renderer.domElement);
创建 Scene、Camera、Renderer。
改下 style.css
body {
margin: 0;
}
创建 mesh.js
import * as THREE from 'three';
const geometry = new THREE.BufferGeometry();
const point1 = new THREE.Vector3(0, 0, 0);
const point2 = new THREE.Vector3(0, 100, 0);
const point3 = new THREE.Vector3(100, 0, 0);
geometry.setFromPoints([point1, point2, point3]);
const colors = new Float32Array([
1, 0, 0,
0, 1, 0,
0, 0, 1
]);
geometry.attributes.color = new THREE.BufferAttribute(colors, 3);
const material = new THREE.PointsMaterial({
vertexColors:true,
size: 30,
});
const points = new THREE.Points(geometry, material);
export default points;
我们用 BufferGeometry 来创建自定义几何体,用 setFromPoints 来确定顶点位置。
之后设置 geometry.attributes.color 和顶点一一对应的顶点颜色。
color 的三个值分别是红绿蓝,从 0 到 1
这里一定要在材质里设置 vertexColors 为 true 才会用你自定义的顶点颜色。
看下效果:
npm run dev


可以看到,我们用 Points 点模型渲染的三个点确实是自定义的颜色。
那如果把点模型换成线模型呢?

const material = new THREE.LineBasicMaterial({
vertexColors: true
});
const line = new THREE.LineLoop(geometry, material);
export default line;
换成线模型,用首尾相连的 LineLoop。
把 AxesHelper 注释掉,看下效果:

那如果我们用网格模型渲染呢?

const material = new THREE.MeshBasicMaterial({
vertexColors: true
});
const mesh = new THREE.Mesh(geometry, material);
export default mesh;

因为这里顶点顺序是顺时针构成的三角形,是反面,默认不渲染反面,要反过来才能看到。
可以看到,是一个渐变色构成的三角形。
所以,自定义顶点颜色可以实现渐变色的效果。
当然,这种渐变比较简单,如果是曲线的渐变色,就会复杂很多。
创建 mesh2.js
import * as THREE from 'three';
const geometry = new THREE.BufferGeometry();
const p1 = new THREE.Vector2(0, 0);
const p2 = new THREE.Vector2(50, 200);
const p3 = new THREE.Vector2(100, 0);
const curve = new THREE.QuadraticBezierCurve(p1, p2, p3);
const pointsArr = curve.getPoints(20);
geometry.setFromPoints(pointsArr);
const positions = geometry.attributes.position;
const colorsArr = [];
for (let i = 0; i < positions.count; i++) {
const percent = i / positions.count;
colorsArr.push(0, percent, 1 - percent);
}
const colors = new Float32Array(colorsArr);
geometry.attributes.color = new THREE.BufferAttribute(colors, 3);
const material = new THREE.LineBasicMaterial({
vertexColors: true
});
const line = new THREE.Line(geometry, material);
export default line;
我们画了二次贝塞尔曲线 QuadraticBezierCurve,然后取 20 个来设置到 BufferGeometry 来自定义几何体。
之后设置这些顶点的颜色。
color 的三个值是红、绿、蓝,我们只改绿和蓝,分成 count 份,那每次的百分比就是 i / count
这样我们就算出了和 20 个顶点一一对应的 20 个颜色值。
看下效果:


可以看到,确实是一个渐变的效果。
当然,对这种情况,不用根据百分比来计算当前颜色值,Color 里有对应的 api:
改下代码:

const color1 = new THREE.Color('orange');
const color2 = new THREE.Color('blue');
for (let i = 0; i < positions.count; i++) {
const percent = i / positions.count;
const c = color1.clone().lerp(color2, percent);
colorsArr.push(c.r, c.g, c.b);
}
还是和之前一样,20 个顶点,根据 i / count 计算百分比。
但不用自己计算百分比的颜色了,确定起点颜色、终点颜色,用 color.lerp 可以计算中间某个百分比位置的颜色。
看下效果:

这样,就可以轻松计算出 20 个点的渐变色。
案例代码上传了小册仓库。
总结
这节我们学了 geometry.attributes.color 自定义顶点颜色。
它需要在材质开启 vertexColors 选项才会生效。
我们通过 geometry.attributes.position 定义顶点位置后,可以通过 geometry.attributes.color 来定义和他一一对应的颜色,这样顶点之间的线、三角形都会用渐变色填充。
曲线的渐变色要计算很多个点的颜色,这种可以用 color.lerp 方法,确定起始点、结束点颜色之后,根据百分比计算某个位置的颜色。
当你想实现渐变色的时候,就可以用自定义顶点颜色的方式来实现。