在 three.js 中,可以通过添加事件监听器来实现点击交互事件。具体步骤如下:
1. 获取场景中的所有物体,并为每个物体添加一个点击事件监听器。
javascript
scene.traverse(function(object){if(object instanceofTHREE.Mesh){
object.addEventListener('click',function(){// 处理点击事件 });}});
2. 在点击事件处理函数中,可以获取到被点击的物体对象,并进行相应的操作。
functionhandleClick(event){var mouse =newTHREE.Vector2();
mouse.x =(event.clientX / window.innerWidth)*2-1;
mouse.y =-(event.clientY / window.innerHeight)*2+1;var raycaster =newTHREE.Raycaster();
raycaster.setFromCamera(mouse, camera);var intersects = raycaster.intersectObjects(scene.children,true);if(intersects.length >0){var clickedObject = intersects[0].object;// 处理被点击的物体对象 }}
需要注意的是,有时候点击的物体与看到的物体不一致,这可能是由于非全屏或多边形偏移等问题导致的。解决方法如下:
1. 非全屏问题:在创建渲染器时,将 canvas 的宽高设置为窗口的宽高。
var renderer =newTHREE.WebGLRenderer({canvas: canvas });
renderer.setSize(window.innerWidth, window.innerHeight);
2. 多边形偏移问题:在创建材质时,设置 polygonOffset 属性。
var material =newTHREE.MeshBasicMaterial({color:0xffffff,polygonOffset:true,polygonOffsetFactor:1,polygonOffsetUnits:1});
通过以上方法,可以实现 three.js 中的点击交互事件,并解决点击的物体与看到的不一致问题。
示例
<template><div class="hello"><div class="tip"><canvas id="three"></canvas></div><Mabtn @cocorbtn="cocorbtn" @caizhibtn="caizhibtn" @btnopen="btnopen"/></div></template><script>import Mabtn from"../components/mabtn.vue";import*asTHREEfrom'three'import{ GLTFLoader }from'three/examples/jsm/loaders/GLTFLoader.js'// 添加轨道控制器import{ OrbitControls }from'three/examples/jsm/controls/OrbitControls'import{ EffectComposer }from"three/examples/jsm/postprocessing/EffectComposer"import{RenderPass}from"three/examples/jsm/postprocessing/RenderPass"import{OutlinePass}from"three/examples/jsm/postprocessing/OutlinePass"import{ShaderPass}from"three/examples/jsm/postprocessing/ShaderPass"import{FXAAShader}from"three/examples/jsm/shaders/FXAAShader"import{setcolor}from'../../public/setColor'varOBJ=''exportdefault{name:'HelloWorld',components:{
Mabtn
},data(){return{renderer:null,nameNode:null,composer:null,outlinePass:null,renderPass:null,scene:null,camera:null,gltfscene:null,bujian:'',faguang:false,}},mounted(){this.initThree()// this.render()},methods:{btnopen(){// document.querySelector('#three').style.height='100%'},cocorbtn(b){
console.log(b);
console.log(this.faguang);if(!this.faguang){if(this.nameNode.children.length>0){//点击颜色let img =setcolor(b)// 更换纹理贴图var texture =newTHREE.TextureLoader().load(img +'.png');OBJ.material.map = texture
for(let i =0; i <this.nameNode.children.length; i++){this.nameNode.children[i].material.color.set(b)}}else{this.nameNode.material.color.set(b)}}},caizhibtn(a){//点击部件
console.log(a);this.bujian=a
this.nameNode=this.gltfscene.getObjectByName(a)if(this.nameNode.children.length>0){//点击颜色this.outlineObj([this.nameNode.children[0]])OBJ=this.nameNode.children[0]
console.log(this.nameNode.children[0]);}else{this.outlineObj([this.nameNode])OBJ=this.nameNode
}this.faguang=!this.faguang
},initThree(){let that =thisthis.scene =newTHREE.Scene()// 创建一个scene this.scene.background =newTHREE.Color('#eee')// 背景颜色const canvas = document.querySelector('#three')// 渲染器锯齿属性.antialias
that.renderer =newTHREE.WebGLRenderer({ canvas,antialias:true})// 创建一个WebGLRenderer,将canvas和配置参数传入// 引入3D模型 gltf放置public目录 const gltfLoader =newGLTFLoader()
gltfLoader.load('/ShoeOne/ShoeOne.gltf',(gltf)=>{let model = gltf.scene
// 递归遍历所有模型节点批量修改材质
gltf.scene.traverse(function(obj){if(obj.isMesh){//判断是否是网格模型// console.log('模型节点',obj);// console.log('模型节点名字',obj.name);// console.log('gltf默认材质',obj.material); }});
console.log(gltf.scene)this.gltfscene = gltf.scene
// 设置模型离中心点的位置// gltf.scene.scale.set(40,40,40)// gltf.scene.position.x = 0// gltf.scene.position.y = -80// gltf.scene.position.z = 0// console.log(nameNode);// for (let i = 0; i < nameNode.children.length; i++) {// nameNode.children[i].material.color.set(0xff0000)// nameNode.children[i].position.x = 0// nameNode.children[i].position.y = -10// nameNode.children[i].position.z = 0// } // const nameNode = gltf.scene.getObjectByName("内里");// nameNode.material.color.set(0xff0000);//改变Mesh材质颜色this.scene.add(model)})
that.renderer.domElement.addEventListener('click', that.modelMouseClick,false)//设置点击方法
that.renderer.setSize(window.innerWidth, window.innerHeight)const hemLight =newTHREE.HemisphereLight(0xffffff,0xffffff,1)
hemLight.position.set(0,48,0)this.scene.add(hemLight)//平行光 (这里可以用点光源PointLight和环境光AmbientLight没有特定方向,整体改变场景的光照明暗)const dirLight =newTHREE.DirectionalLight(0xffffff,1)//光源等位置
dirLight.position.set(-10,8,-5)//使用PerspectiveCamera(透视摄像机):透视相机用来模拟人眼所看到的景象,物体的大小会受远近距离的影响,它是3D场景的渲染中使用得最普遍的投影模式。this.camera =newTHREE.PerspectiveCamera(50,
window.innerWidth / window.innerHeight,1,1000)// camera.position.z = 10 // 正方位this.camera.position.set(3,4,3);const controls =newOrbitControls(this.camera, that.renderer.domElement)// 阻尼感
controls.enableDamping =true// 动画循环函数functionanimate(){
controls.update()
that.renderer.render(that.scene, that.camera)requestAnimationFrame(animate)if(resizeRendererToDisplaySize(that.renderer)){const canvas = that.renderer.domElement
that.camera.aspect = canvas.clientWidth / canvas.clientHeight
that.camera.updateProjectionMatrix()}if(that.composer){
that.composer.render()}}animate()// 物理像素分辨率与CSS像素分辨率functionresizeRendererToDisplaySize(renderer){const canvas = renderer.domElement
var width = window.innerWidth
var height = window.innerHeight
var canvasPixelWidth = canvas.width / window.devicePixelRatio
var canvasPixelHeight = canvas.height / window.devicePixelRatio
const needResize =
canvasPixelWidth !== width || canvasPixelHeight !== height
if(needResize){
renderer.setSize(width, height,false)}return needResize
}},// 窗口监听函数onWindowResize(){this.camera.aspect = window.innerWidth / window.innerHeight;this.camera.updateProjectionMatrix();this.renderer.setSize(window.innerWidth, window.innerHeight);},// 模型的点击事件modelMouseClick(event){let raycaster =newTHREE.Raycaster();let mouse =newTHREE.Vector2();// 将鼠标位置归一化为设备坐标。x 和 y 方向的取值范围是 (-1 to +1)
mouse.x =( event.clientX / window.innerWidth )*2-1;
mouse.y =-( event.clientY / window.innerHeight )*2+0.8;
raycaster.setFromCamera(mouse,this.camera);const intersects = raycaster.intersectObjects(this.scene.children);// if (intersects[0].object) {if(this.nameNode==intersects[0].object){
console.log('jin1');this.faguang=!this.faguang
}else{
console.log('jin2');this.faguang=false}// }
console.log(this.faguang,'001');this.nameNode= intersects[0].object
// 根据它来判断点击的什么,length为0即没有点击到模型
console.log(intersects.length ? intersects[0].object.name : intersects,'intersects----->>>')// 获取选中最近的 Mesh 对象if(intersects.length !=0&& intersects[0].object instanceofTHREE.Mesh){this.outlineObj([intersects[0].object])}// if(intersects.length){// }},//高亮显示模型(呼吸灯)outlineObj(selectedObjects){
console.log(selectedObjects);// let that = this// 创建一个EffectComposer(效果组合器)对象,然后在该对象上添加后期处理通道。this.composer =newEffectComposer(this.renderer)// 新建一个场景通道 为了覆盖到原理来的场景上this.renderPass =newRenderPass(this.scene,this.camera)this.composer.addPass(this.renderPass);// 物体边缘发光通道this.outlinePass =newOutlinePass(newTHREE.Vector2(window.innerWidth, window.innerHeight),this.scene,this.camera, selectedObjects)
console.log(this.faguang,'002');if(!this.faguang){this.outlinePass.selectedObjects = selectedObjects
}else{this.outlinePass.selectedObjects =[]}
console.log('显示',this.faguang);this.outlinePass.edgeStrength =15.0// 边框的亮度this.outlinePass.edgeGlow =2// 光晕[0,1]this.outlinePass.usePatternTexture =false// 是否使用父级的材质this.outlinePass.edgeThickness =1.0// 边框宽度this.outlinePass.downSampleRatio =1// 边框弯曲度this.outlinePass.pulsePeriod =0// 呼吸闪烁的速度this.outlinePass.visibleEdgeColor.set(parseInt(0x0000FF))// 呼吸显示的颜色this.outlinePass.hiddenEdgeColor =newTHREE.Color(0x0000FF)// 呼吸消失的颜色this.outlinePass.clear =truethis.composer.addPass(this.outlinePass)// 自定义的着色器通道 作为参数let effectFXAA =newShaderPass(FXAAShader)
effectFXAA.uniforms.resolution.value.set(1/ window.innerWidth,1/ window.innerHeight)
effectFXAA.renderToScreen =truethis.composer.addPass(effectFXAA)},}}</script><style scoped>.tip{width:100%;height:80%;position: fixed;top:0;left:0;}
#three {width:100%!important;height:100%!important;}</style>
本文转载自: https://blog.csdn.net/weixin_66948502/article/details/131530291
版权归原作者 城南已开979 所有, 如有侵权,请联系我们删除。
版权归原作者 城南已开979 所有, 如有侵权,请联系我们删除。