第04章-场景编辑与模型管理
第四章:场景编辑与模型管理
4.1 场景编辑器界面
Astral3D编辑器提供了专业级的场景编辑界面,包含工具栏、侧边栏、视口、属性面板等核心区域。
4.1.1 界面布局
┌─────────────────────────────────────────────────────────────────────┐
│ 工具栏 (Toolbar) │
│ [文件] [编辑] [视图] [对象] [工具] [窗口] [帮助] │
├──────────────┬─────────────────────────────────┬────────────────────┤
│ │ │ │
│ 场景树 │ 3D视口 │ 属性面板 │
│ (Scene │ (Viewport) │ (Properties) │
│ Tree) │ │ │
│ │ │ ┌──────────────┐ │
│ ├─ 场景 │ │ │ 变换属性 │ │
│ │ ├─ 灯光 │ [视图控制工具] │ ├──────────────┤ │
│ │ ├─ 相机 │ │ │ 材质属性 │ │
│ │ └─ 模型 │ │ ├──────────────┤ │
│ │ ├─ 建筑│ │ │ 几何属性 │ │
│ │ └─ 车辆│ │ └──────────────┘ │
│ │ │ │
├──────────────┴─────────────────────────────────┴────────────────────┤
│ 资源面板 (Assets) │
│ [模型库] [材质库] [贴图库] [预制件] [动画] │
├─────────────────────────────────────────────────────────────────────┤
│ 状态栏 (Status Bar) │
│ FPS: 60 | 对象: 128 | 顶点: 1.2M | 选中: 3个对象 │
└─────────────────────────────────────────────────────────────────────┘
4.1.2 工具栏功能
| 功能分组 | 工具 | 快捷键 | 说明 |
|---|---|---|---|
| 文件 | 新建场景 | Ctrl+N | 创建空白场景 |
| 文件 | 打开场景 | Ctrl+O | 打开已有场景文件 |
| 文件 | 保存场景 | Ctrl+S | 保存当前场景 |
| 文件 | 导出 | Ctrl+E | 导出为各种格式 |
| 编辑 | 撤销 | Ctrl+Z | 撤销上一步操作 |
| 编辑 | 重做 | Ctrl+Y | 重做撤销的操作 |
| 编辑 | 复制 | Ctrl+C | 复制选中对象 |
| 编辑 | 粘贴 | Ctrl+V | 粘贴对象 |
| 编辑 | 删除 | Delete | 删除选中对象 |
| 视图 | 顶视图 | Numpad 7 | 切换到顶视图 |
| 视图 | 前视图 | Numpad 1 | 切换到前视图 |
| 视图 | 右视图 | Numpad 3 | 切换到右视图 |
| 对象 | 移动 | W | 激活移动工具 |
| 对象 | 旋转 | E | 激活旋转工具 |
| 对象 | 缩放 | R | 激活缩放工具 |
4.1.3 快捷键大全
通用操作:
Ctrl+N 新建场景
Ctrl+O 打开场景
Ctrl+S 保存场景
Ctrl+Shift+S 另存为
Ctrl+Z 撤销
Ctrl+Y 重做
Delete 删除选中
Ctrl+A 全选
Ctrl+D 取消选择
Ctrl+C 复制
Ctrl+V 粘贴
Ctrl+Shift+D 复制并粘贴
变换工具:
W 移动工具
E 旋转工具
R 缩放工具
Q 选择工具
G 抓取移动(实时移动)
X/Y/Z 限制到对应轴
视图控制:
鼠标中键拖动 旋转视图
Shift+中键拖动 平移视图
滚轮 缩放视图
F 聚焦到选中对象
Home 重置视图
Numpad 0 切换透视/正交
Numpad 1 前视图
Numpad 3 右视图
Numpad 7 顶视图
对象操作:
H 隐藏选中对象
Alt+H 显示所有隐藏对象
L 锁定选中对象
Alt+L 解锁所有对象
M 移动到图层
P 设置父级
Alt+P 清除父级
4.2 模型导入
4.2.1 支持的导入格式
Astral3D支持30+种3D模型格式的导入:
| 格式类别 | 支持格式 | 推荐程度 |
|---|---|---|
| 通用格式 | GLTF/GLB | ⭐⭐⭐⭐⭐ 最推荐 |
| 通用格式 | FBX | ⭐⭐⭐⭐ |
| 通用格式 | OBJ | ⭐⭐⭐ |
| 通用格式 | STL | ⭐⭐⭐ |
| 通用格式 | DAE (Collada) | ⭐⭐⭐ |
| 通用格式 | 3DS | ⭐⭐ |
| BIM格式 | IFC | ⭐⭐⭐⭐ |
| BIM格式 | RVT (Revit) | ⭐⭐⭐⭐ |
| CAD格式 | DWG | ⭐⭐⭐ |
| CAD格式 | DXF | ⭐⭐⭐ |
| CAD格式 | STEP/STP | ⭐⭐⭐⭐ |
| 点云格式 | PCD | ⭐⭐⭐ |
| 点云格式 | XYZ | ⭐⭐⭐ |
4.2.2 导入操作
方法一:拖拽导入
- 打开文件管理器找到模型文件
- 直接将文件拖拽到3D视口区域
- 等待加载完成
方法二:菜单导入
- 点击菜单栏
文件 > 导入 - 选择文件格式
- 在文件对话框中选择文件
- 配置导入选项
- 点击确认导入
方法三:资源面板导入
- 打开底部资源面板
- 点击
导入按钮 - 选择文件
- 拖拽到场景中
4.2.3 导入选项配置
// 通用导入选项
interface ImportOptions {
// 变换设置
position: { x: number, y: number, z: number }; // 初始位置
rotation: { x: number, y: number, z: number }; // 初始旋转(度)
scale: { x: number, y: number, z: number }; // 初始缩放
// 单位转换
convertUnits: boolean; // 是否转换单位
sourceUnit: 'mm' | 'cm' | 'm' | 'inch' | 'feet';
targetUnit: 'm';
// 材质设置
importMaterials: boolean; // 导入材质
embedTextures: boolean; // 嵌入贴图
// 优化设置
mergeGeometries: boolean; // 合并几何体
removeHidden: boolean; // 移除隐藏对象
simplify: boolean; // 简化网格
simplifyRatio: number; // 简化比例 (0-1)
// 阴影设置
castShadow: boolean; // 投射阴影
receiveShadow: boolean; // 接收阴影
}
// BIM导入选项
interface BIMImportOptions extends ImportOptions {
lightweight: boolean; // 轻量化处理
extractProperties: boolean; // 提取属性信息
preserveHierarchy: boolean; // 保留层级结构
mergeByCategory: boolean; // 按类别合并
lodLevels: number[]; // LOD级别
}
// CAD导入选项
interface CADImportOptions extends ImportOptions {
importLayers: boolean; // 导入图层
layerToGroup: boolean; // 图层转换为组
importDimensions: boolean; // 导入尺寸标注
convertTo3D: boolean; // 转换为3D
extrudeHeight: number; // 拉伸高度
}
4.2.4 大模型导入优化
对于大型模型,建议进行以下优化:
// 大型模型导入配置示例
const largeModelOptions = {
// 启用轻量化
lightweight: true,
// 合并相同材质的几何体
mergeGeometries: true,
// 简化网格
simplify: true,
simplifyRatio: 0.5, // 保留50%的面数
// 生成LOD
generateLOD: true,
lodLevels: [
{ distance: 50, ratio: 1.0 }, // 50米内显示原始
{ distance: 100, ratio: 0.5 }, // 50-100米显示50%
{ distance: 200, ratio: 0.2 }, // 100-200米显示20%
{ distance: 500, ratio: 0.05 } // 200米外显示5%
],
// 禁用不必要的功能
importMaterials: false, // 稍后单独设置材质
castShadow: false, // 稍后启用阴影
};
4.3 对象变换
4.3.1 变换工具
移动工具 (W)
- 功能:在3D空间中移动对象
- 操作:拖动箭头沿单轴移动,拖动平面沿双轴移动
- 约束:按住Shift键进行精确移动
Y
↑
│ 移动Gizmo
│ ╱
│ ╱
│ ╱
│╱─────────→ X
╱
╱
↓
Z
旋转工具 (E)
- 功能:绕轴旋转对象
- 操作:拖动圆环绕对应轴旋转
- 约束:按住Ctrl键以15度为单位旋转
缩放工具 (R)
- 功能:调整对象大小
- 操作:拖动方块沿单轴缩放,拖动中心均匀缩放
- 约束:按住Shift保持比例缩放
4.3.2 变换坐标系
| 坐标系 | 说明 | 适用场景 |
|---|---|---|
| 世界坐标 (World) | 使用场景的全局坐标轴 | 对齐到场景方向 |
| 本地坐标 (Local) | 使用对象自身的坐标轴 | 沿对象方向移动 |
| 视图坐标 (View) | 使用当前视图的坐标轴 | 屏幕空间操作 |
| 父级坐标 (Parent) | 使用父对象的坐标轴 | 相对父级操作 |
4.3.3 精确变换
通过属性面板可以进行精确的数值变换:
// 位置设置
object.position.set(10, 0, 5); // X=10, Y=0, Z=5
// 旋转设置(弧度)
object.rotation.set(0, Math.PI / 2, 0); // Y轴旋转90度
// 旋转设置(角度)
object.rotation.y = THREE.MathUtils.degToRad(45); // Y轴旋转45度
// 缩放设置
object.scale.set(2, 2, 2); // 均匀放大2倍
object.scale.set(1, 2, 1); // 只在Y轴方向放大2倍
4.3.4 变换枢轴点
枢轴点是变换操作的中心点:
| 枢轴模式 | 说明 |
|---|---|
| 边界框中心 | 对象边界框的几何中心 |
| 原点 | 对象的本地原点 |
| 底部中心 | 边界框底面的中心 |
| 顶点 | 选中的顶点位置 |
| 3D游标 | 场景中的3D游标位置 |
4.4 层级管理
4.4.1 父子关系
// 设置父级
child.parent = parent;
parent.add(child);
// 清除父级
parent.remove(child);
scene.add(child); // 添加到场景根节点
// 获取层级信息
const parentObj = object.parent;
const children = object.children;
const ancestors = [];
let current = object.parent;
while (current) {
ancestors.push(current);
current = current.parent;
}
4.4.2 场景树操作
通过界面操作:
- 拖拽排序:在场景树中拖动节点改变顺序
- 拖拽到父级:将节点拖到另一个节点上建立父子关系
- 拖出父级:将节点拖到根节点区域清除父级关系
- 右键菜单:
- 重命名
- 复制/粘贴
- 删除
- 显示/隐藏
- 锁定/解锁
- 创建分组
- 解散分组
4.4.3 分组管理
// 创建分组
const group = new THREE.Group();
group.name = '建筑群';
scene.add(group);
// 将对象添加到分组
building1.parent?.remove(building1);
building2.parent?.remove(building2);
group.add(building1);
group.add(building2);
// 解散分组
group.children.forEach(child => {
group.remove(child);
group.parent.add(child);
});
group.parent.remove(group);
// 分组批量操作
group.visible = false; // 隐藏整个分组
group.traverse(child => {
if (child instanceof THREE.Mesh) {
child.castShadow = true;
}
});
4.5 对象属性
4.5.1 基础属性
| 属性 | 类型 | 说明 |
|---|---|---|
| name | string | 对象名称 |
| uuid | string | 唯一标识(只读) |
| type | string | 对象类型(只读) |
| visible | boolean | 是否可见 |
| castShadow | boolean | 是否投射阴影 |
| receiveShadow | boolean | 是否接收阴影 |
| frustumCulled | boolean | 是否进行视锥剔除 |
| renderOrder | number | 渲染顺序 |
4.5.2 变换属性
| 属性 | 类型 | 说明 |
|---|---|---|
| position | Vector3 | 位置 (x, y, z) |
| rotation | Euler | 旋转 (x, y, z) 弧度 |
| scale | Vector3 | 缩放 (x, y, z) |
| quaternion | Quaternion | 四元数旋转 |
| matrix | Matrix4 | 本地变换矩阵 |
| matrixWorld | Matrix4 | 世界变换矩阵 |
4.5.3 网格属性(Mesh)
| 属性 | 类型 | 说明 |
|---|---|---|
| geometry | BufferGeometry | 几何体 |
| material | Material | 材质 |
| morphTargetInfluences | number[] | 形变目标权重 |
4.5.4 用户数据
// 存储自定义数据
object.userData = {
// 业务数据
buildingId: 'B001',
floor: 3,
area: 1500,
// 状态数据
isSelected: false,
isHighlighted: false,
// 元数据
source: 'BIM导入',
importTime: Date.now(),
category: '办公楼'
};
// 读取数据
const buildingId = object.userData.buildingId;
// 遍历查找
scene.traverse(obj => {
if (obj.userData.category === '办公楼') {
console.log(obj.name, obj.userData.buildingId);
}
});
4.6 场景导出
4.6.1 支持的导出格式
| 格式 | 说明 | 用途 |
|---|---|---|
| GLTF | glTF 2.0格式 | 通用3D交换格式 |
| GLB | glTF二进制格式 | 单文件分发 |
| OBJ | Wavefront OBJ | 通用模型格式 |
| STL | STL格式 | 3D打印 |
| DAE | Collada格式 | 动画交换 |
| FBX | FBX格式 | 游戏引擎 |
| JSON | Three.js JSON | 场景序列化 |
4.6.2 导出选项
interface ExportOptions {
// 通用选项
format: 'gltf' | 'glb' | 'obj' | 'stl' | 'fbx';
filename: string;
// 内容选项
exportSelected: boolean; // 仅导出选中
exportVisible: boolean; // 仅导出可见
includeAnimations: boolean; // 包含动画
includeLights: boolean; // 包含灯光
includeCameras: boolean; // 包含相机
// 优化选项
embedTextures: boolean; // 嵌入贴图
optimizeTextures: boolean; // 优化贴图尺寸
maxTextureSize: number; // 最大贴图尺寸
draco: boolean; // Draco压缩 (仅GLTF)
// 变换选项
applyTransforms: boolean; // 应用变换到几何体
centerOrigin: boolean; // 居中原点
}
4.6.3 导出示例
导出为GLB(推荐):
import { GLTFExporter } from '@astral3d/engine';
const exporter = new GLTFExporter();
// 导出整个场景
exporter.export(viewer.scene, {
format: 'glb',
filename: 'scene.glb',
embedTextures: true,
draco: true
}).then(blob => {
// 下载文件
const url = URL.createObjectURL(blob);
const link = document.createElement('a');
link.href = url;
link.download = 'scene.glb';
link.click();
});
// 导出选中对象
const selected = viewer.selection.getSelected();
exporter.export(selected, {
format: 'glb',
filename: 'selected.glb'
});
4.6.4 场景序列化
// 序列化场景
const sceneData = viewer.serialize();
// 保存到本地存储
localStorage.setItem('scene', JSON.stringify(sceneData));
// 保存到服务器
fetch('/api/scenes', {
method: 'POST',
body: JSON.stringify(sceneData),
headers: { 'Content-Type': 'application/json' }
});
// 反序列化加载
const savedData = JSON.parse(localStorage.getItem('scene'));
viewer.deserialize(savedData);
4.7 对象复制与实例化
4.7.1 普通复制
// 深度复制(复制几何体和材质)
const clone = object.clone();
clone.position.x += 10;
scene.add(clone);
// 浅复制(共享几何体和材质)
const shallowClone = object.clone(false);
4.7.2 实例化渲染
对于大量相同对象,使用实例化渲染可以显著提高性能:
import * as THREE from 'three';
// 创建实例化网格
const geometry = new THREE.BoxGeometry(1, 1, 1);
const material = new THREE.MeshStandardMaterial({ color: 0x00ff00 });
const count = 1000; // 实例数量
const instancedMesh = new THREE.InstancedMesh(geometry, material, count);
// 设置每个实例的变换
const dummy = new THREE.Object3D();
for (let i = 0; i < count; i++) {
dummy.position.set(
Math.random() * 100 - 50,
Math.random() * 100 - 50,
Math.random() * 100 - 50
);
dummy.rotation.set(
Math.random() * Math.PI,
Math.random() * Math.PI,
Math.random() * Math.PI
);
dummy.scale.setScalar(Math.random() * 0.5 + 0.5);
dummy.updateMatrix();
instancedMesh.setMatrixAt(i, dummy.matrix);
}
instancedMesh.instanceMatrix.needsUpdate = true;
scene.add(instancedMesh);
4.7.3 阵列复制
// 线性阵列
function linearArray(object, count, spacing) {
const group = new THREE.Group();
group.name = `${object.name}_Array`;
for (let i = 0; i < count; i++) {
const clone = object.clone();
clone.position.x += i * spacing.x;
clone.position.y += i * spacing.y;
clone.position.z += i * spacing.z;
group.add(clone);
}
return group;
}
// 环形阵列
function circularArray(object, count, radius, axis = 'Y') {
const group = new THREE.Group();
group.name = `${object.name}_CircularArray`;
for (let i = 0; i < count; i++) {
const angle = (i / count) * Math.PI * 2;
const clone = object.clone();
if (axis === 'Y') {
clone.position.x = Math.cos(angle) * radius;
clone.position.z = Math.sin(angle) * radius;
clone.rotation.y = angle;
}
// 处理其他轴...
group.add(clone);
}
return group;
}
// 使用示例
const tree = scene.getObjectByName('树');
const treeRow = linearArray(tree, 10, { x: 5, y: 0, z: 0 });
scene.add(treeRow);
const pillar = scene.getObjectByName('柱子');
const pillars = circularArray(pillar, 8, 10);
scene.add(pillars);
4.8 对象对齐与分布
4.8.1 对齐工具
// 对齐函数
function alignObjects(objects, axis, alignTo) {
if (objects.length < 2) return;
// 计算对齐参考值
let reference;
switch (alignTo) {
case 'min':
reference = Math.min(...objects.map(o => getBounds(o).min[axis]));
break;
case 'max':
reference = Math.max(...objects.map(o => getBounds(o).max[axis]));
break;
case 'center':
const bounds = objects.map(o => getBounds(o));
const centers = bounds.map(b => (b.min[axis] + b.max[axis]) / 2);
reference = centers.reduce((a, b) => a + b) / centers.length;
break;
}
// 应用对齐
objects.forEach(obj => {
const bounds = getBounds(obj);
const currentCenter = (bounds.min[axis] + bounds.max[axis]) / 2;
obj.position[axis] += reference - currentCenter;
});
}
// 使用示例
const selected = viewer.selection.getSelected();
alignObjects(selected, 'x', 'min'); // X轴最小值对齐
alignObjects(selected, 'y', 'center'); // Y轴居中对齐
alignObjects(selected, 'z', 'max'); // Z轴最大值对齐
4.8.2 分布工具
// 均匀分布函数
function distributeObjects(objects, axis) {
if (objects.length < 3) return;
// 按轴排序
objects.sort((a, b) => a.position[axis] - b.position[axis]);
// 计算分布间距
const first = objects[0].position[axis];
const last = objects[objects.length - 1].position[axis];
const spacing = (last - first) / (objects.length - 1);
// 应用分布
objects.forEach((obj, i) => {
if (i !== 0 && i !== objects.length - 1) {
obj.position[axis] = first + i * spacing;
}
});
}
// 使用示例
distributeObjects(selected, 'x'); // X轴均匀分布
4.9 场景优化
4.9.1 性能分析
// 获取场景统计信息
function getSceneStats(scene) {
let meshCount = 0;
let triangleCount = 0;
let vertexCount = 0;
let materialCount = new Set();
let textureCount = new Set();
scene.traverse(obj => {
if (obj instanceof THREE.Mesh) {
meshCount++;
const geometry = obj.geometry;
if (geometry.index) {
triangleCount += geometry.index.count / 3;
} else {
triangleCount += geometry.attributes.position.count / 3;
}
vertexCount += geometry.attributes.position.count;
const materials = Array.isArray(obj.material)
? obj.material
: [obj.material];
materials.forEach(mat => {
materialCount.add(mat.uuid);
Object.keys(mat).forEach(key => {
if (mat[key] instanceof THREE.Texture) {
textureCount.add(mat[key].uuid);
}
});
});
}
});
return {
meshCount,
triangleCount,
vertexCount,
materialCount: materialCount.size,
textureCount: textureCount.size
};
}
4.9.2 优化建议
| 问题 | 优化方案 |
|---|---|
| 三角面数过多 | 使用简化工具减少面数 |
| 材质数量过多 | 合并相似材质,使用材质图集 |
| 贴图尺寸过大 | 压缩贴图,使用合适的分辨率 |
| 网格数量过多 | 合并静态网格,使用实例化 |
| 层级过深 | 扁平化场景结构 |
| Draw Call过多 | 使用批处理、实例化渲染 |
4.9.3 自动优化
// 场景优化函数
async function optimizeScene(scene, options) {
// 合并几何体
if (options.mergeGeometries) {
mergeStaticGeometries(scene);
}
// 简化网格
if (options.simplify) {
await simplifyMeshes(scene, options.simplifyRatio);
}
// 压缩贴图
if (options.compressTextures) {
await compressTextures(scene, options.maxTextureSize);
}
// 生成LOD
if (options.generateLOD) {
generateLODs(scene, options.lodLevels);
}
// 优化材质
if (options.optimizeMaterials) {
mergeMaterials(scene);
}
}
4.10 本章小结
本章详细介绍了Astral3D的场景编辑与模型管理功能,主要内容包括:
- 界面布局:工具栏、场景树、属性面板、资源面板的使用方法
- 模型导入:30+种格式的导入支持和优化配置
- 对象变换:移动、旋转、缩放工具的使用和精确变换
- 层级管理:父子关系、分组管理、场景树操作
- 对象属性:基础属性、变换属性、用户数据的管理
- 场景导出:多种格式导出和场景序列化
- 复制与实例化:普通复制、实例化渲染、阵列复制
- 对齐与分布:对象对齐和均匀分布工具
- 场景优化:性能分析和自动优化方案
通过本章的学习,读者应该能够熟练使用Astral3D进行场景编辑和模型管理,为构建复杂的3D场景打下基础。
下一章预告:第五章将介绍Astral3D的BIM轻量化和CAD解析功能,包括Revit模型导入、IFC解析、DWG/DXF预览等专业功能的使用方法。

浙公网安备 33010602011771号