基于Three.js的网页版Minecraft开发:从3D渲染到多人联机全流程
最近在开发网页游戏项目时遇到了一个有趣的需求如何在浏览器中实现类似Minecraft的3D沙盒游戏体验。传统的Minecraft需要下载客户端但通过现代Web技术我们完全可以在网页端打造一个功能丰富的网页版MC。本文将分享一套完整的网页版Minecraft开发方案涵盖从基础3D渲染到复杂游戏逻辑的全流程实现。1. 网页版Minecraft开发概述1.1 什么是网页版Minecraft网页版Minecraft是指基于Web技术HTML5、JavaScript、WebGL在浏览器中运行的沙盒游戏。与传统客户端版本相比网页版具有即开即玩、无需安装、跨平台等优势。通过Three.js等3D图形库我们可以实现接近原版的视觉体验和游戏功能。1.2 技术选型与架构设计核心技术支持包括WebGL用于3D渲染、Three.js作为图形引擎、Web Audio API处理音效、WebSocket实现多人联机。整个系统架构分为渲染层、游戏逻辑层、网络层和资源管理层各层之间通过事件驱动的方式进行通信。1.3 开发环境准备推荐使用Visual Studio Code作为开发环境配合Live Server插件进行本地调试。需要安装Node.js环境用于包管理和构建工具主要依赖包括Three.js、Cannon.js物理引擎、Howler.js音频处理等。2. 基础3D场景搭建2.1 初始化Three.js场景首先创建基本的3D场景结构包括场景对象、相机、渲染器和灯光系统// 场景初始化 const scene new THREE.Scene(); scene.background new THREE.Color(0x87CEEB); // 天空蓝 // 相机设置 const camera new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000); camera.position.set(0, 10, 10); // 渲染器配置 const renderer new THREE.WebGLRenderer({ antialias: true }); renderer.setSize(window.innerWidth, window.innerHeight); document.body.appendChild(renderer.domElement); // 灯光系统 const ambientLight new THREE.AmbientLight(0xffffff, 0.6); scene.add(ambientLight); const directionalLight new THREE.DirectionalLight(0xffffff, 0.8); directionalLight.position.set(50, 50, 50); scene.add(directionalLight);2.2 创建立方体网格系统Minecraft的核心视觉元素是体素立方体我们需要创建可复用的方块生成系统class VoxelGeometry { constructor() { this.geometry new THREE.BoxGeometry(1, 1, 1); this.materials this.createMaterials(); } createMaterials() { const textures { grass: new THREE.MeshLambertMaterial({ color: 0x00ff00 }), dirt: new THREE.MeshLambertMaterial({ color: 0x8B4513 }), stone: new THREE.MeshLambertMaterial({ color: 0x808080 }) }; return textures; } createVoxel(type, position) { const material this.materials[type]; const voxel new THREE.Mesh(this.geometry, material); voxel.position.set(position.x, position.y, position.z); return voxel; } }2.3 实现无限地形生成使用噪声算法生成自然的地形高度图创建无限延伸的游戏世界class TerrainGenerator { constructor() { this.noise new SimplexNoise(); this.chunkSize 16; this.terrainHeight 20; } generateChunk(chunkX, chunkZ) { const chunk new THREE.Group(); for (let x 0; x this.chunkSize; x) { for (let z 0; z this.chunkSize; z) { const worldX chunkX * this.chunkSize x; const worldZ chunkZ * this.chunkSize z; const height this.getHeight(worldX, worldZ); this.createColumn(chunk, worldX, worldZ, height); } } return chunk; } getHeight(x, z) { const scale 0.01; const noiseValue this.noise.noise2D(x * scale, z * scale); return Math.floor(noiseValue * this.terrainHeight) this.terrainHeight; } }3. 玩家控制系统实现3.1 第一人称摄像机控制实现流畅的鼠标和键盘控制让玩家可以自由移动和视角旋转class PlayerController { constructor(camera, domElement) { this.camera camera; this.domElement domElement; this.moveSpeed 0.1; this.velocity new THREE.Vector3(); this.direction new THREE.Vector3(); this.initControls(); this.setupEventListeners(); } initControls() { this.moveState { forward: false, backward: false, left: false, right: false, jump: false }; this.pointerLocked false; this.pitch 0; this.yaw 0; } setupEventListeners() { document.addEventListener(keydown, this.onKeyDown.bind(this)); document.addEventListener(keyup, this.onKeyUp.bind(this)); document.addEventListener(mousemove, this.onMouseMove.bind(this)); this.domElement.addEventListener(click, () { this.domElement.requestPointerLock(); }); document.addEventListener(pointerlockchange, () { this.pointerLocked document.pointerLockElement this.domElement; }); } update() { this.direction.z Number(this.moveState.forward) - Number(this.moveState.backward); this.direction.x Number(this.moveState.right) - Number(this.moveState.left); this.direction.normalize(); if (this.moveState.forward || this.moveState.backward) { this.velocity.z - this.direction.z * this.moveSpeed; } if (this.moveState.left || this.moveState.right) { this.velocity.x - this.direction.x * this.moveSpeed; } this.camera.position.add(this.velocity); this.velocity.multiplyScalar(0.9); // 摩擦力 } }3.2 物理碰撞检测实现简单的AABB轴对齐边界框碰撞检测系统class PhysicsEngine { constructor() { this.voxels new Map(); } addVoxel(position) { const key ${position.x},${position.y},${position.z}; this.voxels.set(key, position); } checkCollision(position, size 1) { const playerBox new THREE.Box3( new THREE.Vector3(position.x - size/2, position.y - size/2, position.z - size/2), new THREE.Vector3(position.x size/2, position.y size/2, position.z size/2) ); for (const [key, voxelPos] of this.voxels) { const voxelBox new THREE.Box3( new THREE.Vector3(voxelPos.x - 0.5, voxelPos.y - 0.5, voxelPos.z - 0.5), new THREE.Vector3(voxelPos.x 0.5, voxelPos.y 0.5, voxelPos.z 0.5) ); if (playerBox.intersectsBox(voxelBox)) { return true; } } return false; } }4. 方块交互系统4.1 方块放置与破坏机制实现基于射线检测的方块交互系统class BlockInteraction { constructor(camera, scene, physicsEngine) { this.camera camera; this.scene scene; this.physicsEngine physicsEngine; this.raycaster new THREE.Raycaster(); this.selectedBlock null; this.reachDistance 5; } update() { this.raycaster.setFromCamera(new THREE.Vector2(0, 0), this.camera); const intersects this.raycaster.intersectObjects(this.scene.children, true); if (intersects.length 0) { const intersect intersects[0]; if (intersect.distance this.reachDistance) { this.selectedBlock intersect.object; this.showSelectionIndicator(intersect.point, intersect.face); } else { this.selectedBlock null; this.hideSelectionIndicator(); } } } placeBlock(blockType) { if (!this.selectedBlock) return; const face this.getTargetFace(); const newPosition this.selectedBlock.position.clone().add(face); if (!this.physicsEngine.checkCollision(newPosition)) { const voxelGeometry new VoxelGeometry(); const newBlock voxelGeometry.createVoxel(blockType, newPosition); this.scene.add(newBlock); this.physicsEngine.addVoxel(newPosition); } } breakBlock() { if (!this.selectedBlock) return; this.scene.remove(this.selectedBlock); const key ${this.selectedBlock.position.x},${this.selectedBlock.position.y},${this.selectedBlock.position.z}; this.physicsEngine.voxels.delete(key); } }4.2 方块类型与材质系统创建丰富的方块类型和对应的材质纹理class BlockRegistry { constructor() { this.blocks new Map(); this.loadTextures(); } async loadTextures() { const textureLoader new THREE.TextureLoader(); this.blocks.set(grass, { material: new THREE.MeshLambertMaterial({ map: await textureLoader.load(textures/grass.png) }), hardness: 1, transparent: false }); this.blocks.set(stone, { material: new THREE.MeshLambertMaterial({ map: await textureLoader.load(textures/stone.png) }), hardness: 3, transparent: false }); this.blocks.set(water, { material: new THREE.MeshLambertMaterial({ color: 0x0000ff, transparent: true, opacity: 0.7 }), hardness: 0, transparent: true }); } getBlock(type) { return this.blocks.get(type); } }5. 游戏世界管理5.1 区块加载与卸载实现动态区块管理系统优化大型世界的性能class WorldManager { constructor() { this.loadedChunks new Map(); this.renderDistance 3; this.chunkSize 16; } update(playerPosition) { const playerChunkX Math.floor(playerPosition.x / this.chunkSize); const playerChunkZ Math.floor(playerPosition.z / this.chunkSize); // 卸载超出渲染距离的区块 this.unloadDistantChunks(playerChunkX, playerChunkZ); // 加载新区块 for (let x -this.renderDistance; x this.renderDistance; x) { for (let z -this.renderDistance; z this.renderDistance; z) { const chunkX playerChunkX x; const chunkZ playerChunkZ z; this.loadChunk(chunkX, chunkZ); } } } loadChunk(x, z) { const chunkKey ${x},${z}; if (!this.loadedChunks.has(chunkKey)) { const terrainGenerator new TerrainGenerator(); const chunk terrainGenerator.generateChunk(x, z); this.loadedChunks.set(chunkKey, chunk); scene.add(chunk); } } unloadDistantChunks(centerX, centerZ) { for (const [key, chunk] of this.loadedChunks) { const [x, z] key.split(,).map(Number); const distance Math.max(Math.abs(x - centerX), Math.abs(z - centerZ)); if (distance this.renderDistance) { scene.remove(chunk); this.loadedChunks.delete(key); } } } }5.2 日夜循环系统实现动态光照和天空盒变化创造更沉浸的游戏体验class DayNightCycle { constructor(scene) { this.scene scene; this.timeOfDay 0; // 0-1, 0午夜, 0.5正午 this.dayLength 120000; // 2分钟一个完整循环 this.setupSkybox(); this.setupLighting(); } setupSkybox() { const skyboxGeometry new THREE.BoxGeometry(1000, 1000, 1000); const skyboxMaterials this.createSkyboxMaterials(); this.skybox new THREE.Mesh(skyboxGeometry, skyboxMaterials); this.skybox.material.side THREE.BackSide; this.scene.add(this.skybox); } update(deltaTime) { this.timeOfDay (this.timeOfDay deltaTime / this.dayLength) % 1; // 更新环境光 const daylight Math.abs(Math.sin(this.timeOfDay * Math.PI)); this.ambientLight.intensity 0.3 daylight * 0.5; // 更新太阳位置 const sunAngle this.timeOfDay * Math.PI * 2; this.sunLight.position.set( Math.cos(sunAngle) * 100, Math.sin(sunAngle) * 100, 50 ); // 更新天空颜色 this.updateSkyColor(daylight); } }6. 性能优化策略6.1 视锥体剔除与LOD实现多层次细节和视锥体剔除提升渲染性能class RenderingOptimizer { constructor(camera, scene) { this.camera camera; this.scene scene; this.frustum new THREE.Frustum(); this.cameraMatrix new THREE.Matrix4(); } update() { this.cameraMatrix.multiplyMatrices( this.camera.projectionMatrix, this.camera.matrixWorldInverse ); this.frustum.setFromProjectionMatrix(this.cameraMatrix); this.cullDistantObjects(); this.applyLOD(); } cullDistantObjects() { const cameraPosition this.camera.position; this.scene.children.forEach(object { if (object.isGroup) { // 区块组 const distance object.position.distanceTo(cameraPosition); object.visible distance 100; // 100单位外的区块不可见 } }); } applyLOD() { this.scene.children.forEach(object { if (object.isMesh) { const distance object.position.distanceTo(this.camera.position); if (distance 50) { object.geometry this.getLowLODGeometry(); } else { object.geometry this.getHighLODGeometry(); } } }); } }6.2 内存管理与垃圾回收优化JavaScript内存使用避免内存泄漏class MemoryManager { constructor() { this.textureCache new Map(); this.geometryCache new Map(); this.materialCache new Map(); } getTexture(url) { if (!this.textureCache.has(url)) { const texture new THREE.TextureLoader().load(url); this.textureCache.set(url, texture); } return this.textureCache.get(url); } disposeUnusedResources() { // 定期清理未使用的资源 const now Date.now(); const maxAge 60000; // 1分钟未使用则清理 for (const [key, resource] of this.textureCache) { if (now - resource.lastUsed maxAge) { resource.dispose(); this.textureCache.delete(key); } } } optimizeGeometry(geometry) { geometry.computeVertexNormals(); geometry.computeBoundingSphere(); geometry.computeBoundingBox(); // 合并顶点减少绘制调用 geometry.mergeVertices(); return geometry; } }7. 多人联机功能7.1 WebSocket通信架构实现基于WebSocket的实时多人游戏功能class MultiplayerClient { constructor(serverUrl) { this.socket new WebSocket(serverUrl); this.players new Map(); this.setupEventHandlers(); } setupEventHandlers() { this.socket.onopen () { console.log(连接到服务器成功); this.sendPlayerJoin(); }; this.socket.onmessage (event) { const data JSON.parse(event.data); this.handleServerMessage(data); }; this.socket.onclose () { console.log(与服务器断开连接); }; } handleServerMessage(data) { switch (data.type) { case player_join: this.addRemotePlayer(data.playerId, data.position); break; case player_move: this.updatePlayerPosition(data.playerId, data.position); break; case block_update: this.updateBlock(data.position, data.blockType); break; } } sendPlayerMove(position) { this.socket.send(JSON.stringify({ type: player_move, position: position })); } }7.2 状态同步与冲突解决实现平滑的玩家状态同步和冲突检测class StateSynchronizer { constructor() { this.playerStates new Map(); this.interpolationBuffer new Map(); } addPlayerState(playerId, position, timestamp) { if (!this.interpolationBuffer.has(playerId)) { this.interpolationBuffer.set(playerId, []); } const buffer this.interpolationBuffer.get(playerId); buffer.push({ position, timestamp }); // 保持最近5个状态用于插值 if (buffer.length 5) { buffer.shift(); } } interpolatePlayerPosition(playerId) { const buffer this.interpolationBuffer.get(playerId); if (!buffer || buffer.length 2) return null; const now Date.now(); const renderTimestamp now - 100; // 100ms延迟 // 找到插值区间 for (let i buffer.length - 1; i 0; i--) { if (buffer[i].timestamp renderTimestamp) { const nextState buffer[i]; const prevState buffer[i - 1]; const alpha (renderTimestamp - prevState.timestamp) / (nextState.timestamp - prevState.timestamp); return this.lerpPosition(prevState.position, nextState.position, alpha); } } return buffer[buffer.length - 1].position; } lerpPosition(start, end, alpha) { return { x: start.x (end.x - start.x) * alpha, y: start.y (end.y - start.y) * alpha, z: start.z (end.z - start.z) * alpha }; } }8. 常见问题与解决方案8.1 性能优化问题问题现象可能原因解决方案帧率下降区块加载过多减少渲染距离优化区块管理内存占用过高纹理未压缩使用压缩纹理格式实现资源缓存加载卡顿同步加载资源改为异步加载添加加载进度提示8.2 渲染相关问题// 解决锯齿问题 renderer.setPixelRatio(window.devicePixelRatio); renderer.antialias true; // 解决透明度排序问题 renderer.sortObjects true; // 优化阴影渲染 renderer.shadowMap.enabled true; renderer.shadowMap.type THREE.PCFSoftShadowMap;8.3 跨浏览器兼容性确保在各种浏览器中都能正常运行function checkBrowserCompatibility() { const requirements { webgl: !!window.WebGLRenderingContext, webaudio: !!window.AudioContext || !!window.webkitAudioContext, websocket: !!window.WebSocket, pointerlock: !!document.pointerLockElement }; const missing Object.keys(requirements).filter(key !requirements[key]); if (missing.length 0) { alert(您的浏览器不支持以下功能: ${missing.join(, )}); return false; } return true; }通过以上完整的实现方案我们可以构建一个功能丰富的网页版Minecraft。从基础3D渲染到复杂的游戏逻辑每个环节都需要精心设计和优化。在实际开发过程中建议先实现核心功能再逐步添加高级特性确保项目的可维护性和扩展性。开发网页游戏的关键在于平衡功能和性能特别是在浏览器环境中。通过合理的架构设计和持续的优化完全可以打造出令人满意的游戏体验。希望本文提供的技术方案能为您的网页版Minecraft开发提供有价值的参考。

相关新闻

Upscayl批处理深度解析:从单张处理到批量智能化的技术突破

Upscayl批处理深度解析:从单张处理到批量智能化的技术突破

Upscayl批处理深度解析:从单张处理到批量智能化的技术突破 【免费下载链接】upscayl 🆙 Upscayl - #1 Free and Open Source AI Image Upscaler for Linux, MacOS and Windows. 项目地址: https://gitcode.com/GitHub_Trending/up/upscayl 在数字…

2026/10/13 11:45:13 阅读更多 →
LangChain Prompt Template高阶应用与优化指南

LangChain Prompt Template高阶应用与优化指南

1. LangChain Prompt Template深度解析 在LangChain生态系统中,Prompt Template(提示模板)是连接用户输入与语言模型的关键桥梁。作为系列教程的第十篇,我们将深入探讨这个看似简单却蕴含巨大能量的组件。不同于基础用法&#xff…

2026/10/13 11:45:13 阅读更多 →
Spring Security核心功能与生产实践指南

Spring Security核心功能与生产实践指南

1. Spring Security 基础概念与核心能力解析Spring Security 作为 Spring 生态中经过工业级验证的安全框架,其设计哲学可以概括为"约定优于配置"与"可插拔架构"的完美结合。我在多个企业级项目中实践发现,它通过分层抽象解决了安全领…

2026/10/6 16:11:44 阅读更多 →

最新新闻

SpringBoot+Vue+MySQL党员学习交流平台毕业设计全解析

SpringBoot+Vue+MySQL党员学习交流平台毕业设计全解析

做毕业设计选课题,很多人第一反应是找个"看起来简单"的题目。但真正踩过坑的人都知道,技术栈主流、业务场景明确、功能边界清晰的题目才是最省心的。这个"SpringBootVueMySQL 党员学习交流平台"就是比较典型的三年制/四年制计算机毕…

2026/10/13 11:44:21 阅读更多 →
统一大模型API接入:用litellm搭建模型网关的实战指南

统一大模型API接入:用litellm搭建模型网关的实战指南

最近在做 AI 应用的时候,被各家大模型 API 的差异折腾得够呛。同一个功能,OpenAI 的调用方式和 Anthropic 不一样,换到本地模型又是另一套参数,代码里到处都是 if-else 分支,维护起来非常痛苦。后来我在一个开源社区里…

2026/10/13 11:44:21 阅读更多 →
基于PHP开发学生管理系统Web网站:从环境搭建到部署实战

基于PHP开发学生管理系统Web网站:从环境搭建到部署实战

简介:一套基于PHP构建的学生管理系统Web网站源码,面向教育机构、教师以及初中级PHP开发者,主要解决学生档案、课程、专业和管理员信息的高效维护问题。系统采用PHPMySQL实现数据存取,前端由HTML、CSS、JavaScript构成,…

2026/10/13 11:44:21 阅读更多 →
多变量线性回归实战:特征工程、梯度下降与调参避坑指南

多变量线性回归实战:特征工程、梯度下降与调参避坑指南

1. 从单变量到多变量:为什么房价预测不能只看面积 很多人学完单变量线性回归之后,都会有一种“我好像已经会了”的错觉。毕竟公式简单,梯度下降跑一跑,画条直线拟合数据,看起来挺像那么回事。但真正拿到一个稍微贴近现…

2026/10/13 11:44:21 阅读更多 →
微信小程序+Spring Boot+UniApp的大学生兼职推荐系统全栈开发

微信小程序+Spring Boot+UniApp的大学生兼职推荐系统全栈开发

微信小程序 springboot uniapp 的大学生兼职推荐系统,这个题目我盯了很久。说实话,大学生兼职类小程序市面上已经有不少,但真正把"推荐"两个字做扎实的并不多。多数产品就是做了一个职位列表加搜索,用户来了自己翻&am…

2026/10/13 11:44:21 阅读更多 →
蒙特卡洛VaR实战:交易员可用的风险温度计

蒙特卡洛VaR实战:交易员可用的风险温度计

1. 这不是数学考试,是给交易员用的“风险温度计”你手头有一笔股票组合,明天开盘前想快速知道:如果市场突然变脸,最坏可能亏多少?不是拍脑袋猜,也不是等收盘后看报表——而是现在、立刻、用一台普通笔记本&…

2026/10/13 11:43:20 阅读更多 →

日新闻

物联网系统集成避坑指南:从选型到验收的关键要点

物联网系统集成避坑指南:从选型到验收的关键要点

做了这么多年物联网系统集成的项目,我最大的感受是,大部分坑不是出在技术前沿,而是出在一些看起来很平常的地方。设备选了便宜的、协议没问清楚、现场没摸透、验收标准写得含糊——这些细枝末节,最后都会变成延期、超支和扯皮的大…

2026/10/13 0:00:57 阅读更多 →
ESP32舵机控制全攻略:从PWM原理到高级玩法与常见故障排查

ESP32舵机控制全攻略:从PWM原理到高级玩法与常见故障排查

如果你玩ESP32有一阵子了,很难不碰到舵机控制这道坎。做智能小车要转方向,做机械臂要抬关节,做云台要偏航俯仰,背后都是同一个东西在干活:舵机。而我见过太多人卡在“代码能编译、舵机就是不听话”这个阶段&#xff0c…

2026/10/13 0:00:57 阅读更多 →
CAN总线原理与STM32实战:抗干扰、仲裁、错误处理全解析

CAN总线原理与STM32实战:抗干扰、仲裁、错误处理全解析

1. 为什么CAN总线至今仍是工业现场的“硬通货”?——从汽车底盘到智能温室的真实需求你有没有拆过一辆老款燃油车的中控台?或者调试过某高校实验室里那台跑着Modbus协议却频频丢帧的PLC控制柜?又或者,在某个农业物联网项目里&…

2026/10/13 0:00:57 阅读更多 →

周新闻

流感时间序列预测实战:ARIMA/LSTM全流程拆解与避坑指南

流感时间序列预测实战:ARIMA/LSTM全流程拆解与避坑指南

简介:基于 ARIMA、LSTM、Transformer 等模型的流感时间序列预测 Python 源码,面向计算机相关专业课程设计与期末大作业学生,以及项目实战学习者。内容覆盖预处理、平稳性检验、定阶、残差分析、多模型对比预测的完整时序建模流程,…

2026/10/13 2:54:56 阅读更多 →
影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别

影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别

影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别 做影刀RPA自动化,十个新手有八个栽在"往输入框里填东西"这件事上:要么填不进去,要么填了一半,要么直接把原来内容追加在后面。这背后的根因&…

2026/10/12 0:16:38 阅读更多 →
影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容

影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容

影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容 1. 认识影刀:什么场景该用RPA采小说数据 起点中文网的页面结构相对稳定——分类榜单、书籍详情、章节内容三块独立页面,跳转链路清晰。这种场景非常适合影刀自动化&#x…

2026/10/13 1:58:08 阅读更多 →

月新闻

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/12 21:05:09 阅读更多 →
Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/12 14:48:49 阅读更多 →
黑夜航拍船只数据集训练YOLOV5模型全流程解析

黑夜航拍船只数据集训练YOLOV5模型全流程解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/12 18:11:17 阅读更多 →