I'm working on a simulation of the famous Prague Astronomical Clock: https://shetline.com/orloj/
I've already produced a lat/lon grid in a crude way, by 2-d drawing lines onto the image which is used for the surface of the globe, but this method doesn't work very well:
(Yes, the globe is supposed to be upside down, as it is on the original clock.)
The grid lines are very jagged this way, and they fade out near the poles because of the scaling near the poles of the original rectangular map image.
I'm trying to find a tutorial on how to do a simple bit of decoration like this by using Three.js/WebGL constructs, but no luck so far. I can get a bit of a start on what I want with the following code:
this.camera = new PerspectiveCamera(FIELD_OF_VIEW, 1);
this.scene = new Scene();
const globe = new SphereGeometry(GLOBE_RADIUS, 50, 50);
globe.rotateY(-PI / 2);
this.globeMesh = new Mesh(globe, new MeshBasicMaterial({ map: new CanvasTexture(Globe.mapCanvas) }));
this.renderer = new WebGLRenderer({ alpha: true });
this.renderer.setSize(GLOBE_PIXEL_SIZE, GLOBE_PIXEL_SIZE);
this.rendererHost.appendChild(this.renderer.domElement);
this.scene.add(this.globeMesh);
const circle = new Line(new CircleGeometry(GLOBE_RADIUS + 0.1, 50),
new LineBasicMaterial({ color: GRID_COLOR, linewidth: 20 }));
this.globeMesh.add(circle);
...by adding a circle, and making the edges of the circle poke out just a bit beyond the surface of the globe, but the result is only a very faint line that doesn't respond to my attempts to make the line thicker. I've also tried something similar with a torus floating just above the surface of the globe, but that doesn't work very well either.
What I want is some sort of additional layer for the lines, or a composite material that combines my pixel image with geometrically-defined lines (rather than painted lines), but I'm not finding the Three.js documentation very clear for figuring out how to do this.
I'm not even sure what kind of geometric model to use for these lines. They're all circles of a sort, but to have a real visual extent it seems the geometry would need to be some sort of globe-hugging, narrow, thin ribbons resting on the surface of the globe.
I ended up using (very, very short) cylinders as my lines of longitude and latitude, floating ever so slightly above the surface of the globe. It seems like anything composed of true line objects can't be guaranteed to honor linewidth, and I didn't want to settle for always-one-pixel lines.
The lines of longitude are simple hollow cylinders, as is the equator. All of the other lines of latitude are flared cylinders, wider at one end to conform to the shape of the globe.
const LINE_THICKNESS = 0.03;
const HAG = 0.01; // Sleight distance above globe that longitude/latitude lines are drawn.
// ...
// Lines of longitude
for (let n = 0; n < 24; ++n) {
const line = new CylinderGeometry(GLOBE_RADIUS + HAG, GLOBE_RADIUS + HAG, LINE_THICKNESS, 50, 1, true);
line.translate(0, -LINE_THICKNESS / 2, 0);
line.rotateX(PI / 2);
line.rotateY(n * PI / 12);
const mesh = new Mesh(line, new MeshBasicMaterial({ color: GRID_COLOR }));
this.globeMesh.add(mesh);
}
// Lines of latitude
for (let n = 1; n < 12; ++n) {
const lat = (n - 6) * PI / 12;
const r = GLOBE_RADIUS * cos(lat);
const y = GLOBE_RADIUS * sin(lat);
const r1 = r - LINE_THICKNESS * sin(lat) / 2;
const r2 = r + LINE_THICKNESS * sin(lat) / 2;
const line = new CylinderGeometry(r1 + HAG, r2 + HAG, cos(lat) * LINE_THICKNESS, 50, 8, true);
line.translate(0, -cos(lat) * LINE_THICKNESS / 2 + y, 0);
const mesh = new Mesh(line, new MeshBasicMaterial({ color: GRID_COLOR }));
this.globeMesh.add(mesh);
}
Perhaps there is a better way where the lines are part of some sort of texture applied to the globe, but this is at least giving satisfactory results for now.