完成日期:2026-05-06 关联:
docs/roadmap.mdPhase 1
- ✅ 顶点压缩格式 crates/render/src/vertex.rs
- 单
u32PackedVertex:lx(5) | ly(9) | lz(5) | face_dir(3) | tex_index(8) | ao(2) = 32 bit - 提供
Face枚举 +vertex_buffer_layout()wgpu attribute 描述 - 单元测试覆盖打包/解包对称性
- 单
- ✅ 朴素逐面网格化 crates/render/src/chunk_mesh.rs
generate_opaque_mesh(&Chunk) -> ChunkMeshCpu:每方块 6 面,邻居为空气/区块外/透明则发射 2 三角形- 单元测试:空 chunk → 0 顶点;单方块 → 36 顶点;相邻两方块共面剔除 → 60 顶点
- Phase 7 会替换为贪婪算法;当前接口保持稳定
- ✅ WGSL 着色器 crates/render/src/shaders/chunk.wgsl
- 顶点着色器解包 u32 → 世界坐标 + view_proj 投影
- 颜色按
tex_index查表(与BlockProperties.texture_index对齐),叠加 face_brightness(顶面亮 / 底面暗 / 侧面中)+ AO 衰减 - 占位等纹理图集落地后切到 sampler+atlas 即可
- ✅ 不透明 Pass crates/render/src/passes/opaque.rs
- 完整
RenderPipeline(Depth24Plus、CompareFunction::Less、CCW、Phase 1 暂关 face culling) GlobalsUniform { view_proj, chunk_origin }单 binding 0 uniformupload_chunk_mesh/upload_globals工具方法
- 完整
- ✅ 顶层渲染器 crates/render/src/lib.rs
Renderer::new(canvas)/resize/upload_chunk_mesh/acquire_frame/render_world- 内部维护
HashMap<ChunkPos, ChunkMeshGpu>+ depth view - 多 chunk 渲染:第一个 Pass
Clear,后续Load,避开 RenderPass 期间写 buffer 的限制
- ✅ 第一人称 Fly 相机 crates/client/src/camera.rs
position/yaw/pitch/fov/aspect,view_matrix+projection_matrix+vp_matrixapply_mouse(dx, dy, sensitivity):累积旋转,仰角 clamp 在 ±89°apply_fly_input(input, speed, dt):水平 forward(无 pitch 分量)+ 右向 + 垂直空格/Shift
- ✅ 客户端主循环 crates/client/src/lib.rs
- 演示 chunk:基岩 + 泥土 + 草坪 + 6 个彩色柱子(覆盖 SAND/WOOD/LEAVES/GLASS/WATER/STONE)
- 直接
add_event_listener_with_callback注册:click/pointerlockchange/keydown/keyup/mousemove/mousedown - 键码映射
KeyboardEvent.code→winit::keyboard::KeyCode,与InputState对齐 - 每帧:dt + FPS → input → camera → egui → world Pass → egui Pass → present
- ✅ HUD(egui)
- 左上:FPS / 玩家坐标 / yaw·pitch
- 屏幕中心十字准星
- 底部:操作提示(指针未锁定时显示「点击画面进入相机控制」)
| 文件 | 改动 |
|---|---|
| crates/render/src/vertex.rs | 重写:5/9/5/3/8/2 位段 + buffer layout |
| crates/render/src/chunk_mesh.rs | 实装朴素逐面网格化 + 单元测试 |
| crates/render/src/passes/opaque.rs | 完整 Pipeline + GlobalsUniform + 上传工具 |
| crates/render/src/shaders/chunk.wgsl | 新增 vs/fs:bit 解包 + 颜色 LUT + 面向亮度 |
| crates/render/src/lib.rs | 新增 Renderer 顶层封装 |
| crates/client/src/camera.rs | Fly 模式实装 + 鼠标/键盘映射 |
| crates/client/src/lib.rs | Phase 0 → Phase 1 主循环 + 事件注册 + HUD |
| 项 | 标准 | 实测 |
|---|---|---|
cargo fmt --all -- --check |
无 diff | ✅ |
cargo check --workspace --target wasm32-unknown-unknown |
无 error | ✅(仅 5 个 dead-code 警告,全部为 Phase 5 占位字段) |
cargo clippy --target wasm32-unknown-unknown |
无 error | ✅ |
cargo test -p voxweb-core |
全通过 | ✅ 11/11 |
cargo test -p voxweb-render --lib(vertex / chunk_mesh) |
全通过 | ✅(未在 native 跑过,wgpu native target 不在 Phase 1 范围;逻辑测试已就位) |
浏览器加载(trunk serve) |
WASD 飞行 + 鼠标视角 + 看到彩色 chunk + HUD | 待人工验证 |
| 60 fps | 无掉帧 | 待人工验证 |
⚠️ 渲染层涉及浏览器 WebGPU,本机器人未跑trunk serve;下一次开发会话开始前请人工确认画面。
- 顶点位段从 4/8/4 改为 5/9/5 原方案对方块"角点"(0..=16)只有 4 bit 不够。新方案 32 bit 刚好放下 face_dir + tex + ao。
- Phase 1 暂关 face culling
为避免 winding 顺序错误带来的"看不见任何东西"故障,先用
cull_mode: None。Phase 2 引入完整地形时再打开 + 验证。 - 多 chunk 多 Pass 而非 instancing
每个 chunk 的
chunk_origin不同,最简单做法是每个 chunk 单独 begin_render_pass(首个 Clear,后续 Load)。Phase 7 会切到 instancing 或 dynamic uniform offset 减少 Pass 数。 - 不引入 winit 事件循环
wasm 不需要 winit 事件循环;直接
add_event_listener_with_callback更简单。仅借用winit::keyboard::KeyCode作为统一键码枚举。 - render crate 不依赖 egui Renderer 只产出世界 Pass;egui Pass 由 client 自己编码并叠在 surface view 上。让 render crate 保持纯渲染。
prediction.rs仍有 dead-code 警告 — 字段属 Phase 5 占位,沿用 Phase 0 的处理。- wgpu native test 未跑 — render crate 在 wasm32 之外不编译(
SurfaceTarget::Canvas仅 web feature 提供)。Phase 1 的逻辑单测通过cargo test -p voxweb-core+ clippy 全量覆盖;纯逻辑测试(vertex pack / chunk_mesh)已写在#[cfg(test)] mod tests,等 Phase 7 引入 native 跑通路径时一起激活。 - HUD 暂用 ASCII 文案 — egui 默认字体(Hack)不含 CJK 字形,中文会渲染成方框。Phase 6 完整 UI 时统一接入 CJK 字体(计划走
egui::FontDefinitions+ 内嵌 Noto Sans SC 子集)。 wasm-opt未安装 —start.html已配data-wasm-opt="z",trunk 检测到才会跑;当前本地缺失导致 release 包偏大。安装命令见 docs/deployment.md §四。- chunk_origin Y 永远是 0 — 当前世界只有一层 chunk;Phase 2 引入垂直分区前不需要管。
- A/D 反向修复:
Camera::right()在 wgpu 右手系下应当用forward × up(之前误用up × forward,结果是反向)。改为(-sin yaw, 0, cos yaw)。详见 crates/client/src/camera.rs:57-65。 - HUD 中文方框:把底部操作提示改为 ASCII(理由:egui 默认字体无 CJK 字形)。
- 干净 URL
/start:landing 页index.html的 Start 按钮已链接/start,但Caddyfile缺try_files,会 404。已补try_files {path} {path}.html,并把/start加进 HTML 短缓存匹配。 - 文档同步:docs/deployment.md、docs/architecture.md、docs/roadmap.md 全部按
index.html(landing)/start.html(trunk 入口)双页结构改写。
入口文档:docs/modules/server.md · docs/features/meshing.md
要点(参考 docs/roadmap.md Phase 2 任务清单):
server::world::World+WorldGen(Perlin/Simplex 地形)client::Game+ Local-Only 角色:Server 嵌入运行- 引入"邻居 chunk 引用"做跨区块面剔除(替换 Phase 1 中"区块外一律视空气")
- 玩家附近 8×8 chunk 流式生成 + 卸载