我知道这个问题已经问过很多次了,但我不明白射线广播如何工作。我有一个从geo json文件创建国家边界以及要单击的国家填充的功能。单击后,应显示有关被单击国家的信息。
var borders = new THREE.Group();
function createBorders(dataJSON) {
var SIZE_AMPLIFIER = 20;
var WIDTH = 2500 * SIZE_AMPLIFIER;
var projection = d3.geoTransverseMercator().rotate([-10, 0, 0]) //center meridian
.center([10, 52]) //longitude, latitude
.scale(WIDTH * 1.5 - 505); //scale
var path = d3.geoPath().projection(projection);
var svg = d3.select("#Map").append("svg");
svg.selectAll(".country")
.data(dataJSON)
.enter()
.append("path")
.attr("class", ".country")
.attr("d", path);
var svgMarkup = svg.node().outerHTML;
var loader = new SVGLoader();
var svgData = loader.parse(svgMarkup);
svgData.paths.forEach((path, i) => {
var shapes = path.toShapes(true);
shapes.forEach((shape, j) => {
var geomSVG = new THREE.ExtrudeBufferGeometry(shape, {
depth: 50,
bevelEnabled: false
})
//needed for click event!
var materialSVG = new THREE.MeshLambertMaterial({
color: 0xFFFFFF,
transparent: true,
opacity: 0.8,
});
var meshSVG = new THREE.Mesh(geomSVG, materialSVG);
this.borders.add(meshSVG);
//create borders
var borderMaterial = new THREE.LineBasicMaterial({ color: 0x000000, linewidth: 3 })
var borderGeometry = new THREE.EdgesGeometry(geomSVG, 15);
var bordermesh = new THREE.LineSegments(borderGeometry, borderMaterial);
this.borders.add(bordermesh);
})
})
this.borders.rotateX(Math.PI / 2)
this.borders.position.z = -300;
this.borders.position.x = 16300;
this.borders.position.y = 0;
//get only meshes, no linesegments
var countryMeshes = [];
for(let m in this.borders.children){
if(this.borders.children[m] instanceof THREE.Mesh){
countryMeshes.push(this.borders.children[m])
}
}
//add callback for each mesh
for(let c in countryMeshes){
countryMeshes[c].callback = function(){console.log(dataJSON[c].properties.name_long)}
}
this.scene.add(this.state.borders);
svg.remove();
}
然后我进行光线投射功能:
var raycaster = new THREE.Raycaster();
function onMeshClick(event) {
event.preventDefault();
var vector = new THREE.Vector3((event.clientX / window.innerWidth) * 2 - 1, -(event.clientY / window.innerHeight) * 2 + 1, 0.5);
raycaster.setFromCamera(vector, camera);
var intersects = raycaster.intersectObjects(this.borders.children, true);
if (intersects.length > 0) {
intersects[0].object.callback();
}
}
最后添加一个事件监听器:
window.addEventListener('click', onMeshClick, false);
回调起作用,但顺序不正确。我必须点击另一个国家,甚至是在地图之外才能呼叫某些国家。
您正在将LineSegments
与Mesh
分组在一起。也许单击时捕获的是直线而不是网格,因为在测量直线时,Raycaster的精度阈值为1世界单位,这可能太大。可以认为它是使用宽笔刷而不是单个像素进行光线投射。
您有2个选项:
var areas = new THREE.Group();
var borders = new THREE.Group();
scene.add(areas);
scene.add(borders);
svgData.paths.forEach((path, i) => {
// ...
areas.add(meshSVG);
// ...
borders.add(bordermesh);
})
// When raycasting, only test country areas, leaving borders out
var intersects = raycaster.intersectObjects(areas, true);
raycaster.params.Line.threshold = 0.1