Three.js中如何使用CSS3DRenderer和CSS3DSprite实现模型标签文字
导语
在Three.js
中,使用CSS3DRenderer
和CSS3DSprite
可以轻松地实现模型标签文字的效果,为场景中的模型提供更直观的信息展示。本文将介绍如何使用这两个工具来实现模型标签文字,并提供相应的代码示例。
引言
Three.js
是一款强大的JavaScript 3D
库,用于在Web上
创建交互式的3D
图形应用程序。在Three.js
中,CSS3DRenderer
和CSS3DSprite
是两个重要的工具,它们可以用于在3D
场景中渲染HTML
元素,为用户提供更丰富的交互体验。
实现的效果
实现模型标签文字的步骤
步骤一、导入所需库
// Three.js库 import * as THREE from 'three'; // CSS3DRenderer用于渲染CSS3D对象 import { CSS3DRenderer, CSS3DSprite } from "three/examples/jsm/renderers/CSS3DRenderer.js";
步骤二、初始化CSS3DRenderer
const labelRenderer = new CSS3DRenderer(); labelRenderer.setSize(window.innerWidth, window.innerHeight); // 设置渲染器尺寸 labelRenderer.domElement.style.position = 'absolute'; // 设置渲染器样式 labelRenderer.domElement.style.top = '0'; // 设置渲染器样式 document.body.appendChild(labelRenderer.domElement); // 将渲染器挂载到页面上
步骤三、创建css3D标签
const div = document.createElement('div'); div.className = 'workshop-text'; // 添加样式类 div.innerHTML = '<p>浮法车间</p>'; // 添加标签文字
步骤四、创建CSS3DSprite对象
const sprite = new CSS3DSprite(div); sprite.position.set(8, 3, 42); // 设置标签位置,这里根据模型具体位置调整
步骤五、将CSS3DSprite添加到模型中,并通过GUI控制器控制文字位置
object.add(sprite); // object是模型对象,这里需要替换为实际的模型对象 // 在GUI中添加文件夹用于调整标签位置 const tagFolder = gui.addFolder('浮法车间标签'); tagFolder.add(sprite.position, 'x', -200, 200).name('X Position'); // 调整标签x位置 tagFolder.add(sprite.position, 'y', -200, 200).name('Y Position'); // 调整标签y位置 tagFolder.add(sprite.position, 'z', -200, 200).name('Z Position'); // 调整标签z位置 tagFolder.open(); // 打开文件夹,默认显示控制器
所有代码:
<template> <!-- 用于展示Three.js场景的HTML容器 --> <div id="my-three"></div> </template> <script setup> import { ref, onMounted, getCurrentInstance } from 'vue' import * as THREE from 'three' // 导入Three.js库 import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls' // 导入轨道控制器以实现场景的旋转、缩放等交互 import { GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader'; // 导入GLTF模型加载器 import { GUI } from "three/examples/jsm/libs/lil-gui.module.min.js"; import { CSS3DRenderer, CSS3DSprite } from "three/examples/jsm/renderers/CSS3DRenderer.js"; const { proxy } = getCurrentInstance(); // 获取当前Vue组件实例 const gui = new GUI(); // 设置cube纹理加载器,立方体纹理加载器 const cubeTextureLoader = new THREE.CubeTextureLoader(); // 设置环境贴图 const envMapTexture = cubeTextureLoader.load([ "../../public/posx.jpg", "../../public/negx.jpg", "../../public/posy.jpg", "../../public/negy.jpg", "../../public/posz.jpg", "../../public/negz.jpg", ]); onMounted(() => { document.getElementById('my-three')?.appendChild(renderer.domElement) // 将渲染器的DOM元素挂载到页面上 init() // 初始化场景、相机和光源 renderModel() // 设置渲染参数 gltfModel1() // 加载GLTF模型 render() // 启动渲染循环 }) // 定义场景宽度和高度 const width = window.innerWidth, height = window.innerHeight; const scene = new THREE.Scene(); // 创建场景 const renderer = new THREE.WebGLRenderer() // 创建渲染器 const loader = new GLTFLoader(); // 创建GLTF加载器 const camera = new THREE.PerspectiveCamera(45, width / height, 1, 10000); // 创建透视相机 const controls = new OrbitControls(camera, renderer.domElement) // 创建控制器 let glbModel; function init() { // 光源设置 const ambient = new THREE.AmbientLight(0xffffff, 0.5); // 添加环境光 scene.add(ambient); const directionalLight = new THREE.DirectionalLight(0xffffff, 0.8);// 添加平行光 directionalLight.position.set(95, 585, 39); // 设置光源位置 scene.add(directionalLight); //设置相机位置 camera.position.set(-20, 300, 700); // 设置相机位置 //设置相机方向 camera.lookAt(0, 0, 0); // 设置相机朝向场景中心 //辅助坐标轴 const axesHelper = new THREE.AxesHelper(200);//参数200标示坐标系大小,可以根据场景大小去设置 scene.add(axesHelper); // 设置场景背景色 // scene.background = envMapTexture; // 设置渲染器像素比,适应设备分辨率 renderer.setPixelRatio(window.devicePixelRatio); renderer.antialias = true; // 初始化 GUI 控件 const camFolder = gui.addFolder('Camera'); camFolder.add(camera.position, 'x', -6500, 6500, 10).name('X Axis'); camFolder.add(camera.position, 'y', -6500, 6500, 10).name('Y Axis'); camFolder.add(camera.position, 'z', -6500, 6500, 10).name('Z Axis'); camFolder.open(); const lightFolder = gui.addFolder('Light'); lightFolder.add(directionalLight.position, 'x', -6500, 6500).name('X Axis'); lightFolder.add(directionalLight.position, 'y', -6500, 6500).name('Y Axis'); lightFolder.add(directionalLight.position, 'z', -6500, 6500).name('Z Axis'); lightFolder.add(directionalLight, 'intensity', 0, 1).name('Intensity'); lightFolder.open(); // 设置渲染器参数 renderer.setPixelRatio(window.devicePixelRatio); renderer.setSize(window.innerWidth, window.innerHeight); renderer.toneMapping = THREE.ACESFilmicToneMapping; renderer.toneMappingExposure = 2.5; // 初始化 CSS3DRenderer const labelRender = new CSS3DRenderer(); labelRender.setSize(window.innerWidth, window.innerHeight); labelRender.domElement.style.position = 'absolute'; labelRender.domElement.style.top = '0px'; labelRender.domElement.style.pointerEvents = 'none'; document.getElementById('my-three').appendChild(labelRender.domElement); proxy.labelRender = labelRender; // 更新控制器 controls.update(); } function gltfModel1() { // 加载GLTF模型 loader.load("../../public/yuanqu.glb", function (gltf) { // 模型加载完成后的回调函数 glbModel = gltf.scene; scene.add(gltf.scene) // 将模型添加到场景中 glbModel.traverse((object) => { if (object.name === "fufachejian") { const div = document.createElement('div'); div.className = 'workshop-text'; div.innerHTML = '<p>浮法车间</p>'; // 创建CSS3DSprite const tag = new CSS3DSprite(div); tag.position.set(8, 3, 42); // 调整标签位置 object.add(tag); // 在GUI中添加一个用于调整tag位置的文件夹 // const tagFolder = gui.addFolder('浮法车间标签'); // // 将tag.position对象中的x, y, z属性添加到GUI中 // tagFolder.add(tag.position, 'x', -200, 200).name('X Position'); // tagFolder.add(tag.position, 'y', -200, 200).name('Y Position'); // tagFolder.add(tag.position, 'z', -200, 200).name('Z Position'); // tagFolder.open(); // 打开此文件夹以默认显示控制器 } if (object.name === "bangongqu") { const div = document.createElement('div'); div.className = 'workshop-text'; div.innerHTML = '<p>办公楼</p>'; // 创建CSS3DSprite const tag = new CSS3DSprite(div); tag.position.set(8, 3, 78); // 调整标签位置 object.add(tag); // 在GUI中添加一个用于调整tag位置的文件夹 // const tagFolder = gui.addFolder('办公区标签'); // // 将tag.position对象中的x, y, z属性添加到GUI中 // tagFolder.add(tag.position, 'x', -200, 200).name('X Position'); // tagFolder.add(tag.position, 'y', -200, 200).name('Y Position'); // tagFolder.add(tag.position, 'z', -200, 200).name('Z Position'); // tagFolder.open(); // 打开此文件夹以默认显示控制器 } if (object.name === "qiye_01") { const div = document.createElement('div'); div.className = 'workshop-text'; div.innerHTML = '<p>企业1</p>'; // 创建CSS3DSprite const tag = new CSS3DSprite(div); tag.position.set(8, 3, 38); // 调整标签位置 object.add(tag); } if (object.name === "qiye_002") { const div = document.createElement('div'); div.className = 'workshop-text'; div.innerHTML = '<p>企业2</p>'; // 创建CSS3DSprite const tag = new CSS3DSprite(div); tag.position.set(12, 5, 54); // 调整标签位置 object.add(tag); // 在GUI中添加一个用于调整tag位置的文件夹 // const tagFolder = gui.addFolder('qiye--------'); // // 将tag.position对象中的x, y, z属性添加到GUI中 // tagFolder.add(tag.position, 'x', -200, 200).name('X Position'); // tagFolder.add(tag.position, 'y', -200, 200).name('Y Position'); // tagFolder.add(tag.position, 'z', -200, 200).name('Z Position'); // tagFolder.open(); // 打开此文件夹以默认显示控制器 } if (object.name === "chejian_06") { const div = document.createElement('div'); div.className = 'workshop-text'; div.innerHTML = '<p>车间6</p>'; // 创建CSS3DSprite const tag = new CSS3DSprite(div); tag.position.set(8, 3, 38); // 调整标签位置 object.add(tag); // 在GUI中添加一个用于调整tag位置的文件夹 // const tagFolder = gui.addFolder('食堂标签'); // // 将tag.position对象中的x, y, z属性添加到GUI中 // tagFolder.add(tag.position, 'x', -200, 200).name('X Position'); // tagFolder.add(tag.position, 'y', -200, 200).name('Y Position'); // tagFolder.add(tag.position, 'z', -200, 200).name('Z Position'); // tagFolder.open(); // 打开此文件夹以默认显示控制器 } if (object.name === "chejian_03") { const div = document.createElement('div'); div.className = 'workshop-text'; div.innerHTML = '<p>仓库3</p>'; // 创建CSS3DSprite const tag = new CSS3DSprite(div); tag.position.set(8, 3, 38); // 调整标签位置 object.add(tag); // 在GUI中添加一个用于调整tag位置的文件夹 // const tagFolder = gui.addFolder('食堂标签'); // // 将tag.position对象中的x, y, z属性添加到GUI中 // tagFolder.add(tag.position, 'x', -200, 200).name('X Position'); // tagFolder.add(tag.position, 'y', -200, 200).name('Y Position'); // tagFolder.add(tag.position, 'z', -200, 200).name('Z Position'); // tagFolder.open(); // 打开此文件夹以默认显示控制器 } if (object.name === "chejian_02") { const div = document.createElement('div'); div.className = 'workshop-text'; div.innerHTML = '<p>仓库2</p>'; // 创建CSS3DSprite const tag = new CSS3DSprite(div); tag.position.set(8, 3, 38); // 调整标签位置 object.add(tag); // 在GUI中添加一个用于调整tag位置的文件夹 // const tagFolder = gui.addFolder('食堂标签'); // // 将tag.position对象中的x, y, z属性添加到GUI中 // tagFolder.add(tag.position, 'x', -200, 200).name('X Position'); // tagFolder.add(tag.position, 'y', -200, 200).name('Y Position'); // tagFolder.add(tag.position, 'z', -200, 200).name('Z Position'); // tagFolder.open(); // 打开此文件夹以默认显示控制器 } if (object.name === "chejian_01") { const div = document.createElement('div'); div.className = 'workshop-text'; div.innerHTML = '<p>仓库1</p>'; // 创建CSS3DSprite const tag = new CSS3DSprite(div); tag.position.set(8, 3, 38); // 调整标签位置 object.add(tag); // 在GUI中添加一个用于调整tag位置的文件夹 // const tagFolder = gui.addFolder('食堂标签'); // // 将tag.position对象中的x, y, z属性添加到GUI中 // tagFolder.add(tag.position, 'x', -200, 200).name('X Position'); // tagFolder.add(tag.position, 'y', -200, 200).name('Y Position'); // tagFolder.add(tag.position, 'z', -200, 200).name('Z Position'); // tagFolder.open(); // 打开此文件夹以默认显示控制器 } if (object.name === "yuanliaolou") { const div = document.createElement('div'); div.className = 'workshop-text'; div.innerHTML = '<p>原料楼</p>'; // 创建CSS3DSprite const tag = new CSS3DSprite(div); tag.position.set(-10, -1, 38); // 调整标签位置 object.add(tag); // 在GUI中添加一个用于调整tag位置的文件夹 const tagFolder = gui.addFolder('食堂标签'); } if (object.name === "qizhan") { const div = document.createElement('div'); div.className = 'workshop-text'; div.innerHTML = '<p>气站</p>'; // 创建CSS3DSprite const tag = new CSS3DSprite(div); tag.position.set(-10, -1, 20); // 调整标签位置 object.add(tag); // 在GUI中添加一个用于调整tag位置的文件夹 // const tagFolder = gui.addFolder('食堂标签'); } if (object.name === "yuanpian") { const div = document.createElement('div'); div.className = 'workshop-text'; div.innerHTML = '<p>原片仓库</p>'; // 创建CSS3DSprite const tag = new CSS3DSprite(div); tag.position.set(-6, 7, 30); // 调整标签位置 object.add(tag); } if (object.name === "yuchuli002" || object.name === "yuchuli003" || object.name === "yuchuli") { const div = document.createElement('div'); div.className = 'workshop-text'; if (object.name === "yuchuli002") { div.innerHTML = '<p>预处理车间1</p>'; } else if(object.name === "yuchuli003") { div.innerHTML = '<p>预处理车间2</p>'; }else if(object.name === "yuchuli"){ div.innerHTML = '<p>预处理车间1</p>'; } // 创建CSS3DSprite const tag = new CSS3DSprite(div); tag.position.set(-6, 7, 30); // 调整标签位置 object.add(tag); } }) }, function (xhr) { // 加载进度回调 const percent = Math.floor((xhr.loaded / xhr.total) * 100); // 计算加载进度百分比 // console.log(`模型加载进度:${percent}%`); } ) } function renderModel() { //渲染 renderer.setSize(width, height)//设置渲染区尺寸 renderer.render(scene, camera)//执行渲染操作、指定场景、相机作为参数 // renderer.setClearColor(0x00ff00); // 设置背景颜色为绿色/ renderer.toneMapping = THREE.ACESFilmicToneMapping; // 设置曝光度 renderer.toneMappingExposure = 3; // 适当调整曝光度 // 设置控制器的角度限制 controls.minPolarAngle = Math.PI / 4; // 最小极角为 45 度 controls.maxPolarAngle = Math.PI / 2; // 最大极角为 90 度 } //渲染循环函数 function render() { renderer.render(scene, camera); // 执行渲染操作 controls.update() // 更新控制器 proxy.labelRender.render(scene, camera); // 渲染 CSS3D 标签 requestAnimationFrame(render); // 请求下一帧 } // 画布跟随窗口变化 window.onresize = function () { renderer.setSize(window.innerWidth, window.innerHeight); // 重设渲染器尺寸 camera.aspect = window.innerWidth / window.innerHeight; // 更新相机的长宽比 camera.updateProjectionMatrix(); // 更新相机的投影矩阵 }; window.addEventListener('keydown', function (event) { switch (event.key) { case 'ArrowLeft': // 按左箭头键 moveLeft(); break; case 'ArrowRight': // 按右箭头键 moveRight(); break; } }); // 交互事件 addEventListener('dblclick', onMouseDblclick, false) function onMouseDblclick(event) { console.log("event") let intersects = getIntersects(event); if (intersects.length !== 0 && intersects[0].object instanceof THREE.Mesh) { const selectedObject = intersects[0].object; let selectedObjects = []; selectedObjects.push(selectedObject); console.log(selectedObjects) // outlinePass.selectedObjects = selectedObjects; } } function moveLeft() { if (glbModel) { glbModel.position.x -= 10; // 向左移动10单位 } } function moveRight() { if (glbModel) { glbModel.position.x += 10; // 向右移动10单位 } } //获取与射线相交的对象数组 function getIntersects(event) { let rayCaster = new THREE.Raycaster(); let mouse = new THREE.Vector2(); //通过鼠标点击位置,计算出raycaster所需点的位置,以屏幕为中心点,范围-1到1 mouse.x = (event.clientX / window.innerWidth) * 2 - 1; mouse.y = -(event.clientY / window.innerHeight) * 2 + 1; //这里为什么是-号,没有就无法点中 //通过鼠标点击的位置(二维坐标)和当前相机的矩阵计算出射线位置 rayCaster.setFromCamera(mouse, camera); return rayCaster.intersectObjects(scene.children); } </script> <style> .workshop-text {} .workshop-text p { font-size: 0.9rem; font-weight: bold; padding: 10px; color: #0ff; } #tag { padding: 0px 10px; border: #00ffff solid 1px; height: 40px; border-radius: 5px; width: 65px; } </style>
效果:
到此这篇关于在Three.js中使用CSS3DRenderer和CSS3DSprite实现模型标签文字的文章就介绍到这了,更多相关Three.js模型标签文字内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!
相关文章
bootstrap如何让dropdown menu按钮式下拉框长度一致
bootstrap框架提供了下拉菜单组件(dropdown),即点击一个元素或按钮,触发隐藏的列表显示出来。下面通过本文给大家介绍bootstrap如何让dropdown menu按钮式下拉框长度一致,需要的朋友可以参考下2017-04-04
最新评论