vue threejs
最近要写个vue 的3d展示效果,由于布局问题拾取3d物体不准确,
参考 https://blog.csdn.net/hard_reward/article/details/107859968
<template> <div id="webgl" ref="webgl" class="webgl_style" /> </template> <script> import * as THREE from 'three' import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls.js' import { OutlinePass } from 'three/examples/jsm/postprocessing/OutlinePass.js' import { EffectComposer } from 'three/examples/jsm/postprocessing/EffectComposer.js' import { RenderPass } from 'three/examples/jsm/postprocessing/RenderPass.js' import { FXAAShader } from 'three/examples/jsm/shaders/FXAAShader.js' import { ShaderPass } from 'three/examples/jsm/postprocessing/ShaderPass.js' export default { data() { return { renderer: '', // 渲染器 scene: '', // 场景 camera: '', // 相机 controls: '', // 控制器 composer: '', // outlineSelect effectFXAA: '', // outlineSelect outlinePass: '', // outlineSelect selectedObjects: [], // outlineSelect raycaster: new THREE.Raycaster(), stats: '', // 性能监控器 mygroup: '', // 模型组 action: '', // 控制动画的值 clock: '', // 时钟 mixer: '', // 混合实例 rotateAnimate: '', // 旋转动画 isRotate: 1, // 是否开启旋转 mesh: null, pointer: new THREE.Vector2(), INTERSECTED: null } }, mounted() { this.init() this.animate() }, methods: { init() { this.initScene()// 初始化场景 this.initCamera()// 初始化相机 this.initLight()// 初始化光线 this.Panel() // 地面 this.initRenender()// 初始化渲染器 this.outlineSelect() // 模型选择 this.ininControl() // 初始化控制 this.$refs.webgl.addEventListener('mousemove', this.onWindowResize) // this.$refs.webgl.addEventListener('mousemove', this.launchHover) // }, // 初始化场景 initScene() { this.scene = new THREE.Scene()// 创建场景 // this.scene.background = new THREE.Color(0xcce0ff) this.scene.fog = new THREE.Fog(this.scene.background, 1, 5000) }, // 初始化相机 initCamera() { this.camera = new THREE.PerspectiveCamera(50, document.getElementById('webgl').clientWidth / document.getElementById('webgl').clientHeight, 0.1, 5000)// 创建相机对象 this.camera.position.set(20, 60, 150)// 设置相机位置 this.camera.lookAt(new THREE.Vector3(0, 0, 0)) // 设置相机方向(指向的场景对象) }, // 初始化光线 initLight() { // LIGHTS HemisphereLight const hemiLight = new THREE.HemisphereLight(0xffffff, 0xffffff, 0.4) hemiLight.color.setHSL(0.6, 1, 0.6) hemiLight.groundColor.setHSL(0.095, 1, 0.75) hemiLight.position.set(0, 50, 0) this.scene.add(hemiLight) // 光线加入场景中 const hemiLightHelper = new THREE.HemisphereLightHelper(hemiLight, 10) this.scene.add(hemiLightHelper) // LIGHTS DirectionalLight const dirLight = new THREE.DirectionalLight(0xffffff, 1) dirLight.color.setHSL(0.1, 1, 0.95) dirLight.position.set(-1, 1.75, 1) dirLight.position.multiplyScalar(30) this.scene.add(dirLight) // 光线加入场景中 dirLight.castShadow = true dirLight.shadow.mapSize.width = 2048 dirLight.shadow.mapSize.height = 2048 const d = 50 dirLight.shadow.camera.left = -d dirLight.shadow.camera.right = d dirLight.shadow.camera.top = d dirLight.shadow.camera.bottom = -d dirLight.shadow.camera.far = 15000 dirLight.shadow.bias = -0.0001 const dirLightHelper = new THREE.DirectionalLightHelper(dirLight, 10) this.scene.add(dirLightHelper) }, // 控制 ininControl() { this.controls = new OrbitControls(this.camera, this.renderer.domElement)// 创建控件对象 this.controls.minDistance = 5 this.controls.maxDistance = 220 this.controls.maxPolarAngle = Math.PI * 0.4 // 最大角度 地平面与camera this.controls.enablePan = true this.controls.enableDamping = true this.controls.dampingFactor = 0.05 }, // 初始化渲染器 ——指定容器 initRenender() { this.renderer = new THREE.WebGLRenderer() // 创建渲染器 this.renderer.shadowMap.enabled = true // 倒影阴影贴图 this.renderer.setClearColor(0xb9d3ff, 1) // 设置背景颜色 this.renderer.setPixelRatio(window.devicePixelRatio) // 接口返回当前显示设备 的物理像素分辨率与CSS 像素分辨率 //高清 this.renderer.setSize(document.getElementById('webgl').clientWidth, document.getElementById('webgl').clientHeight) this.$refs.webgl.appendChild(this.renderer.domElement) }, // 定义窗口的设置 onWindowResize() { const container = document.getElementById('webgl') this.camera.aspect = container.clientWidth / container.clientHeight this.renderer.setSize(container.clientWidth, container.clientHeight) this.camera.updateProjectionMatrix() this.effectFXAA.uniforms[ 'resolution' ].value.set(1 / container.clientWidth, 1 / container.clientHeight) }, // 地面 Panel() { // 在平面上创建栅格覆盖 var grid = new THREE.GridHelper(175, 20, 0xffffff, 0xffffff) grid.material.opacity = 0.5 grid.material.transparent = true grid.position.y = 0.005 this.scene.add(grid) // 创建Threejs平面 const blockPlane = new THREE.Mesh( new THREE.BoxBufferGeometry(), new THREE.MeshPhongMaterial({ color: 0xffffff, transparent: true, opacity: 0.25 }) ) const pos = { x: 0, y: -0.25, z: 0 } const scale = { x: 175, y: 0.5, z: 175 } blockPlane.position.set(pos.x, pos.y, pos.z) blockPlane.scale.set(scale.x, scale.y, scale.z) blockPlane.receiveShadow = true blockPlane.name = '地板' this.scene.add(blockPlane) }, outlineSelect() { this.composer = new EffectComposer(this.renderer) const renderPass = new RenderPass(this.scene, this.camera) this.composer.addPass(renderPass) const container = document.getElementById('webgl') this.outlinePass = new OutlinePass(new THREE.Vector2(container.clientWidth, container.clientHeight), this.scene, this.camera) this.outlinePass.edgeStrength = Number(5)// 边缘长度 this.outlinePass.edgeThickness = Number(3.6)// 边缘厚度 值越小越明显 this.outlinePass.pulsePeriod = Number(2.9) // 一闪一闪周期 this.outlinePass.visibleEdgeColor.set('#ffff00')// 没有被遮挡的outline的颜色 this.outlinePass.hiddenEdgeColor.set(0xff0000)// 被遮挡的outline的颜色 this.composer.addPass(this.outlinePass) this.effectFXAA = new ShaderPass(FXAAShader) this.effectFXAA.uniforms['resolution'].value.set( 1 / window.innerWidth, 1 / window.innerHeight ) this.composer.addPass(this.effectFXAA) this.renderer.domElement.style.touchAction = 'none' // CSSStyleDeclaration this.$refs.webgl.addEventListener('pointermove', this.onPointerMove) }, onPointerMove(event) { if (event.isPrimary === false) return var mouse = new THREE.Vector2() const container = document.getElementById('webgl') const getBoundingClientRect = container.getBoundingClientRect() // 获取元素位置 确定三维坐标 mouse.x = ((event.clientX - getBoundingClientRect.left) / container.offsetWidth) * 2 - 1 mouse.y = -((event.clientY - getBoundingClientRect.top) / container.offsetHeight) * 2 + 1 this.raycaster.setFromCamera(mouse, this.camera) const intersects = this.raycaster.intersectObject(this.scene, true) if (intersects.length > 0) { const selectedObject = intersects[0].object this.addSelectedObject(selectedObject) this.outlinePass.selectedObjects = this.selectedObjects } else { this.outlinePass.selectedObjects = [] } }, addSelectedObject(object) { this.selectedObjects = [] this.selectedObjects.push(object) }, launchHover(event) { event.preventDefault() var mouse = new THREE.Vector2() const container = document.getElementById('webgl') const getBoundingClientRect = container.getBoundingClientRect() // 获取元素位置 确定三维坐标 mouse.x = ((event.clientX - getBoundingClientRect.left) / container.offsetWidth) * 2 - 1 mouse.y = -((event.clientY - getBoundingClientRect.top) / container.offsetHeight) * 2 + 1 var raycaster = new THREE.Raycaster() raycaster.setFromCamera(mouse, this.camera) var intersects = raycaster.intersectObjects([this.scene], true) if (intersects.length > 0) { document.querySelector('body').style.cursor = 'pointer' } else { document.querySelector('body').style.cursor = 'default' } }, // 动画 animate() { this.controls.update() // Update controls this.renderer.render(this.scene, this.camera)// Render 渲染 this.composer.render() requestAnimationFrame(this.animate) // Call tick again on the next frame } } } </script> <style> .webgl_style{ position: absolute; width: 100%; height: calc(100vh - 84px); /* 高度减去 面包屑-撑满 */ } </style>