From 4e3455a5b60081e369e5c0195df120d35a82c228 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=88=98=20=E7=9B=B8=E5=8D=BF?= Date: Fri, 21 Nov 2025 13:30:33 +0800 Subject: [PATCH] docs --- README_2.md | 93 ++++++++++++ src/views/three/12/index.vue | 67 +++++++++ src/views/three/5/index.vue | 282 +++++++++++++++++++++++++---------- src/views/three/6/index.vue | 142 ++++++++++++++---- src/views/three/7/index.vue | 281 ++++++++++++++++++++-------------- 5 files changed, 647 insertions(+), 218 deletions(-) create mode 100644 README_2.md diff --git a/README_2.md b/README_2.md new file mode 100644 index 0000000..5cdaf3d --- /dev/null +++ b/README_2.md @@ -0,0 +1,93 @@ +# Three.js 3D 可视化开发 + +本文档整理了化工/工业仿真平台开发中 8 大核心功能的实现原理,以及 PBR 材质系统的基础概念。 + +--- + +## 第一部分:8 大核心功能模块实现原理 + +### 1. 设置地面 (Ground Setup) +* **原理**:使用 `PlaneGeometry` 创建平面网格,并旋转 90 度使其水平平铺。 +* **材质风格**: + * **实景风**:使用 PBR 材质加载沥青、水泥或草地贴图。 + * **科技风**:使用 `GridHelper` 或自定义 Shader 制作深色背景下的发光网格(工业软件常用)。 +* **关键点**:必须开启 `receiveShadow = true`,否则模型会有“悬浮感”。 + +### 2. 地面光效 (Ground Lighting Effects) +* **原理**:利用 **Shader(着色器)** 或 **贴图映射** 模拟光效,而非消耗性能的实时光源。 +* **常见效果**: + * **镜面反射**:使用 `Reflector` 或 `SSR` 模拟潮湿/打蜡地面。 + * **雷达/扩散波**:在 Shader 中利用距离和正弦波 (`sin(time)`) 计算透明度,形成向外扩散的光环。 + * **聚焦光**:配合 `SpotLight` 和体积光技术模拟丁达尔效应。 + +### 3. 模型漫游 (Roaming / Inspection) +* **本质**:摄像机(Camera)位置和朝向的连续变化。 +* **实现方式**: + * **自动巡检**:使用 `CatmullRomCurve3` 定义路径,通过 `progress (0-1)` 获取坐标赋值给摄像机。使用 `getTangentAt` 保持视线朝前。 + * **点击移动**:利用 **Raycaster** 获取地面点击坐标,使用动画库(GSAP/TWEEN)平滑过渡摄像机位置。 + +### 4. 模型动画 (Model Animation) +* **原理**:播放模型文件(GLTF/FBX)自带的骨骼或关键帧动画。 +* **核心系统**:`AnimationMixer`。 +* **流程**:获取 `clips` -> 创建 `mixer` -> 生成 `action` -> 调用 `.play()`。 +* **驱动**:必须在渲染循环中每一帧调用 `mixer.update(deltaTime)`。 + +### 5. 修改材质与告警 (Material Changes / Alerts) +* **原理**:遍历模型树,查找目标 Mesh 并修改 Material 属性。 +* **告警实现**: + * **查找**:`scene.getObjectByName('设备ID')`。 + * **高亮**:修改 `material.emissive`(自发光颜色)及强度,确保在暗处可见。 + * **呼吸闪烁**:在渲染循环中利用 `Math.sin(Date.now())` 动态改变发光强度或透明度。 + +### 6. 粒子效果 (Particles - 蒸汽/火焰) +* **原理**:大量始终面向摄像机的微小平面(Billboards/Sprites)。 +* **数据结构**:`THREE.Points` + `BufferGeometry`。 +* **运动逻辑**:每一帧更新粒子 Y 轴(上升),增加 X/Z 轴随机扰动(扩散),生命周期结束时重置位置。 + +### 7. 模型与 Web 交互 (Interaction) +* **鼠标 -> 3D (点击)**: + * 利用 **Raycaster** 将屏幕坐标转为射线,检测与 Mesh 的相交,获取点击对象 ID 以触发 UI 弹窗。 +* **3D -> 2D (标签跟随)**: + * 利用 **`CSS2DRenderer`** 或坐标投影 (`vector.project(camera)`)。 + * 将 3D 坐标转为屏幕 XY 坐标,通过 CSS `transform` 移动 HTML 标签,使其“粘”在模型上。 + +### 8. 后期处理 (Post-processing) +* **原理**:`EffectComposer` 接管渲染流程,对离屏缓冲区图像进行像素级处理。 +* **常用流水线**: + 1. `RenderPass` (基础画面) + 2. `OutlinePass` (鼠标悬停高亮/描边) + 3. `UnrealBloomPass` (辉光/泛光,增加科技感) + 4. `FXAAPass` / `SMAAPass` (抗锯齿) + +--- + +## 第二部分:PBR 材质贴图详解 + +在物理渲染(PBR)流程中,这三张贴图决定了模型的质感与真实度。 + +### 1. 纹理贴图 (Albedo / Base Color Map) +* **通俗解释**:**“包装纸”** 或 **“颜值”**。 +* **作用**:决定物体表面的基础颜色和图案。 +* **代码属性**:`material.map` + +### 2. 法线贴图 (Normal Map) +* **通俗解释**:**“伪造的凹凸”** 或 **“光影魔术”**。 +* **特征**:通常为紫蓝色图片。 +* **作用**:欺骗光线,让平坦的模型表面呈现出凹凸细节(如砖缝、刻痕),无需增加几何体面数。 +* **代码属性**:`material.normalMap` + +### 3. 粗糙度贴图 (Roughness Map) +* **通俗解释**:**“磨砂纸的目数”** 或 **“质感”**。 +* **特征**:黑白灰图片。 +* **作用**:决定表面反光的清晰度。 + * **黑色 (0.0)**:光滑、镜面反射(如湿瓷砖)。 + * **白色 (1.0)**:粗糙、漫反射(如水泥、灰尘)。 + * **灰色**:半哑光(如拉丝金属)。 +* **代码属性**:`material.roughnessMap` + +### 综合应用示例:旧化工反应罐 +| 贴图类型 | 视觉贡献 | +| :--- | :--- | +| **Color Map** | 决定罐子是绿色的,局部有棕色锈迹。 | +| **Normal Map** | 让锈迹看起来凹凸不平,漆面有剥落感。 | +| **Roughness Map** | 让绿色漆面有微弱反光,而锈迹部分完全粗糙哑光。 | diff --git a/src/views/three/12/index.vue b/src/views/three/12/index.vue index f29d57d..e22895c 100644 --- a/src/views/three/12/index.vue +++ b/src/views/three/12/index.vue @@ -40,6 +40,8 @@ const flameEmitters: any[] = [] // 所有火焰发射器数组 const burningMeshes = new Set() // 已着火的网格集合 let spreadTimer = 0 // 蔓延计时器 const spreadInterval = 2 // 每2秒蔓延一次 +let centerFireEmitter: any = null // 中心大火发射器 +let totalMeshCount = 0 // 模型中网格总数 // 地板流动效果 let flowingGround: any @@ -204,8 +206,11 @@ function loadModel(particleUtils: any) { loader.load('/mzjc_bansw.glb', (gltf) => { model = gltf.scene + // 统计网格总数 + totalMeshCount = 0 model.traverse((child) => { if (child instanceof THREE.Mesh) { + totalMeshCount++ child.castShadow = true child.receiveShadow = true child.userData.objectType = '建筑构件' @@ -292,6 +297,54 @@ function spreadFire() { } }) }) + + // 检查是否已经蔓延到整个模型(80%以上的网格着火) + if (burningMeshes.size >= totalMeshCount * 0.8 && !centerFireEmitter) { + createCenterFire() + } +} + +// 创建中心大火效果 +function createCenterFire() { + if (!model) + return + + const particleUtils = useParticleSystem() + + // 移除所有小火焰发射器 + flameEmitters.forEach(({ emitter }) => { + emitter.stop() + // 保留发光效果 + }) + flameEmitters.length = 0 + + // 计算模型中心和范围 + const modelBox = new THREE.Box3().setFromObject(model) + const modelCenter = modelBox.getCenter(new THREE.Vector3()) + const modelSize = modelBox.getSize(new THREE.Vector3()) + + // 在中心创建大型火焰发射器 + centerFireEmitter = particleUtils.createFlameEmitter( + scene, + new THREE.Vector3(modelCenter.x, modelBox.min.y + modelSize.y * 0.3, modelCenter.z), + 30, // 大量粒子 + ) + + // 添加多个位置的火焰,模拟整体燃烧 + const positions = [ + new THREE.Vector3(modelBox.min.x, modelBox.min.y + 1, modelCenter.z), + new THREE.Vector3(modelBox.max.x, modelBox.min.y + 1, modelCenter.z), + new THREE.Vector3(modelCenter.x, modelBox.min.y + 1, modelBox.min.z), + new THREE.Vector3(modelCenter.x, modelBox.min.y + 1, modelBox.max.z), + ] + + positions.forEach((pos) => { + const emitter = particleUtils.createFlameEmitter(scene, pos, 20) + flameEmitters.push({ emitter, mesh: null }) + }) + + // 添加主中心发射器 + flameEmitters.push({ emitter: centerFireEmitter, mesh: null }) } function animate() { @@ -495,6 +548,20 @@ function toggleFlame() { emitter.stop() // 移除发光效果 + if (mesh && mesh.material) { + const material = mesh.material as THREE.MeshStandardMaterial + material.emissive = new THREE.Color(0x000000) + material.emissiveIntensity = 0 + } + }) + + // 停止中心大火 + if (centerFireEmitter) { + centerFireEmitter = null + } + + // 移除所有物体的发光效果 + burningMeshes.forEach((mesh) => { if (mesh.material) { const material = mesh.material as THREE.MeshStandardMaterial material.emissive = new THREE.Color(0x000000) diff --git a/src/views/three/5/index.vue b/src/views/three/5/index.vue index 7974008..16aa620 100644 --- a/src/views/three/5/index.vue +++ b/src/views/three/5/index.vue @@ -1,3 +1,16 @@ +