Reputation: 9
I am here today because I would like your help. I'm trying to create the earth globe with lat and long, but I can't get the geographic coordinates.
This is my code:
const scene = new THREE.Scene();
const camera = new THREE.PerspectiveCamera(
75,
window.innerWidth / window.innerHeight,
0.1,
1000
);
camera.position.z = 5;
scene.add(camera);
const renderer = new THREE.WebGLRenderer();
renderer.setSize(window.innerWidth, window.innerHeight);
renderer.setPixelRatio(Math.min(window.devicePixelRatio, 2));
document.body.appendChild(renderer.domElement);
renderer.render(scene, camera);
function anime() {
renderer.render(scene, camera);
renderer.setPixelRatio(Math.min(window.devicePixelRatio, 2));
renderer.setSize(window.innerWidth, window.innerHeight);
requestAnimationFrame(anime);
}
anime();
class Earth {
constructor(radius, scene, camera, renderer) {
this.scene = scene;
this.radius = radius;
this.camera = camera;
this.renderer = renderer;
this.raycaster = new THREE.Raycaster();
this.mouse = new THREE.Vector2();
this.geometry = new THREE.SphereGeometry(this.radius, 32, 32);
this.texture = new THREE.TextureLoader().load('https://i.ibb.co/4KGwCLD/earth-atmos-2048.jpg');
this.material = new THREE.MeshBasicMaterial({
color: 0xffffff,
map: this.texture,
});
this.mesh = new THREE.Mesh(this.geometry, this.material);
this.obj = new THREE.Object3D();
}
initObject() {
// this.scene.add(this.scene);
this.obj.add(this.mesh);
this.obj.name = 'earth';
this.scene.add(this.obj);
}
onClick(event) {
event.preventDefault();
let canvas = this.renderer.domElement;
let vector = new THREE.Vector3(
((event.offsetX) / canvas.width) * 2 - 1, -((event.offsetY) / canvas.height) * 2 + 1,
0.5);
vector.unproject(this.camera);
this.raycaster.set(this.camera.position, vector.sub(this.camera.position).normalize());
let intersects = this.raycaster.intersectObjects(this.scene.children);
if (intersects.length > 0) {
console.log(intersects[0].point.x);
console.log(intersects[0].point.y);
console.log(intersects[0].point.z);
}
}
earthRotate() {
this.obj.rotation.y += 0.01;
requestAnimationFrame(this.earthRotate.bind(this));
}
}
const earth = new Earth(3, scene, camera, renderer);
earth.initObject();
addEventListener("click", (event) => {
earth.onClick(event);
});
/* earth.earthRotate(); */
body {
margin: 0;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r99/three.min.js" integrity="sha512-0tlhMhMGPohLm/YwaskxH7jJuUGqU/XPTl+HE0dWrhGbpEBRIZYMQdbHC0CmyNPzZKTBd8JoVZnvMcL7hzlFOg==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r99/three.js" integrity="sha512-RixpleQfVXfhJUrmXlxWwitZGvMWk13+KhCsaYdeod5xryBN6gqo3RJ9xvaHn8VUeNuUnYfvzKBhLBnJnpEsgA==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>
Is it possible to have geographic coordinates on a sphere with ThreeJS. If so do you know how to do it?
Thank you in advance for your help !
Upvotes: 0
Views: 876
Reputation: 8886
Intersections are returned in world coordinates, so if you click the same place over and over as your globe is spinning, you will get (about) the same hit position every time.
THAT SAID, these hit points are based on the geometry of the sphere, so a low-poly sphere could actually return a position that does not match a lat/lon position as if the sphere were perfect.
To get a better result, you'll need to take a couple more steps.
Use a Sphere
to create a mathematically perfect representation of your globe.
The Three Raycaster
is a great tool for easily setting up raycasting. But you can use Raycaster.ray
(Ray
) to perform manual steps, too.
For example, use Ray.intersectSphere
against your perfect sphere to get the "real" intersection point.
You were getting the same(-ish) click point because the intersection is in world coordinates. To convert to local coordinates, use the very convenient Object3D.worldToLocal
to transform the returned world hit point into a local hit point (the Vector3
is changed using this method, so keep this in mind!).
Pick a point on your globe which will represent the intersection point of your equator and prime meridian (0, 0). For a sphere where north is +Y, your origin could be new Vector3( 0, 0, sphere.radius )
Assuming you rotate about the Y axis...
For longitude, remove the Y component of your hit vector (hit.y = 0
), set the length of the vector to your sphere radius (hit.setLength( sphere.radius )
), then use Vector3.angleTo
to find the angle (in radians) between the hit vector and and your "zero" vector, and convert to degrees. Use the X component to determine the East/West component of your value.
For latitude, create a copy of your hit vector. Remove the Y component from the copy and set its length to the sphere's radius, just like for longitude. But instead of comparing to the origin vector, use angleTo
to get the angle between the copy and the hit vectors. Convert to degrees. Use the Y value to determine the North/South component of your value.
It's possible to use normalized vectors to perform these comparisons instead, but I think it's easier to visualize if you picture the vectors representing points on the surface of your sphere.
Upvotes: 1