three.js 点击交互事件 含解决点击的物体与看到的不一致问题(非全屏/多边形偏移)

在 three.js 中,可以通过添加事件监听器来实现点击交互事件。具体步骤如下:

1. 获取场景中的所有物体,并为每个物体添加一个点击事件监听器。

javascript 
scene.traverse(function(object) { 
  if (object instanceof THREE.Mesh) { 
    object.addEventListener('click', function() { 
      // 处理点击事件 
    }); 
  } 
}); 

2. 在点击事件处理函数中,可以获取到被点击的物体对象,并进行相应的操作。


function handleClick(event) { 
  var mouse = new THREE.Vector2(); 
  mouse.x = (event.clientX / window.innerWidth) * 2 - 1; 
  mouse.y = -(event.clientY / window.innerHeight) * 2 + 1; 
 
  var raycaster = new THREE.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 = new THREE.WebGLRenderer({ canvas: canvas }); 
renderer.setSize(window.innerWidth, window.innerHeight); 

2. 多边形偏移问题:在创建材质时,设置 polygonOffset 属性。

var material = new THREE.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 * as THREE from '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'
var OBJ = ''
export default {
  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 = new THREE.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 = this
      this.scene = new THREE.Scene() // 创建一个scene 
      this.scene.background = new THREE.Color('#eee') // 背景颜色

      const canvas = document.querySelector('#three')
      // 渲染器锯齿属性.antialias
      that.renderer = new THREE.WebGLRenderer({ canvas, antialias: true }) // 创建一个WebGLRenderer,将canvas和配置参数传入
   
      // 引入3D模型 gltf放置public目录 
      const gltfLoader = new GLTFLoader()
      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 = new THREE.HemisphereLight(0xffffff, 0xffffff, 1)
      hemLight.position.set(0, 48, 0)
      this.scene.add(hemLight)

      //平行光 (这里可以用点光源PointLight和环境光AmbientLight没有特定方向,整体改变场景的光照明暗)
      const dirLight = new THREE.DirectionalLight(0xffffff, 1)
      //光源等位置
      dirLight.position.set(-10, 8, -5)
  
      //使用PerspectiveCamera(透视摄像机):透视相机用来模拟人眼所看到的景象,物体的大小会受远近距离的影响,它是3D场景的渲染中使用得最普遍的投影模式。
      this.camera = new THREE.PerspectiveCamera(
        50,
        window.innerWidth / window.innerHeight,
        1,
        1000
      )
      // camera.position.z = 10 // 正方位
      this.camera.position.set(3, 4, 3);

      const controls = new OrbitControls(this.camera, that.renderer.domElement)
      // 阻尼感
      controls.enableDamping = true
        // 动画循环函数
      function animate() {
        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像素分辨率
      function resizeRendererToDisplaySize(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 = new THREE.Raycaster();
      let mouse = new THREE.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 instanceof THREE.Mesh) {
        this.outlineObj([intersects[0].object] )
      }
      // if(intersects.length){

      // }
    },
    //高亮显示模型(呼吸灯)
    outlineObj (selectedObjects) {
      console.log(selectedObjects);
      // let that = this
       // 创建一个EffectComposer(效果组合器)对象,然后在该对象上添加后期处理通道。
       this.composer = new EffectComposer(this.renderer)
      // 新建一个场景通道  为了覆盖到原理来的场景上
      this.renderPass = new RenderPass(this.scene, this.camera)
      this.composer.addPass(this.renderPass);
      // 物体边缘发光通道
      this.outlinePass = new OutlinePass(new THREE.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 = new THREE.Color(0x0000FF) // 呼吸消失的颜色
      this.outlinePass.clear = true
      this.composer.addPass(this.outlinePass)
      // 自定义的着色器通道 作为参数
      let effectFXAA = new ShaderPass(FXAAShader)
      effectFXAA.uniforms.resolution.value.set(1 / window.innerWidth, 1 / window.innerHeight)
      effectFXAA.renderToScreen = true
      this.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>

你可能感兴趣的:(three.js,javascript,前端,开发语言)