4、三维可视化引擎搭建:基于Three.js/Unity的风场场景构建、LOD技术
好,咱们进入正题。三维可视化引擎,说白了就是让风场“活”起来的那个底层框架。你想想看,一堆风机、塔筒、叶片,如果只是用二维地图上的小图标表示,那跟看报表有什么区别?我们要的是沉浸感,是“我就在风场里”的体验。
我个人习惯把引擎选型分成两派:轻量Web派和重型应用派。Three.js代表前者,Unity代表后者。别急着站队,先看看你的场景需求。
4.1 引擎选型:Three.js vs Unity
我做过好几个风场项目,踩过不少坑。这里直接给结论:
| 对比维度 | Three.js | Unity |
|---|---|---|
| 部署方式 | 浏览器打开即用,无需安装 | 需要下载客户端或WebGL打包 |
| 开发门槛 | 前端工程师友好,JavaScript上手快 | 需要C#基础,学习曲线稍陡 |
| 渲染性能 | 中等,依赖浏览器GPU加速 | 高,原生渲染管线优化好 |
| 大规模场景 | 需要配合LOD和实例化,否则卡顿 | 自带LOD Group,性能调优方便 |
| 典型场景 | Web端轻量展示、数据看板 | 高保真模拟、VR/AR交互 |
4.2 基于Three.js的风场场景构建
咱们先看Three.js怎么搭。核心思路就三步:场景初始化 → 模型加载 → 环境增强。
先来个最基础的架子:
// 1. 创建场景、相机、渲染器
const scene = new THREE.Scene();
const camera = new THREE.PerspectiveCamera(60, window.innerWidth / window.innerHeight, 0.1, 1000);
const renderer = new THREE.WebGLRenderer({ antialias: true });
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);
// 2. 加载风机模型(GLTF格式)
const loader = new THREE.GLTFLoader();
loader.load('models/wind_turbine.gltf', (gltf) => {
const turbine = gltf.scene;
turbine.position.set(100, 0, 200); // 放在风场坐标
scene.add(turbine);
});
// 3. 添加环境光和阴影
const ambientLight = new THREE.AmbientLight(0x404040);
scene.add(ambientLight);
const dirLight = new THREE.DirectionalLight(0xffffff, 1);
dirLight.position.set(50, 100, 50);
scene.add(dirLight);
嗯,这里要注意:风场坐标系统。我见过有人直接把风机放在(0,0,0),结果几十台风机叠在一起,画面惨不忍睹。正确的做法是:先定义风场范围(比如5km×5km),然后根据实际经纬度或相对坐标,把每台风机摆到对应位置。
4.3 基于Unity的风场场景构建
Unity这边,流程类似但更“可视化”。我个人习惯用Terrain系统做地形,然后用Prefab管理风机。
核心步骤:
- 创建地形:用Heightmap导入真实DEM数据,或者用Terrain工具手动刷。
- 导入风机模型:FBX格式,拖进Project面板,做成Prefab。
- 批量摆放:写个C#脚本,读取CSV坐标文件,Instantiate风机Prefab。
- 添加环境:天空盒、雾效、风场粒子(模拟气流)。
代码示例(C#):
using UnityEngine;
using System.IO;
public class WindFarmBuilder : MonoBehaviour
{
public GameObject turbinePrefab;
public string csvPath = "Assets/Data/turbine_positions.csv";
void Start()
{
string[] lines = File.ReadAllLines(csvPath);
foreach (string line in lines)
{
string[] parts = line.Split(',');
float x = float.Parse(parts[0]);
float z = float.Parse(parts[1]);
Vector3 pos = new Vector3(x, 0, z);
Instantiate(turbinePrefab, pos, Quaternion.identity);
}
}
}
你看,逻辑其实很简单。但Unity的好处是,你可以直接在Editor里看到效果,调整位置、旋转、缩放都很直观。Three.js就得刷新浏览器,调试效率低一些。
4.4 LOD技术:让大规模场景跑得动
好,场景搭起来了。但问题来了:一个风场少说几十台风机,多则上百台。如果每台风机都用高精度模型(几万个面),帧率直接掉到个位数。怎么办?LOD(Level of Detail)就是干这个的。
LOD的核心思想很简单:远处的物体用低精度模型,近处的用高精度。人眼对远处细节不敏感,省掉那些面数,性能就上去了。
4.4.1 Three.js中的LOD实现
Three.js自带THREE.LOD对象,用起来很方便:
const lod = new THREE.LOD();
// 高精度模型(距离0-50米)
const highDetail = loadModel('turbine_high.gltf');
lod.addLevel(highDetail, 0);
// 中精度模型(距离50-200米)
const midDetail = loadModel('turbine_mid.gltf');
lod.addLevel(midDetail, 50);
// 低精度模型(距离200米以上)
const lowDetail = loadModel('turbine_low.gltf');
lod.addLevel(lowDetail, 200);
scene.add(lod);
这里有个细节:切换阈值怎么定?我一般根据屏幕占比来算。比如高精度模型在屏幕上占200像素以上才显示,低于50像素就用最低精度。你可以写个辅助函数,根据相机距离和模型大小动态计算。
4.4.2 Unity中的LOD实现
Unity就更方便了,直接用LOD Group组件。你只需要把不同精度的模型拖进去,设置好百分比阈值,Unity自动帮你切换。
操作步骤:
- 选中风机Prefab,添加LOD Group组件。
- 在Inspector里,添加3个LOD级别(LOD 0、LOD 1、LOD 2)。
- 把高、中、低精度模型分别拖到对应级别下。
- 调整每个级别的切换百分比(比如LOD 0占屏幕高度100%,LOD 1占50%,LOD 2占10%)。
嗯,这里要提醒一下:LOD 0一定要用最高精度的模型,因为玩家可能走到风机底下抬头看。LOD 2可以用一个简单的圆柱体加几个面片,反正远处看不清。
4.5 知识体系总览
为了让你对整个章节有个全局认识,我画了张图:
4.6 性能优化实战经验
最后,分享几个我在项目中用过的优化技巧:
- 实例化(Instancing):几十台风机共用同一个几何体,只是位置不同。Three.js用
InstancedMesh,Unity用GPU Instancing。性能提升10倍以上。 - 遮挡剔除(Occlusion Culling):被山体挡住的风机,直接不渲染。Unity自带这个功能,Three.js需要自己实现或者用第三方库。
- 纹理压缩:风机贴图用2K就够了,别上4K。我见过有人把叶片贴图搞成8K,结果加载花了30秒,用户早跑了。
Stats.js实时监控帧率和Draw Call数量。如果Draw Call超过500,就该考虑合并网格或者用实例化了。
好了,三维引擎搭建这块就聊到这儿。记住:引擎是工具,场景是载体,性能是底线。别光顾着炫酷,忽略了用户能不能跑得动。
公众号:蓝海资料掘金营,微信deep3321