<HEAD>
<TITLE>Dynamical Environment Mapping</TITLE>
</HEAD>

<BODY BACKGROUND="../Image/Back2.GIF"
	TEXT="#E0E0E0"
	BGCOLOR="#191919"
	LINK="#90D0FF"
	VLINK="#4080D0"
	ALINK="#0000FF">

<BASEFONT SIZE="3" FACE="ＭＳ Ｐ明朝">

<BR>
<CENTER>
<FONT SIZE="7" COLOR="80FF80"><I>Dynamical<BR>Environment Mapping</I></FONT>
<P>
<P>
</CENTER>

<CENTER>
<IMG SRC="../Image/Bar2.gif" ALT "-------------------------------------------" width=100% height="8">
</CENTER>
<P>

<P>
<DL>
<DT>
<FONT SIZE="3" COLOR="#FFFF80"><B><I>98/11/07（土）　</I></B></FONT>
<FONT SIZE="4"><B>− Dynamical Environment Mapping − 動的環境マッピング</B></FONT>
<P>
<DD>
　<FONT COLOR ="FFA0A0">3DFC'98 Fall</FONT>
での私の講演ネタその１「<FONT COLOR ="FFA0A0">動的環境マッピングの試み</FONT>」
についての詳細(という程でもないが)を述べることにする。
<P>
　<FONT COLOR ="FFA0A0">環境マッピング(Environment Mapping)</FONT>
または<FONT COLOR ="FFA0A0">リフレクションマッピング(Reflection Mapping)</FONT>は
すでに多くの方がご存知のことと思う。
一応簡単に説明すると、
<FONT COLOR ="FFA0A0">周囲３６０°</FONT>の背景を描画した
<FONT COLOR ="FFA0A0">テクスチャ画像(環境マップ)</FONT>を
あらかじめ用意しておき、
描画時に、オブジェクトの頂点毎に
<FONT COLOR ="FFA0A0">視線の正反射(鏡面反射)ベクトル</FONT>を求め、
その方向のテクスチャ座標を指定することで、
あたかも周囲の背景がオブジェクトに映り込んでいるように見せかける
特殊なテクスチャマッピングである。
<BR>
　実装方法などはご存知ない方が多いようなので
ここで簡単に説明しておく。
<P>
　まず、
<FONT COLOR ="FFA0A0">頂点の法線ベクトル</FONT>と
<FONT COLOR ="FFA0A0">視線ベクトル</FONT>から、
視線の頂点における<FONT COLOR ="FFA0A0">正反射ベクトル</FONT>を計算する。
正反射ベクトルの計算は
<FONT COLOR ="FFA0A0">フォン(Phong)のモデル</FONT>でも使われるが、
<FONT COLOR ="FFA0A0">視線の逆方向ベクトル</FONT>を
<FONT SIZE="4" COLOR="#FFFF99"><B><I>Ｅ</I></B></FONT>、
<FONT COLOR ="FFA0A0">頂点の法線ベクトル</FONT>を
<FONT SIZE="4" COLOR="#FFFF99"><B><I>Ｎ</I></B></FONT>とおくと、
<FONT COLOR ="FFA0A0">視線の頂点における正反射ベクトル</FONT>
<FONT SIZE="4" COLOR="#FFFF99"><B><I>Ｅ’</I></B></FONT>は
<P>
<CENTER>
<FONT SIZE="6" COLOR="#FFFF99">
<I>Ｅ’</I>＝２（<I>Ｅ</I>・<I>Ｎ</I>）<I>Ｎ</I>−<I>Ｅ</I>
</FONT>
</CENTER>
<P>
で求めることができる。
ちなみに
<FONT SIZE="4" COLOR="#FFFF99"><B><I>Ｎ・Ｅ</I></B></FONT>は
視線の逆方向ベクトルと
法線ベクトルの<FONT COLOR ="FFA0A0">内積</FONT>である。
この<FONT COLOR ="FFA0A0">内積</FONT>は
<FONT COLOR ="FFA0A0">２つのベクトルの成す角度</FONT>を
<FONT SIZE="4" COLOR="#FFFF99"><B><I>θ</I></B></FONT>としたときの
<FONT SIZE="4" COLOR="#FFFF99"><B><I>cosθ</I></B></FONT>に等しい。
<BR>
　次に、求めた<FONT COLOR ="FFA0A0">視線の正反射ベクトル</FONT>
<FONT SIZE="4" COLOR="#FFFF99"><B><I>Ｅ’</I></B></FONT>
から、
<FONT COLOR ="FFA0A0">環境マップ上の対応するテクスチャ座標(u, v)</FONT>を
計算する。
つまり、<FONT COLOR ="FFA0A0">
視線が反射した向きからテクスチャ座標を決定する</FONT>訳だ。
<BR>
　ところが実は困ったことに、この計算は
<FONT COLOR ="FFA0A0">環境マップの形式</FONT>によって
いくつかの方法がある。
ここで<FONT COLOR ="FFA0A0">環境マップの形式</FONT>と書いたのは、
例えば
<FONT COLOR ="FFA0A0">
上下左右前後</FONT>の
<FONT COLOR ="FFA0A0">計６枚のテクスチャ画像を環境マップとして使用する</FONT>
方法や、
地図の<FONT COLOR ="FFA0A0">
メルカトル図法</FONT>のような<FONT COLOR ="FFA0A0">極座標系</FONT>を用いて
<FONT COLOR ="FFA0A0">
１枚のテクスチャで３６０°全景をカバーする</FONT>方法などである。
<BR>
　おそらく最も理解しやすいのは
<FONT COLOR ="FFA0A0">極座標系</FONT>を用いた方法であるが、
ここでは<FONT COLOR ="FFA0A0">
OpenGL</FONT>が採用している<FONT COLOR ="FFA0A0">
少々特殊な形式</FONT>を利用することにする。
<BR>
　OpenGLの環境マップは、一口で言うなら
<BR>
<FONT COLOR ="#FFFF99" SIZE="4"><B>
「無限遠に配置した完全鏡面反射球に映り込んでいる円形の背景」</B></FONT>
<BR>
である。
<BR>
これだけでは訳わかめなのでもう少し詳しく説明すると、
<FONT COLOR ="FFA0A0">無限遠</FONT>というのは、
<FONT COLOR ="FFA0A0">
理論上球体のどの部分に届く視線ベクトルも、
すべて平行になっている必要がある</FONT>ためである。
このような条件を満たしている場合、球体の
<FONT COLOR ="FFA0A0">視点側の半球部分</FONT>には、
<FONT COLOR ="FFA0A0">３６０°全景が映り込む</FONT>ことになる。
下図を御覧いただきたい。

<CENTER>
<IMG SRC="Image/spheremap.gif" BORDER="0" WIDTH="340" HEIGHT="375">
</CENTER>

<FONT COLOR ="FFA0A0">
環境マップとして使用するのは映り込んだ円形部分</FONT>であり、
<FONT COLOR ="FFA0A0">
視線反射ベクトル</FONT>
<FONT SIZE="4" COLOR="#FFFF99"><B>
（<I>ｘ，ｙ，ｚ</I>）</B></FONT><FONT COLOR="#88FFFF">(上の水色の→)</FONT>
から<FONT COLOR ="FFA0A0">
テクスチャ座標</FONT><FONT SIZE="4" COLOR="#FFFF99"><B>（<I>ｕ，ｖ</I>）</B></FONT>を
求める変換は以下のようになる。
<P>

<CENTER>
<IMG SRC="Image/calc_m.gif" BORDER="0" WIDTH="332" HEIGHT="54">
</CENTER>

とした時<P>

<CENTER>
<FONT SIZE="6" COLOR="#FFFF99">
<I>ｕ</I>＝<I>ｘ</I>／<I>ｍ</I>＋１／２<P>
<I>ｖ</I>＝<I>ｙ</I>／<I>ｍ</I>＋１／２<BR>
</FONT>

</CENTER>
<P>
求まった<FONT COLOR ="FFA0A0">
テクスチャ座標(u, v)</FONT>は、
<FONT COLOR ="FFA0A0">(0, 0)〜(1, 1)の正方形に内接する
円領域に必ず収まる</FONT>。
正方領域内でも、<FONT COLOR ="FFA0A0">円領域以外の部分</FONT>は
まったく使用されない。
このため、テクスチャ画像には<FONT COLOR ="FFA0A0">多少の無駄</FONT>ができる。
しかし、この形式を利用すると
<FONT COLOR ="FFA0A0">極座標系を用いる方法よりも
計算負荷が少ない</FONT>という利点がある。
<BR>
　<FONT COLOR ="FFA0A0">極座標を用いた環境マップ</FONT>では、
<FONT COLOR ="FFA0A0">視線反射ベクトル</FONT>から
<FONT COLOR ="FFA0A0">テクスチャ座標(u, v)</FONT>を求める際、
<FONT COLOR ="FFA0A0">逆三角関数が必要</FONT>になる。
しかしこちらの方法では、負荷のかかる部分はルート計算１回くらいである。
<P>
　<FONT COLOR ="FFA0A0">(u, v)</FONT>が求まったら、
<FONT COLOR ="FFA0A0">それをテクスチャ座標</FONT>として
指定するだけである。環境マッピングの方法については以上だ。
<BR>
　それにしても、<FONT COLOR ="FFA0A0">OpenGL</FONT>では
<FONT COLOR ="FFA0A0">モデリング行列とビュー行列が１つにまとまっている</FONT>のが
不便である。
おかげで<FONT COLOR ="FFA0A0">カメラがローリングしても
映り込みは基本的に回転しない</FONT>...。

<P>

　さて、ここでようやく本論に戻ろう。
環境マップは<FONT COLOR ="FFA0A0">あらかじめ用意した静的な画像</FONT>を
使うため、
通常動的に動く(つまりシーンの状態によって映り込むオブジェクトも変化する)
ことはない。
これを結構真面目に計算して<FONT COLOR ="FFA0A0">
動的に動かしてしまえ</FONT>
というのが今回のネタである。
<BR>
　上で説明した変換式を利用すると、
<FONT COLOR ="FFA0A0">
シーン内のオブジェクトから環境マップを描画する</FONT>ことができる。
<FONT COLOR ="FFA0A0">
最終的に環境マッピングを施したいオブジェクト
(とりあえずターゲットのオブジェクトと呼ぶことにする)</FONT>から見た
シーン内のさまざまな<FONT COLOR ="FFA0A0">
オブジェクトの頂点座標</FONT>をもとに、
<FONT COLOR ="FFA0A0">
環境マップ上の対応する(u, v)座標を計算できる</FONT>からだ。
<BR>
　ちょっと具体的に書くと(必要ないって？)、
他のオブジェクトの頂点座標からターゲットのオブジェクトの座標を
<FONT COLOR ="FFA0A0">引いて</FONT>
<FONT COLOR ="FFA0A0">単位化</FONT>すれば
<FONT COLOR ="FFA0A0">方向ベクトルが求まる</FONT>ので、
それを上の式に当てはめて<FONT COLOR ="FFA0A0">(u, v)</FONT>を計算すれば良い。
つまり、
<FONT COLOR ="FFA0A0">
シーン内のオブジェクトにモデリング変換、ビューイング変換、
透視変換をかけてスクリーン座標を求める</FONT>代わりに、
<FONT COLOR ="FFA0A0">
上の変換をかけて求めた(u, v)座標</FONT>で描画するだけで
<FONT COLOR ="FFA0A0">環境マップを作成できる</FONT>訳だ。
<P>
<CENTER>
<IMG SRC="Image/envrendering.jpg" BORDER="2" WIDTH="253" HEIGHT="254">
</CENTER>
<P>
　後は<FONT COLOR ="FFA0A0">作成した環境マップをテクスチャに設定</FONT>し、
環境マッピングを施して通常通り描画すれば良い。
<P>
<CENTER>
<IMG SRC="Image/tunnel.jpg" BORDER="2" WIDTH="254" HEIGHT="255">
</CENTER>
<P>
これを<FONT COLOR ="FFA0A0">毎フレーム行う</FONT>訳だ。
<P>
　ところで既にお気づきの方も多いと思うが、
この方法で良好な結果を得ることができるのは、
あくまで
<FONT COLOR ="FFA0A0">
ターゲットのオブジェクト</FONT>に対して
<FONT COLOR ="FFA0A0">環境マッピングした時だけ</FONT>である。
厳密には、
第一段階として環境マップを作成した際の、
<FONT COLOR ="FFA0A0">ターゲットのオブジェクトの座標</FONT>から
<FONT COLOR ="FFA0A0">
少しでもずれている頂点</FONT>(通常ずれていない方が珍しいだろう)は、
<FONT COLOR ="FFA0A0">すべておかしく</FONT>なっている。
したがって実際に使用する際には、
このことを充分に考慮しておく必要がある。
例えば<FONT COLOR ="FFA0A0">ターゲットのオブジェクトにしか
環境マッピングを施さない</FONT>という手もあるし、また、
環境マップ作成時に、<FONT COLOR ="FFA0A0">
ある程度以上離れているオブジェクトしか環境マップに描き込まない</FONT>
という手もあるだろう。
<BR>
　<FONT COLOR ="FFA0A0">3DFC'98 Fall</FONT>で
デモをした、トンネルの中をグラス(？)が飛んでゆく
サンプルでは、そもそも
<FONT COLOR ="FFA0A0">
環境マッピングを施しているオブジェクト</FONT>は
<FONT COLOR ="FFA0A0">
１つしかない</FONT>ため、見てわかる程の不自然さは出ていない。
実は、<FONT COLOR ="FFA0A0">
屈折マッピング(Refraction Mapping)</FONT>も同じ方法で
既に動いているのだが、<FONT COLOR ="FFA0A0">
不自然さがあまりに目立って</FONT>しまったため、
今回はお見せできなかった。
実用可能にするためにはかなりの改良が必要である。
<P>
　さて、<FONT COLOR ="FFA0A0">環境マップの描画</FONT>には
さまざまな方法が考えられる。
トンネルのデモでは、<FONT COLOR ="FFA0A0">フレームバッファに描画</FONT>
した後で、
<FONT SIZE="4" COLOR="#FFFF99"><B><I>
glCopyTexImage2D()
</I></B></FONT>
という<FONT COLOR ="FFA0A0">OpenGLのコマンド</FONT>を利用して
<FONT COLOR ="FFA0A0">フレームバッファの内容をテクスチャにコピー</FONT>
している。
実は今回のトンネルデモの場合、<FONT COLOR ="FFA0A0">座標変換テーブル</FONT>を
用意して<FONT COLOR ="FFA0A0">環境マップを自前で描画</FONT>した方が
高速だったと思う。
特に<FONT COLOR ="FFA0A0">AGP</FONT>に対応したカードでは、
確実にそちらの方が速いはずである。
ではなぜ、敢えて<FONT COLOR ="FFA0A0">OpenGLコマンド</FONT>を利用したのか？
<BR>
　理由は単純明解、<BR>
<FONT SIZE="4" COLOR="#FFFF99"><B>
「ドライバとハードウェアが対応すれば
プログラムの変更なく高速化できる」</B></FONT><BR>
の一言に尽きる。
以上である。
<FONT COLOR ="FFA0A0">現時点では</FONT>自前でバッファを用意して
レンダリングした方が高速でも、
<FONT COLOR ="FFA0A0">将来速いハードが現れた時に逆転する</FONT>可能性が高い。
おそらく今でも、<FONT COLOR ="FFA0A0">ジオメトリエンジン</FONT>を
搭載している<FONT COLOR ="FFA0A0">ハイエンドグラフィクスカード</FONT>なら、
OpenGLコマンドを利用した方が速いだろう。
<BR>
　さらに別の方法として、G<FONT COLOR ="FFA0A0">lide API</FONT>を
利用する方法もある。
今は時間がないので手を出せないが、
Glideを使えばそもそも<FONT COLOR ="FFA0A0">描画した環境マップを
テクスチャにコピー</FONT>する
作業すら<FONT COLOR ="FFA0A0">必要ない</FONT>ようである。
しかも<FONT COLOR ="FFA0A0">
ジオメトリ演算の概念すらない単なるラスタライザ</FONT>であるため、
可能な限り無駄を省くことができる。
おそらく今回のようなデモなら、比べ物にならないくらい高速化できるだろう。
<P>
　トンネルデモは、
<BR>
<A HREF="http://lpserv01.bais.chubu.ac.jp:8080/~g93088/archives/tunnel.zip">
http://lpserv01.bais.chubu.ac.jp:8080/~g93088/archives/tunnel.zip
</A>
<BR>
からダウンロードできるので、興味のある人は試して頂くとよい。
<FONT COLOR ="FFA0A0">背景のトンネルの模様</FONT>が、
きちんと<FONT COLOR ="FFA0A0">動的に中央のグラスに映り込んでいる</FONT>
ことが理解できると思う。
また、マウス右ボタンでメニューが出るので、適当にいろいろ試してみて欲しい。
<BR>
　ただし、<FONT COLOR ="FFA0A0">
Voodooやソフトウェア描画では正常に実行できない</FONT>ので
悪しからず御容赦を....。
何せ<FONT COLOR ="FFA0A0">glCopyTexImage2D()</FONT>を
<FONT COLOR ="FFA0A0">実行できない</FONT>とその時点でアウトなうえに
<FONT COLOR ="FFA0A0">
ソフトウェア描画のテクスチャマッピングはクリッピングでバグバグ</FONT>。
明らかにバグなのに直らない...。
</DL>

<!-- Signature -->
<FONT SIZE="3" FACE="ＭＳ Ｐ明朝">

<CENTER>
<IMG SRC="../Image/Bar2.gif" ALT "-------------------------------------------" width=90% height="16">
</CENTER>

<UL>
	<A HREF="index.html"><B><I>Masa's Column</I></B>に戻る</A>

	<TABLE>
	<TR>
	<TD>
	<A HREF="../index.html"><IMG SRC="../Image/MasaPlate.gif" BORDER="0" WIDTH="112" HEIGHT="48"></A>
	</TD>
	<TD><A HREF="../index.html">ホームページに戻る</A></TD>
	</TR>
	</TABLE>

	本ページの御意見・御感想は<BR>
<B>
<ADDRESS>
	<A HREF="mailto:masa@daionet.gr.jp">
		<I>E-Mail: masa@daionet.gr.jp</I></A>
</ADDRESS>
</B></UL>

</FONT>
<!-- Signature -->

</BODY>
