Three.js 球体线条教程

发布时间:2026/7/5 8:28:08
Three.js 球体线条教程 球体线条 ·Sphere Line· ▶ 在线运行案例案例合集三维可视化功能案例threehub.cn开源仓库github地址https://github.com/z2586300277/three-cesium-examples400个案例代码:网盘链接你将学到什么OrbitControls 相机轨道交互THREE.Points 粒子点渲染BufferGeometry 自定义顶点/索引数据requestAnimationFrame渲染循环与resize自适应效果说明本案例演示球体线条效果基于 WebGL 实现「球体线条」可视化效果附完整可运行源码核心用到 OrbitControls、THREE.Points、BufferGeometry。建议先打开文首在线案例查看动态画面再对照下方源码逐步理解。核心概念OrbitControls轨道旋转缩放开enableDamping时每帧需controls.update()。Points大量顶点用点精灵渲染InstancedMesh相同几何体批量绘制降低 draw call。实现步骤搭建 Scene / Camera / Renderer 与 OrbitControlsrAF 循环中 update 并 render代码要点import * as THREE from threeimport { OrbitControls } from three/examples/jsm/controls/OrbitControls.js import * as dat from dat.guiconst box document.getElementById(box)const scene new THREE.Scene()const camera new THREE.PerspectiveCamera(50, box.clientWidth / box.clientHeight, 0.1, 10000)camera.position.set(1000, 1000, 1000)const renderer new THREE.WebGLRenderer({ antialias: true, alpha: true, logarithmicDepthBuffer: true })renderer.setSize(box.clientWidth, box.clientHeight)box.appendChild(renderer.domElement)const controls new OrbitControls(camera, renderer.domElement)controls.enableDamping truelet group; let particlesData []; let positions, colors; let particles; let pointCloud; let particlePositions; let linesMesh;let maxParticleCount 1000; let particleCount 500; let r 800;let effectController { showDots: true, showLines: true, minDistance: 150, limitConnections: false, maxConnections: 20, particleCount: 500 }group new THREE.Group(); scene.add(group);let segments maxParticleCount * maxParticleCount;positions new Float32Array(segments * 3); colors new Float32Array(segments * 3);let pMaterial new THREE.PointsMaterial({ color: 0xFFFFFF, size: 3, blending: THREE.AdditiveBlending, transparent: true, sizeAttenuation: false });particles new THREE.BufferGeometry(); particlePositions new Float32Array(maxParticleCount * 3);function getPos(radius, a, b) { const x radiusMath.sin(a)Math.cos(b); const y radiusMath.sin(a)Math.sin(b); const z radius * Math.cos(a); return { x, y, z }; }for (let i 0; i maxParticleCount; i) {const p getPos(r, Math.PI2Math.random(), Math.PI2Math.random())let x p.x; let y p.y; let z p.z;particlePositions[i * 3] x; particlePositions[i * 3 1] y; particlePositions[i * 3 2] z;particlesData.push({ velocity: new THREE.Vector3(- 1 Math.random()2, - 1 Math.random()2, - 1 Math.random() * 2), numConnections: 0 });}particles.setDrawRange(0, particleCount); particles.setAttribute(position, new THREE.BufferAttribute(particlePositions, 3).setUsage(THREE.DynamicDrawUsage));pointCloud new THREE.Points(particles, pMaterial); group.add(pointCloud);let geometry new THREE.BufferGeometry();geometry.setAttribute(position, new THREE.BufferAttribute(positions, 3).setUsage(THREE.DynamicDrawUsage)); geometry.setAttribute(color, new THREE.BufferAttribute(colors, 3).setUsage(THREE.DynamicDrawUsage));geometry.computeBoundingSphere();geometry.setDrawRange(0, 0);let material new THREE.LineBasicMaterial({ vertexColors: true, blending: THREE.AdditiveBlending, transparent: true });linesMesh new THREE.LineSegments(geometry, material); group.add(linesMesh); animate()function animate() {let vertexpos 0; let colorpos 0; let numConnected 0; let O new THREE.Vector3(0, 0, 0)for (let i 0; i particleCount; i) particlesData[i].numConnections 0;for (let i 0; i particleCount; i) {// get the particle let particleData particlesData[i];particlePositions[i * 3] particleData.velocity.x; particlePositions[i * 3 1] particleData.velocity.y; particlePositions[i * 3 2] particleData.velocity.z;const v new THREE.Vector3(particlePositions[i3], particlePositions[i3 1], particlePositions[i * 3 2]) if (v.distanceTo(O) r 20) { particleData.velocity.x - particleData.velocity.x; particleData.velocity.y - particleData.velocity.y; particleData.velocity.z - particleData.velocity.z; } if (v.distanceTo(O) r) { particleData.velocity.x particleData.velocity.x; particleData.velocity.y particleData.velocity.y; particleData.velocity.z particleData.velocity.z; }if (effectController.limitConnections particleData.numConnections effectController.maxConnections) continue;for (let j i 1; j particleCount; j) {let particleDataB particlesData[j]; if (effectController.limitConnections particleDataB.numConnections effectController.maxConnections) continue;let dx particlePositions[i3] - particlePositions[j3]; let dy particlePositions[i3 1] - particlePositions[j3 1]; let dz particlePositions[i3 2] - particlePositions[j3 2]; let dist Math.sqrt(dxdx dydy dz * dz);if (dist effectController.minDistance) {particleData.numConnections; particleDataB.numConnections;let alpha 1.0 - dist / effectController.minDistance;positions[vertexpos] particlePositions[i * 3]; positions[vertexpos] particlePositions[i * 3 1]; positions[vertexpos] particlePositions[i * 3 2];positions[vertexpos] particlePositions[j * 3]; positions[vertexpos] particlePositions[j * 3 1]; positions[vertexpos] particlePositions[j * 3 2];colors[colorpos] alpha; colors[colorpos] alpha; colors[colorpos] alpha;colors[colorpos] alpha; colors[colorpos] alpha; colors[colorpos] alpha;numConnected;}}}linesMesh.geometry.setDrawRange(0, numConnected * 2); linesMesh.geometry.attributes.position.needsUpdate true; linesMesh.geometry.attributes.color.needsUpdate true;pointCloud.geometry.attributes.position.needsUpdate true;requestAnimationFrame(animate);group.rotation.y 0.001 renderer.render(scene, camera); controls.update() }let gui new dat.GUI()gui.add(effectController, showDots).onChange(function (value) {pointCloud.visible value;}); gui.add(effectController, showLines).onChange(function (value) {linesMesh.visible value;}); gui.add(effectController, minDistance, 10, 300); gui.add(effectController, limitConnections); gui.add(effectController, maxConnections, 0, 30, 1); gui.add(effectController, particleCount, 0, maxParticleCount, 1).onChange(function (value) {particleCount parseInt(value); particles.setDrawRange(0, particleCount);});完整源码GitHub小结本文提供球体线条完整 Three.js 源码与在线 Demo建议先运行案例再改 uniform/参数做二次实验更多 Three.js 实战案例见 three-cesium-examples 合集 与 GitHub 开源仓库