-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathindex.html
More file actions
146 lines (134 loc) · 5.81 KB
/
Copy pathindex.html
File metadata and controls
146 lines (134 loc) · 5.81 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Refracta · Real-time refractive vision simulator</title>
<link rel="icon" href="data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 100 100'><text y='.9em' font-size='90'>👁️</text></svg>">
<link rel="stylesheet" href="css/style.css">
<script type="importmap">{ "imports": { "three": "./libs/three.module.min.js" } }</script>
</head>
<body>
<header>
<div class="brand">
<h1>Refracta</h1>
<p>what the world looks like <em>before</em> your glasses</p>
</div>
<div class="rx-box">
<div id="rx-readout" class="mono">+0.00 sph</div>
<div id="rx-detail" class="dim">…</div>
</div>
<button id="about-btn" class="ghost">the optics ⓘ</button>
</header>
<div id="presets"></div>
<main id="app">
<section id="stage-wrap">
<div id="stage">
<canvas id="view" width="1280" height="720"></canvas>
<div id="divider"><div class="knob">⟨⟩</div></div>
<span id="label-left" class="side-label left">CORRECTED</span>
<span id="label-right" class="side-label right">UNCORRECTED</span>
<span id="fps" class="badge mono">–</span>
<span id="scene-title" class="badge scene mono">–</span>
</div>
<p id="scene-desc" class="dim"></p>
</section>
<aside id="panel">
<details open>
<summary>Environment</summary>
<div id="scene-grid"></div>
<div id="upload-tools" style="display:none">
<button id="depth-ai-btn" class="ghost wide">Estimate depth with AI (~40 MB)</button>
<label class="filebtn wide">Upload matching depth map
<input id="depth-file-input" type="file" accept="image/*" hidden>
</label>
<div class="row">
<label>near <input id="near-input" type="number" value="0.4" step="0.1" min="0.1"> m</label>
<label>far <input id="far-input" type="number" value="50" step="1" min="1"> m</label>
</div>
</div>
<input id="file-input" type="file" accept="image/*" hidden>
</details>
<details open>
<summary>Prescription</summary>
<div id="sliders-rx"></div>
</details>
<details open>
<summary>Eye & viewing</summary>
<div id="sliders-eye"></div>
<div id="flat-group"><div id="sliders-flat"></div></div>
</details>
<details>
<summary>Rendering</summary>
<div id="sliders-render"></div>
<div class="row">
<label>quality
<select id="quality">
<option value="low">low</option>
<option value="med" selected>medium</option>
<option value="high">high</option>
</select>
</label>
<label><input type="checkbox" id="swap"> swap sides</label>
<label><input type="checkbox" id="show-depth"> depth view</label>
</div>
<button id="reset" class="ghost wide">Reset to perfect vision</button>
</details>
<details open>
<summary>Point-spread function</summary>
<canvas id="psf-canvas" width="170" height="130"></canvas>
<div id="psf-detail" class="dim mono"></div>
<p class="dim small">The blur kernel applied to a distant light,
computed from the prescription above. Drag AXIS and watch it rotate.</p>
</details>
<p class="dim small footer-links">
<a href="?validate=1">shader validation</a> ·
<a href="README.md" title="the full writeup">source & README</a> ·
not a medical device
</p>
</aside>
</main>
<section id="validate-panel" style="display:none">
<h2>Shader vs Python reference</h2>
<p class="dim">Each case renders the GLSL shader in exact mode and diffs it
against the OpenCV reference implementation (<code>reference/simulate.py</code>).
RMS is measured on 8-bit sRGB values.</p>
<p id="validate-summary" class="mono">running…</p>
<table>
<thead><tr><th>case</th><th>prescription</th><th>RMS</th><th>PSNR</th><th>result</th></tr></thead>
<tbody id="validate-rows"></tbody>
</table>
<p><a id="validate-json" download="validation.json">download results JSON</a> ·
<a href="./">back to the simulator</a></p>
</section>
<dialog id="about">
<h2>The optics in 60 seconds</h2>
<p>A refractive error focuses light in front of or behind the retina.
The blur disk of a defocused point subtends
<code>β ≈ p · ΔD</code> radians, where <code>p</code> is the pupil
diameter and <code>ΔD</code> the defocus in diopters. Pixels:
<code>r = ½ p ΔD / radPerPixel</code>.</p>
<p><b>Astigmatism</b> gives the eye two focal powers on perpendicular
meridians (SPH and SPH+CYL, rotated by AXIS). A point source maps to an
oriented <i>ellipse</i>, a cross-section of Sturm's conoid. That
directional smear is why night lights streak, and why an isotropic
Gaussian cannot fake it.</p>
<p><b>Depth</b>: each pixel's defocus is
<code>ΔD = V<sub>object</sub> + SPH − A</code> with
<code>V = 1/distance</code>. The simulated eye accommodates
(<code>A</code>, limited by age via the ADD) to put the circle of least
confusion on the retina for your chosen focus target, so the focal plane
is sharp and everything else blurs by its true dioptric distance.</p>
<p>Bright pixels are expanded back to plausible radiance before blurring
(inverse tonemap), which is what keeps headlight streaks vivid instead of
washing out grey. The optional chromatic term models the eye's ~1.2 D
of longitudinal chromatic aberration.</p>
<p class="dim">The same formulas exist three times: NumPy/OpenCV reference,
these uniforms, and the GLSL kernel. The validation page proves they
agree.</p>
<form method="dialog"><button class="ghost">close</button></form>
</dialog>
<div id="toasts"></div>
<script type="module" src="js/main.js"></script>
</body>
</html>