diff --git a/TODO.txt b/TODO.txt index a80e54d..f7eb8c4 100644 --- a/TODO.txt +++ b/TODO.txt @@ -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 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. @@ -18,3 +15,4 @@ Add licenses Add instructions Fix up code locality Give the uniforms simpler names +Allow setting top and bottom angles in case the photo doesn't reach to the poles diff --git a/panorama-viewer.js b/panorama-viewer.js index 559c52e..537dbf0 100644 --- a/panorama-viewer.js +++ b/panorama-viewer.js @@ -1,19 +1,18 @@ "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. // Two meridians would make an XZ plane. - if (meridians < 3) { - throw "meridians must be ≥ 3"; - } + assert(meridians >= 3); + assert(Number.isInteger(meridians)); - // The number of parallels, including the poles, dividing the sphere's surface. - // Two parallels would make a vertical line segment. - if (parallels < 3) { - // throw "parallels must be ≥ 3"; - } + // The number of latitude bands comprising each hemisphere. + assert(bandsPerHemisphere >= 1); + assert(Number.isInteger(bandsPerHemisphere)); - const radius = 1; + const parallels = bandsPerHemisphere * 2 + 1; // The seam needs two of each vertex so we can map the texture correctly. const vertices = new Float32Array((meridians + 1) * parallels * 5); @@ -21,15 +20,25 @@ const positionOffset = 0; const textureCoordOffset = 3 * vertices.constructor.BYTES_PER_ELEMENT; 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++) { - 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); for (let m = 0; m < meridians + 1; m++) { const long = m * 2 * Math.PI / meridians; - const x = Math.cos(lat) * Math.sin(long) * radius; - const z = Math.cos(lat) * Math.cos(long) * radius; + const x = Math.cos(lat) * Math.sin(long); + const z = Math.cos(lat) * Math.cos(long); const u = m / meridians; - const v = p / (parallels - 1); + const v = lat / π + 0.5; vertices[verticesIdx++] = x; vertices[verticesIdx++] = y; @@ -183,7 +192,6 @@ const minZoom = 1; let zoom = 3; viewer.addEventListener("wheel", (evt) => { - console.log(evt.deltaY); zoom = Math.max(zoom * (1 - evt.deltaY * 0.005), minZoom); evt.preventDefault(); });