Skip to content

23. 自定义顶点颜色实现渐变

Published:

我们学过了 geometry 的很多属性:

position:定义顶点位置,可以实现任意形状

uv:定义顶点 uv 坐标,可以对颜色贴图做裁切

normal:自定义顶点法线方向,配合可反射光线的材质可以实现反光

这节再来学一个属性 gemetry.attributes.color 自定义顶点颜色。

它可以用来实现各种渐变色。

我们来试一下:

npx create-vite geometry-color

image.png

进入项目,安装依赖:

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

image.png

image.png

可以看到,我们用 Points 点模型渲染的三个点确实是自定义的颜色。

那如果把点模型换成线模型呢?

image.png

const material = new THREE.LineBasicMaterial({
    vertexColors: true
});

const line = new THREE.LineLoop(geometry, material);

export default line;

换成线模型,用首尾相连的 LineLoop。

把 AxesHelper 注释掉,看下效果:

image.png

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

image.png

const material = new THREE.MeshBasicMaterial({
    vertexColors: true
});

const mesh = new THREE.Mesh(geometry, material);

export default mesh;

2025-04-04 17.33.05.gif

因为这里顶点顺序是顺时针构成的三角形,是反面,默认不渲染反面,要反过来才能看到。

可以看到,是一个渐变色构成的三角形。

所以,自定义顶点颜色可以实现渐变色的效果。

当然,这种渐变比较简单,如果是曲线的渐变色,就会复杂很多。

创建 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 个颜色值。

看下效果:

image.png

image.png

可以看到,确实是一个渐变的效果。

当然,对这种情况,不用根据百分比来计算当前颜色值,Color 里有对应的 api:

改下代码:

image.png

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 可以计算中间某个百分比位置的颜色。

看下效果:

image.png

这样,就可以轻松计算出 20 个点的渐变色。

案例代码上传了小册仓库

总结

这节我们学了 geometry.attributes.color 自定义顶点颜色。

它需要在材质开启 vertexColors 选项才会生效。

我们通过 geometry.attributes.position 定义顶点位置后,可以通过 geometry.attributes.color 来定义和他一一对应的颜色,这样顶点之间的线、三角形都会用渐变色填充。

曲线的渐变色要计算很多个点的颜色,这种可以用 color.lerp 方法,确定起始点、结束点颜色之后,根据百分比计算某个位置的颜色。

当你想实现渐变色的时候,就可以用自定义顶点颜色的方式来实现。

评论