Use more parallels towards the poles.

This commit is contained in:
Brandon Dyck 2024-07-22 22:29:39 -06:00
parent 01d421d47b
commit 65fb4f748f
2 changed files with 24 additions and 18 deletions

View File

@ -1,6 +1,3 @@
Make parallels denser at the poles where it makes a bigger difference
The south pole looks really good with very dense vertices.
The parallels should be the circles where the sphere would intersect with a series of concentric, evenly spaced cylinders.
Make the panning match the cursor Make the panning match the cursor
Enforce a minimum zoom that will keep the edges of the sphere from showing Enforce a minimum zoom that will keep the edges of the sphere from showing
This probably just needs simple math with the aspect and/or the viewport's diagonal length. This probably just needs simple math with the aspect and/or the viewport's diagonal length.
@ -18,3 +15,4 @@ Add licenses
Add instructions Add instructions
Fix up code locality Fix up code locality
Give the uniforms simpler names Give the uniforms simpler names
Allow setting top and bottom angles in case the photo doesn't reach to the poles

View File

@ -1,19 +1,18 @@
"use strict"; "use strict";
(() => { (() => {
const makeSphere = (meridians = 3, parallels = 3) => { const makeSphere = (meridians = 3, bandsPerHemisphere = 1) => {
const assert = (cond, msg) => { if (!cond) throw (msg ? msg : "failed assertion"); };
// The number of meridians dividing the sphere's surface. // The number of meridians dividing the sphere's surface.
// Two meridians would make an XZ plane. // Two meridians would make an XZ plane.
if (meridians < 3) { assert(meridians >= 3);
throw "meridians must be ≥ 3"; assert(Number.isInteger(meridians));
}
// The number of parallels, including the poles, dividing the sphere's surface. // The number of latitude bands comprising each hemisphere.
// Two parallels would make a vertical line segment. assert(bandsPerHemisphere >= 1);
if (parallels < 3) { assert(Number.isInteger(bandsPerHemisphere));
// throw "parallels must be ≥ 3";
}
const radius = 1; const parallels = bandsPerHemisphere * 2 + 1;
// The seam needs two of each vertex so we can map the texture correctly. // The seam needs two of each vertex so we can map the texture correctly.
const vertices = new Float32Array((meridians + 1) * parallels * 5); const vertices = new Float32Array((meridians + 1) * parallels * 5);
@ -21,15 +20,25 @@
const positionOffset = 0; const positionOffset = 0;
const textureCoordOffset = 3 * vertices.constructor.BYTES_PER_ELEMENT; const textureCoordOffset = 3 * vertices.constructor.BYTES_PER_ELEMENT;
let verticesIdx = 0; let verticesIdx = 0;
/*
In order to cut down on distortion at the poles, the parallels
are not at equal intervals of latitude. Instead, the parallels
describe the intersections of the (circum)sphere with a set of
concentric, evenly spaced, vertically oriented cylinders. This
makes the parallels denser towards the poles, in proportion to
how badly that density is needed at any given latitude.
*/
for (let p = 0; p < parallels; p++) { for (let p = 0; p < parallels; p++) {
const lat = Math.PI * (p / (parallels - 1) - 0.5); const π = Math.PI;
const sin = Math.sin;
const lat = π * sin(π * (p - bandsPerHemisphere) / 2 / bandsPerHemisphere) / 2;
const y = Math.sin(lat); const y = Math.sin(lat);
for (let m = 0; m < meridians + 1; m++) { for (let m = 0; m < meridians + 1; m++) {
const long = m * 2 * Math.PI / meridians; const long = m * 2 * Math.PI / meridians;
const x = Math.cos(lat) * Math.sin(long) * radius; const x = Math.cos(lat) * Math.sin(long);
const z = Math.cos(lat) * Math.cos(long) * radius; const z = Math.cos(lat) * Math.cos(long);
const u = m / meridians; const u = m / meridians;
const v = p / (parallels - 1); const v = lat / π + 0.5;
vertices[verticesIdx++] = x; vertices[verticesIdx++] = x;
vertices[verticesIdx++] = y; vertices[verticesIdx++] = y;
@ -183,7 +192,6 @@
const minZoom = 1; const minZoom = 1;
let zoom = 3; let zoom = 3;
viewer.addEventListener("wheel", (evt) => { viewer.addEventListener("wheel", (evt) => {
console.log(evt.deltaY);
zoom = Math.max(zoom * (1 - evt.deltaY * 0.005), minZoom); zoom = Math.max(zoom * (1 - evt.deltaY * 0.005), minZoom);
evt.preventDefault(); evt.preventDefault();
}); });