3D Graph 功能亮点
告别传统的“列表”或“网格”展示,用“空间距离”来直观呈现文章间的相关性,打造立体化的导航体验。
- • 自研 3D 物理引擎,实现 GPU 友好的粒子排斥与吸引计算
- • 基于 BufferGeometry 的动态更新,支持 100+ 节点与连线的实时同步
- • 利用 MeshPhysicalMaterial 打造极具未来感的玻璃质感与折射效果
- • 借助 Astro 5 Server Islands 实现重负载 WebGL 场景的局部注水(Hydration)
鼠标拖拽旋转 • 滚轮缩放查看
为什么“阅读博客”总是一层不变的自上而下的滚动呢?
人类的大脑并不是以列表( bullet points)的形式存储信息的,而是将其视为一个互相交织的“网络”。 既然如此,博客的交互界面是否也应该向大脑的生理结构靠拢?基于这一假设,我在个人开发的博客项目“HonoGear”中,实验性地实装了这个 “3D Graph” 系统。
技术栈:现代 WebGL 的协同进化
这个 3D 空间是基于以下前沿技术栈构建的:
- 框架 :Astro 5 (Server Islands)
- 库 :React 19 + React Three Fiber (R3F)
- 物理模拟 : 完全自研的物理引擎 (实现自定义排斥力与引力)
- 视觉增强 :@react-three/drei, @react-three/postprocessing (光晕、噪点)
技术深挖 1:为什么要“手搓” 3D 物理引擎?
起初我也考虑过使用 d3-force-3d,但为了让 React 的状态更新与 WebGL 的渲染循环在更高维度同步并最小化开销,我最终选择直接在 useFrame 内部实现坐标计算逻辑。
// useFrame 内部简化的斥力计算逻辑
useFrame((state) => {
const REPULSION = 8.5; // 斥力系数
const DAMPING = 0.95; // 阻尼系数
for (let i = 0; i < nodes.length; i++) {
for (let j = i + 1; j < nodes.length; j++) {
const dist = nodes[i].pos.distanceTo(nodes[j].pos);
if (dist > 0.1 && dist < 6) {
// 模拟库仑定律(平方反比定律)施加斥力
const force = (REPULSION / (dist * dist)) * 0.01;
const dir = nodes[i].pos
.clone()
.sub(nodes[j].pos)
.normalize()
.multiplyScalar(force);
nodes[i].velocity.add(dir);
nodes[j].velocity.sub(dir);
}
}
}
// 应用惯性与阻尼
nodes.forEach((n) => {
n.pos.add(n.velocity.multiplyScalar(DAMPING));
});
});
这种实现方式绕过了外部库的桥接开销,直接操控 Three.js 的 Vector3 对象,使得 100 多个节点在网页端也能以 60FPS 的帧率流畅飘动。
技术深挖 2:连接线的动态渲染
为了绘制连接各节点的“星座连线”,我采用了每帧动态更新 bufferGeometry 位置信息的技术。
function Connections({ links }: any) {
const geomRef = useRef<THREE.BufferGeometry>(null);
useFrame(() => {
if (!geomRef.current) return;
const positions = geomRef.current.attributes.position.array as Float32Array;
let i = 0;
links.forEach((link) => {
// 直接将 source 和 target 的最新坐标写入 BufferAttribute
positions[i++] = link.source.pos.x;
positions[i++] = link.source.pos.y;
positions[i++] = link.source.pos.z;
positions[i++] = link.target.pos.x;
positions[i++] = link.target.pos.y;
positions[i++] = link.target.pos.z;
});
geomRef.current.attributes.position.needsUpdate = true;
});
return (
<lineSegments>
<bufferGeometry ref={geomRef}>
<bufferAttribute attach="attributes-position" count={links.length * 2} itemSize={3} ... />
</bufferGeometry>
<lineBasicMaterial transparent opacity={0.08} />
</lineSegments>
);
}
通过使用 lineSegments 原生基元而非逐个创建 Mesh,极大地减轻了图像处理器的负担。
技术深挖 3:玻璃质感与沉浸感
为了营造“赛博星空”的氛围,节点采用了基于 MeshPhysicalMaterial 的玻璃立方体。
// 物理材质设置
child.material = new THREE.MeshPhysicalMaterial({
color: "#ffffff",
roughness: "0.1",
metalness: "0.1",
transmission: "0.9", // 极高的透光度
thickness: "1.5", // 模拟真实的折射厚度
ior: "1.5", // 折射率(接近真实玻璃)
transparent: true,
});
背景中微弱的 Sparkles(火花粒子)穿过玻璃节点产生折射的效果,为读者提供了一种平面网页无法比拟的视觉层次感。
Deep Dive: 三维空间中的斥力与引力计算公式
为了保持优美的网络状形态,我们在节点之间应用了类似于“库仑定律”的物理法则。
- 斥力 (Repulsion): 与距离平方成反比的排斥力。防止节点重叠。
- 引力 (Attraction): 仅在有连接的节点之间起作用的、类似于弹簧的拉力。
// 基于平方反比定律的计算
const force = strength / (distance * distance);
通过逐帧微调这种平衡,混乱的数据群会随着时间推移收敛成秩序井然的“银河”状形态。
未来展望:通往空间计算的垫脚石
在 Apple Vision Pro 等空间计算设备逐渐普及的 2026 年,网站也需要从“平面”向“空间”进化。后续,我计划为这个 3D Graph 增加 WebXR 支持,让读者能够在 VR/AR 环境中“亲手抓取”想要阅读的文章,彻底模糊数字与现实的边界。
目前该功能以 Beta 版形式在 /3d-graph 开启。您可以点击这里直接体验 → 进入 3D 宇宙
建议在电脑大屏幕上开启,体验最佳。






⚠️ コメントのルール
※違反コメントはAIおよび管理者により予告なく削除されます
まだコメントがありません。最初のコメントを投稿しましょう!