一个实验性尝试,使用 webgl 开发的三维开放世界笔记系统《赛博城寨》

一个实验性尝试,使用 webgl 开发的三维开放世界笔记系统《赛博城寨》 一个实验性尝试使用 WebGL 开发的三维开放世界笔记系统《赛博城寨》在数字时代的浪潮中笔记工具从纸质笔记本进化到云笔记再到如今的三维空间笔记每一步都承载着人类对信息组织方式的探索。今天我想分享一个实验性的项目——《赛博城寨》它是一个使用 WebGL 技术构建的三维开放世界笔记系统。在这里笔记不再是一维的文本列表而是漂浮在虚拟城市中的“知识建筑”用户可以像探索城市一样穿梭于自己的思维之间。## 从零搭建WebGL 基础与三维世界的诞生WebGLWeb Graphics Library是浏览器中的 3D 图形 API它允许 JavaScript 直接调用 GPU 渲染三维场景。要理解《赛博城寨》的底层逻辑我们先从最基础的 WebGL 渲染管线开始。### 基础概念顶点、着色器与三角形三维世界由无数个三角形组成。每个三角形有三个顶点每个顶点包含位置、颜色等信息。WebGL 通过两个关键程序——顶点着色器和片元着色器——将数据处理成屏幕上的像素。下面是一个最简单的 WebGL 示例绘制一个彩色三角形javascript// 步骤1获取 WebGL 上下文const canvas document.getElementById(myCanvas);const gl canvas.getContext(webgl);// 步骤2定义三角形的三个顶点x, y, z 坐标const vertices [ -0.5, -0.5, 0.0, // 左下 0.5, -0.5, 0.0, // 右下 0.0, 0.5, 0.0 // 顶部];// 步骤3创建缓冲区存储顶点数据const vertexBuffer gl.createBuffer();gl.bindBuffer(gl.ARRAY_BUFFER, vertexBuffer);gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(vertices), gl.STATIC_DRAW);// 步骤4编写顶点着色器处理顶点位置const vertexShaderSource attribute vec3 aPosition; void main() { gl_Position vec4(aPosition, 1.0); };// 步骤5编写片元着色器决定颜色const fragmentShaderSource void main() { gl_FragColor vec4(0.0, 0.8, 0.6, 1.0); // 青色 };// 步骤6编译并链接着色器function createShader(gl, source, type) { const shader gl.createShader(type); gl.shaderSource(shader, source); gl.compileShader(shader); return shader;}const vertexShader createShader(gl, vertexShaderSource, gl.VERTEX_SHADER);const fragmentShader createShader(gl, fragmentShaderSource, gl.FRAGMENT_SHADER);const program gl.createProgram();gl.attachShader(program, vertexShader);gl.attachShader(program, fragmentShader);gl.linkProgram(program);gl.useProgram(program);// 步骤7绑定顶点属性const positionAttrib gl.getAttribLocation(program, aPosition);gl.vertexAttribPointer(positionAttrib, 3, gl.FLOAT, false, 0, 0);gl.enableVertexAttribArray(positionAttrib);// 步骤8绘制三角形gl.clearColor(0.1, 0.1, 0.2, 1.0); // 深蓝背景gl.clear(gl.COLOR_BUFFER_BIT);gl.drawArrays(gl.TRIANGLES, 0, 3);这段代码展示了 WebGL 的核心流程定义几何数据 → 编写着色器 → 编译链接 → 绘制。在《赛博城寨》中每个笔记卡片都被视为一个独立的“建筑”其六面体结构就是由 12 个三角形每个面 2 个构成的。## 进阶探索从静态三角形到动态开放世界掌握了基础之后我们进入《赛博城寨》的核心如何让笔记在三维空间中交互这里需要引入矩阵变换和用户输入处理。### 三维变换让笔记“漂浮”起来每个笔记卡片在场景中有一个位置、旋转和缩放。这些通过模型矩阵Model Matrix实现。同时我们需要相机矩阵View Matrix和投影矩阵Projection Matrix来实现第一人称视角。下面是一个更完整的交互示例用户可以通过鼠标拖拽旋转视角点击笔记卡片查看内容javascript// 假设已加载 Three.js 库简化 WebGL 开发import * as THREE from three;// 初始化场景、相机、渲染器const scene new THREE.Scene();scene.background new THREE.Color(0x0a0a23); // 赛博朋克风格深蓝const camera new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000);camera.position.set(10, 5, 15);camera.lookAt(0, 0, 0);const renderer new THREE.WebGLRenderer({ antialias: true });renderer.setSize(window.innerWidth, window.innerHeight);document.body.appendChild(renderer.domElement);// 创建笔记卡片一个带文字的立方体function createNoteCard(content, position, color 0x00ffcc) { // 几何体立方体 const geometry new THREE.BoxGeometry(2, 1.5, 0.3); const material new THREE.MeshPhongMaterial({ color: color, emissive: 0x004422, transparent: true, opacity: 0.9 }); const cube new THREE.Mesh(geometry, material); cube.position.set(position.x, position.y, position.z); // 添加文字标签使用 Canvas 纹理 const canvas document.createElement(canvas); canvas.width 256; canvas.height 128; const ctx canvas.getContext(2d); ctx.fillStyle rgba(0,0,0,0.8); ctx.fillRect(0, 0, 256, 128); ctx.fillStyle #00ffcc; ctx.font 20px monospace; ctx.fillText(content.substring(0, 12) ..., 10, 60); const texture new THREE.CanvasTexture(canvas); const textMaterial new THREE.SpriteMaterial({ map: texture }); const sprite new THREE.Sprite(textMaterial); sprite.position.set(0, 0, 0.2); // 在立方体前面 cube.add(sprite); return cube;}// 添加几个笔记卡片到场景const notes [];notes.push(createNoteCard(WebGL 基础顶点与着色器, { x: 2, y: 0, z: -3 }, 0xff6600));notes.push(createNoteCard(Three.js 高级粒子系统, { x: -2, y: 1, z: 1 }, 0x00ccff));notes.push(createNoteCard(赛博城寨架构设计, { x: 0, y: -1, z: 4 }, 0xff00aa));notes.forEach(note scene.add(note));// 添加灯光const ambientLight new THREE.AmbientLight(0x404040);scene.add(ambientLight);const dirLight new THREE.DirectionalLight(0xffffff, 1);dirLight.position.set(5, 10, 7);scene.add(dirLight);// 鼠标交互点击检测const raycaster new THREE.Raycaster();const mouse new THREE.Vector2();document.addEventListener(click, (event) { // 将鼠标坐标转换为归一化设备坐标 mouse.x (event.clientX / window.innerWidth) * 2 - 1; mouse.y -(event.clientY / window.innerHeight) * 2 1; raycaster.setFromCamera(mouse, camera); const intersects raycaster.intersectObjects(notes); if (intersects.length 0) { const clickedNote intersects[0].object; // 打开笔记详情此处简化为控制台输出 console.log(打开笔记, clickedNote.children[0].material.map.image.toDataURL()); // 实际项目中可弹出模态框显示完整内容 }});// 动画循环function animate() { requestAnimationFrame(animate); // 让笔记卡片缓慢旋转增加沉浸感 notes.forEach((note, index) { note.rotation.y 0.005 * (index 1); }); renderer.render(scene, camera);}animate();这段代码展示了《赛博城寨》的交互核心每个笔记卡片是一个独立的 3D 对象拥有自己的位置和外观通过射线检测实现点击交互通过动画循环保持场景动态。用户可以在城市中漫步点击漂浮的“知识建筑”查看内容。## 架构设计如何组织三维笔记系统《赛博城寨》的架构分为三层1.数据层使用 IndexedDB 存储笔记元数据标题、内容、位置坐标。每个笔记是一个 JSON 对象包含{id, title, content, x, y, z, color}。2.逻辑层管理笔记的增删改查以及三维世界的更新。当用户新建笔记时逻辑层会在场景中生成新的卡片并分配唯一的空间坐标。3.渲染层基于 Three.jsWebGL 封装库实现 3D 渲染包括光照、阴影、粒子特效模拟城市灯光。### 核心流程-加载从数据库读取所有笔记按坐标生成卡片用户看到的是“知识城市”。-导航用户通过键盘 WASD 移动鼠标拖动旋转视角实现第一人称探索。-交互点击卡片打开笔记详情长按可拖拽移动卡片位置重新组织“建筑布局”。-持久化任何位置变化都会自动保存到 IndexedDB。## 总结《赛博城寨》是一个实验性的尝试它将笔记系统从二维平面提升到三维空间利用 WebGL 技术创造了一个可交互的“知识城市”。从最基础的三角形绘制到动态的交互式世界我们看到了 WebGL 在数据可视化领域的潜力。虽然这个项目目前还处于原型阶段但它展示了几个有趣的可能性-空间记忆人类对空间的记忆优于对列表的记忆三维布局可能帮助用户更直观地组织知识。-沉浸式学习在虚拟城市中“漫步”于自己的笔记之间可能激发新的思维连接。-技术栈融合WebGL IndexedDB 现代前端框架可以构建轻量级的 3D 应用。如果你也想尝试可以从上面的代码片段开始逐步添加更多功能——比如笔记之间的“空中走廊”连接线、按主题划分的“街区”区域颜色编码、甚至多人协作的“城市共建”。这只是一个起点三维笔记的世界等待你的探索。