three.js 添加viewCUbe/视图控制/视角指示

本文档展示了如何使用Three.js库创建一个透视相机并设置坐标轴显示,同时介绍了viewCube的实现,包括坐标方向显示及后续视角控制、重置和恢复功能的计划。代码中详细说明了相机、viewCube以及坐标轴的创建和渲染过程。

效果:

 功能:

目前实现了viewCube的坐标方向显示

后续实现viewCube视角控制/重置/恢复

代码:

//【相机】主相机
			camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 1, 20000000000);//创建一个透视相机 可视角度45度 浏览器的全屏的宽高 水平视锥 最近1 最远2000
			camera.position.set(3000, 4000, 3000);
			camera.lookAt(scene.position);
			scene.add(camera);
			//次相机
			var aspect = window.innerWidth / window.innerHeight;
			var frustumSize = 200;
			cameraOrtho = new THREE.OrthographicCamera(frustumSize * aspect / -2, frustumSize * aspect / 2, frustumSize / 2, frustumSize / -2, 0.1, 20000000000);
			sceneOrtho.add(cameraOrtho);
//【viewCube】
			var materialArr = [];
			materialArr.push(new THREE.MeshBasicMaterial({ color: 0x009e60 }));
			materialArr.push(new THREE.MeshBasicMaterial({ color: 0x0051ba }));
			materialArr.push(new THREE.MeshBasicMaterial({ color: 0xffd500 }));
			materialArr.push(new THREE.MeshBasicMaterial({ color: 0xff5800 }));
			materialArr.push(new THREE.MeshBasicMaterial({ color: 0xc41e3a }));
			materialArr.push(new THREE.MeshBasicMaterial({ color: 0xff00ff }));
			var viewBox = new THREE.Mesh(new THREE.BoxBufferGeometry(90, 70, 90, 1, 1, 1), materialArr);
			sceneOrtho.add(viewBox);
			//【坐标轴】
			var axes = new THREE.AxesHelper(100);
			viewBox.add(axes);
//【渲染】
renderScene();
function renderScene() {
			orbitControls.update();//相机控制刷新
			requestAnimationFrame(renderScene);//动画循环渲染起来
			//主场景
			renderer.setViewport(0, 0, window.innerWidth, window.innerHeight);//主场景视区
			renderer.autoClear = false;//【scene.autoClear一定要关闭】
			renderer.render(scene, camera);//渲染
			//次场景:1.复制主场景相机的位置、四元数,2.设置场景视区,3.渲染
			cameraOrtho.position.copy(camera.position);
			cameraOrtho.quaternion.copy(camera.quaternion);//Quaternion(表示对象局部旋转的四元数)
			cameraOrtho.lookAt(scene.position);
			renderer.setViewport(window.innerWidth - 150, window.innerHeight - 150, 150, 150);//【设置次场景视区视口,(x, y,width,height),用来显示viewCube】
			renderer.render(sceneOrtho, cameraOrtho);
		};

<!-- components/MiddlePanel.vue --> <script setup lang="ts"> import { onMounted, onBeforeUnmount } from 'vue'; import * as THREE from 'three'; import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls.js'; let scene: THREE.Scene, camera: THREE.PerspectiveCamera, renderer: THREE.WebGLRenderer, controls: OrbitControls; onMounted(() => { initThree(); initGrid(); initControllers(); setupDragAndDrop(); animate(); }); function initThree() { scene = new THREE.Scene(); window.THREE_SCENE = scene; // 挂载到全局用于导出 camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000); camera.position.set(5, 5, 5); camera.lookAt(0, 0, 0); renderer = new THREE.WebGLRenderer({ antialias: true }); renderer.setSize(window.innerWidth - 620, window.innerHeight - 200); // 留出左右和底部空间 renderer.setClearColor(0xeeeeee); document.querySelector('.middle-panel')?.appendChild(renderer.domElement); // 添加坐标轴辅助 const axesHelper = new THREE.AxesHelper(10); scene.add(axesHelper); } function initGrid() { const grid = new THREE.GridHelper(50, 50); scene.add(grid); } function initControllers() { controls = new OrbitControls(camera, renderer.domElement); controls.enableDamping = true; } function setupDragAndDrop() { renderer.domElement.addEventListener('dragover', (e) => { e.preventDefault(); e.dataTransfer!.dropEffect = 'copy'; }); renderer.domElement.addEventListener('drop', async (e) => { e.preventDefault(); const files = e.dataTransfer!.files; for (const file of files) { if (file.name.endsWith('.glb')) { const url = URL.createObjectURL(file); await loadGLB(url, file.name.replace('.glb', '')); URL.revokeObjectURL(url); } } }); } async function loadGLB(url: string, name: string) { const { GLTFLoader } = await import('three/examples/jsm/loaders/GLTFLoader.js'); const loader = new GLTFLoader(); const gltf = await loader.loadAsync(url); const model = gltf.scene; model.name = name; model.position.set(0, 0, 0); scene.add(model); // 同步到左侧场景树 useSceneTreeStore().addToTree(model); } function animate() { requestAnimationFrame(animate); controls.update(); renderer.render(scene, camera); } onBeforeUnmount(() => { renderer.dispose(); controls.dispose(); }); </script> <template> <div class="three-editor" style="position: relative; height: 100%;"> <!-- 左下角坐标 --> <div style="position: absolute; bottom: 20px; left: 20px; color: black; font-size: 12px;"> X: {{ camera.position.x.toFixed(2) }} | Y: {{ camera.position.y.toFixed(2) }} | Z: {{ camera.position.z.toFixed(2) }} </div> <!-- 右下角视图控制器 --> <div style="position: absolute; bottom: 20px; right: 20px;"> <ViewCube :camera="camera" /> </div> <!-- 右上角按钮 --> <div style="position: absolute; top: 20px; right: 20px; display: flex; gap: 10px;"> <button @click="saveScene" class="btn">💾 保存</button> <button @click="previewScene" class="btn">👁️ 预览</button> <button @click="publishScene" class="btn">🚀 发布</button> </div> </div> </template> <style scoped> .btn { padding: 8px 12px; background: #1976d2; color: white; border: none; border-radius: 4px; cursor: pointer; } </style> Property 'THREE_SCENE' does not exist on type 'Window & typeof globalThis'. Cannot find name 'useSceneTreeStore'. Property 'saveScene' does not exist on type '{ camera: PerspectiveCamera; $: ComponentInternalInstance; $data: {}; $props: {}; $attrs: Data; $refs: Data; $slots: Readonly<InternalSlots>; ... 12 more ...; $router: Router; }'.
最新发布
12-19
评论 4
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值