-
- Sofa
-
-
- Model Name
-
-
-
- Transform
+
{{ $t('threeModel._3DAsset') }}
+
{{ $t('threeModel.fileFormat') }}
+
{{ $t('threeModel.transform') }}
X
@@ -57,20 +49,18 @@ const {} = toRefs(data);
{{ config?.glbInfoObj?.centroid?.[2].toFixed(2) || 0 }}
-
- Dimensions
-
+
{{ $t('threeModel.dimensions') }}
-
Height
+
{{ $t('threeModel.height') }}
{{ config?.glbInfoObj?.size?.[0].toFixed(2) || 0 }}
-
Width
+
{{ $t('threeModel.width') }}
{{ config?.glbInfoObj?.size?.[1].toFixed(2) || 0 }}
-
Depth
+
{{ $t('threeModel.depth') }}
{{ config?.glbInfoObj?.size?.[2].toFixed(2) || 0 }}
@@ -100,7 +90,7 @@ const {} = toRefs(data);
}
> .captureView ,
> .download{
- margin-left: 4.2rem;
+ transform: translateX(calc(13rem / 2));
line-height: 3rem;
width: 20rem;
border-radius: 1.5rem;
diff --git a/src/components/Canvas/FlowCanvas/components/tools/threeModel/index.vue b/src/components/Canvas/FlowCanvas/components/tools/threeModel/index.vue
index 7cf7217..25e19bd 100644
--- a/src/components/Canvas/FlowCanvas/components/tools/threeModel/index.vue
+++ b/src/components/Canvas/FlowCanvas/components/tools/threeModel/index.vue
@@ -29,10 +29,11 @@ const captureView = async ()=>{
formData.append('file', file)
const minioUrl = await uploadImage(formData, true)
ElMessage.warning('Your new view has been captured.')
-
emit('captureView', {
minioUrl,
- nodeId: props?.currentData?.nodeId
+ nodeId: props?.currentData?.nodeId,
+ nodeType: props?.currentData?.nodeType,
+ superiorGenerateImg: props?.currentData?.superiorGenerateImg,
})
}
onMounted(()=>{
diff --git a/src/components/Canvas/FlowCanvas/components/tools/threeModel/model.vue b/src/components/Canvas/FlowCanvas/components/tools/threeModel/model.vue
index 53ec2ee..338f50e 100644
--- a/src/components/Canvas/FlowCanvas/components/tools/threeModel/model.vue
+++ b/src/components/Canvas/FlowCanvas/components/tools/threeModel/model.vue
@@ -4,7 +4,7 @@ import { useI18n } from 'vue-i18n'
import gsap from 'gsap';
import * as THREE from 'three';
-import { ThreeManager } from './threeTool copy'
+import { ThreeManager } from './threeTool'
//const props = defineProps({
//})
diff --git a/src/components/Canvas/FlowCanvas/components/tools/threeModel/threeTool copy.ts b/src/components/Canvas/FlowCanvas/components/tools/threeModel/threeTool copy.ts
deleted file mode 100644
index 0232b2e..0000000
--- a/src/components/Canvas/FlowCanvas/components/tools/threeModel/threeTool copy.ts
+++ /dev/null
@@ -1,351 +0,0 @@
-import * as THREE from 'three';
-import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls.js';
-import { GLTFLoader } from "three/examples/jsm/loaders/GLTFLoader.js";
-import { DRACOLoader } from 'three/examples/jsm/loaders/DRACOLoader.js'
-import { RGBELoader } from 'three/examples/jsm/loaders/RGBELoader.js'
-import { RectAreaLightUniformsLib } from 'three/examples/jsm/lights/RectAreaLightUniformsLib.js';
-import hdri from '@/assets/images/three/hdri.hdr'
-
-interface ModelInfo {
- box: THREE.Box3;
- center: THREE.Vector3;
- size: THREE.Vector3;
- maxSize: number;
-}
-const CONFIG = {
- hdriIntensity: 7.4,
- exposureBase: 0.92,
- backlightBoost: { min: 2.1, max: 4.8 }, // 背光增强系数
- hdriUrl: hdri,
-};
-export class ThreeManager {
- threeDom: HTMLElement;
- scene: THREE.Scene;//场景对象
- camera: THREE.PerspectiveCamera;//相机对象
- renderer: THREE.WebGLRenderer;//渲染器对象
- controls: OrbitControls;//轨道控制器对象
-
-
- pointLight: THREE.AmbientLight;//环境光对象
- studioLights: any;//工作室光对象数组
- v1: THREE.Vector3;//相机前向向量
- camDir: THREE.Vector3;//相机前向向量
- camForward: THREE.Vector3;//相机前向向量
- camToTarget: THREE.Vector3;//相机目标向量
-
- currentModel: any;//当前模型对象
- animate: any;//动画对象
-
- modelInfo: ModelInfo;//模型信息对象
- defaultSoftboxPositions: Array<{ x: number; y: number; z: number; intensity: number; w: number; h: number }> = [
- { x: 0, y: 5.8, z: 3.5, intensity: 3.5, w: 6, h: 4.8 }, // 主光
- { x: 0, y: 2.8, z: 7.5, intensity: 2.2, w: 5, h: 4 }, // 前光
- { x: 0, y: 2.8, z: -7.5, intensity: 6.5, w: 5, h: 4 }, // 背光
- { x: -7.5, y: 2.8, z: 0, intensity: 10.8, w: 5, h: 4 }, // 侧光
- { x: 7.5, y: 2.8, z: 0, intensity: 2.0, w: 5, h: 4 }, // 侧光
- { x: 0, y: -2.2, z: 3, intensity: 0.8, w: 9, h: 4 } // 地面反光板
- ];
- constructor(threeDom: HTMLElement,config:any = {}){
-
- this.threeDom = threeDom;
- this.studioLights = []
- //创建场景
- this.scene = new THREE.Scene();
- this.scene.background = new THREE.Color(0xffffff);
- //创建相机
- this.camera = new THREE.PerspectiveCamera(45, threeDom.offsetWidth / threeDom.offsetHeight, 0.1, 10000);
- this.camera.position.set(0, 1.5, 6); //设置相机位置
-
- this.v1 = new THREE.Vector3();
- this.camDir = new THREE.Vector3();
- this.camForward = new THREE.Vector3();
- this.camToTarget = new THREE.Vector3();
- //设置渲染器
- this.renderer = new THREE.WebGLRenderer({ antialias: true, powerPreference: "high-performance", preserveDrawingBuffer: true });
- //设置环境光
- this.scene.add(new THREE.AmbientLight(0xffffff, 0.15));
-
- // 关键优化:物理光照与色彩管理
- this.renderer.outputEncoding = THREE.sRGBEncoding;
- this.renderer.toneMapping = THREE.ACESFilmicToneMapping;
- this.renderer.toneMappingExposure = CONFIG.exposureBase;
- this.renderer.physicallyCorrectLights = true; // 开启物理光照模式,光衰减更真实
- this.renderer.setPixelRatio(window.devicePixelRatio);
- this.renderer.setSize(threeDom.offsetWidth, threeDom.offsetHeight); //设置渲染区域尺寸
- this.renderer.setClearColor(0xffffff, 1); //设置背景颜色
- threeDom.innerHTML = '';
- threeDom.appendChild(this.renderer.domElement);
-
- RectAreaLightUniformsLib.init();
- //设置轨道控制器
- this.controls = new OrbitControls(this.camera,this.renderer.domElement)//监听鼠标、键盘事件;
- // controls.minDistance = 500; // 设置相机与焦点的最小距离
- // controls.maxDistance = 4000; // 设置相机与焦点的最大距离
- this.controls.mouseButtons = {
- // LEFT:THREE.MOUSE.PAN, // 左键 拖动(默认旋转:ROTATE)
- LEFT:THREE.MOUSE.ROTATE, // 左键 拖动(默认旋转:ROTATE)
- MIDDLE:THREE.MOUSE.DOLLY, // 滑轮 缩放
- RIGHT:THREE.MOUSE.PAN // 右键 旋转(默认拖动:PAN)
- // RIGHT:THREE.MOUSE.ROTAafTE // 右键 旋转(默认拖动:PAN)
- }
- this.controls.enableDamping = true;
-
- }
- /**
- * 根据模型大小计算自适应配置
- */
- calculateAdaptiveConfig(modelInfo: ModelInfo) {
- const { size, maxSize, center } = modelInfo;
- // 基础距离系数(可根据需要调整)
- let distanceFactor = 1.5;
- // 根据模型形状调整距离系数
- const aspectRatio = size.x / size.y;
- if (aspectRatio > 2) {
- // 扁平模型,拉远一点
- distanceFactor = 3.0;
- } else if (aspectRatio < 0.5) {
- // 高瘦模型,稍微拉近
- distanceFactor = 2.0;
- }
- // 计算相机距离
- const fov = this.camera.fov * (Math.PI / 180);
- const cameraDistance = (maxSize / 2) / Math.tan(fov / 2) * distanceFactor;
- // 灯光缩放系数
- const lightScale = Math.max(0.5, Math.min(2.0, maxSize / 2.5));
- // 强度缩放系数(模型越大,灯光需要越强)
- const intensityScale = Math.max(0.6, Math.min(2.5, maxSize / 2));
-
- console.log('自适应配置:', {
- 相机距离: cameraDistance,
- 灯光缩放: lightScale,
- 强度缩放: intensityScale,
- 目标中心: center
- });
-
- return {
- cameraDistance,
- lightScale,
- intensityScale,
- targetCenter: center.clone()
- };
- }
- /**
- * 获取模型信息(包围盒、中心点、尺寸等)
- */
- getModelInfo(model: THREE.Object3D): ModelInfo {
- const box = new THREE.Box3().setFromObject(model);
- const center = box.getCenter(new THREE.Vector3());
- const size = box.getSize(new THREE.Vector3());
- const maxSize = Math.max(size.x, size.y, size.z);
-
- console.log('模型信息:', {
- 中心点: center,
- 尺寸: { x: size.x, y: size.y, z: size.z },
- 最大尺寸: maxSize
- });
-
- return { box, center, size, maxSize };
- }
- /**
- * 根据模型大小自适应创建柔光箱灯光
- */
- createAdaptiveSoftboxes(modelInfo: ModelInfo) {
- const { lightScale, intensityScale, targetCenter } = this.calculateAdaptiveConfig(modelInfo);
- // 清除现有灯光
- this.studioLights.forEach(item => {
- this.scene.remove(item.light);
- });
- this.studioLights = [];
-
- // 根据模型大小创建自适应灯光
- this.defaultSoftboxPositions.forEach(pos => {
- const scaledX = pos.x * lightScale;
- const scaledY = pos.y * lightScale;
- const scaledZ = pos.z * lightScale;
- const scaledW = pos.w * lightScale;
- const scaledH = pos.h * lightScale;
- const scaledIntensity = pos.intensity * intensityScale;
-
- const light = new THREE.RectAreaLight(0xffffff, scaledIntensity, scaledW, scaledH);
- light.position.set(
- targetCenter.x + scaledX,
- targetCenter.y + scaledY,
- targetCenter.z + scaledZ
- );
- light.lookAt(targetCenter);
- this.scene.add(light);
-
- this.studioLights.push({
- light,
- offset: new THREE.Vector3(scaledX, scaledY, scaledZ),
- baseIntensity: scaledIntensity
- });
- });
- console.log('自适应灯光已创建,缩放系数:', lightScale, '强度系数:', intensityScale);
- }
- /**
- * 根据模型大小调整相机位置
- */
- adjustCameraToModel(modelInfo: ModelInfo) {
- const { cameraDistance, targetCenter } = this.calculateAdaptiveConfig(modelInfo);
- // 正面俯视角度:相机在模型正前方,稍高于模型中心
- // X 轴:模型中心(正面视角,不左右偏移)
- // Y 轴:相机高度 = 模型中心高度 + 模型高度的 0.3 倍(俯视效果)
- // Z 轴:相机距离 = 根据模型大小计算的距离
- const x = targetCenter.x;
- const y = targetCenter.y + modelInfo.size.y * 0.3; // 相机高度为模型中心的 0.3 倍
- const z = targetCenter.z + cameraDistance;
-
- this.camera.position.set(x, y, z);
- this.controls.target.copy(targetCenter);
- this.controls.update();
- }
-
-
- createSoftbox(x, y, z, intensity, w = 5, h = 4) {
- const light = new THREE.RectAreaLight(0xffffff, intensity, w, h);
- light.position.set(x, y, z);
- light.lookAt(0, 0, 0);
- this.scene.add(light);
- // 存储 offset 向量副本,避免后续重复创建
- this.studioLights.push({
- light,
- offset: new THREE.Vector3(x, y, z),
- baseIntensity: intensity
- });
- }
- async setHDRI(){
- const rgbeLoader = new RGBELoader()
- await rgbeLoader.load(CONFIG.hdriUrl, (texture)=>{
- texture.mapping = THREE.EquirectangularReflectionMapping;
- const pmremGenerator = new THREE.PMREMGenerator(this.renderer);
- const envMap = pmremGenerator.fromEquirectangular(texture).texture;
-
- this.scene.environment = envMap;
- this.scene.environmentIntensity = CONFIG.hdriIntensity;
-
- pmremGenerator.dispose();
- texture.dispose();
- console.log('✅ HDRI Loaded');
- });
- this.createSoftbox(0, 5.8, 3.5, 3.5, 6, 4.8); // 主光
- this.createSoftbox(0, 2.8, 7.5, 2.2); // 前光
- this.createSoftbox(0, 2.8, -7.5, 6.5); // 背光 (增强)
- this.createSoftbox(-7.5, 2.8, 0, 10.8); // 侧光
- this.createSoftbox(7.5, 2.8, 0, 2.0);
- this.createSoftbox(0, -2.2, 3, 0.8, 9, 4); // 地面反光板
- }
- // 更新工作室光位置
- updateStudioLighting(){
- this.camera.getWorldDirection(this.camDir);
- this.studioLights.forEach(item => {
- // 使用 applyQuaternion 同步灯光位置
- this.v1.copy(item.offset).applyQuaternion(this.camera.quaternion);
- item.light.position.copy(this.controls.target).add(this.v1);
- item.light.lookAt(this.controls.target);
-
- // 动态背光逻辑
- if (item.offset.z < -2) {
- this.v1.copy(item.light.position).sub(this.controls.target).normalize();
- const dot = this.v1.dot(this.camDir);
- const factor = THREE.MathUtils.lerp(CONFIG.backlightBoost.min, CONFIG.backlightBoost.max, Math.max(0, 1 - dot));
- item.light.intensity = item.baseIntensity * factor;
- }
- });
- }
- //动态设置曝光
- updateImagePipeline() {
- let exposure = 1.0;
- this.camera.getWorldDirection(this.camForward);
- this.camToTarget.copy(this.controls.target).sub(this.camera.position).normalize();
- const facing = Math.abs(this.camForward.dot(this.camToTarget));
- const targetExposure = THREE.MathUtils.lerp(1.25, 0.90, facing);
- exposure = THREE.MathUtils.lerp(exposure, targetExposure, 0.08);
- this.renderer.toneMappingExposure = CONFIG.exposureBase * exposure;
- }
-
- async setModel(modelUrl,load){
- await new Promise((resolve, reject) => {
- const drac = new DRACOLoader()
- drac.setDecoderPath('/draco/')
- const loader = new GLTFLoader().setDRACOLoader(drac);
-
- if (this.currentModel) {
- this.scene.remove(this.currentModel);
- this.dispose(this.currentModel); // 核心优化:释放显存
- }
-
- loader.load(modelUrl,
- (gltf) => {
- this.currentModel = gltf.scene;
-
- // 遍历模型:增强材质表现
- this.currentModel.traverse(child => {
- if (child.isMesh) {
- child.castShadow = true;
- child.receiveShadow = true;
- if (child.material) {
- child.material.envMapIntensity = 1.2; // 增强 HDRI 反射强度
- }
- }
- });
-
- const box = new THREE.Box3().setFromObject(this.currentModel);
- const center = box.getCenter(new THREE.Vector3());
- // this.currentModel.position.sub(center);
- this.scene.add(this.currentModel);
- this.modelInfo = this.getModelInfo(this.currentModel)
- // 根据模型大小调整相机位置
- this.adjustCameraToModel(this.modelInfo);
-
- // 根据模型大小创建自适应灯光
- this.createAdaptiveSoftboxes(this.modelInfo);
- resolve('')
- },
- (xhr: any) => { // 加载进度回调
- const percent = xhr.total == 0 ? 100 : (xhr.loaded / xhr.total * 100).toFixed(2);
- load.value.progress = percent
- console.log('模型加载进度:', percent);
- },
- (error: any) => { // 加载失败回调
- console.error('模型加载失败:', error);
- resolve('')
- }
- );
- })
- }
-
- operation(){
- let this_ = this
- const animate = () => {
- requestAnimationFrame(animate);
- this.controls.update();
- this.updateStudioLighting();
- this.updateImagePipeline();
- this.renderer.render(this.scene, this.camera);
- }
- animate();
- }
-
- // 释放模型资源
- dispose(obj){
- if (!obj) return;
- obj.traverse(node => {
- if (node.isMesh) {
- if (node.geometry) node.geometry.dispose();
- if (node.material) {
- if (Array.isArray(node.material)) {
- node.material.forEach(m => m.dispose());
- } else {
- node.material.dispose();
- }
- }
- }
- });
- }
-
-
- exportAsImage(){
- return this.renderer.domElement.toDataURL('image/png');
- }
-}
\ No newline at end of file
diff --git a/src/components/Canvas/FlowCanvas/components/tools/threeModel/threeTool.ts b/src/components/Canvas/FlowCanvas/components/tools/threeModel/threeTool.ts
index 8c4b9db..0232b2e 100644
--- a/src/components/Canvas/FlowCanvas/components/tools/threeModel/threeTool.ts
+++ b/src/components/Canvas/FlowCanvas/components/tools/threeModel/threeTool.ts
@@ -3,291 +3,349 @@ import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls.js';
import { GLTFLoader } from "three/examples/jsm/loaders/GLTFLoader.js";
import { DRACOLoader } from 'three/examples/jsm/loaders/DRACOLoader.js'
import { RGBELoader } from 'three/examples/jsm/loaders/RGBELoader.js'
+import { RectAreaLightUniformsLib } from 'three/examples/jsm/lights/RectAreaLightUniformsLib.js';
import hdri from '@/assets/images/three/hdri.hdr'
-export const initThree = async (threeDom)=>{
- const scene = new THREE.Scene();
- const group = new THREE.Group()
- scene.add(group)
- const studioLights = []
- //创建相机对象
- // this.camera = new THREE.OrthographicCamera(-s * k, s * k, s, -s, 1, 1000);
- const camera = new THREE.PerspectiveCamera(45, threeDom.offsetWidth / threeDom.offsetHeight, 0.1, 1000);
- camera.position.set(0, 1.5, 5);
- camera.lookAt(scene.position); //设置相机方向(指向的场景对象)
- const controls = new THREE.OrbitControls(camera, renderer.domElement);
- controls.enableDamping = true;
-
- /**
- * 创建渲染器对象
- */
+interface ModelInfo {
+ box: THREE.Box3;
+ center: THREE.Vector3;
+ size: THREE.Vector3;
+ maxSize: number;
+}
+const CONFIG = {
+ hdriIntensity: 7.4,
+ exposureBase: 0.92,
+ backlightBoost: { min: 2.1, max: 4.8 }, // 背光增强系数
+ hdriUrl: hdri,
+};
+export class ThreeManager {
+ threeDom: HTMLElement;
+ scene: THREE.Scene;//场景对象
+ camera: THREE.PerspectiveCamera;//相机对象
+ renderer: THREE.WebGLRenderer;//渲染器对象
+ controls: OrbitControls;//轨道控制器对象
- const width = threeDom.offsetWidth; //窗口宽度
- const height = threeDom.offsetHeight; //窗口高度
- const renderer = new THREE.WebGLRenderer({
- antialias: true,
- logarithmicDepthBuffer: true,//深度缓存 防止模型闪烁重影
- });
+ pointLight: THREE.AmbientLight;//环境光对象
+ studioLights: any;//工作室光对象数组
+ v1: THREE.Vector3;//相机前向向量
+ camDir: THREE.Vector3;//相机前向向量
+ camForward: THREE.Vector3;//相机前向向量
+ camToTarget: THREE.Vector3;//相机目标向量
- renderer.toneMapping = THREE.ACESFilmicToneMapping;//设置色调
- renderer.toneMappingExposure = 1.3;
+ currentModel: any;//当前模型对象
+ animate: any;//动画对象
- renderer.shadowMap.enabled = true;
- renderer.setPixelRatio(window.devicePixelRatio);
- renderer.setSize(width, height); //设置渲染区域尺寸
- renderer.setClearColor(0xffffff, 1); //设置背景颜色
- threeDom.innerHTML = '';
- threeDom.appendChild(renderer.domElement);
+ modelInfo: ModelInfo;//模型信息对象
+ defaultSoftboxPositions: Array<{ x: number; y: number; z: number; intensity: number; w: number; h: number }> = [
+ { x: 0, y: 5.8, z: 3.5, intensity: 3.5, w: 6, h: 4.8 }, // 主光
+ { x: 0, y: 2.8, z: 7.5, intensity: 2.2, w: 5, h: 4 }, // 前光
+ { x: 0, y: 2.8, z: -7.5, intensity: 6.5, w: 5, h: 4 }, // 背光
+ { x: -7.5, y: 2.8, z: 0, intensity: 10.8, w: 5, h: 4 }, // 侧光
+ { x: 7.5, y: 2.8, z: 0, intensity: 2.0, w: 5, h: 4 }, // 侧光
+ { x: 0, y: -2.2, z: 3, intensity: 0.8, w: 9, h: 4 } // 地面反光板
+ ];
+ constructor(threeDom: HTMLElement,config:any = {}){
- // 设置渲染器大小
+ this.threeDom = threeDom;
+ this.studioLights = []
+ //创建场景
+ this.scene = new THREE.Scene();
+ this.scene.background = new THREE.Color(0xffffff);
+ //创建相机
+ this.camera = new THREE.PerspectiveCamera(45, threeDom.offsetWidth / threeDom.offsetHeight, 0.1, 10000);
+ this.camera.position.set(0, 1.5, 6); //设置相机位置
+ this.v1 = new THREE.Vector3();
+ this.camDir = new THREE.Vector3();
+ this.camForward = new THREE.Vector3();
+ this.camToTarget = new THREE.Vector3();
+ //设置渲染器
+ this.renderer = new THREE.WebGLRenderer({ antialias: true, powerPreference: "high-performance", preserveDrawingBuffer: true });
+ //设置环境光
+ this.scene.add(new THREE.AmbientLight(0xffffff, 0.15));
+
+ // 关键优化:物理光照与色彩管理
+ this.renderer.outputEncoding = THREE.sRGBEncoding;
+ this.renderer.toneMapping = THREE.ACESFilmicToneMapping;
+ this.renderer.toneMappingExposure = CONFIG.exposureBase;
+ this.renderer.physicallyCorrectLights = true; // 开启物理光照模式,光衰减更真实
+ this.renderer.setPixelRatio(window.devicePixelRatio);
+ this.renderer.setSize(threeDom.offsetWidth, threeDom.offsetHeight); //设置渲染区域尺寸
+ this.renderer.setClearColor(0xffffff, 1); //设置背景颜色
+ threeDom.innerHTML = '';
+ threeDom.appendChild(this.renderer.domElement);
+
+ RectAreaLightUniformsLib.init();
+ //设置轨道控制器
+ this.controls = new OrbitControls(this.camera,this.renderer.domElement)//监听鼠标、键盘事件;
+ // controls.minDistance = 500; // 设置相机与焦点的最小距离
+ // controls.maxDistance = 4000; // 设置相机与焦点的最大距离
+ this.controls.mouseButtons = {
+ // LEFT:THREE.MOUSE.PAN, // 左键 拖动(默认旋转:ROTATE)
+ LEFT:THREE.MOUSE.ROTATE, // 左键 拖动(默认旋转:ROTATE)
+ MIDDLE:THREE.MOUSE.DOLLY, // 滑轮 缩放
+ RIGHT:THREE.MOUSE.PAN // 右键 旋转(默认拖动:PAN)
+ // RIGHT:THREE.MOUSE.ROTAafTE // 右键 旋转(默认拖动:PAN)
+ }
+ this.controls.enableDamping = true;
- const controls = new OrbitControls(camera,renderer.domElement)//监听鼠标、键盘事件;
- // controls.minDistance = 500; // 设置相机与焦点的最小距离
- // controls.maxDistance = 4000; // 设置相机与焦点的最大距离
- controls.mouseButtons = {
- // LEFT:THREE.MOUSE.PAN, // 左键 拖动(默认旋转:ROTATE)
- LEFT:THREE.MOUSE.ROTATE, // 左键 拖动(默认旋转:ROTATE)
- MIDDLE:THREE.MOUSE.DOLLY, // 滑轮 缩放
- RIGHT:THREE.MOUSE.PAN // 右键 旋转(默认拖动:PAN)
- // RIGHT:THREE.MOUSE.ROTAafTE // 右键 旋转(默认拖动:PAN)
}
-
- //使用hdri文件
- try {
- const rgbeLoader = new RGBELoader()
- const hdrTexture = await rgbeLoader.loadAsync(hdri)
- hdrTexture.mapping = THREE.EquirectangularMapping
+ /**
+ * 根据模型大小计算自适应配置
+ */
+ calculateAdaptiveConfig(modelInfo: ModelInfo) {
+ const { size, maxSize, center } = modelInfo;
+ // 基础距离系数(可根据需要调整)
+ let distanceFactor = 1.5;
+ // 根据模型形状调整距离系数
+ const aspectRatio = size.x / size.y;
+ if (aspectRatio > 2) {
+ // 扁平模型,拉远一点
+ distanceFactor = 3.0;
+ } else if (aspectRatio < 0.5) {
+ // 高瘦模型,稍微拉近
+ distanceFactor = 2.0;
+ }
+ // 计算相机距离
+ const fov = this.camera.fov * (Math.PI / 180);
+ const cameraDistance = (maxSize / 2) / Math.tan(fov / 2) * distanceFactor;
+ // 灯光缩放系数
+ const lightScale = Math.max(0.5, Math.min(2.0, maxSize / 2.5));
+ // 强度缩放系数(模型越大,灯光需要越强)
+ const intensityScale = Math.max(0.6, Math.min(2.5, maxSize / 2));
+
+ console.log('自适应配置:', {
+ 相机距离: cameraDistance,
+ 灯光缩放: lightScale,
+ 强度缩放: intensityScale,
+ 目标中心: center
+ });
+
+ return {
+ cameraDistance,
+ lightScale,
+ intensityScale,
+ targetCenter: center.clone()
+ };
+ }
+ /**
+ * 获取模型信息(包围盒、中心点、尺寸等)
+ */
+ getModelInfo(model: THREE.Object3D): ModelInfo {
+ const box = new THREE.Box3().setFromObject(model);
+ const center = box.getCenter(new THREE.Vector3());
+ const size = box.getSize(new THREE.Vector3());
+ const maxSize = Math.max(size.x, size.y, size.z);
+
+ console.log('模型信息:', {
+ 中心点: center,
+ 尺寸: { x: size.x, y: size.y, z: size.z },
+ 最大尺寸: maxSize
+ });
+
+ return { box, center, size, maxSize };
+ }
+ /**
+ * 根据模型大小自适应创建柔光箱灯光
+ */
+ createAdaptiveSoftboxes(modelInfo: ModelInfo) {
+ const { lightScale, intensityScale, targetCenter } = this.calculateAdaptiveConfig(modelInfo);
+ // 清除现有灯光
+ this.studioLights.forEach(item => {
+ this.scene.remove(item.light);
+ });
+ this.studioLights = [];
- // 设置环境贴图(影响材质反射)
- scene.environment = hdrTexture
- // 可选:同时设置为背景
- scene.background = hdrTexture
+ // 根据模型大小创建自适应灯光
+ this.defaultSoftboxPositions.forEach(pos => {
+ const scaledX = pos.x * lightScale;
+ const scaledY = pos.y * lightScale;
+ const scaledZ = pos.z * lightScale;
+ const scaledW = pos.w * lightScale;
+ const scaledH = pos.h * lightScale;
+ const scaledIntensity = pos.intensity * intensityScale;
+
+ const light = new THREE.RectAreaLight(0xffffff, scaledIntensity, scaledW, scaledH);
+ light.position.set(
+ targetCenter.x + scaledX,
+ targetCenter.y + scaledY,
+ targetCenter.z + scaledZ
+ );
+ light.lookAt(targetCenter);
+ this.scene.add(light);
+
+ this.studioLights.push({
+ light,
+ offset: new THREE.Vector3(scaledX, scaledY, scaledZ),
+ baseIntensity: scaledIntensity
+ });
+ });
+ console.log('自适应灯光已创建,缩放系数:', lightScale, '强度系数:', intensityScale);
+ }
+ /**
+ * 根据模型大小调整相机位置
+ */
+ adjustCameraToModel(modelInfo: ModelInfo) {
+ const { cameraDistance, targetCenter } = this.calculateAdaptiveConfig(modelInfo);
+ // 正面俯视角度:相机在模型正前方,稍高于模型中心
+ // X 轴:模型中心(正面视角,不左右偏移)
+ // Y 轴:相机高度 = 模型中心高度 + 模型高度的 0.3 倍(俯视效果)
+ // Z 轴:相机距离 = 根据模型大小计算的距离
+ const x = targetCenter.x;
+ const y = targetCenter.y + modelInfo.size.y * 0.3; // 相机高度为模型中心的 0.3 倍
+ const z = targetCenter.z + cameraDistance;
- console.log('HDR 环境贴图加载成功:', hdri)
- } catch (error) {
- console.error('HDR 加载失败:', error)
- // 降级方案:使用环境光
- const ambientLight = new THREE.AmbientLight(0xffffff, 0.8)
- scene.add(ambientLight)
+ this.camera.position.set(x, y, z);
+ this.controls.target.copy(targetCenter);
+ this.controls.update();
}
- /**
- * 光源设置
- */
- //点光源
- /**
- * AmbientLight 环境光
- PointLight 点光源
- DirectionalLight 平行光,比如太阳光
- SpotLight 聚光源
- */
-
-
- //设置环境光全亮
- //环境光
- const pointLight = new THREE.AmbientLight(0xffffff,.8);
- // scene.add(pointLight);
-
- function createSoftbox(x, y, z, intensity, w = 5, h = 4) {
+ createSoftbox(x, y, z, intensity, w = 5, h = 4) {
const light = new THREE.RectAreaLight(0xffffff, intensity, w, h);
light.position.set(x, y, z);
light.lookAt(0, 0, 0);
- scene.add(light);
+ this.scene.add(light);
// 存储 offset 向量副本,避免后续重复创建
- studioLights.push({
+ this.studioLights.push({
light,
offset: new THREE.Vector3(x, y, z),
baseIntensity: intensity
});
}
- // 灯光布局优化
- createSoftbox(0, 5.8, 3.5, 3.5, 6, 4.8); // 主光
- createSoftbox(0, 2.8, 7.5, 2.2); // 前光
- createSoftbox(0, 2.8, -7.5, 6.5); // 背光 (增强)
- createSoftbox(-7.5, 2.8, 0, 10.8); // 侧光
- createSoftbox(7.5, 2.8, 0, 2.0);
- createSoftbox(0, -2.2, 3, 0.8, 9, 4); // 地面反光板
-
- // const pointLight = new THREE.AmbientLight(0xffffff,1.0);
- // pointLight.intensity = 1.2//光源强度
- // pointLight.castShadow = true//开启阴影
- // pointLight.shadow.mapSize = new THREE.Vector2(width, height)
- // scene.add(pointLight); //点光源添加到场景中
- // pointLight.position.set(400, 200, 300); //点光源位置
- // pointLight.position.y = 100;
- // pointLight.position.z = 50;
- // pointLight.position.x = 100;
-
- // let floorGeometry = new THREE.PlaneGeometry(5000, 3000)//地板大小
- // let floorMaterial = new THREE.MeshPhongMaterial({ color: "#7e7ab0", })
- // let floorMesh = new THREE.Mesh(floorGeometry, floorMaterial);
- // floorMesh.rotation.x = -0.5 * Math.PI;
- // floorMesh.receiveShadow = true;
- // floorMesh.position.y = -0.001;
- // scene.add(floorMesh);
- const textureLoader = new THREE.TextureLoader();
- // const texture = textureLoader.load('/3dModel/sketch-thick.jpg');
- scene.background = new THREE.Color("#fff");
- return {scene,group,camera,renderer,controls,pointLight,studioLights}
-}
-export const clearModel = (group,scene)=>{
- const oldGroup:any = group.value;
- group.value = new THREE.Group();
- scene.value.add(group.value);
- scene.value.remove(oldGroup);
-}
-
-// 计算模型包围盒
-export const getModelInfo = (model: THREE.Object3D) => {
- const box = new THREE.Box3().setFromObject(model);
- const center = box.getCenter(new THREE.Vector3());
- const size = box.getSize(new THREE.Vector3());
- const maxSize = Math.max(size.x, size.y, size.z);
- return {
- box,
- center,
- size,
- maxSize
- };
-};
-// 根据模型信息计算相机位置
-export const calculateCameraPosition = (
- modelInfo: ReturnType
,
- camera: THREE.PerspectiveCamera,
- options?: {
- distanceFactor?: number; // 距离系数,默认1.5
- heightFactor?: number; // 高度偏移系数,默认0.3
- angle?: number; // 观察角度,默认正前方
- }
-) => {
- const { center, size, maxSize } = modelInfo;
- const fov = camera.fov * (Math.PI / 180);
-
- const distanceFactor = options?.distanceFactor ?? 1.5;
- const heightFactor = options?.heightFactor ?? 0.3;
-
- // 计算合适的相机距离
- const distance = (maxSize / 2) / Math.tan(fov / 2) * distanceFactor;
-
- // 根据角度计算相机位置
- const angle = options?.angle ?? 0; // 0表示正前方
-
- return {
- position: new THREE.Vector3(
- center.x + distance * Math.sin(angle),
- center.y + size.y * heightFactor,
- center.z + distance * Math.cos(angle)
- ),
- target: center.clone(),
- distance,
- center,
- size
- };
-};
-// 根据模型信息计算光源位置
-export const calculateLightPosition = (
- modelInfo: ReturnType,
- options?: {
- xFactor?: number; // X轴偏移系数,默认0.5
- yFactor?: number; // Y轴偏移系数,默认0.8
- zFactor?: number; // Z轴偏移系数,默认0.5
- }
-) => {
- const { center, size } = modelInfo;
-
- const xFactor = options?.xFactor ?? 0.5;
- const yFactor = options?.yFactor ?? 0.8;
- const zFactor = options?.zFactor ?? 0.5;
-
- return new THREE.Vector3(
- center.x + size.x * xFactor,
- center.y + size.y * yFactor,
- center.z + size.z * zFactor
- );
-};
-export const addModel = async (
- url: any,
- controls: OrbitControls,
- camera: THREE.PerspectiveCamera,
- pointLight: THREE.DirectionalLight,
- group: THREE.Group,
- load: any
-) => {
- await new Promise((resolve, reject) => {
- const fbxLoader = new GLTFLoader();
- const drac = new DRACOLoader()
- drac.setDecoderPath('/draco/')
- fbxLoader.setDRACOLoader(drac)
-
- fbxLoader.load(url,
- (obj: any) => {
- const scene = obj.scene;
- scene.traverse((child: any) => {
- if (child.isMesh) {
- // 如果是基础材质,转换为标准材质
- if (child.material instanceof THREE.MeshBasicMaterial) {
- const oldMat = child.material;
- child.material = new THREE.MeshStandardMaterial({
- map: oldMat.map,
- color: oldMat.color,
- roughness: 0.4,
- metalness: 0
- });
- }
- // 如果是标准材质,调整粗糙度
- else if (child.material instanceof THREE.MeshStandardMaterial) {
- child.material.roughness = 0.4;
- child.material.metalness = 0;
- }
- }
- });
- // 获取模型信息
- const modelInfo = getModelInfo(scene);
- const { position: cameraPos, target } = calculateCameraPosition(
- modelInfo,
- camera.value,
- {
- distanceFactor: 1.5,
- heightFactor: 0.3,
- angle: 0 // 正前方
- }
- );
- // 设置相机位置
- camera.value.position.copy(cameraPos);
- // 设置控制器目标点
- controls.value.target.copy(target);
- // 计算并设置光源位置
- const lightPos = calculateLightPosition(modelInfo);
- pointLight.value.position.copy(lightPos);
- // 将模型添加到场景
- group.value.add(scene);
- // 可选:将模型信息存储在模型上,方便后续使用
- (scene as any).userData.modelInfo = modelInfo;
-
- resolve('')
- },
- (xhr: any) => { // 加载进度回调
- const percent = xhr.total == 0 ? 100 : (xhr.loaded / xhr.total * 100).toFixed(2);
- load.value.progress = percent
- console.log('模型加载进度:', percent);
- },
- (error: any) => { // 加载失败回调
- console.error('模型加载失败:', error);
- reject('')
- }
- )
- })
-}
-// 导出当前视图为图片
-export const exportAsImage = (renderer, camera, scene, filename = 'model.png')=>{
- // 渲染当前场景
- renderer.render(scene, camera)
+ async setHDRI(){
+ const rgbeLoader = new RGBELoader()
+ await rgbeLoader.load(CONFIG.hdriUrl, (texture)=>{
+ texture.mapping = THREE.EquirectangularReflectionMapping;
+ const pmremGenerator = new THREE.PMREMGenerator(this.renderer);
+ const envMap = pmremGenerator.fromEquirectangular(texture).texture;
- // 获取 canvas 数据
- const canvas = renderer.domElement
- const dataURL = canvas.toDataURL('image/png')
- return dataURL
+ this.scene.environment = envMap;
+ this.scene.environmentIntensity = CONFIG.hdriIntensity;
+
+ pmremGenerator.dispose();
+ texture.dispose();
+ console.log('✅ HDRI Loaded');
+ });
+ this.createSoftbox(0, 5.8, 3.5, 3.5, 6, 4.8); // 主光
+ this.createSoftbox(0, 2.8, 7.5, 2.2); // 前光
+ this.createSoftbox(0, 2.8, -7.5, 6.5); // 背光 (增强)
+ this.createSoftbox(-7.5, 2.8, 0, 10.8); // 侧光
+ this.createSoftbox(7.5, 2.8, 0, 2.0);
+ this.createSoftbox(0, -2.2, 3, 0.8, 9, 4); // 地面反光板
+ }
+ // 更新工作室光位置
+ updateStudioLighting(){
+ this.camera.getWorldDirection(this.camDir);
+ this.studioLights.forEach(item => {
+ // 使用 applyQuaternion 同步灯光位置
+ this.v1.copy(item.offset).applyQuaternion(this.camera.quaternion);
+ item.light.position.copy(this.controls.target).add(this.v1);
+ item.light.lookAt(this.controls.target);
+
+ // 动态背光逻辑
+ if (item.offset.z < -2) {
+ this.v1.copy(item.light.position).sub(this.controls.target).normalize();
+ const dot = this.v1.dot(this.camDir);
+ const factor = THREE.MathUtils.lerp(CONFIG.backlightBoost.min, CONFIG.backlightBoost.max, Math.max(0, 1 - dot));
+ item.light.intensity = item.baseIntensity * factor;
+ }
+ });
+ }
+ //动态设置曝光
+ updateImagePipeline() {
+ let exposure = 1.0;
+ this.camera.getWorldDirection(this.camForward);
+ this.camToTarget.copy(this.controls.target).sub(this.camera.position).normalize();
+ const facing = Math.abs(this.camForward.dot(this.camToTarget));
+ const targetExposure = THREE.MathUtils.lerp(1.25, 0.90, facing);
+ exposure = THREE.MathUtils.lerp(exposure, targetExposure, 0.08);
+ this.renderer.toneMappingExposure = CONFIG.exposureBase * exposure;
+ }
+
+ async setModel(modelUrl,load){
+ await new Promise((resolve, reject) => {
+ const drac = new DRACOLoader()
+ drac.setDecoderPath('/draco/')
+ const loader = new GLTFLoader().setDRACOLoader(drac);
+
+ if (this.currentModel) {
+ this.scene.remove(this.currentModel);
+ this.dispose(this.currentModel); // 核心优化:释放显存
+ }
+
+ loader.load(modelUrl,
+ (gltf) => {
+ this.currentModel = gltf.scene;
+
+ // 遍历模型:增强材质表现
+ this.currentModel.traverse(child => {
+ if (child.isMesh) {
+ child.castShadow = true;
+ child.receiveShadow = true;
+ if (child.material) {
+ child.material.envMapIntensity = 1.2; // 增强 HDRI 反射强度
+ }
+ }
+ });
+
+ const box = new THREE.Box3().setFromObject(this.currentModel);
+ const center = box.getCenter(new THREE.Vector3());
+ // this.currentModel.position.sub(center);
+ this.scene.add(this.currentModel);
+ this.modelInfo = this.getModelInfo(this.currentModel)
+ // 根据模型大小调整相机位置
+ this.adjustCameraToModel(this.modelInfo);
+
+ // 根据模型大小创建自适应灯光
+ this.createAdaptiveSoftboxes(this.modelInfo);
+ resolve('')
+ },
+ (xhr: any) => { // 加载进度回调
+ const percent = xhr.total == 0 ? 100 : (xhr.loaded / xhr.total * 100).toFixed(2);
+ load.value.progress = percent
+ console.log('模型加载进度:', percent);
+ },
+ (error: any) => { // 加载失败回调
+ console.error('模型加载失败:', error);
+ resolve('')
+ }
+ );
+ })
+ }
+
+ operation(){
+ let this_ = this
+ const animate = () => {
+ requestAnimationFrame(animate);
+ this.controls.update();
+ this.updateStudioLighting();
+ this.updateImagePipeline();
+ this.renderer.render(this.scene, this.camera);
+ }
+ animate();
+ }
+
+ // 释放模型资源
+ dispose(obj){
+ if (!obj) return;
+ obj.traverse(node => {
+ if (node.isMesh) {
+ if (node.geometry) node.geometry.dispose();
+ if (node.material) {
+ if (Array.isArray(node.material)) {
+ node.material.forEach(m => m.dispose());
+ } else {
+ node.material.dispose();
+ }
+ }
+ }
+ });
+ }
+
+
+ exportAsImage(){
+ return this.renderer.domElement.toDataURL('image/png');
+ }
}
\ No newline at end of file
diff --git a/src/components/Canvas/FlowCanvas/components/tools/upload-file.vue b/src/components/Canvas/FlowCanvas/components/tools/upload-file.vue
index 20bb8cf..64d4ed0 100644
--- a/src/components/Canvas/FlowCanvas/components/tools/upload-file.vue
+++ b/src/components/Canvas/FlowCanvas/components/tools/upload-file.vue
@@ -8,8 +8,8 @@