Skip to content

Commit 3361ec8

Browse files
feat: Another example as a visual end to end test
1 parent 5726368 commit 3361ec8

File tree

2 files changed

+138
-0
lines changed

2 files changed

+138
-0
lines changed
227 KB
Loading

examples/raster-globe.html/index.html

Lines changed: 138 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,138 @@
1+
<!DOCTYPE html>
2+
<html lang="en">
3+
<head>
4+
<meta charset="utf-8">
5+
<title>Raster projection with GPU.js</title>
6+
<script src="../../bin/gpu-browser.js"></script>
7+
</head>
8+
<body>
9+
<h1>Raster projection with GPU.js from <a href="https://observablehq.com/d/b70d084526a1a764">https://observablehq.com/d/b70d084526a1a764</a></h1>
10+
<div id="log-fps"></div>
11+
</body>
12+
<script>
13+
function frac(n) {
14+
return n - Math.floor(n);
15+
}
16+
function applyRotation(rotatex, rotatey, rotatez, lambda, phi, which) {
17+
const degrees = 57.29577951308232;
18+
19+
lambda = lambda / degrees;
20+
phi = phi / degrees;
21+
22+
var cosphi = Math.cos(phi),
23+
x = Math.cos(lambda) * cosphi,
24+
y = Math.sin(lambda) * cosphi,
25+
z = Math.sin(phi);
26+
27+
// inverse rotation
28+
var deltaLambda = rotatex / degrees; // rotate[0]
29+
var deltaPhi = -rotatey / degrees; // rotate[1]
30+
var deltaGamma = -rotatez / degrees; // rotate[2]
31+
32+
var cosDeltaPhi = Math.cos(deltaPhi),
33+
sinDeltaPhi = Math.sin(deltaPhi),
34+
cosDeltaGamma = Math.cos(deltaGamma),
35+
sinDeltaGamma = Math.sin(deltaGamma);
36+
37+
var k = z * cosDeltaGamma - y * sinDeltaGamma;
38+
39+
lambda = Math.atan2(
40+
y * cosDeltaGamma + z * sinDeltaGamma,
41+
x * cosDeltaPhi + k * sinDeltaPhi
42+
) - deltaLambda;
43+
k = k * cosDeltaPhi - x * sinDeltaPhi;
44+
45+
phi = Math.asin(k);
46+
47+
lambda *= degrees;
48+
phi *= degrees;
49+
50+
// return [lambda,phi]; // fails so we need to call this function twice
51+
// and ask first for lambda, then for phi
52+
if (which === 0) return lambda;
53+
else return phi;
54+
}
55+
// the kernel runs for each pixel, with:
56+
// - this.thread.x = horizontal position in pixels from the left edge
57+
// - this.thread.y = vertical position in pixels from the bottom edge (*opposite of canvas*)
58+
const kernel = function(pixels, rotate0, rotate1, rotate2, scale) {
59+
60+
// azimuthal equal area
61+
function radius(rho) {
62+
return 2.0 * Math.asin(rho / 2.0);
63+
}
64+
// orthographic
65+
function __radius(rho) {
66+
return Math.asin(rho);
67+
}
68+
69+
// equirectangular projection (reads the (lon,lat) color from the base image)
70+
function pixelx(lon, srcw) {
71+
lon = frac((lon + 180) / 360);
72+
return Math.floor(lon * srcw);
73+
}
74+
function pixely(lat, srch) {
75+
lat = frac((lat + 90) / 180);
76+
return Math.floor(lat * srch);
77+
}
78+
79+
var x = (this.thread.x / this.constants.w - 1 / 2) / scale,
80+
y = ((this.thread.y - this.constants.h / 2) / this.constants.w) / scale;
81+
82+
// inverse projection
83+
const rho = Math.sqrt(x * x + y * y) + 1e-12;
84+
85+
const c = radius(rho),
86+
sinc = Math.sin(c),
87+
cosc = Math.cos(c);
88+
89+
// x, y : pixel coordinates if rotation was null
90+
const lambda = Math.atan2(x * sinc, rho * cosc) * 57.29577951308232;
91+
const z = y * sinc / rho;
92+
if (Math.abs(z) < 1) {
93+
const phi = Math.asin(z) * 57.29577951308232;
94+
95+
// apply rotation
96+
// [lambda, phi] = applyRotation(centerx, centery, centerz, lambda, phi); // TODO
97+
const lambdan = applyRotation(rotate0, rotate1, rotate2, lambda, phi, 0);
98+
const phin = applyRotation(rotate0, rotate1, rotate2, lambda, phi, 1);
99+
//var n = n0(lambda, phi, this.constants.srcw, this.constants.srch);
100+
//this.color(pixels[n]/256, pixels[n+1]/256,pixels[n+2]/256,1);
101+
const pixel = pixels[pixely(phin, this.constants.srch)][pixelx(lambdan, this.constants.srcw)];
102+
this.color(pixel[0], pixel[1], pixel[2], 1);
103+
}
104+
};
105+
const gpu = new GPU();
106+
const logFps = document.querySelector('#log-fps');
107+
const image = new Image();
108+
image.src = './earth-map.jpg';
109+
image.onload = () => {
110+
const w = 975;
111+
const h = 975;
112+
const render = gpu
113+
.createKernel(kernel, { functions: [applyRotation, frac] })
114+
.setConstants({ w, h, srcw: image.width, srch: image.height })
115+
.setOutput([w, h])
116+
.setGraphical(true);
117+
const canvas = render.canvas;
118+
document.body.appendChild(canvas);
119+
var lastCalledTime;
120+
var fps;
121+
function callRender() {
122+
let r0 = (-Date.now() / 30) % 360,
123+
r1 = 35 * Math.sin((-Date.now() / 1030) % 360),
124+
r2 = 0,
125+
scale = 0.49;
126+
render(image, r0, r1, r2, scale);
127+
delta = (Date.now() - lastCalledTime)/1000;
128+
lastCalledTime = Date.now();
129+
fps = 1/delta;
130+
logFps.innerHTML = fps.toFixed(0) + ' FPS';
131+
window.requestAnimationFrame(() => {
132+
callRender();
133+
});
134+
}
135+
callRender();
136+
};
137+
</script>
138+
</html>

0 commit comments

Comments
 (0)