微信小程序加载外部3D模型:Three.js适配与GLTFLoader实战
简介这份资源面向希望在微信小程序中实现3D可视化效果的开发者尤其是具备一定JavaScript基础、想借助three.js突破小程序渲染限制的前端与小程序工程师。它围绕「加载外部3D模型」这一核心问题提供可运行的工程示例帮助读者理解在小程序环境下搭建3D场景、加载模型并完成交互的完整思路。压缩包共11个文件包含4个json配置、4个js脚本、1个md说明文档以及wxss与wxml页面文件整体约167KB体量轻便便于直接导入开发者工具运行与二次修改。目录中涵盖three.js库文件、loaders加载模块、index页面与app入口配置结构清晰方便按模块定位关键代码。目前已有3205人学习下载说明该方案在小程序3D开发领域具有较高的参考价值。读者可从中获得three.js在小程序中的引入方式、场景与相机初始化、模型加载回调、动画循环及触摸事件处理等实践要点并参考性能优化与异常处理思路快速搭建自己的3D小程序原型。1. 从一次“模型加载不出来”的翻车说起去年帮一个做家居展示的团队看小程序需求很朴素在微信里转着看一张沙发的 3D 模型能换颜色、能缩放。他们卡了整整两天控制台干干净净模型就是不出现。最后发现是模型文件放错了目录load回调里又没打日志整个加载过程成了黑匣子。这件事让我意识到微信小程序加载外部 3D 模型这件事坑不在“会不会写 Three.js”而在“小程序这个运行环境到底允许你干什么”。这份miniprogramThree资源干的就是把 Three.js 塞进微信小程序并且跑通外部 glTF/GLB 模型加载这条链路。它适合两类人一类是想在小程序里做 3D 商品展示、AR 预览、模型交互的前端另一类是已经会用 Three.js但被小程序的 DOM 缺失、包体积限制、文件系统差异卡住的开发者。JavaScript 在这里既是胶水也是主角因为整个适配层就是靠 JS 把浏览器的document、canvas这些概念“翻译”成小程序能听懂的话。2. 项目定位与技术选型为什么不是直接引 Three.js2.1 小程序环境到底缺了什么浏览器里跑 Three.js底层依赖三样东西document.createElement(canvas)拿绘图上下文、window上的各种事件、以及XMLHttpRequest或fetch去拉模型文件。微信小程序一个都不给。它给的是wx.createSelectorQuery()拿 canvas 节点、wx.createCanvasContext()或canvas.getContext(2d)、以及wx.request/wx.getFileSystemManager()读文件。所以任何“把 Three.js 直接 import 进来”的做法都会在第一步就崩报document is not defined。这个项目的核心思路是提供一个适配层把 Three.js 需要的浏览器接口用小程序 API 补出来。常见做法是写一个adapter目录里面用wx.createCanvas()或canvas组件拿到 WebGL 上下文再把这个上下文和伪造的document对象喂给 Three.js 的WebGLRenderer。这样 Three.js 内部以为自己还在浏览器里实际上渲染指令已经落到小程序的 canvas 上了。2.2 技术栈与依赖关系从资源结构看这个项目是典型的“小程序原生 Three.js 内核 自研适配”组合。JavaScript 承担全部逻辑没有用 TypeScript也没有上构建工具链好处是直接丢进微信开发者工具就能跑坏处是模块管理得靠require或import手动理清。层级技术/模块作用视图层WXML canvas 组件提供 WebGL 画布容器逻辑层小程序 Page/Component生命周期、事件绑定适配层自研 adapter伪造 document/window桥接 canvas渲染层Three.js 核心场景、相机、渲染器、加载器资源层glTF / GLB 文件外部 3D 模型数据这里有个关键取舍Three.js 版本不能太新。较新版本对OffscreenCanvas和模块化要求更高适配成本陡增。项目里用的应该是经过验证的、对小程序友好的版本通常锁在 r1xx 附近。如果你自己升级 Three.js第一件事是看WebGLRenderer构造时有没有强依赖document.createElementNS有的话适配层就得跟着改。2.3 目录结构长什么样一个能跑通的miniprogramThree项目目录大致是这样组织的miniprogramThree/ ├── app.js ├── app.json ├── app.wxss ├── pages/ │ └── index/ │ ├── index.js │ ├── index.json │ ├── index.wxml │ └── index.wxss ├── libs/ │ ├── three.js # Three.js 核心适配版 │ └── GLTFLoader.js # 模型加载器 ├── adapter/ │ ├── document.js # 伪造 document │ ├── window.js # 伪造 window │ └── canvas.js # canvas 桥接 └── models/ └── demo.glb # 外部模型文件libs放的是“不能动”的库adapter放的是“必须动”的桥接代码models放的是业务资源。这个分法很实用升级 Three.js 只碰libs换模型只碰models适配出问题只查adapter。提示模型文件不要放在pages下面。小程序对页面目录有打包策略大文件放进去容易触发体积告警放独立目录更稳。3. 核心模块拆解从 canvas 初始化到模型落地3.1 页面结构canvas 组件怎么摆WXML 里必须用canvas组件并且指定typewebgl。这是整个渲染的入口没有它后面全白搭。!-- pages/index/index.wxml -- canvas typewebgl idwebgl canvas-idwebgl stylewidth: 100%; height: 100vh; bindtouchstartonTouchStart bindtouchmoveonTouchMove /canvastypewebgl是关键不写默认是 2D 上下文Three.js 拿不到 WebGL。id和canvas-id都写上因为不同基础库版本取节点的方式不一样双保险。bindtouchstart/bindtouchmove是为后面做旋转缩放准备的Three.js 的OrbitControls在小程序里不能直接用得自己把触摸事件转成相机角度变化。3.2 适配层伪造 document 和 windowThree.js 在初始化WebGLRenderer时会访问document.createElementNS来创建 canvas。小程序没有document所以要在引入 Three.js 之前先把全局的document和window补上。// adapter/document.js // 伪造一个最小可用的 document只满足 Three.js 初始化所需 const canvas wx.createCanvas(); // 离屏 canvas用于承载 WebGL 上下文 const document { createElementNS: function (namespace, tagName) { if (tagName canvas) { return canvas; } return {}; }, createElement: function (tagName) { if (tagName canvas) { return canvas; } return {}; }, // Three.js 部分版本会读 document.body给个空对象兜底 body: {}, addEventListener: function () {}, removeEventListener: function () {} }; module.exports document;这段代码的逻辑很直白Three.js 要 canvas就给它一个wx.createCanvas()创建的离屏 canvas要body就给个空对象要事件监听就给空函数。参数上createElementNS的namespace在 Three.js 里通常是http://www.w3.org/1999/xhtml但这里不需要区分直接按tagName返回即可。window的伪造类似主要是补requestAnimationFrame和devicePixelRatio// adapter/window.js const window { devicePixelRatio: 1, // 小程序里通常取 1避免高分屏下渲染尺寸翻倍 requestAnimationFrame: function (callback) { // 用小程序 canvas 的 requestAnimationFrame没有就用 setTimeout 兜底 if (typeof canvas ! undefined canvas.requestAnimationFrame) { return canvas.requestAnimationFrame(callback); } return setTimeout(callback, 16); }, cancelAnimationFrame: function (id) { if (typeof canvas ! undefined canvas.cancelAnimationFrame) { return canvas.cancelAnimationFrame(id); } clearTimeout(id); }, addEventListener: function () {}, removeEventListener: function () {} }; module.exports window;devicePixelRatio设成 1 是有意为之。小程序 canvas 的物理像素和逻辑像素关系跟浏览器不一样设成wx.getSystemInfoSync().pixelRatio反而容易让渲染尺寸对不上模型显示偏小或模糊。先用 1 跑通再按需调。3.3 渲染器初始化把 canvas 交给 Three.js适配层准备好之后页面逻辑里就可以正常new THREE.WebGLRenderer()了。但要注意必须把小程序 canvas 节点传进去。// pages/index/index.js const document require(../../adapter/document.js); const window require(../../adapter/window.js); // 关键在引入 Three.js 之前挂到全局 global.document document; global.window window; const THREE require(../../libs/three.js); const { GLTFLoader } require(../../libs/GLTFLoader.js); Page({ data: { renderer: null, scene: null, camera: null }, onReady() { this.initThree(); }, initThree() { const query wx.createSelectorQuery(); query.select(#webgl) .fields({ node: true, size: true }) .exec((res) { const canvas res[0].node; const width res[0].width; const height res[0].height; // 用真实 canvas 节点创建渲染器 const renderer new THREE.WebGLRenderer({ canvas: canvas, antialias: true, alpha: true }); renderer.setSize(width, height); renderer.setClearColor(0x000000, 0); const scene new THREE.Scene(); const camera new THREE.PerspectiveCamera(45, width / height, 0.1, 1000); camera.position.set(0, 0, 5); // 补光不然模型是黑的 const ambientLight new THREE.AmbientLight(0xffffff, 0.8); scene.add(ambientLight); const directionalLight new THREE.DirectionalLight(0xffffff, 0.6); directionalLight.position.set(1, 1, 1); scene.add(directionalLight); this.setData({ renderer, scene, camera }); this.loadModel(); this.animate(); }); } });这里有几个参数值得说。antialias: true开抗锯齿模型边缘会顺滑很多代价是性能略降低端机可以关。alpha: true配合setClearColor(0x000000, 0)让背景透明方便叠在小程序页面背景上。PerspectiveCamera的45是视场角0.1和1000是近远裁剪面模型如果特别大或特别小这两个值要跟着调否则会被裁掉。3.4 加载外部模型GLTFLoader 的正确用法模型加载是整条链路最容易翻车的地方。GLTFLoader在小程序里不能直接用fetch去拉文件得把wx.request或文件系统读出来的ArrayBuffer喂给它。loadModel() { const loader new GLTFLoader(); const { scene } this.data; // 方式一从本地包内读取模型放在 models 目录 const fs wx.getFileSystemManager(); fs.readFile({ filePath: ${wx.env.USER_DATA_PATH}/demo.glb, // 或包内路径 success: (res) { loader.parse(res.data, , (gltf) { const model gltf.scene; model.scale.set(1, 1, 1); model.position.set(0, 0, 0); scene.add(model); console.log(模型加载成功); }, (err) { console.error(模型解析失败, err); }); }, fail: (err) { console.error(文件读取失败, err); } }); }loader.parse接收三个参数ArrayBuffer数据、资源路径前缀这里传空字符串、成功回调和失败回调。资源路径前缀在模型引用外部贴图时才需要单文件 GLB 传空即可。失败回调一定要写不然就像开头那个团队一样模型不出现但控制台没线索。如果模型放在网络服务器上就用wx.request拿ArrayBufferwx.request({ url: https://your-domain.com/models/demo.glb, responseType: arraybuffer, success: (res) { loader.parse(res.data, , onLoad, onError); } });responseType: arraybuffer是必须的默认是text拿到的数据没法解析。域名还要在小程序后台配好合法域名否则请求直接被拦。3.5 渲染循环与触摸交互模型加进场景后得让它动起来并且能用手转。animate() { const { renderer, scene, camera } this.data; const loop () { renderer.render(scene, camera); this._raf canvas.requestAnimationFrame(loop); }; loop(); }, onTouchStart(e) { this._lastX e.touches[0].x; this._lastY e.touches[0].y; }, onTouchMove(e) { const { scene } this.data; const model scene.children.find(c c.type Group || c.type Mesh); if (!model) return; const dx e.touches[0].x - this._lastX; const dy e.touches[0].y - this._lastY; this._lastX e.touches[0].x; this._lastY e.touches[0].y; // 水平拖动绕 Y 轴转垂直拖动绕 X 轴转 model.rotation.y dx * 0.01; model.rotation.x dy * 0.01; }0.01是旋转灵敏度数值越大转得越快。太小手感发涩太大容易转飞。这个值没有标准答案得在真机上试。requestAnimationFrame用 canvas 节点自带的比setTimeout更跟手也不容易在后台被节流。4. 避坑与排查模型不显示、白屏、卡顿的常见原因4.1 模型加载成功但看不见现象控制台打印了“模型加载成功”但画布上一片空白。原因相机位置和模型尺寸不匹配模型在视锥体外或者模型本身没有材质、没有光照。解决先把相机拉远camera.position.set(0, 0, 10)试试再确认场景里加了AmbientLight最后检查模型是否真的在scene.children里用console.log(scene.children)看一眼。4.2 报document is not defined现象一进页面就崩提示document is not defined或window is not defined。原因适配层没有在引入 Three.js 之前挂到global上或者挂载顺序反了。解决确保require(../../adapter/document.js)和global.document document写在require(../../libs/three.js)之前。JavaScript 的模块加载是同步的顺序错了后面全错。4.3 模型文件读不到现象fs.readFile报fail file not exist。原因小程序包内文件和用户目录是两套路径。包内文件用相对路径或wx.env.USER_DATA_PATH拼网络文件要先下载到本地。解决包内模型直接写models/demo.glb试试如果是从网络下载的先用wx.downloadFile存到USER_DATA_PATH再读。4.4 真机上卡顿、发热现象开发者工具里流畅真机上掉帧、手机发烫。原因渲染分辨率过高、模型面数太多、每帧都在重建对象。解决把renderer.setPixelRatio(1)显式设上模型面数控制在 5 万以内animate里不要new任何对象所有几何体和材质在初始化时建好。4.5 触摸旋转方向反了现象手指往右滑模型往左转。原因dx的正负和旋转方向没对齐。解决把model.rotation.y dx * 0.01改成-或者把dx取反。这个纯属手感问题没有对错以“跟手”为准。5. 二次开发换模型、加交互、做性能兜底拿到这个项目之后最常改的三个地方是换自己的模型、加点击选中、以及给低端机做降级。换模型最简单把models/demo.glb替换掉然后调model.scale和camera.position让新模型居中显示。不同来源的模型坐标系不一样有的 Y 轴朝上有的 Z 轴朝上加载后如果发现模型躺着或倒着改model.rotation.x就行。我一般会先写一个normalizeModel(model)函数自动算包围盒、居中、缩放到统一尺寸function normalizeModel(model, targetSize 2) { const box new THREE.Box3().setFromObject(model); const size box.getSize(new THREE.Vector3()); const center box.getCenter(new THREE.Vector3()); const maxDim Math.max(size.x, size.y, size.z); const scale targetSize / maxDim; model.scale.set(scale, scale, scale); model.position.sub(center.multiplyScalar(scale)); return model; }targetSize是归一化后的最大边长设成 2 意味着模型最大维度占 2 个单位配合camera.position.z 5刚好占满画面。Box3算包围盒getSize拿尺寸getCenter拿中心点然后先缩放再平移把模型中心挪到原点。这个函数对任何模型都适用省得每次手动调。加点击选中需要用到射线检测。小程序里没有mouse事件用tap事件拿坐标再转成 Three.js 的归一化设备坐标onTap(e) { const { scene, camera } this.data; const touch e.touches[0] || e.changedTouches[0]; const x (touch.x / this._width) * 2 - 1; const y -(touch.y / this._height) * 2 1; const raycaster new THREE.Raycaster(); raycaster.setFromCamera(new THREE.Vector2(x, y), camera); const intersects raycaster.intersectObjects(scene.children, true); if (intersects.length 0) { const hit intersects[0].object; console.log(点中了, hit.name || hit.uuid); // 这里可以改材质颜色、弹信息框 } }x和y的换算公式是固定的屏幕坐标除以宽高再映射到-1到1。intersectObjects第二个参数true表示递归检测子对象模型是层级结构不传true点不中。性能兜底方面我习惯在onReady里先读一次设备信息低端机自动降级const sysInfo wx.getSystemInfoSync(); const isLowEnd sysInfo.benchmarkLevel 20; // 基准分低于 20 视为低端 if (isLowEnd) { renderer.setPixelRatio(0.8); renderer.shadowMap.enabled false; }benchmarkLevel是小程序给的设备性能分级低于 20 的机器渲染压力大降分辨率、关阴影是最直接的手段。阴影特别吃性能关掉之后帧率能稳不少。从那以后我每次接 3D 小程序都强制先跑一遍“空场景 一个立方体”确认渲染链路通了再换真模型。这样出问题时能立刻判断是适配层的事还是模型的事省掉大量瞎猜的时间。希望帮到你。本文还有配套的精品资源点击获取

相关新闻

技术方案评审:从功能清单提炼核心特性,用压测量化性能优势

技术方案评审:从功能清单提炼核心特性,用压测量化性能优势

做技术方案评审这几年,我最常被问到的章节就是“核心技术特性与性能优势”。这个章节写得好,项目汇报基本成功一半;写不好,功能明明做了一大堆,评审老师还是追问:“你的核心优势到底是什么?”问…

2026/10/17 12:20:47 阅读更多 →
Ubuntu 18.04上Apache安装配置与虚拟主机实战指南

Ubuntu 18.04上Apache安装配置与虚拟主机实战指南

这个系列走到第五篇,终于要聊到把Web服务真正拉起来这件事了。虽然前面铺垫了不少Linux基础,但到了Apache安装这一步,很多人依然会卡在各种细节上:装是装上了,服务起不来;默认页打不开;防火墙一…

2026/10/17 12:20:47 阅读更多 →
向量数据库与RAG架构:存储设计与索引原理深度解析

向量数据库与RAG架构:存储设计与索引原理深度解析

做了几年的知识库项目,我越来越觉得 RAG 的瓶颈往往不在大模型,而在那套负责“找资料”的架构里。而支撑这套架构的两个核心底座,正是向量数据库以及它背后的索引与存储设计——业内常打的比方是:向量数据库是 RAG 的仓库与高速公…

2026/10/17 12:20:47 阅读更多 →

最新新闻

codex针对java+vue的Prompt提示词:用TaoToken统一Key跑通前后端联调

codex针对java+vue的Prompt提示词:用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/17 15:00:39 阅读更多 →
Oracle 11g 升级 19C 实战避坑:DBUA 报错、RMAN 恢复与时区版本全解析

Oracle 11g 升级 19C 实战避坑:DBUA 报错、RMAN 恢复与时区版本全解析

简介:这份Oracle 11g到19C数据库升级详尽指导手册,面向具备一定运维能力的数据库管理员(DBA)及运维工程师,聚焦利用DBUA工具将11g单机数据库安全迁移至19C的完整流程。内容涵盖前期备份与恢复、升级路线选择、环境说明…

2026/10/17 15:00:39 阅读更多 →
ZooKeeper节点类型详解:持久、临时、顺序节点的原理与实战避坑

ZooKeeper节点类型详解:持久、临时、顺序节点的原理与实战避坑

我这几年在分布式系统上踩的坑,有一大半都跟ZooKeeper的节点类型有关。不管是做分布式锁、服务注册发现,还是元数据存储,选错了节点类型,轻则功能异常,重则线上事故。今天就把这四种节点的特点、原理和使用场景彻底讲透…

2026/10/17 15:00:39 阅读更多 →
好写作AI:期刊论文选题到投稿全流程实操指南

好写作AI:期刊论文选题到投稿全流程实操指南

凌晨三点,对着屏幕上那块光标闪了一晚上的空白文档,手边的咖啡已经第三杯了。讲实话,这种场景在科研生活里太常见了:Introduction写不出逻辑链、文献综述堆了几十篇却不知道从哪句话开始、明明实验数据都齐了结果讨论部分憋不出来…

2026/10/17 15:00:39 阅读更多 →
Spring Boot内嵌Tomcat启动链路:原理、定制与避坑实战

Spring Boot内嵌Tomcat启动链路:原理、定制与避坑实战

简介:《Spring Boot启动过程(四)之内嵌Tomcat启动》是一份面向中高级Spring Boot开发者的PDF解析文档,帮助厘清启动阶段内嵌Web容器的创建、配置与初始化链路。文档以源码调用顺序为主线,先解释createEmbeddedServletC…

2026/10/17 15:00:39 阅读更多 →
分布式计算与并行计算区别详解:选型与实战指南

分布式计算与并行计算区别详解:选型与实战指南

我一直觉得,很多人一听到“分布式计算”和“并行计算”,第一反应是“这不差不多嘛,都是把任务拆开同时算”。实际上这两个概念在大数据处理里的定位差异非常大,用错场景轻则性能没提升,重则整套架构白搭。这篇文章我就…

2026/10/17 14:59:38 阅读更多 →

日新闻

DeepSeek选型与部署:V3/R1、Ollama、API全攻略

DeepSeek选型与部署:V3/R1、Ollama、API全攻略

简介:《DeepSeek使用方法.pdf》是一份面向DeepSeek入门及进阶用户的完整使用指南,重点围绕V3与R1两款模型的能力差异与适用场景展开,帮助读者根据任务类型与成本选择合适的模型。资源共1个PDF文件,压缩包大小约311KB,篇…

2026/10/17 0:00:25 阅读更多 →
200+Deepseek润色指令拆解:从底层原理到场景落地的完整指南

200+Deepseek润色指令拆解:从底层原理到场景落地的完整指南

简介:这是一份聚焦DeepSeek提示词优化的《200Deepseek润色指令》PDF文档,共收录两百余条可直接套用的润色指令,涵盖语法修正、风格统一、逻辑梳理、表达精炼等维度,并针对商务写作、学术论文、广告文案等场景给出差异化建议。文档…

2026/10/17 0:00:25 阅读更多 →
OpenCV轮廓法物体计数系统:轻量、可控、可落地的工业级实现

OpenCV轮廓法物体计数系统:轻量、可控、可落地的工业级实现

简介:本资源是一份基于OpenCV的本科课程设计完整报告,面向计算机视觉初学者、图像处理学习者及高校通信/信息类专业学生,解决物体智能计数与检测结果结构化记录的实际问题。项目覆盖图像预处理、轮廓检测、边界框绘制、轨迹栏交互式参数调节、…

2026/10/17 0:00:25 阅读更多 →

周新闻

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

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

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

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

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

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

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

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

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

2026/10/16 15:04:24 阅读更多 →

月新闻

我发现了一个新思路:用 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/16 21:54:38 阅读更多 →
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/16 3:49:43 阅读更多 →
黑夜航拍船只数据集训练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/16 14:23:36 阅读更多 →