微信小程序 使用three.js射线点击选中模型,点击子层级模型对应父级名称执行事件

项目场景:

加载一个街区glb模型,点击不同建筑跳转对应介绍页面


问题描述

1.`射线点击不管用,点击方法执行了,射线没打到模型
2.点击物体是模型的层级很多的子层级,需要对应父级名称,

原因分析:

1.创建相机时使用界面的宽高问题
2.鼠标点击的坐标问题
3.名称递归往上找


解决方案:

提示:使用的threeX插件,建议懂得人看,因为我是刚学,代码都是凑起来的,也不知道对不对,只是功能实现了,记一下

const THREE = requirePlugin('ThreeX');

const {
  document,
  window,
	performance,
	HTMLCanvasElement,
	requestAnimationFrame,
	cancelAnimationFrame,
  core,
	Event,
  Event0
} = THREE.DHTML
import { nodeFrame } from '../../three/examples/jsm/renderers/webgl-legacy/nodes/WebGLNodes.js';

import { OrbitControls } from '../../three/examples/jsm/controls/OrbitControls.js';
import { OrbitControls0 } from '../../three/examples/jsm/controls/OrbitControls0.js';
import { GLTFLoader } from '../../three/examples/jsm/loaders/GLTFLoader.js';

 // 创建一个raycaster实例
 var raycaster = new THREE.Raycaster();
 // 创建一个二维空间的向量用于存储鼠标点击的屏幕位置
 var mouse = new THREE.Vector2();

 var camera=null,scene=null,canvasHeight=0,touchx=0,touchy=0

var requestId

Page({

	onUnload() {
		cancelAnimationFrame(requestId, this.canvas)
		this.worker && this.worker.terminate()
    if(this.canvas) this.canvas = null
		setTimeout(() => {
			if (this.renderer instanceof THREE.WebGLRenderer) {
				this.renderer.dispose()
				this.renderer.forceContextLoss()
				this.renderer.context = null
				this.renderer.domElement = null
				this.renderer = null
			}
		}, 10)
  },
 
  onLoad() {
		document.createElementAsync("canvas", "webgl2",this).then(canvas => {
      this.canvas = canvas
      this.body_load(canvas).then()
    })

  },
  async body_load(canvas3d) {
    let  renderer, controls, clock, mixer;
    //添加了高度,用的是设定的canvas的高度,有的用的是全屏展示,有的是设定宽高,我这里是获取了一下
    canvasHeight=canvas3d.height;

    init();
    animate();

    function init() {

      clock = new THREE.Clock();

      const container = document.createElement( 'div' );
      document.body.appendChild( container );
      //原来的写法,用的都是window的宽高
      //var camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000);
      //改成了canvas的宽高
      camera = new THREE.PerspectiveCamera(75, canvas3d.width / canvas3d.height, 0.1, 1000 );


      camera.position.set( 0, 8, 3 );
      camera.lookAt(new THREE.Vector3(0,0,0))

      scene = new THREE.Scene();
      scene.background=new THREE.Color(0xbdbdbd);
      //灯
      const light = new THREE.AmbientLight(0xffffff,4 ); // soft white light
      scene.add( light );


      const loader = new GLTFLoader();
      loader.load( '模型地址模型地址模型地址.glb', function ( gltf ) {

          scene.add( gltf.scene );

      },function ( xhr ) {
        console.log("进度",( xhr.loaded / xhr.total * 100 ) + '% loaded')

    
      },
         (err)=>{
        console.log(err)
        
      });

      renderer = new THREE.WebGLRenderer( { antialias: true } );
      renderer.setPixelRatio( window.devicePixelRatio );
      renderer.setSize( window.innerWidth, window.innerHeight );
      renderer.toneMapping = THREE.ACESFilmicToneMapping;
      renderer.toneMappingExposure = 1;
      container.appendChild( renderer.domElement );

      controls = new (window.platform=="devtools"?OrbitControls:OrbitControls0)( camera, renderer.domElement );
      controls.enableDamping = true;
      controls.minDistance = 0.5;
      controls.maxDistance = 100;
      controls.target.set( 0, 1, 0 );
      controls.minPolarAngle= 0;
      controls.maxPolarAngle= Math.PI-2;
      controls.panSpeed=5;
      controls.update();

      window.addEventListener( 'resize', onWindowResize );
     
      
    }
    function onWindowResize() {

      camera.aspect = window.innerWidth / window.innerHeight;
      camera.updateProjectionMatrix();

      renderer.setSize( window.innerWidth, window.innerHeight );

    }
    function animate() {

      requestId = requestAnimationFrame( animate );

      if ( mixer ) mixer.update( clock.getDelta() );

      controls.update(); // required if damping enabled

      render();

    }

    function render() {

      nodeFrame.update();

      renderer.render( scene, camera );

    }
  },
  rayClick(e){
      //射线点击检测
  //原来使用的都是window的宽高
    // 将浏览器的2D鼠标位置转换为Three.js的标准设备坐标(-1到+1)
    //   mouse.x = ( e.clientX / window.innerWidth ) * 2 - 1;
    //   mouse.y = - ( e.clientY / window.innerHeight ) * 2 + 1;
    
    //改成了这样
    mouse.x=(touchx/wx.getWindowInfo().windowWidth)*2-1;
    mouse.y=-(touchy/canvasHeight)*2+1;
    
      // 使用鼠标的2D位置和当前相机的位置来更新射线
      raycaster.setFromCamera(mouse, camera);
    
      // 计算物体和射线的交点
      var intersects = raycaster.intersectObjects(scene.children);
    
      // 如果存在交点
      if (intersects.length > 0) {
          // 这里的intersects[0].object就是被点击的模型
         
          console.log('Model clicked:', intersects[0].object);
          if(this.checkClickModel(intersects[0].object,'要检测对应的建筑名称')){
              console.log("点到了建筑");
        
    }
      }

  },

  checkClickModel(object,name){
      // 递归地检查点击到的模型是否属于name建筑的子物体,只要点到一部分都算是点到了该建筑
      while (object !== null) {
        if (object.name === name) {
            return true;
        }
        object = object.parent;
      }
      return false;
  },
  webgl_touch(e){

		const web_e = (window.platform=="devtools"?Event:Event0).fix(e)
    this.canvas.dispatchEvent(web_e)

    //本来在点击方法里直接用的x,y,发现end不会给出x,y
    touchx=e.touches[0].x
    touchy=e.touches[0].y

  },
  webgl_touch_end(e){

    //原来是在加载完模型后这样绑定的点击,不管用,查了说是wxml要不获取点击的界面元素绑定,要不在wxml直接绑定,选了后者,前者不会
    //window.addEventListener('click', onMouseClick, false);
    this.rayClick(e)

  }
})

  • 3
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值