我有一个 360 度全景图像,我使用三个 js 显示该图像。现在我想在 360 度全景图像上显示一个标记(标记:就像地图上的位置标记一样)。放大或缩小时,360 度图像应放大或缩小,但标记保持其理想的恒定位置。标记不应放大或缩小。为了更好地理解我想要的实际行为,您可以查看谷歌地图上的位置标记。 这是我如何使用三个 js 显示 360 度图像的代码。
import React, { useRef, useEffect, useState } from 'react';
import * as THREE from 'three';
import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls';
import { CSS2DRenderer, CSS2DObject } from 'three/addons/renderers/CSS2DRenderer.js';
import { EVENTS, TYPES, ThreeJsDrawing } from './threeJsDrawing';
import { setThreeJsData } from './mouseEvents';
import panImg from "../assets/shot.jpg";
import panImg2 from "../assets/shot2.jpg";
import panImg3 from "../assets/shot3.png";
import panImg4 from "../assets/shot4.png";
let scene, camera, controls, sphere, polygons = [], drawingTool,
images = [
{
url: panImg,
location: {
x: 369794,
y: 2212918,
lat: 20.008508,
lng: 73.755165
}
},
{
url: panImg2,
location: {
x: 369779,
y: 2212922,
lat: 20.008382,
lng: 73.755117
}
},
{
url: panImg3,
location: {
x: 369769,
y: 2212912,
lat: 20.008251,
lng: 73.755074
}
},
{
url: panImg4,
location: {
x: 369762,
y: 2212895,
lat: 20.008226,
lng: 73.755165
}
}
]
const PanoramaViewer = () => {
const [activeImage, setActiveImage] = useState(null)
const [renderer, setRenderer] = useState(null)
useEffect(() => {
if (activeImage || activeImage === 0) loadImage(activeImage)
}, [activeImage])
const [loading, setLoading] = useState(true)
const containerRef = useRef(null);
const loadImage = (index, initCall) => {
const img = images[index]
const [x, y] = [0, 0]
// Load the panorama texture
const loader = new THREE.TextureLoader();
const texture = loader.load(img.url, (texture) => {
// Texture loaded successfully
// Set the texture wrapping and flipping options
texture.wrapS = THREE.RepeatWrapping;
// texture.repeat.x = -1;
sphere.material.map = texture;
sphere.material.needsUpdate = true; // Ensure material updates
// controls.object.rotation.set(-2.3553, 1.57, 2.3553)
// Optional: Clean up the previous texture resources
sphere.material.map.dispose();
setLoading(false)
});
if (initCall) {
// Create a sphere geometry
const geometry = new THREE.SphereGeometry(5, 32, 32);
geometry.scale(-1, 1, 1);
// Create a material with the panorama texture
const material = new THREE.MeshBasicMaterial({ map: texture });
// Create a mesh with the geometry and material
sphere = new THREE.Mesh(geometry, material);
sphere.position.set(x, y, 0);
// Add the mesh to the scene
scene.add(sphere);
camera.position.set(x, y, 0.1);
controls.target.set(x, y, 0)
controls.update();
}
}
const nextImage = () => {
if (activeImage + 1 < images.length) {
setLoading(true)
setActiveImage(activeImage + 1)
}
}
const prevImage = () => {
if (activeImage - 1 >= 0) {
setLoading(true)
setActiveImage(activeImage - 1)
}
}
const initThreeJs = () => {
// Create a scene
scene = new THREE.Scene();
// Create a camera
camera = new THREE.PerspectiveCamera(
75,
containerRef.current.clientWidth / containerRef.current.clientHeight,
1,
1000
);
// Create a renderer
const renderer = new THREE.WebGLRenderer();
setRenderer(renderer)
renderer.setSize(containerRef.current.clientWidth, containerRef.current.clientHeight);
containerRef.current.appendChild(renderer.domElement);
// Add orbit controls for rotation
controls = new OrbitControls(camera, renderer.domElement);
controls.enableZoom = true;
controls.rotateSpeed = -0.25; // Adjust the value as needed
loadImage(0, true)
setThreeJsData(renderer, scene, camera, controls)
// Render the scene
const animate = () => {
requestAnimationFrame(animate);
renderer.render(scene, camera);
};
animate();
// Clean up
return () => {
// containerRef.current.removeEventListener('click', handleClick);
containerRef.current.removeChild(renderer.domElement);
controls.dispose();
};
}
useEffect(initThreeJs, []);
const Loader = () => {
return loading ? <div style={{ position: 'absolute', width: "100%", height: "100%", display: "flex", justifyContent: "center", alignItems: "center", color: 'white' }}>Loading...</div> : <></>
}
const markIssue = (type) => {
if (drawingTool) {
console.log("if called", drawingTool)
drawingTool.enableDrawing(type)
drawingTool.on(EVENTS.GEOMETRY_ADDED, (geometry => {
polygons.push(geometry)
}))
}
else {
//activate drawing tool
console.log("else called")
console.log("scene:", scene, "camera:", camera, "sphere:", [sphere])
drawingTool = new ThreeJsDrawing(scene, camera, [sphere], false)
drawingTool.enableDrawing(type)
drawingTool.on(EVENTS.GEOMETRY_ADDED, (geometry => {
console.log("geometry", geometry)
polygons.push(geometry)
}))
console.log("polygon arrayyy:", polygons)
}
}
return <>
<Loader />
<div style={{ position: "absolute", display: "flex", width: "100%", height: "40px", alignItems: 'center', justifyContent: 'space-around', background: "#00000082", color: 'white' }}>
<div onClick={prevImage} style={{ cursor: "pointer" }}>prevImage</div>
<div onClick={nextImage} style={{ cursor: "pointer" }}>nextImage</div>
</div>
<div style={{ position: "absolute", display: "flex", width: "100%", height: "40px", bottom: 0, alignItems: 'center', justifyContent: 'space-around', background: "#00000082", color: 'white', cursor: "pointer" }}>
<div onClick={() => {
markIssue("Polygon");
console.log("polygon click")
}}>
polygon
</div>
<div onClick={() => {
markIssue("Rectangle");
console.log("Rectangle click")
}}>
Rectangle
</div>
<div onClick={() => {
markIssue("Hotspot");
console.log("Hotspot clcik")
}}>
Hotspot
</div>
</div>
<div ref={containerRef} style={{ width: '100%', height: '100vh' }} />
</>;
};
export default PanoramaViewer;
我尝试使用球体、css2Dobject、纹理作为标记,但它不起作用。
要使用Three.js在360度全景图像上显示标记,并确保放大或缩小时标记保持恒定位置,可以按照以下步骤操作:
这是经过必要修改的代码的更新版本:
import React, { useRef, useEffect, useState } from 'react';
import * as THREE from 'three';
import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls';
let scene, camera, controls, sphere, marker, polygons = [];
const PanoramaViewer = () => {
// ... (previous code remains the same)
const initThreeJs = () => {
// ... (previous code remains the same)
// Create a marker geometry and material
const markerGeometry = new THREE.BoxGeometry(0.2, 0.2, 0.2);
const markerMaterial = new THREE.MeshBasicMaterial({ color: 0xff0000 });
marker = new THREE.Mesh(markerGeometry, markerMaterial);
// Add the marker to the scene
scene.add(marker);
// Render the scene
const animate = () => {
requestAnimationFrame(animate);
renderer.render(scene, camera);
// Update the marker's position relative to the camera
const markerPosition = new THREE.Vector3();
markerPosition.set(0, 0, -5); // Adjust the position as needed
markerPosition.applyMatrix4(camera.matrixWorld);
marker.position.copy(markerPosition);
};
animate();
// Clean up
return () => {
containerRef.current.removeChild(renderer.domElement);
controls.dispose();
};
};
// ... (rest of the code remains the same)
return (
<>
<Loader />
{/* ... (previous JSX code remains the same) */}
</>
);
};
export default PanoramaViewer;
在此代码中,我们创建一个简单的立方体标记并将其添加到场景中。我们在动画循环中更新标记相对于相机位置的位置。这可确保无论放大或缩小,标记都保持恒定位置。
您可以自定义标记的几何形状和材料以满足您的需求。确保根据需要调整标记的位置和大小。