3Dコンテンツの制作において「三角関数は必須」とよく聞きます。Webサイト制作で三角関数を使う場面はほとんどなかったため筆者は意外に思っていましたが、WebGLの勉強をすすめるうちに3Dでは三角関数を使う場面がとても多いことに気づきました。そこで、本記事では3Dコンテンツ制作で使用頻度が多いであろう基本的な数式と概念をまとめました。

今回解説する内容は地味ですが、ゲームやデータビジュアライゼーションを作る上でこの数式が基本となってきます。高校数学で学んだことをベースに、3つのサンプルを通して学習できるようまとめましたので、ぜひ最後までお付き合いください。WebGLの人気ライブラリの一つ「Three.js」を使って解説しています。

三角関数を使ったデモの紹介

まずは、本題に入る前に三角関数を使ったデモを作成したので紹介します。次のリンクをクリックしてご覧ください。

地球をモチーフにしたサンプルです。地球の赤道上には衛星が回り、日本と世界の主要都市は線で結ばれています。本記事の内容を応用することでこのような表現ができるようになります。ソースコードはGitHubに公開してますので、あわせて参照ください。

※この記事のソースコードは 2017年8月時点で最新のThree.js(r86)とTypeScript(ver2.4)、Webpack 3によって書かれています。環境構築の手順は記事「TypeScript+Webpackの環境構築まとめ(Three.jsのサンプル付き)」を参考ください。
※デモで使用しているテクスチャ画像は「Natural Earth III」から引用しています。

1. 円周上を移動する座標の計算方法

円運動とは1点を中心に円周上を動くモーションのことです。地球の人工衛星や土星の輪っかを想像するとわかりやすいと思いますが、球体の円周上を動く計算式を紹介します。次のサンプルでは、球体のまわりを赤い点が移動します。

下図は円周上にある点のX座標、Y座標を求めるものです。三角関数を使えば角度と半径をもとに公式から座標を求めることができます。水平の位置を求めるにはコサインを、垂直の位置を求めるにはサインを使います。「サイン・コサイン・タンジェント」と高校のときに暗記しましたが、こんなところで役に立つのですね。

1601_trigonometric_function_sample1_diagram

この式を参考に半径と角度を渡して位置情報を返却するコードをJavaScriptで表現してみます。Three.jsでは角度ではなくラジアンを使うので、角度degreeに対してMath.PI / 180を掛け算することでラジアンに変換します。サインとコサインはともにMath.sin()Math.cos()という命令を使います。

// 角度をラジアンに変換します
const rad = degree * Math.PI / 180;

// X座標 = 半径 x Cosθ
const x = radius * Math.cos(rad);
// Y座標 = 半径 x Sinθ
const y = radius * Math.sin(rad);

フレーム毎に角度degreeを少しずつ増やすことで、円を描く位置情報を取得することができます。

2. 緯度/経度/高度から地球上に点を打つ

世界地図などで地点を示すには緯度と経度がよく利用されますが、緯度と経度を3次元の座標(X・Y・Z)に変換することで都市の場所をプロットできます。次のサンプルでは、地球上に日本や北京、ロンドンの都市の場所に赤や緑の丸を配置しています。

指定した緯度、経度、高度で地球上に点を打てるようにしてみます。前章の円運動では二次元上での位置計算ですが、今回は三次元上での計算になります。下図の式は球上にある点のX座標、Y座標、Z座標を求めるものです。

1601_trigonometric_function_sample2_diagram

上の式を参考に緯度、経度、高度を渡して位置情報を返却するJavaScriptの関数を組んでみましょう。

/**
 * 緯度経度から位置を算出します。
 * @param {number} latitude 緯度です。
 * @param {number} longitude 経度です。
 * @param {number} radius 半径です。
 * @returns {Vector3} 3Dの座標です。
 * @see https://ics.media/entry/10657
 */
function translateGeoCoords(latitude, longitude, radius) {
  // 仰角
  const phi = (latitude) * Math.PI / 180;
  // 方位角
  const theta = (longitude - 180) * Math.PI / 180;

  const x = -(radius) * Math.cos(phi) * Math.cos(theta);
  const y = (radius) * Math.sin(phi);
  const z = (radius) * Math.cos(phi) * Math.sin(theta);

  return new THREE.Vector3(x, y, z);
}

translateGeoCoords()関数を使って緯度経度から地球上の位置を導き出す事ができるようになりました。

次のページでは球体状の2点を線でつなぐ方法として、クォータニオンを解説します。