Japanese | English | Korean

Samples

視錐台カリング

あらかじめ画面外の3Dオブジェクトを判定し、このオブジェクトを描画処理関数に渡さないことで、実行速度の向上と描画時に使用するメモリの節減を行うことができます。この手法を「視錐台カリング」といいます。

目次


3Dオブジェクト単位で視錐台カリングを行うメリットは

  • 通常のポリゴン単位のカリングと比べて、速度が向上する
  • 描画時に必要となるメモリの節減になる
  • 結果として、画面内に大量の3Dオブジェクトを描画できる

が挙げられます。


Sample_Culling実行画面


1. 視錐台

視錐台とは、3次元空間上において、ディスプレイに表示される領域のことで、カメラを頂上とする四角錐を近平面(Near Clip)と遠平面(Far Clip)で切り取った形をしています。
視錐台のワールド座標系における位置および大きさは、カメラの位置と向き、ニアクリップ・ファークリップおよび画角によって決定されます。また、カメラ座標系においては、視錐台は原点(カメラがある位置)を頂上として、カメラの向いている方向に向かって配置されます。

画角をαとするとき、e = 1 / tan(α/2) によって表される値を焦点距離といいます。焦点距離をeとすると、カメラ座標系において、平面z = eは視錐台の左右の平面とx =±1で交わります。
ディスプレイの高さを幅で割った値のことをアスペクト比といいます。アスペクト比をaとすると、カメラ座標系において、平面z = eは視錐台の上下の平面とy =±aで交わります。



2.カリング

モデルをレンダリングしても、実際にディスプレイに表示されるのは視錐台内にあるモデルだけです。そこで、あらかじめ各モデルが視錐台内にあるかどうかを判定し、視錐台内にあるモデルだけをレンダリングすることを考えます。視錐台内にないことが分かったモデルをレンダリングしないようにすることをカリングといいます。ここでは球体モデルに対する視錐台カリングについて説明します。

球体モデルの半径をrとし、モデルの位置をモデルの中心の位置ベクトルで表すこととします。モデルが視錐台内にあるかどうかを判定するには、モデルが視錐台を構成する6つの各平面より内側にあるかどうかを調べます。それには、モデルの位置ベクトルと各平面の視錐台内へ向かう法線ベクトルとの内積をとり、その値に視錐台平面からカメラの位置までの距離を加えます。その値が正ならばモデルの中心は視錐台内にあります。値が負の場合でも、その値が –r よりも大きければ、モデルの一部は視錐台内にあります。

なお、カメラ座標において、焦点距離をe、アスペクト比をa、視錐台のある方向をz軸正の向きとするとき、視錐台の各平面の法線ベクトルは次のように与えられます。

  • 近平面:( 0, 0, 1 )
  • 遠平面:( 0, 0, -1 )
  • 左平面:( e, 0, 1 )
  • 右平面:( -e, 0, 1 )
  • 下平面:( 0, e, a )
  • 上平面:( 0, -e, a )

内積を計算する際はこれらのベクトルを正規化する必要があります。



3.カリング判定のサンプルコード

ここではサンプルコード内の球体オブジェクトに対する視錐台カリングの判定をする関数checkCulling_sphereについて説明します。

この関数はプレイヤー、カリング判定対象となるオブジェクト、およびカメラの情報からカリング判定を行います。以下のように変数を用意します。

/* 球体オブジェクトに対する視錐台カリング */
hi_bool checkCulling_Sphere
( Player *player, BSphere *sphere, CameraFrame *cam_frame )
{
hi_sint32 aspect;      /* アスペクト比 */
hi_sint32 focus;        /* 焦点距離 */

Vec3i camera_pos;    /* camera 位置 */

/* 視錐台平面法線ベクトル */
Vec3i clip_dir;
Vec3i left_dir;
Vec3i right_dir;
Vec3i under_dir;
Vec3i up_dir;

hi_sint32 cam_dir;    /* 平面からカメラ位置までの距離 */

hi_sint32 dot;         /* 内積 */

カメラ位置および、焦点距離、アスペクト比を求めます。ここでVRAM_HEIGHT、VRAM_WIDTHはそれぞれディスプレイの高さと幅です。

/* カメラ位置の計算 */
camera_pos.x =
player -> position.x - ( cam_frame -> dir.x - cam_frame -> pos.x );
camera_pos.y =
player -> position.y - ( cam_frame -> dir.y - cam_frame -> pos.y );
camera_pos.z =
player -> position.z - ( cam_frame -> dir.z - cam_frame -> pos.z );


/* 焦点距離・アスペクト比の計算 */
focus =
( OEMC_Micro3D_Util3D_sin12( cam_frame -> angle ) << 12 )
/ OEMC_Micro3D_Util3D_cos12( cam_frame -> angle );
aspect = ( VRAM_HEIGHT << 12 ) / VRAM_WIDTH;

視錐台の各平面の法線ベクトルを計算します。近平面と遠平面については向きが逆になるだけなので、近平面の方のみ求めています( clip_dir )。

/* 視錐台平面法線ベクトルの作成 */
clip_dir.x = 0;
clip_dir.y = 0;
clip_dir.z = 1 << 12;
OEMC_Micro3D_Vec3i_normalize12( &clip_dir, &clip_dir );

left_dir.x = focus;
left_dir.y = 0;
left_dir.z = 1 << 12;
OEMC_Micro3D_Vec3i_normalize12( &left_dir, &left_dir );

right_dir.x = -focus;
right_dir.y = 0;
right_dir.z = 1 << 12;
OEMC_Micro3D_Vec3i_normalize12( &right_dir, &right_dir );

under_dir.x = 0;
under_dir.y = focus;
under_dir.z = aspect;
OEMC_Micro3D_Vec3i_normalize12( &under_dir, &under_dir );

up_dir.x = 0;
up_dir.y = -focus;
up_dir.z = aspect;
OEMC_Micro3D_Vec3i_normalize12( &up_dir, &up_dir );

視錐台各平面と球体オブジェクトの内包判定を行います。dotの値が –r 以下になるものが一つでもあればこのオブジェクトはカリングすることができます。

/* 視錐台平面ベクトルとの内包判定 */
/* 近平面 */
dot = OEMC_Micro3D_Vec3i_dot( &clip_dir, &sphere -> position );
dot = dot >> 12;
if( dot - cam_frame -> z_near - cam_frame -> pos.z < -sphere -> r )
return HI_TRUE;

/* 遠平面 */
if( cam_frame -> z_far - dot - cam_frame -> pos.z < -sphere -> r )
return HI_TRUE;

/* 左平面 */
dot = OEMC_Micro3D_Vec3i_dot( &left_dir, &sphere -> position );
dot = dot >> 12;
cam_dir = OEMC_Micro3D_Vec3i_dot( &left_dir, &camera_pos );
cam_dir = cam_dir >> 12;
if( dot - cam_dir < -sphere -> r )
return HI_TRUE;

/* 右平面 */
dot = OEMC_Micro3D_Vec3i_dot( &right_dir, &sphere -> position );
dot = dot >> 12;
cam_dir = OEMC_Micro3D_Vec3i_dot( &right_dir, &camera_pos );
cam_dir = cam_dir >> 12;
if( dot - cam_dir < -sphere -> r )
return HI_TRUE;

/* 下平面 */
dot = OEMC_Micro3D_Vec3i_dot( &under_dir, &sphere -> position );
dot = dot >> 12;
cam_dir = OEMC_Micro3D_Vec3i_dot( &under_dir, &camera_pos );
cam_dir = cam_dir >> 12;
if( dot - cam_dir < -sphere -> r )
return HI_TRUE;

/* 上平面 */
dot = OEMC_Micro3D_Vec3i_dot( &up_dir, &sphere -> position );
dot = dot >> 12;
cam_dir = OEMC_Micro3D_Vec3i_dot( &up_dir, &camera_pos );
cam_dir = cam_dir >> 12;
if( dot - cam_dir < -sphere -> r )
return HI_TRUE;

return HI_FALSE;
}