是否可以将鼠标悬停在线段的三个部分上.js以识别只有一个几何图形的片段?



我在three.js中创建了两个画线的例子,一个使用不同的geometrymaterial,另一个只使用一个geometry和一个material。小提琴的链接分别是:https://jsfiddle.net/sounakgarai/6reawwot/4/和 https://jsfiddle.net/sounakgarai/2xL70me3/。

我可以将鼠标悬停在第一个示例中的行上,并可以看到它们在鼠标悬停时更改materialcolor。在我悬停的第二个示例中,整个lineMeshcolor都发生了变化,因为只有一个material

现在我想做的是只使用一个geometry,就像第二个例子一样。但是我想看到在第一个例子中看到的效果:(第二个例子)LineSegment的所有不同部分,我希望看到它们以某种方式悬停,以便我可以清楚地识别它们(但我不想为此使用不同的geometry)。

有可能实现吗?

重要的是,当我使用多个geometry时,它会使浏览器挂起并变慢,同时渲染具有相当多成员的大型模型。我还想在未来的页面中在不同的div 元素中render多个three.js模型。因此,它可能会使浏览器在渲染它们时变慢。

带有THREE.BufferGeometry()THREE.LineSegments().vertexColors = THREE.VertexColors的选项:

var scene = new THREE.Scene();
var camera = new THREE.PerspectiveCamera(60, window.innerWidth / window.innerHeight, 1, 1000);
camera.position.set(0, 0, 10);
var renderer = new THREE.WebGLRenderer({
antialias: true
});
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);
var points = [
new THREE.Vector3(-5, 5, 0),
new THREE.Vector3(),
new THREE.Vector3(),
new THREE.Vector3(5, 5, 0),
new THREE.Vector3(5, 5, 0),
new THREE.Vector3(5, -5, 0)
];
var geom = new THREE.BufferGeometry().setFromPoints(points);
geom.addAttribute("color", new THREE.BufferAttribute(new Float32Array([1, 0, 1, 0, 0, 1, 0, 1, 1, 1, 1, 1, .5, .5, .5, 1, 0, 1]), 3)); // we'll change this color attribute
geom.addAttribute("colorBase", new THREE.BufferAttribute(new Float32Array([1, 0, 1, 0, 0, 1, 0, 1, 1, 1, 1, 1, .5, .5, .5, 1, 0, 1]), 3)); // this attribute contains original colors for restoring
var mat = new THREE.LineBasicMaterial({
vertexColors: THREE.VertexColors
});
var line = new THREE.LineSegments(geom, mat);
scene.add(line);
// all of those variables are for re-use
var raycaster = new THREE.Raycaster();
var mouse = new THREE.Vector2();
var intersects = [];
var oldIndex = -1;
var col = new THREE.Color();
window.addEventListener("mousemove", onMouseMove, false);
function onMouseMove(event) {
mouse.x = (event.clientX / window.innerWidth) * 2 - 1;
mouse.y = -(event.clientY / window.innerHeight) * 2 + 1;
raycaster.setFromCamera(mouse, camera);
intersects = raycaster.intersectObject(line);
if (intersects.length === 0) return;
let idx = intersects[0].index;
if (idx !== oldIndex) highlightSegment(idx, 0xFFFF00);
}
function highlightSegment(idx, color) {
setColor(idx, color);
if (oldIndex !== -1) {
restoreColor();
}
line.geometry.attributes.color.needsUpdate = true;
oldIndex = idx; // save current index as an old one
}
function setColor(idx, color) { // change color for the current segment
let idxNear = idx % 2 === 0 ? idx + 1 : idx - 1; // 
// if 'idx' is an index of the start point in a segment, then its pair will be idx + 1, 
// otherwise is idx - 1.
col.set(color);
let colors = line.geometry.attributes.color;
colors.setXYZ(idx, col.r, col.g, col.b); // a very useful method of 'THREE.BufferAttribute()'
colors.setXYZ(idxNear, col.r, col.g, col.b);
}
function restoreColor() { // restore the original color for the old segment
let oldIndexNear = oldIndex % 2 === 0 ? oldIndex + 1 : oldIndex - 1;
let colors = line.geometry.attributes.color;
let colorBase = line.geometry.attributes.colorBase;
colors.copyAt(oldIndex, colorBase, oldIndex); // another useful method of 'THREE.BufferAttribute()'
colors.copyAt(oldIndexNear, colorBase, oldIndexNear);
}
render();
function render() {
requestAnimationFrame(render);
renderer.render(scene, camera);
}
body {
overflow: hidden;
margin: 0;
}
<script src="https://cdn.jsdelivr.net/npm/three@0.122.0/build/three.min.js"></script>

您必须提供顶点颜色而不是材质颜色。喜欢这个-

var lineMaterial = new THREE.LineBasicMaterial({ vertexColors: THREE.VertexColors});

对于每个顶点,推送相应的颜色。喜欢这个-

geometry.colors.push(new THREE.Color("rgb(0, 0, 255)"), new THREE.Color("rgb(0, 0, 255)"));

在鼠标悬停处理程序中,您需要获取当前相交线的索引并更改该顶点的颜色。喜欢这个-

intersects[0].object.geometry.colors[intersects[0].index].setHex(0xff0000);
let nextIndex = intersects[0].index + (intersects[0].index % 2 == 0 ? 1 : -1 );
intersects[0].object.geometry.colors[nextIndex].setHex(0xff0000);
intersects[0].object.geometry.colorsNeedUpdate = true;

这是基于您的第二个版本的简化代码 -

var container, scene, camera, renderer;
var mouse = new THREE.Vector2();
var intersected;
init();
animate();
function guid() {
function s4() {
return Math.floor((1 + Math.random()) * 0x10000)
.toString(16)
.substring(1);
}
return s4() + s4() + '-' + s4() + '-' + s4() + '-' +
s4() + '-' + s4() + s4() + s4();
}
function init() {
container = document.getElementById("myDiv");
scene = new THREE.Scene();
scene.background = new THREE.Color(0xf7f7f7);
var WIDTH = container.clientWidth,
HEIGHT = container.clientHeight;
renderer = new THREE.WebGLRenderer({ antialias: true, alpha: true });
renderer.setSize(WIDTH, HEIGHT);
container.appendChild(renderer.domElement);
camera = new THREE.PerspectiveCamera(45, WIDTH / HEIGHT, 0.1, 20000);
camera.up.set(0, 0, 1);
camera.lookAt(scene.position);
camera.position.set(0, 0, 100);

var nsize = 1, nspace = 5, nrange;
nrange = nsize * nspace;
var i, j, x, y, z = 0;
var lineMaterial = new THREE.LineBasicMaterial({ vertexColors: THREE.VertexColors});
var geometry = new THREE.Geometry();
for (i = 0; i <= nsize; i++) {
for (j = 0, x = 0, y = 0; j <= nsize; j++) {
geometry.vertices.push(new THREE.Vector3(x, y, z), new THREE.Vector3(x, y + nspace, z));
geometry.colors.push(new THREE.Color("rgb(0, 0, 255)"), new THREE.Color("rgb(0, 0, 255)"));
x += nspace;
}
for (j = 0, x = 0, y = 0; j <= nsize; j++) {
geometry.vertices.push(new THREE.Vector3(x, y, z), new THREE.Vector3(x + nspace, y, z));
geometry.colors.push(new THREE.Color("rgb(0, 0, 255)"), new THREE.Color("rgb(0, 0, 255)"));
y += nspace;
}
z += nspace;
}
y = -10;
for (i = 0; i <= nsize; i++) {
for (j = 0, x = 0, z = 0; j <= nsize; j++) {
geometry.vertices.push(new THREE.Vector3(x, y, z), new THREE.Vector3(x, y, z + nspace));
geometry.colors.push(new THREE.Color("rgb(0, 0, 255)"), new THREE.Color("rgb(0, 0, 255)"));
x += nspace;
}
for (j = 0, x = 0, z = 0; j <= nsize; j++) {
geometry.vertices.push(new THREE.Vector3(x, y, z), new THREE.Vector3(x + nspace, y, z));
geometry.colors.push(new THREE.Color("rgb(0, 0, 255)"), new THREE.Color("rgb(0, 0, 255)"));
z += nspace;
}
y += nspace;
}
var lineMesh = new THREE.LineSegments(geometry, lineMaterial);
lineMesh.name = guid();
lineMesh.position.x = -(nsize * nspace) / 2;
lineMesh.position.y = -(nsize * nspace) / 2;
lineMesh.position.z = -(nsize * nspace) / 2;
scene.add(lineMesh);
}
function animate() {
requestAnimationFrame(animate);
// Render the scene.
render();
}

function onMouseMove(event) {
event.preventDefault();
// calculate mouse position in normalized device coordinates
// (-1 to +1) for both components
mouse.x = (event.clientX / container.clientWidth) * 2 - 1;
mouse.y = - (event.clientY / container.clientHeight) * 2 + 1;
if (mouse !== null && camera !== null) {
var ray = new THREE.Raycaster();
ray.setFromCamera(mouse, camera);
var intersects = ray.intersectObjects(scene.children);
if (intersects.length > 0) {

intersects[0].object.geometry.colors[intersects[0].index].setHex(0xff0000);
let nextIndex = intersects[0].index + (intersects[0].index % 2 == 0 ? 1 : -1 );
intersects[0].object.geometry.colors[nextIndex].setHex(0xff0000);
intersects[0].object.geometry.colorsNeedUpdate = true;
}
}
}
function render() {
renderer.render(scene, camera);
}
container.addEventListener('mousemove', onMouseMove, false);

作为光线投射器的替代方案,您可以使用 GPU 拾取来识别相交线。

如果您使用单个几何图形,则无法更改单个线段的材料,但是您可以做的是在要突出显示的线段之上叠加动态添加的额外线段,我认为这将产生您正在寻找的效果。

您可以通过创建没有深度测试的材质来实现这一点:

material.depthTest=false

它将始终出现在具有深度测试材质的几何体的顶部。

最新更新