这个Three.js示例展示了如何使用**索引几何体(Indexed Geometry)**创建复杂的分形线条图案。通过递归算法生成科赫雪花(Koch Snowflake)曲线,并利用索引缓冲区优化顶点数据存储,实现高效的线条渲染。
核心技术包括:
DOCTYPE html>
<html lang="en">
<head>
<title>three.js webgl - buffergeometry - lines - indexedtitle>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0">
<link type="text/css" rel="stylesheet" href="main.css">
head>
<body>
<div id="container">div>
<div id="info"><a href="https://threejs.org" target="_blank" rel="noopener">three.jsa> webgl - buffergeometry - lines - indexeddiv>
<script type="importmap">
{
"imports": {
"three": "../build/three.module.js",
"three/addons/": "./jsm/"
}
}
script>
<script type="module">
import * as THREE from 'three';
import Stats from 'three/addons/libs/stats.module.js';
let container, stats;
let camera, scene, renderer;
let parent_node;
init();
function init() {
container = document.getElementById( 'container' );
// 初始化相机
camera = new THREE.PerspectiveCamera( 27, window.innerWidth / window.innerHeight, 1, 10000 );
camera.position.z = 9000;
// 初始化场景
scene = new THREE.Scene();
// 创建几何体和材质
const geometry = new THREE.BufferGeometry();
// 使用顶点颜色的线材质
const material = new THREE.LineBasicMaterial( { vertexColors: true } );
// 存储索引、位置和颜色数据
const indices = [];
const positions = [];
const colors = [];
let next_positions_index = 0;
// 分形迭代次数
const iteration_count = 4;
// 旋转角度(用于科赫曲线)
const rangle = 60 * Math.PI / 180.0;
// 添加顶点到几何体
function add_vertex( v ) {
positions.push( v.x, v.y, v.z );
// 为每个顶点设置随机颜色(偏蓝色调)
colors.push( Math.random() * 0.5 + 0.5, Math.random() * 0.5 + 0.5, 1 );
return next_positions_index ++;
}
// 科赫曲线递归生成函数
function snowflake_iteration( p0, p4, depth ) {
// 递归终止条件
if ( -- depth < 0 ) {
// p0已经添加,获取其索引
const i = next_positions_index - 1;
// 添加p4顶点
add_vertex( p4 );
// 添加一条从p0到p4的线
indices.push( i, i + 1 );
return;
}
// 计算线段的1/3和2/3点
const v = p4.clone().sub( p0 );
const v_tier = v.clone().multiplyScalar( 1 / 3 );
const p1 = p0.clone().add( v_tier );
// 计算向外凸起的点p2
const angle = Math.atan2( v.y, v.x ) + rangle;
const length = v_tier.length();
const p2 = p1.clone();
p2.x += Math.cos( angle ) * length;
p2.y += Math.sin( angle ) * length;
// 计算线段的2/3点
const p3 = p0.clone().add( v_tier ).add( v_tier );
// 递归处理四个子线段
snowflake_iteration( p0, p1, depth );
snowflake_iteration( p1, p2, depth );
snowflake_iteration( p2, p3, depth );
snowflake_iteration( p3, p4, depth );
}
// 生成雪花图案
function snowflake( points, loop, x_offset ) {
for ( let iteration = 0; iteration != iteration_count; iteration ++ ) {
// 添加起始顶点
add_vertex( points[ 0 ] );
// 处理所有线段
for ( let p_index = 0, p_count = points.length - 1; p_index != p_count; p_index ++ ) {
snowflake_iteration( points[ p_index ], points[ p_index + 1 ], iteration );
}
// 如果是闭合图形,连接最后一个点和第一个点
if ( loop ) snowflake_iteration( points[ points.length - 1 ], points[ 0 ], iteration );
// 为下一次迭代平移输入曲线
for ( let p_index = 0, p_count = points.length; p_index != p_count; p_index ++ ) {
points[ p_index ].x += x_offset;
}
}
}
// 生成不同形状的科赫曲线
let y = 0;
// 线段科赫曲线
snowflake(
[
new THREE.Vector3( 0, y, 0 ),
new THREE.Vector3( 500, y, 0 )
],
false, 600
);
y += 600;
// 三角形科赫雪花
snowflake(
[
new THREE.Vector3( 0, y, 0 ),
new THREE.Vector3( 250, y + 400, 0 ),
new THREE.Vector3( 500, y, 0 )
],
true, 600
);
y += 600;
// 矩形科赫雪花
snowflake(
[
new THREE.Vector3( 0, y, 0 ),
new THREE.Vector3( 500, y, 0 ),
new THREE.Vector3( 500, y + 500, 0 ),
new THREE.Vector3( 0, y + 500, 0 )
],
true, 600
);
y += 1000;
// 星形科赫曲线
snowflake(
[
new THREE.Vector3( 250, y, 0 ),
new THREE.Vector3( 500, y, 0 ),
new THREE.Vector3( 250, y, 0 ),
new THREE.Vector3( 250, y + 250, 0 ),
new THREE.Vector3( 250, y, 0 ),
new THREE.Vector3( 0, y, 0 ),
new THREE.Vector3( 250, y, 0 ),
new THREE.Vector3( 250, y - 250, 0 ),
new THREE.Vector3( 250, y, 0 )
],
false, 600
);
// 设置几何体的索引和属性
geometry.setIndex( indices );
geometry.setAttribute( 'position', new THREE.Float32BufferAttribute( positions, 3 ) );
geometry.setAttribute( 'color', new THREE.Float32BufferAttribute( colors, 3 ) );
geometry.computeBoundingSphere();
// 创建线段对象
const lineSegments = new THREE.LineSegments( geometry, material );
lineSegments.position.x -= 1200;
lineSegments.position.y -= 1200;
// 创建父对象
parent_node = new THREE.Object3D();
parent_node.add( lineSegments );
// 添加到场景
scene.add( parent_node );
// 初始化渲染器
renderer = new THREE.WebGLRenderer();
renderer.setPixelRatio( window.devicePixelRatio );
renderer.setSize( window.innerWidth, window.innerHeight );
renderer.setAnimationLoop( animate );
container.appendChild( renderer.domElement );
// 添加性能统计
stats = new Stats();
container.appendChild( stats.dom );
// 窗口大小变化事件监听
window.addEventListener( 'resize', onWindowResize );
}
// 窗口大小变化处理函数
function onWindowResize() {
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
renderer.setSize( window.innerWidth, window.innerHeight );
}
// 动画循环
function animate() {
const time = Date.now() * 0.001;
// 旋转整个场景
parent_node.rotation.z = time * 0.5;
// 渲染场景
renderer.render( scene, camera );
// 更新性能统计
stats.update();
}
script>
body>
html>
索引几何体(Indexed Geometry)是一种优化的顶点数据存储方式,它将共享的顶点只存储一次,通过索引数组来引用这些顶点。与非索引几何体相比,索引几何体有以下优势:
在Three.js中,我们通过geometry.setIndex()
方法来设置索引数据。
科赫雪花是一种经典的分形曲线,其构造方法如下:
本示例中的递归函数snowflake_iteration
实现了这一过程:
function snowflake_iteration( p0, p4, depth ) {
// 递归终止条件
if ( -- depth < 0 ) {
// 添加线段
return;
}
// 计算线段的1/3和2/3点
// 计算向外凸起的点p2
// 递归处理四个子线段
}
本示例使用了顶点颜色来为线条添加变化:
LineBasicMaterial
并启用vertexColors: true
// 设置材质
const material = new THREE.LineBasicMaterial( { vertexColors: true } );
// 添加顶点时设置颜色
function add_vertex( v ) {
positions.push( v.x, v.y, v.z );
colors.push( Math.random() * 0.5 + 0.5, Math.random() * 0.5 + 0.5, 1 );
return next_positions_index ++;
}
索引几何体特别适合以下场景:
性能优化建议:
索引几何体是Three.js中一项重要的优化技术,在处理复杂模型和大量线条时能带来显著的性能提升。