1 |
greg |
1.5 |
{ RCSid $Id: fovsample.cal,v 1.4 2019/04/07 15:38:35 greg Exp $ } |
2 |
greg |
1.1 |
{ |
3 |
|
|
Foveated sampling based on central direction vector, |
4 |
|
|
magnification factor at center, and central field of view. |
5 |
|
|
|
6 |
|
|
Greg Ward April 2019 |
7 |
|
|
|
8 |
greg |
1.2 |
Preset constants: |
9 |
greg |
1.1 |
Cx, Cy, Cz : central view direction |
10 |
greg |
1.2 |
M : central FOV magnification factor |
11 |
greg |
1.1 |
D : field of view (full angle in degrees) |
12 |
|
|
|
13 |
|
|
Inputs: |
14 |
greg |
1.4 |
forward = 1 if forward conversion, -1 if reverse |
15 |
|
|
iDx, iDy, iDz = input sampling vector |
16 |
greg |
1.1 |
|
17 |
|
|
Outputs: |
18 |
greg |
1.4 |
oDx, oDy, oDz = output sample direction (normalized) |
19 |
greg |
1.1 |
|
20 |
|
|
Other variables computed: |
21 |
greg |
1.4 |
iTheta = input angle to center (radians) |
22 |
|
|
oTheta = output angle to center (radians) |
23 |
greg |
1.1 |
} |
24 |
greg |
1.2 |
|
25 |
|
|
r : PI/360 * D; { half-angle of foveal region in radians } |
26 |
|
|
rp : M * r; { half-angle of mapped boundary } |
27 |
|
|
|
28 |
greg |
1.1 |
len(x,y,z) : sqrt(x*x + y*y + z*z); |
29 |
greg |
1.3 |
sq(x) : x*x; |
30 |
greg |
1.2 |
{ normalize central vector } |
31 |
greg |
1.1 |
Clen : len(Cx,Cy,Cz); |
32 |
|
|
nCx : Cx/Clen; |
33 |
|
|
nCy : Cy/Clen; |
34 |
|
|
nCz : Cz/Clen; |
35 |
greg |
1.2 |
{ normalize input direction } |
36 |
greg |
1.1 |
iDnf = 1/len(iDx,iDy,iDz); |
37 |
|
|
niDx = iDx*iDnf; |
38 |
|
|
niDy = iDy*iDnf; |
39 |
|
|
niDz = iDz*iDnf; |
40 |
greg |
1.5 |
{ check for degenerate case (center of fovea or opposite) } |
41 |
greg |
1.2 |
iDot = nCx*niDx + nCy*niDy + nCz*niDz; |
42 |
greg |
1.5 |
degen0 = iDot - cos(.05*PI/180); |
43 |
|
|
degen1 = cos(179.95*PI/180) - iDot; |
44 |
greg |
1.1 |
|
45 |
|
|
iTheta = acos(iDot); |
46 |
greg |
1.4 |
{ quadratic function coefficients mate to linear ramp } |
47 |
|
|
qa : (PI - PI/M)/sq(PI - rp); |
48 |
|
|
qb : 1/M - 2*PI*r*(M - 1)/sq(PI - rp); |
49 |
|
|
qc : (PI - PI/M)/sq(PI/rp - 1); |
50 |
|
|
|
51 |
|
|
oTheta = if (forward, |
52 |
|
|
if(rp-iTheta, iTheta/M, qa*iTheta*iTheta + qb*iTheta + qc), |
53 |
|
|
if(r-iTheta, iTheta*M, (sqrt(qb*qb - 4*qa*(qc - iTheta)) - qb)/(2*qa)) |
54 |
|
|
); |
55 |
greg |
1.2 |
{ normalized "up" vector for rotation } |
56 |
|
|
Unf = 1/sqrt(1 - iDot*iDot); |
57 |
greg |
1.1 |
nUx = (nCy*niDz - nCz*niDy)*Unf; |
58 |
|
|
nUy = (nCz*niDx - nCx*niDz)*Unf; |
59 |
|
|
nUz = (nCx*niDy - nCy*niDx)*Unf; |
60 |
greg |
1.2 |
{ rotate central vector by oTheta to get new output } |
61 |
|
|
rcos = cos(oTheta); |
62 |
|
|
rsin = sqrt(1 - rcos*rcos); |
63 |
|
|
{ foveated sample direction } |
64 |
greg |
1.3 |
rDx = nCx*rcos + (nUy*nCz - nUz*nCy)*rsin; |
65 |
|
|
rDy = nCy*rcos + (nUz*nCx - nUx*nCz)*rsin; |
66 |
|
|
rDz = nCz*rcos + (nUx*nCy - nUy*nCx)*rsin; |
67 |
greg |
1.2 |
{ substitute approximation in degenerate case } |
68 |
greg |
1.5 |
oDx = if(degen0, nCx+(niDx-nCx)*if(forward,1/M,M), if(degen1, niDx, rDx)); |
69 |
|
|
oDy = if(degen0, nCy+(niDy-nCy)*if(forward,1/M,M), if(degen1, niDy, rDy)); |
70 |
|
|
oDz = if(degen0, nCz+(niDz-nCz)*if(forward,1/M,M), if(degen1, niDz, rDz)); |