Three.js-硬要自学系列19 (曲线颜色渐变、渐变插值、查看设置gltf顶点、山脉高度可视化)

本章主要学习知识点

  • 学习如何实现曲线颜色渐变
  • 掌握利用插值实现颜色渐变
  • 学会查看gltf顶点并设置其顶点
  • 练习山脉高度可视化

曲线颜色渐变

「曲线颜色渐变」可以理解为让一条3D曲线从起点到终点呈现颜色逐渐变化的效果,就像用不同颜色的彩笔依次涂满线条的不同段落。

创建一条曲线,获取曲线上的点

js 复制代码
const geometry = new THREE.BufferGeometry();
const curve = new THREE.CatmullRomCurve3([
    new THREE.Vector3( -10, 0, 0 ),
    new THREE.Vector3( -5, 5, 0 ),
    new THREE.Vector3( 0, 0, 0 ),
    new THREE.Vector3( 5, -5, 0 ),
    new THREE.Vector3( 10, 0, 0 ),
])
// 曲线取点
const pointsArr = curve.getSpacedPoints( 120 );
// 设置顶点位置
geometry.setFromPoints( pointsArr );

计算每个顶点的颜色

js 复制代码
const pos = geometry.attributes.position;
const count = pos.count;  // 顶点数量
const colorsArr = [];
for ( let i = 0; i < count; i ++ ) {
    //点索引值相对所有点数量的百分比
    const percent = i / count;
    const color = new THREE.Color();
    color.setHSL( percent, 1.0, 0.5 );
    colorsArr.push( color.r, color.g, color.b );
}
const colors = new Float32Array( colorsArr );
// 设置颜色属性
geometry.setAttribute( 'color', new THREE.BufferAttribute( colors, 3 ) );

const material = new THREE.LineBasicMaterial( {
     vertexColors: true  // 使用顶点颜色
    } );
const line = new THREE.Line( geometry, material );

scene.add( line );

颜色渐变插值

颜色渐变插值,可以理解为让颜色在三维模型的顶点之间自动平滑过渡,这个过程就像给模型的不同部位涂上不同颜色,而Three.js 会自动混合中间区域的颜色。

顶点颜色驱动

每个3D模型的几何体由多个顶点组成。如果你给不同顶点设置不同颜色,Three.js 在渲染时会自动计算顶点之间的颜色过渡,形成渐变效果

创建一组顶点

js 复制代码
const geometry = new THREE.BufferGeometry();    
const vertices = new Float32Array([
    0, 0, 0,
    5, 0, 0,
    5, 5, 0,
    0, 5, 0,
])
// 定义索引数据
const indexes = new Uint16Array([
  0,1,2,
  0,2,3
])
// 设置几何体顶点索引数据
geometry.setIndex(new THREE.BufferAttribute(indexes,1))
geometry.setAttribute('position', new THREE.BufferAttribute(vertices, 3))

通过lerp插值计算颜色

js 复制代码
const pos = geometry.attributes.position;
const count = pos.count;
const c1 = new THREE.Color(1, 2, 0)
const c2 = new THREE.Color(0, 1, 1)
const colorsArr = [];
console.log(count);

for (let i = 0; i < count; i++) {
    // 计算百分比
    const percent = i / count;
    // 根据百分比插值计算颜色
    const c = c1.clone().lerp(c2, percent)
    // 将颜色值添加到数组中
    colorsArr.push(c.r, c.g, c.b)
}
geometry.setAttribute('color', new THREE.BufferAttribute(new Float32Array(colorsArr), 3))

查看设置gltf顶点

通过使用GLTFLoader导入模型,遍历所有子对象,attributes.position获取模型的顶点,设置顶点坐标

js 复制代码
const loader = new GLTFLoader(loadingManager);
loader.load('model/free_gmc_motorhome_reimagined_low_poly/scene.glb', function (gltf) {
    scene.add(gltf.scene);

    // 遍历gltf场景中的所有子对象
    gltf.scene.traverse(function (child) {
        if (child.isMesh) {
            // 获取顶点数量
            const pos = child.geometry.attributes.position;
            const count = pos.count;
            // 获取顶点索引
            const index = child.geometry.index;

            for( let i = 0; i < count; i++){
                // 获取pos对象中第i个元素的y坐标
                const y = pos.getY(i);
                // 将pos对象中第i个元素的y坐标乘以2
                pos.setY(i, y * 2);
            }   
        }
    })  
})

山脉高度可视化

js 复制代码
   const loader = new GLTFLoader(loadingManager);
    loader.load( 'model/mountain_terrain2.glb', function ( gltf ) {    
        scene.add( gltf.scene );
        gltf.scene.traverse((child) => {
            if (child.isMesh) {
                const mesh = child;
                const pos = mesh.geometry.attributes.position;
                const count = pos.count;
                
                 // 计算模型y坐标高度差
                const yArr = [];
                for(let i = 0; i < count; i++) {
                    yArr.push(pos.getY(i))
                }
                // 高度排序
                yArr.sort(); // 从小到大排序
                
                const minY = yArr[0];
                const maxY = yArr[yArr.length - 1];
                const height = maxY - minY; // 山脉整体高度
    

                const colorsArr = [];
                const c1 = new THREE.Color(0x00ff00);
                const c2 = new THREE.Color(0xff0000);
                for(let i = 0; i < count; i++) {
                    // 计算当前位置的百分比
                    const percent = (pos.getY(i) - minY) / height;
                    
                    // 根据百分比计算颜色
                    const color = c1.clone().lerp(c2, percent);
                    
                    // 将颜色添加到颜色数组中
                    colorsArr.push(color.r, color.g, color.b);
                }

                const colors = new Float32Array(colorsArr);
                mesh.geometry.setAttribute('color', new THREE.BufferAttribute(colors, 3));

                mesh.material = new THREE.MeshLambertMaterial({
                    vertexColors: true,
                })

            }
        })
    });

以上案例均可在案例中心查看体验

THREE 案例中心

相关推荐
吃瓜群众i1 小时前
理解Javascript闭包
前端·javascript
安大桃子1 小时前
Mapbox GL + Deck.gl 三维实战:Mapbox 加载 Tileset3D 倾斜摄影模型
前端·webgl
yede1 小时前
多行文本省略号显示,更多按钮展开全部
前端
就是我1 小时前
React 应用性能优化实战
前端·react.js·性能优化
G扇子1 小时前
深入解析XSS攻击:从原理到防御的全方位指南
前端·安全
snakeshe10101 小时前
入解析React性能优化策略:eagerState的工作原理
前端
六边形6661 小时前
Vue中的 ref、toRef 和 toRefs 有什么区别
前端·vue.js·面试
kovli1 小时前
红宝书第十八讲:详解JavaScript的async/await与错误处理
前端·javascript
前端付豪1 小时前
🚀 React 应用国际化实战:深入掌握 react-i18next 的高级用法
前端·react.js·架构
代码小学僧1 小时前
使用 Cloudflare workers 做一个定时发送消息的飞书机器人
前端·云原生·serverless