統合趣味環境

その都度、気になったことを綴ります。

レンズの光線追跡式

光線追跡プログラム開発1周年

先日の2021年9月2日をもって、写真レンズ関係のプログラムが開発1周年を迎えました。

まだまだ進化を続けていこうと思いますので、温かい眼差しで見守っていただけると幸いです。



厳密な光線追跡をしよう

レンズ設計・性能解析の要と言って過言でない光線追跡。設計段階の初期では近軸追跡という三角関数の近似計算がメインとなるものの、いずれにせよ厳密な光線追跡は大事。

簡単に解説すると、平面内(メリジョナル断面内)で考えるだけならn\sin i = n'\sin i'と計算したり、3次元なら外積に相当する計算を繰り返して、厳密な光線追跡を行います。

ズボラなので図を1つしか描いていないけども、この記事を読み込めば2D光線追跡プログラムが組めるレベルに仕立て上げたので、興味がある人はぜひ挑戦してみてください。

2次元に限って言えば、高校1年生の知識だけで読めます

3次元はまた次の機会に触れようと思います。





基本のき

光線追跡に限らず、レンズには様々な「流儀」があります。まずはプロフェッショナル:レンズの流儀を身につけましょう。今回の記事で、そこらへんの雰囲気を会得していただくと専門書などが読みやすくなるのではないかなと思います。

基本:初級編

「光線」の概念は当然の如く認める。従って波長とレンズ系の規模の間に制約があることを断っておく。

さらに認めてしまいましょうという原理が、スネルの法則の名で有名な


\begin{aligned}
n\sin i = n' \sin i'
\end{aligned}

である。屈折率と入射角の関係を表す重要な式だが、これを大前提として以下の話を進める。

ちなみに、以下では共軸の屈折光学系だけ考え、軸外し・反射面・トロイダルレンズの存在などは全く考えない(反射球面は案外簡単に議論を拡張できるらしいが踏み込まない)。

うっ、こう考えると制約が多い...

基本:中級編

確か、歪曲収差の記事で「角度は光学屋さんの流儀に則る」という文を複数書いた気がする。これがかなり特殊なので、この段階で明確にしておく。

まず、左に物界、右に像界を持ってくる。もっと広く言えば、だいたい光が左から右へ進むようにする。

レンズと光軸の交点を、レンズ「頂点」とか、屈折面の「頂点」と呼ぶ。

物点・像点はいずれもレンズ頂点から右側(つまり像より)を正に取る。

つまり中学物理の凸レンズは


\dfrac{1}{a} + \dfrac{1}{b} = \dfrac{1}{f}

ではなく


-\dfrac{1}{a} + \dfrac{1}{b} = \dfrac{1}{f}

になる!

まじかよ...(まじです)。

自ずと光線角度の正負に制約が生まれ、右肩下がりを正、右肩上がりを負とする。

例えば、無限に薄い平板上で光軸から距離hだけ離れた点から、平板の後方(つまり右方)にLだけ離れた光軸上の点に向かう光線の場合、角度u


\tan u = \dfrac{h}{L}~~,\quad\therefore\quad u = \arctan \dfrac{h}{L}

で定義するのが自然な感じがするでしょ?だから右肩下がりが正になる。

具体的な状況は、子午面内の光線追跡を読んでいくと分かるだろう。

基本:上級編

まず直感的に説明すると、レンズの曲率半径は頂点が左に凸の場合を正、逆を負と定めます。

実際には曲率半径が負というのは数学的に誤りなうえ、よく考えれば中級編の基準に沿って頂点の右に曲率中心があるのが正と理解するのが適当でしょう。

いずれにせよ、これら基準に沿って計算していくと、レンズの向き・虚像や実像などでイチイチ場合分けする必要がないことが確認できます(本記事では確認しません)。

素晴らしいな。



「球面」屈折光学系の子午面内(メリジョナル面内)光線追跡

球面だけを含む共軸光学系を例に、光軸を含むレンズ断面内の計算を考えよう。回転対称性から、光線が断面内から外れることがないと示せる。

ただ、最近はこれに特化したコードを組む場面は少ないらしい。大は小を兼ねるので3次元光線追跡が幅を利かせているようだ。

ここでは以下の図をもとに話を進める。詳しくはMax Born, Emil Wolfの『光学の原理(1)』を参照されたい。

f:id:positiveclimb:20210905091420j:plain
2D光線追跡式に利用する記号

まず、レンズ面から左側の領域の屈折率をn、右側をn'とする。

光軸に対して、曲率半径rのレンズ頂点から距離Lの点に角度uで入射する光がある。これとレンズの法線がなす角をiとする。

光線はABに沿って図の左方から入射し、Aで屈折することでCへ向かう。

基本の中級編で話した光線角度(uu')は、上図では右肩下がりだから正になる。

ちなみに参考書に則って記号を書いたが、邦書ではLL'よりss'を見かけることが格段に多い。

まず簡単に

\triangle OABに注目する。Oを通るABへの垂線を引くと、その垂線の長さから


\begin{aligned}
(\text{垂線の長さ}) = r\sin i &= (L - r)\sin u\\
\therefore\qquad \sin i &= \dfrac{L - r}{r}\sin u
\end{aligned}

が導ける。

従ってレンズの設計値rと入射光線の向かう場所Bすなわちr + OB = L、そして光線角度uが分かればレンズ曲面への入射角iが分かるようになった。

次に、どれほど屈折するのか、つまりi'を明らかにしたい。スネルの法則から


\begin{aligned}
n\sin i &= n' \sin i'\\
\therefore\qquad \sin i' &= \dfrac{n}{n'}\sin i
\end{aligned}

非常に簡単。

また屈折後に光線が光軸となす角u'は、\triangle ABCの外角Cを求めればいいから


u' = u + i - i'

で求まる。

最後に、最初と同様に\triangle OACOを通るACの垂線の長さから


\begin{aligned}
(\text{垂線の長さ}) = r\sin i' &= (L' - r)\sin u'\\
\therefore\qquad L' &= \dfrac{sin i'}{\sin u'} r + r
\end{aligned}

これで、晴れて単一屈折面の計算が終了した。

屈折面が複数ある場合、新たにレンズ間隔を定義する必要がある。レンズ間隔は、互いの頂点どうしの距離のことを言い、基本的にdで置く。正負は前のレンズ頂点基準で右を正、左を負とする(物点とかと同様)。ただし反射面を考えないと断った以上、 d \geq 0である。

従って第二面の入力値Luを、分かりやすいようにそれぞれL _ 2u _ 2とすれば、明らかに


\begin{gather}
L _ 2 = L' - d\\
u_2 = u'
\end{gather}

と引き継げばよいことが分かる。このように複数のレンズがあるときにdを引く行為にも、きちんとLL'の符号を定める重要性が見いだせる。

お気づきだろうか、除外した場合

ここまで、既に一般的に論じた雰囲気を醸し出しているが、実際には特殊事例を除外している。

早い話、入射・射出角度が0のときに距離LL'を定義できないのである。

この条件は特に初期条件で与える場合が多いので、しっかり対応しておかないと後で何もできなくなる。

ところで、今まで光軸上の距離だけ考えていて、交点Aの高さを気にしなかった点を解消しよう。これは光線をグラフ化する時、特に重要になる。

法線が光軸となす角がu + iあるいはu' + i'になっていることに気付けば、レンズ頂点と交点の光軸方向距離(以下、サグ量とする*1)は


(\text{距離}) = r - r\cos(u + i) = r - r\cos(u' + i')

で求まるし、光軸からの高さh


h = r - r\sin(u + i) = r - r\sin(u' + i')

で求まることがすぐ分かる。前者に「レンズ初面から第k面までの距離」を足せば、描画の問題は解決された。

なぜ突然これを計算したかというと、たとえu=0であれ光線高さの概念は存在するため、上式でu = 0とかを代入しても成立するからである。つまり、今度は光線高さから攻めるんだな。

上の式で、u = 0とした場合に


h = r - r\sin i = r(1 - \sin i)~~,\quad\therefore\quad \sin i = 1 - \dfrac{h}{r}

であることがわかる。屈折自体はスネルの法則を使っていいのでi'が分かるし、さっき使った


u' = u + i - i' = i - i'

がそのまま適用できるから、これ以降は先の方法に沿えばいい。

ただし、このときu' = 0が生じた場合は例外で、無理にL'を求める必要は無い。というのも、この場合は次の面でu = 0になって上の計算を適用するが、これ自体が既にLとかを欲しないシステムであるから。

従って先のナンチャッテ一般論に加えて、光線角度での条件分岐と、光線高さhを逐次求めるくらいで対応可能なことが示された。

初期条件の与え方・1回の屈折計算で計算すること

いろいろ方法があると思うが、私の方法を書いておく。

最初の穴だらけの方法だと、屈折前後で


(L, u) \longrightarrow (L', u')

という変換をしていて非常に単純だが、実際にはuのワガママに付き合う必要があることが判明し、光線高さhを導入する流れになった。

結果、私は(L, u) \longrightarrow (L', u')の中で条件分岐させると共に、内部で暗に(h, u) \longrightarrow (h', u')を行うのが楽だろうと思ったので、以下に概略を示す。

初期条件はあとで色々便利なように、レンズ初面の頂点に接する平面上での光線高さをHとして、(H, u)というセットで与える。ちょっと定義が複雑になったが、頭書の図から明らかにu\neq 0 \dfrac{H}{L} = \tan uであるし、u = 0H = hである。初面以降はHに相当する概念は必要ない。

これをもとに屈折の計算をし、レンズ上の屈折点が厳密に


(\text{サグ量}~,\quad\text{高さ}) = \left(r - r\cos(u' + i')~,\quad r - r\sin(u' + i')\right)

で求まり、これを用いて光線を描画できる。さらに座標の後者はhとなるから、描画以外にも重要である。

この段階でu' = u_2 \neq 0ならばL'が求まっているから、dを引いてL_2を求め、引き続き計算する。

u' = u_2 = 0ならばh = h_2なので、条件分岐させて特殊事例の計算を適用すれば問題ない。

従って、これでプログラムが組めてしまう。

平面の場合

平面の場合はrの概念が通用しなくなり、i = uかつi' = u'となる。従ってスネルの法則が


n\sin u = n'\sin u'

となり、これ自体でu\longrightarrow u'の変換が成立する。

ただしL\longrightarrow L'の受け渡しは、球面の特殊事例以上にhを活用する必要がある。

i面で\left(\text{高さ},\text{サグ量}\right) = (h _ i, z _ i)とする。レンズ間隔d _ iだけ後ろに平板のi + 1面があるとすれば、その点での高さh _ {i + 1}


h _ {i + 1} = h _ i - (d _ i - z _ i) \tan u _ i

で求まり、


L' _ {i + 1} = \dfrac{h _ {i + 1}}{\tan u' _ {i + 1}}

である。

このようにして、平板であれ光線追跡を実装することができる。



まとめ

これであなたも光線追跡マスターになれる!と思いきや3次元光線追跡について論じていません。さすがに文量が膨大になるので、この話題は次回に回しましょう。

ただ、物点・像点や角度の流儀は共通ですし、一面ずつ屈折の様子を探っていくという基本のスタンスは全く変わりません。

素人なので計算が冗長かもしれません。「こうすれば簡単なのにな~」と感じる方はぜひコメントください

では、この辺で。



*1:私は勝手にサグ量と呼んでいるが、厳密に正しい言葉の使い方か判然としない