项目场景:
加载一个街区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)
}
})