Munus

background

「リアルタイムグラフィックスの数学」勉強ログ - 第8章 3Dレンダリング

「リアルタイムグラフィックスの数学」勉強ログ - 第8章 3Dレンダリング
目次

はじめに#

「リアルタイムグラフィックスの数学」の第 8 章の 3D レンダリングについての勉強ログです。

テクスチャマッピング#

地面とレイの交点を計算し、地面に市松模様をテクスチャとしてマッピングします。市松模様のテクスチャは次のようになります。

市松模様のテクスチャ
float text(vec2 st) {
  return mod(floor(st.s) + floor(st.t), 2.0);
}

ここで 3D 空間のカメラの設定をします。カメラの向きをx=(0,0,1)\bm{x} = (0, 0, -1)とし、カメラの上方向をy=(0,1,0)\bm{y} = (0, 1, 0)とします。この値に対して外積をとることで、撮影する向きに対する水平方向(1,0,0)(1, 0, 0)を得ることができます。実際の計算は次のようになります。

x×y=(x2y3x3y2,x3y1x1y3,x1y2x2y1)=(1,0,0)\bm{x}\times\bm{y} = (x_2y_3 - x_3y_2, x_3y_1 - x_1y_3, x_1y_2 - x_2y_1) \\ = (1, 0, 0)

これらの情報を用いることで、カメラからスクリーンまでのベクトルを計算することができます。

カメラからスクリーンまでの距離
vec3 cPos = vec3(0.0, 0.0, 0.0); // 配置位置
vec3 cDir = vec3(0.0, 0.0, -1.0); // カメラの向き
vec3 cUp = vec3(0.0, 1.0, 0.0); // カメラの上方向
vec3 cSide = cross(cDir, cUp); // 外積(カメラの水平方向)
 
float targetDepth = 1.0; // スクリーンまでの距離
vec3 ray = cSide * pos.x + cUp * pos.y + cDir * targetDepth; // レイの方向

サンプルコードでは、マウスの y 座標によってカメラの向きを x 軸を中心に回転させ、マウスの x 座標によってカメラと地面の距離を変更します。

void main() {
  vec2 p = (gl_FragCoord.xy * 2.0 - u_resolution) / min(u_resolution.x, u_resolution.y);
 
  vec3 cPos = vec3(0.0, 0.0, 0.0);
  float t = -0.5 * PI * (u_mouse.y / u_resolution.y); // マウスのy座標を回転角に対応
 
  vec3 cDir = rotX(vec3(0.0, 0.0, - 1.0), t); // カメラの向きをx軸を中心に回転
  vec3 cUp = rotX(vec3(0.0, 1.0, 0.0), t); // カメラの上方向をx軸を中心に回転
  vec3 cSide = cross(cDir, cUp);
 
  float targetDepth = 1.0;
  vec3 ray = cSide * p.x + cUp * p.y + cDir * targetDepth - cPos;
  ray = normalize(ray); // レイを正規化
 
  vec3 groundNormal = vec3(0.0, 1.0, 0.0); // 地面の法線
  float groundHeight = 1.0 + (u_mouse.x / u_resolution.x); // マウスのx座標をカメラと地面の距離に対応
}

続いてレイと地面との交点を計算します。交点の位置は、カメラと地面の位置関係と入射角によって計算できます。

レイと法線と入射角
レイと法線と入射角

入射角はレイが交点に入り込む傾きを表す角度のことです。この入射角θ\theta0<θ<900^\circ < \theta < 90^\circ であるので、cosθcos\thetaの値は 0 以上の値になります。

また、レイ rr と法線 nn を長さ 1 に正規化すると、cosθ=rncos\theta = -r \cdot n が得られます。カメラから地面までの高さを hh とすると、カメラから交点へ向かうベクトルはhcosθr=hrnr\frac{h}{cos\theta}r = -\frac{h}{r \cdot n}rとなります。これをコードで書くと次のようになります。

// 交点判定
if (dot(ray, groundNormal) < 0.0){
    vec3 hit = cPos - ray * groundHeight / dot(ray, groundNormal); // レイと地面の交点
    fragColor.rgb = vec3(text(hit.zx)); // 交点のzx座標をテクスチャ座標に対応
  } else {
    fragColor.rgb = vec3(0.0);
  }

結果は次のようになります。

テクスチャマッピング
テクスチャマッピング

後で詳しく調べるものリスト#

参考書籍#

PR