タンジェント空間の概要
タンジェント空間の基準ベクトル
Tangent(タンジェント)ベクトル: サーフェス上の「U方向」(テクスチャ座標の横方向)に沿ったベクトル。
Bitangent(ビタンジェント)ベクトル: サーフェス上の「V方向」(テクスチャ座標の縦方向)に沿ったベクトル。
Normal(法線)ベクトル: サーフェスの表面に垂直なベクトル。
この3つのベクトルは直交しており、モデルの各頂点やピクセルごとに異なります。
目的 タンジェント空間は、テクスチャ空間(UV座標)に基づくデータ(例: 法線マップ)をモデルのワールド空間やビュー空間に適用するために使用されます。これにより、モデルの細かな凹凸を表現できます。
タンジェント空間の具体的な使用例
法線マップの適用
法線マップは、RGB値を使ってテクスチャ内に擬似的な法線情報を格納したものです。これを利用するには、以下の変換が必要です。
テクスチャ座標での法線(テクスチャ空間)
RGB値をそのまま取り出して、ローカルな法線ベクトルとして利用。
タンジェント空間からワールド空間への変換
タンジェント、ビタンジェント、法線の基準ベクトルを組み合わせて「変換行列」を作成。
テクスチャ空間の法線をワールド空間に変換して正しいライティングを適用。
変換行列の例(TBN行列):
\text{TBN} = \begin{bmatrix}
T_x & B_x & N_x \
T_y & B_y & N_y \
T_z & B_z & N_z
\end{bmatrix}
タンジェント空間の計算方法
頂点ごとのタンジェントとビタンジェント
頂点ごとのUV座標の差分を計算:
サーフェス上のエッジベクトルを計算:
(頂点位置の差)
線形方程式を解いてタンジェントとビタンジェントを求める:
\begin{bmatrix} \Delta U1 & \Delta U2 \ \Delta V1 & \Delta V2 \end{bmatrix}^{-1} \begin{bmatrix} E1 \ E2 \end{bmatrix} ]
正規化
結果のタンジェントとビタンジェントを正規化し、法線ベクトルと直交するように補正します。
タンジェント空間の利用シェーダ例
以下はGLSLでタンジェント空間を用いたシェーダの例です。
// 頂点シェーダ
in vec3 aPosition;
in vec3 aNormal;
in vec2 aTexCoord;
in vec3 aTangent;
out vec3 vNormal;
out vec3 vTangent;
out vec3 vBitangent;
uniform mat4 uModelMatrix;
uniform mat3 uNormalMatrix;
void main() {
// 頂点をワールド空間に変換
gl_Position = uModelMatrix * vec4(aPosition, 1.0);
// 法線、タンジェント、ビタンジェントのワールド空間への変換
vNormal = normalize(uNormalMatrix * aNormal);
vTangent = normalize(uNormalMatrix * aTangent);
vBitangent = cross(vNormal, vTangent); // ビタンジェントを計算}
// フラグメントシェーダ
in vec3 vNormal;
in vec3 vTangent;
in vec3 vBitangent;
uniform sampler2D uNormalMap;
void main() {
// 法線マップから法線を取得(テクスチャ空間の法線)
vec3 normalMap = texture(uNormalMap, vTexCoord).rgb * 2.0 - 1.0;
// TBN行列で法線をワールド空間に変換
mat3 TBN = mat3(vTangent, vBitangent, vNormal);
vec3 worldNormal = normalize(TBN * normalMap);
// ライティング計算(例: ランバート反射)
float lightIntensity = max(dot(worldNormal, lightDir), 0.0);
gl_FragColor = vec4(lightIntensity, lightIntensity, lightIntensity, 1.0);}
タンジェント空間のメリット
ローカル空間の柔軟性: モデルの向きに依存せず、どんな形状でも法線マップを適用可能。
詳細な表現: 高ポリゴンモデルのディテールを低ポリゴンモデルで再現可能(性能向上)。
ライティングのリアル感: テクスチャ空間の凹凸をリアルタイムで反映。
学習ポイント
理解を深めるには、まず法線マップとTBN行列を手で計算し、シェーダコードを実験的に調整して動作を確認してください。
Three.jsやShadertoyを使うと、実験環境を手軽に整えられます。
タンジェント空間の活用は、リアルな3Dグラフィックスを実現するための重要なスキルです!
Q: タンジェント空間(Tangent Space)とは何ですか?
A: メッシュ表面の「接線T・従法線B・法線N」で作る局所座標系です。法線マップのRGBを表面方向へ変換するために使います。
Q: TBN行列とは何ですか?
A: T(tangent)、B(bitangent/binormal)、N(normal)を列(または行)に持つ3×3行列です。法線マップの値(接線空間法線)をワールド/ビュー空間へ変換します。
Q: bitangent と binormal は同じですか?
A: 名称違いで実質同義に扱われます。記事中では「Bベクトル」として統一し、ハンドネス(手の向き)に注意します。
Q: ハンドネス(handedness)とは何ですか?
A: T×B が N と同じ向きになるかを示す符号です。ミラーUVなどで反転が起きるため、B に符号を掛けて整合させます。
Q: 法線マップのGチャンネルを反転する必要がありますか?
A: ツール/エンジンの規約次第です。OpenGL系とDirectX系でY(G)軸が逆のケースがあるため、見た目がおかしければG反転を試します。
Q: 法線マップとバンプマップの違いは?
A: バンプは高さの微分を暗黙的に使うグレースケール。法線マップは接線空間での法線ベクトルそのもの(RGB)を持ち、精度と再現性が高いです。
Q: パララックスマッピングとの違いは?
A: パララックスは視差でテクスチャ座標をずらし奥行きを疑似表現します。法線マップは陰影のみを改善します。併用可能ですがコストは上がります。
Q: TBNは頂点とフラグメントのどちらで計算しますか?
A: 一般に頂点でT・B・Nを計算し補間してフラグメントで使います。高品質が必要ならフラグメントで再直交化(Gram–Schmidt)すると安定します。
Q: 再直交化が必要なのはなぜですか?
A: 補間や数値誤差でT・B・Nが直交しなくなるためです。Nを基準にTを直交化し、B=normalize(cross(N,T))*handedness で再構築します。
Q: MikkTSpace とは何ですか?
A: 接線空間の業界標準定義です。DCC/エンジン間で見た目を一致させる目的で広く採用され、法線マップの互換性を高めます。
Q: 接線Tはどうやって求めますか?
A: 三角形の位置差分とUV差分から連立で解きます。複数三角形で加算・正規化して頂点Tを得ます。BはT×N(×符号)で求めるのが一般的です。
Q: ミラーUVや重なりUVの場合の注意点は?
A: 島ごとにハンドネスが変わるため、符号を頂点属性として保持するか、ピクセルで計算して反転に対応させます。
Q: 接線空間法線(RGB)をそのままライティングに使えますか?
A: いいえ。まず TBN を用いてワールド/ビュー空間へ変換し、ライト方向とドット積を取ります。
Q: Unity/Unreal/Three.js で結果が違うのはなぜ?
A: 接線空間の定義、G反転の有無、法線の空間(ワールド/ビュー/タンジェント)やスケール正規化の差が原因です。各環境の規約に合わせます。
Q: 法線マップが「縫い目」で崩れるのはなぜ?
A: UVシームでTBNが連続でない、ハンドネス不一致、MikkTSpace非対応のベイクなどが原因です。MikkTSpaceベイクとスムージングを見直します。
Q: メッシュスケールの変更で陰影が破綻します。対策は?
A: 行列スケールが法線に影響するため、法線の再正規化(normalize)を必ず行います。非一様スケール時は注意が必要です。
Q: モバイル向けに軽くするには?
A: テクスチャ解像度を適正化、パララックスを省略、TBNの再直交化を簡略化、ライト数を削減します。不要なら切り替え可能にします。
Q: デバッグのコツはありますか?
A: 接線空間法線を可視化(RGB表示)、G反転のオンオフ、T・B・Nの各軸を色で表示、スムーズ/ハード法線の切替などで原因を切り分けます。
Q: この記事のキーワードは何を入れるべき?
A: タンジェント空間, TBN行列, 法線マップ, GLSL, MikkTSpace, bitangent, handedness, 再直交化, OpenGL, DirectX などを自然に本文へ散布します。
Q: どの順番で学べば理解が早いですか?
A: 「座標系の基礎」→「UVと接線の関係」→「TBN行列の構築」→「GLSL実装」→「MikkTSpaceと互換性」→「デバッグと最適化」の順が効率的です。