Skip to content

第07回 ベクトル演算とライティング

前回: 第06回 ノイズ / 次回: 第08回 内積とフレネル、外積と法線マップ

ベクトルで 光の向きを扱う

色と模様の世界から 立体表現へ

ここからが真の シェーダーの世界

前回の振り返り

ノイズを使って不規則ながらも自然に見える表現を作りました

  • 第06回では Simple NoiseGradient NoiseVoronoi を扱いました
  • ノイズ値を色、透明度、UV、マスクに使うことで 自然なムラやディゾルブ表現を作りました
  • Time と組み合わせることで 煙、炎、水面のような動きにも応用できました
  • 今回はベクトルを使い、光の当たり方や反射の表現を学びます

今回の授業で扱うこと

3D 表現の基本になる向きの計算

  • ベクトルの意味
  • 法線とライト方向
  • 内積による明るさの計算
  • 外積による直交方向の計算
  • 反射ベクトルと屈折ベクトル
  • Shader Graph で簡単なライティングを作る
  • 光の向きと面の向きから、立体感を作る考え方

今回の授業内容

内積、外積、法線、反射、屈折

  • ベクトルの復習
  • 法線の役割
  • 内積と Lambert ライティング
  • 外積と接線方向
  • 反射、屈折の考え方
  • 授業内課題

ベクトルとは

複数の数値をまとめたもの

  • ベクトルは、数値の配列 です
  • 2D なら (x, y)、 3D なら (x, y, z) のように表します 4D なら (x, y, z, w) です
  • CG の世界では、主に位置、方向、色などを 表すのに使います
  • 色の RGB 値も、位置座標も、方向も、 シェーダー内ではベクトルとして扱われます
csharp
// C# でのベクトルの例
Vector2 position2D = new Vector2(x, y);
Vector3 position3D = new Vector3(x, y, z);
Vector4 color = new Vector4(r, g, b, a);

// Vector 構造体は
// 配列のようにもアクセスできます
float x = position3D[0]; // x 成分
float y = position3D[1]; // y 成分
float z = position3D[2]; // z 成分

color[0] = 1.0f; // r 成分を 1 にする
color[1] = 0.5f; // g 成分を 0.5 にする
color[2] = 0.0f; // b 成分を 0 にする
color[3] = 1.0f; // a 成分を 1 にする

なぜベクトルを使うのか

3D 空間の情報を幾何学的に扱うため

  • 3DCG は、点、線、面、光などで構成される幾何学の世界です 位置や方向をベクトルで表せば、数学的な演算でそれらを操作できます
  • 例えば加算や減算で、位置を移動させる操作や2点間の方向を求められます 色の情報も RGB を同時に扱うことで計算をまとめられます
  • さらに後ほど説明する内積や外積を使えば、 与えられた情報から新しい情報を導き出すことができます
  • ベクトルは、光の向きや面の向きを計算し、立体感や質感を表現できる 3DCG の世界の便利な共通言語です

ベクトルで扱う「位置」と「方向」

同じ Vector3 でも意味が違う

  • 位置は、空間内のどこにあるかを表します
  • 方向は、どちらへ向いているかを表します
  • 速度は、方向と速さを同時に持つ値です
  • これらはすべて Vector3 で表せますが、同じ Vector3 でも 何を表しているかによって計算の意味が変わってきます
  • シェーダーでは「このベクトルは位置なのか方向なのか」を 意識して使い分ける必要があります

正規化

長さを 1 にそろえる

  • ベクトルには大きさの情報も含まれていますが、 向きだけを使いたい場合があります
  • 例えば、視線の方向やライトの方向などは、通常 向きだけが重要で、大きさは必要ありません
  • ベクトルの向きだけを使いたい場合は、 ベクトルの大きさ(長さ)を得てからベクトル全体をその長さで割ります これで大きさが 1 になり、向きだけを表すベクトルが得られます
  • この操作を正規化、Normalize と呼びます

ベクトルの長さと正規化の式

各成分の二乗の和の平方根

  • 2次元ベクトル v = (x, y) の長さは $$|v| = \sqrt{x^2 + y^2}$$

  • 3次元ベクトル v = (x, y, z) の長さは $$|v| = \sqrt{x^2 + y^2 + z^2}$$

  • ベクトルを正規化する式は $$\text{Normalize}(v) = \frac{v}{|v|}$$

csharp
// C# での正規化の例
Vector3 v = new Vector3(x, y, z);

// ベクトルの長さを計算
float length =
    Mathf.Sqrt(
          v.x * v.x +
          v.y * v.y +
          v.z * v.z);

// ベクトルを長さで割って正規化
Vector3 normalized = v / length;

// Unity には正規化のメソッドもあります
normalized = Vector3.Normalize(v);

ライトの方向

光源から面に向かうベクトル

ライトの方向と面の関係
ライティングの例 光の方向を向いている面は明るくなる

  • 単純に考えると、 法線とライトの向きが同じなら面は明るく、 直角なら面は暗くなるはずです
  • ライトの方向は、光源から面に向かう ベクトルで表せます
  • 例えば日の光の場合、ライトの方向は 太陽から地球に向かうベクトルになります

法線とは

面に垂直な向きを表すベクトル

曲面上の法線ベクトル
面の向きを表す法線ベクトルの例 青のベクトルが法線 出典: Geek3, Wikimedia Commons, CC0 1.0

  • 法線は、面がどちらを向いているかを表す ベクトルです
  • 3D モデルの各面には法線が定義されており、 面の向きや立体感を表現するために使われます
  • ライティングでは、法線とライトの向きから 面の明るさを計算します
  • 法線は面の向きを表す重要な情報であり、 シェーダーの立体表現の基礎になります

内積

2つのベクトルの向きの近さを計算する

  • 方向ベクトル同士の向きの一致度を 計算できれば、面の向きと光の向きから 面の明るさを算出できます
  • その計算が 内積(Dot Product) です 内積は、2つのベクトルの成分ごとの 積の和で計算されます $$ \text{Dot}(A, B) = A.x \cdot B.x + A.y \cdot B.y + A.z \cdot B.z $$
csharp
// C# での内積の例
Vector3 A = new Vector3(aX, aY, aZ);
Vector3 B = new Vector3(bX, bY, bZ);

// 内積を計算
float dot = A.x * B.x +
            A.y * B.y +
            A.z * B.z;

// Unity には内積のメソッドもあります
dot = Vector3.Dot(A, B);

Lambert ライティング

法線とライト方向の内積で明るさを作る

  • Lambert ライティングは拡散反射の基本的なモデルです 光の方向と面の向きの関係 だけで 明るさを計算します
  • 法線とライトの方向ベクトルの内積を計算し、 その値を明るさとして使えば、最小限のライティングが実現できます
  • 単純ですが、例えば月の表面のような マットな質感の表現には十分効果的です

Shader Graph で Lambert ライティングを作る

法線とライト方向の内積を利用する

Lambert ライティングの実装例
Lambert ライティングの実装例

  1. Normal Vector Main Light Direction ノードを追加する
  2. Dot Product で法線とライト方向を計算する
  3. 明るくなる面の法線とライトの方向は 逆向きなので、結果を Negate ノードで反転する
  4. Saturate で 0 から 1 に収める

外積

2つのベクトルに垂直な 新しいベクトルを作る

外積と平行四辺形の関係
a と b の外積の模式図 a と b の外積は、a と b を辺とする 平行四辺形の面に垂直なベクトルになる 出典: Acdx, Wikimedia Commons, Public domain

  • ライティングには面の法線が必要です
  • ベクトルの 外積(Cross Product) は、 2つの方向ベクトルからその両方に垂直な 新しい方向を作る演算です
  • 面の上にある2つの辺の方向が分かれば、 その面に垂直な法線を求められます

外積の式

2つのベクトルから 垂直なベクトルを作る

  • 3次元ベクトル a = (aX, aY, aZ)b = (bX, bY, bZ) の外積は $$ \text{Cross}(a, b) = \begin{pmatrix} aY \cdot bZ - aZ \cdot bY \ aZ \cdot bX - aX \cdot bZ \ aX \cdot bY - aY \cdot bX \end{pmatrix} $$
csharp
// C# での外積の例
Vector3 a = new Vector3(aX, aY, aZ);
Vector3 b = new Vector3(bX, bY, bZ);

// 外積を計算
Vector3 cross = new Vector3(
    a.y * b.z - a.z * b.y,
    a.z * b.x - a.x * b.z,
    a.x * b.y - a.y * b.x);

// Unity には外積のメソッドもあります
cross = Vector3.Cross(a, b);

外積の利用例

面の向きや空間の基準を作る

  • 面の法線を求める 三角形や四角形の辺の方向から、面に垂直な法線ベクトルを計算できます
  • ビルボードの向きを作る カメラ方向と上方向から、スプライトの横方向を求めれば 常にカメラを向くビルボードが作れます
  • エフェクトの広がる方向を決める 進行方向に対して直交する向きを作れば、 軌跡や衝撃波の幅の方向に使うことができます

外積の注意点

  • 外積ベクトルは、計算の順番が逆になると向きも逆になります a x b と b x a は逆向きのベクトルになります $$\text{Cross}(a, b) = -\text{Cross}(b, a)$$

  • 外積ベクトルは大きさも変わります a と b が平行に近いときは、外積の長さが小さくなります 逆に、a と b が直交しているときは、外積の長さが最大になります $$|\text{Cross}(a, b)| = |a| \cdot |b| \cdot \sin(\theta)$$

  • 外積を法線として使う場合は正規化して長さを 1 にする必要がありますが a と b が平行に近いときは外積の長さが 0 に近くなり正規化できなくなります

反射ベクトル

面に当たって跳ね返るベクトル

面、法線、入射方向、反射方向の関係
反射方向は入射方向と法線から求められる 画像はAI生成

  • 反射のベクトルを求めることができれば、 金属、鏡、水面などの光や視線を強く 反射する表現に近づけることができます
  • 反射方向は、入射方向と法線から求められます $$\text{Reflect}(I, N) = I - 2 \cdot \text{Dot}(I, N) \cdot N$$
  • Shader Graph では Reflect ノードで 反射ベクトルを作れます

屈折ベクトル

透明な物体を通るときに方向が曲がる

簡易な屈折表現の例
簡易な屈折表現の例 屈折を正確に扱うには光線の追跡が必要だが 省略した表現でも効果は大きく、ゲームでよく使われる

  • 屈折は、光が透明な物体を通るときに 進む方向が変わる現象です
  • 光が空気中から水やガラスに入るとき、 その進む向きが少し曲がります 屈折方向を計算で求めることができれば ガラスや水の表現に近づけることができます
  • Shader Graph では Refract ノードで 屈折方向のベクトルを作れます

実習

内積でのライティング計算

基本にして奥義な 立体表現

実習1

法線を色として表示する

  1. URP Unlit Shader Graph を作成する
  2. Normal Vector ノードを追加する
  3. 出力を Base Color に接続する
  4. Space を Object、World などに切り替える
  5. モデルの向きや形によって色が変わることを確認する

実習2

内積で面の明るさを計算する

  1. Vector3 プロパティ LightDirection を作る
  2. LightDirectionNormalize に接続する
  3. Normal VectorLightDirectionDot Product に接続する
  4. Saturate で 0 から 1 に収める
  5. 好きな色に Multiply して Base Color に接続する

今回のまとめ

ベクトル演算で光と面の向きを扱う

  • 法線は面の向きを表す重要な情報です
  • 内積を用いて、法線とライト方向の一致度から面の明るさを求めることができます
  • 外積は、2つの向きから直交する新しい向きを作ります
  • 反射や屈折は、視線や光の向きを法線に応じて変化させる考え方です
  • これらのベクトル演算は、シェーダーで立体感や質感を表現するための 基本的なツールになります

おつかれさまでした!

次回予告 第08回 内積とフレネル 外積と法線マップ

角度で変わる より複雑な ライティング表現

使用画像と出典