読者です 読者をやめる 読者になる 読者になる

のぐそんブログ

暗いおじさんがシコシコ書くブログです。

3Dで使う行列の基礎について

行列とは

wikipediaで調べたところ行列とは以下のことらしいです。

数学の線型代数学周辺分野における行列(ぎょうれつ、英: matrix)は、数や記号や式などを行と列に沿って矩形状に配列したものである。行の数と列の数が同じ行列は行列の和(英語版)が成分ごとの計算によって与えられる。行列の積の計算はもっと複雑で、2 つの行列がかけ合わせられるためには、積の左因子の列の数と右因子の行の数が一致していなければならない。 wikipediaより

まったく意味がわかりません。 絵で表すとこんな感じです。

行列とは何をしているのか

やっている事自体はそんなに難しくなさそうです。

それぞれの値をかけて足していくだけです。 このとき、行列と掛ける側の要素数が同じである必要があります。

行列を何に使うのか

行列にはベクトルを掛けることができます。

それで、一体何につかうんだよ、という話ですが、 誤解を恐れないで言えば、ベクトルを変換する便利関数として使うことができます。

3Dでは主に座標変換に使用します。

行列を使って座標を回転する

2次元の回転をやってみます。式は決まっていて以下になります。

θは角度意味する記号。

例)2次元の座標(3,2)を90度回転させるとします。

回転行列に当てはめます。

ここで1点注意が必要なことがあります。 javascriptのMath関数にsin、cosを求める際に実際の正しい値と比べると誤差があります。 正しい値を求める場合はビット演算を使用して処理します。

◎Math.sinの誤差

角度 sin Math.sin
0 0 0
90 1 1
180 0 1.2246467991473532e-16
270 -1 -1
360 0 -2.4492935982947064e-16

◎Math.cosの誤差

角度 cos Math.cos
0 1 1
90 0 6.123233995736766e-17
180 -1 -1
270 0 -1.8369701987210297e-16
360 1 1

ビット演算使用して誤差を補正。

var sin =  Math.sin(angle * (Math.PI / 180));
sin = sin * 100 | 0? sin: 0;

var  cos = Math.cos(angle * (Math.PI / 180));
cos = cos = cos * 100 | 0? cos: 0;

参考:来世はなまけものになる予定なので今世はがんばる

回転行列で計算

座標にいれてみると回転できたことがわかります。

行列の種類

単位行列

正方行列の対角成分が1で、どんな値をかけ合わせても、返される結果がかわらない行列のこと。

転置行列

行と列を入れ替えた行列。3Dの座標変換などで出ててくるみたいです。

逆行列

なんらかの行列を適応した値に対して、適応した行列を無効にしてしまう行列です。

Kobito.xrjv39.png

MVP マトリックス

3Dの座標変換で頻繁に登場する行列です。

M ・・・モデル座標変換行列 V ・・・ビュー座標変換行列 P ・・・プロジェクション座標変換行列

モデル座標変換行列(ワールド変換行列)

仮想的な3次元空間の中での、描画されるモデルの位置や、回転、縮小拡大に関する情報を持ちます。

ビュー座標変換行列

3次元空間の中でのカメラの位置、カメラの向き、カメラの上方向を定義します。

プロジェクション座標変換行列

三次元空間上にあったデータをスクリーンに投影する為の情報。 スクリーンの縦横比やカメラの視野角、撮影範囲の手前の距離などがあたります。

Three.jsでのcameraのカメラ設定

Three.jsのカメラのプロパティはビュー座標変換行列と、プロジェクション座標変換行列に別れます。

 var p = {
            fovy: 60,  // プロジェクション
            aspect: width / height, // プロジェクション
            near: 0.1, // プロジェクション
            far: 10.0, // プロジェクション
            x: 0.0, // ビュー
            y: 0.0, // ビュー
            z: 5.0,  // ビュー
  };

//初期化プロパティはプロジェクション
var camera = new THREE.PerspectiveCamera(
        p.fovy,//視野角
        p.aspect,//アスペクト比,
        p.near,//カメラから視体積の手前までの距離
        p.far,//カメラから視体積の奥までの距離
);

//カメラの位置はビュー
camera.position.set(p.x, p.y, p.z);

//カメラの注視点はビュー
camera.lookAt(new THREE.Vector3(0.0, 0.0, 0.0));

まとめ

行列は便利な関数やツールだと思うようにすると、怖くなくなる。

Three.jsの使いそうなマテリアル

Three.jsで使いそうなマテリアルです。

MeshBasicMaterial

面用のマテリアル。 陰がつかないので塗りつぶした状態になる。

new THREE.MeshBasicMaterial({color: 0x66B6FF});

MeshLambertMaterial

面用のマテリアル。 陰が表示される為奥行きがでる。 光沢は無し。

new THREE.MeshLambertMaterial({color: 0x66B6FF});

MeshPhongMaterial

面用のマテリアル。 陰が表示される為奥行きがでる。 光沢ありなので、emissive(影色)、shininess(光沢度合い)、specular(光沢部の色)を設定できる。

new THREE.MeshPhongMaterial({color: 0x66B6FF,specular: 0xffffff});

MeshFaceMaterial

面用のマテリアル。 各面に対してそれぞれマテリアルを設定できる。 MeshFaceMaterialsに各面に適応するマテリアルを指定します。

let materials = [
    new THREE.MeshBasicMaterial({ color: 0x1abc9c }),
    new THREE.MeshBasicMaterial({ color: 0x3498db }),
    new THREE.MeshBasicMaterial({ color: 0x9b59b6 }),
    new THREE.MeshBasicMaterial({ color: 0xf1c40f }),
    new THREE.MeshBasicMaterial({ color: 0xe74c3c }),
    new THREE.MeshBasicMaterial({ color: 0x34495e })
];

let material = new THREE.MeshFaceMaterial(materials);

MultiMaterial

ボックスの各面に別々のマテリアルを貼りたい場合に使う。

let textuer1 = new THREE.MeshPhongMaterial({color: 0xcccccc });
let textuer2 = new THREE.MeshPhongMaterial({color: 0xcccccc });
let textuer3 = new THREE.MeshPhongMaterial({color: 0xcccccc });
let textuer4 = new THREE.MeshPhongMaterial({color: 0xcccccc });
let textuer5 = new THREE.MeshPhongMaterial({color: 0xcccccc });
let textuer6 = new THREE.MeshPhongMaterial({color: 0xcccccc });
let material = new THREE.MultiMaterial([textuer1, textuer2, textuer3, textuer4, textuer5, textuer6])

f:id:nogson2:20170517003849p:plain

PointsMaterial

ポイント用のマテリアル。 形状を持たない為、視点が変化しても同じ大きさで表示される。

let geometry = new THREE.BoxGeometry(1, 1, 1);
let material = new THREE.PointsMaterial({ color: 0x66B6FF,size: 0.1});
let box= new THREE.Points(geometry, material);

THREE.LineBasicMaterial

ライン用のマテリアル。 ラインの引き方は設定できる。

let geometry = new THREE.BoxGeometry(1, 1, 1);
let material = new THREE.LineBasicMaterial({ color: 0x66B6FF});
let box= new THREE.Line(geometry, material);

まとめ

他にもマテリアルがあるので追加していこうと思います。

3Dで使う基本的な数学の知識その1(javascript編)

3Dを使う上で必須の数学の知識があります。 まずは基礎からやっていこうと思います。

ラジアン

通常円の角度は 度数法を使って表現されます。 360°は度数法です。

javascriptでオブジェクトを回転させたりする場合は度数法ではなく 弧度法(ラジアンを使う必要があります。

弧度法の場合は角度の基準となるものが円周になります。

円周の公式

算数ですが復習の為。

円周 = 2 * PI(π) * r(半径)

半径が1.0の円周は

半径1の円の円周 = 2 * PI * 1

つまり下になります。

 2 * PI

度数法では、円の1周は360°。 弧度法では1周が2PI(π)になります。

ラジアンを使って円の座標を計算してみる

javascriptでPIを求める場合は Math.PIを利用します。

cosの求め方
Math.cos(rad) * 半径

30°のcosは単純に書くと下のようになる。

Math.cos(2 * Math.PI * (30/360)) * radius
sinの求め方
Math.sin(rad) * 半径

30°のsinは単純に書くと下のようになる。

Math.sin(2 * Math.PI * (30/360)) * radius  

ベクトル

ベクトルとは向きと大きさを持った量のことを言います。 簡単に書くと下のようになります。「向きと大きさ(長さ)」をもつ量です。

単位ベクトル

ベクトルの大きさを無視した(1に揃えた)、向きだけをもったベクトル。 大きさを正規化することで1に揃えていきます。

式はこのようになります。これは2Dでも3Dでも同じです。

◎2D

V = [x,y];
Vの大きさ = Math.sqrt(x * x + y * y); //sqrtは平方根
Vの単位ベクトル = [x / 大きさ, y / 大きさ];

◎3D

V = [x,y,z];
Vの大きさ = Math.sqrt(x * x + y * y + z * z); //sqrtは平方根
Vの単位ベクトル = [x / 大きさ, y / 大きさ, z / 大きさ];

単位ベクトルは向きだけに注目したい場合に使用します。

単位ベクトルの例

See the Pen Njzbmx by nogson (@satofaction) on CodePen.

ベクトルの加算

ベクトルの加算はその名の通り、2つのベクトルを足し合わせるだけでよい。

Kobito.VG6lRN.png

参考図はunityドキュメントから引用

まとめ

ラジアンとベクトルは基礎なのでしっかり理解したいと思います。

AR.js導入メモ

AR.jsがリリースされたので、導入手順をメモしました。 わからないパラメータや処理があり、わかったら追記していこうとおもっています。

手順

AR.js導入手順。

  1. ArToolkitSourceを作成
  2. ArToolkitContextを作成
  3. ArMarkerControlsを作成
  4. ARで表示するオブジェクト作成
  5. arToolkitContextをアップデート

ArToolkitSourceを作成

ポジショントラッキング用に表示するオブジェクトを作成。 ‘webcam’, ‘image’, ‘video'のどれかを選ぶ。 ここで作成したオブジェクトをトラッキングして、3Dオブジェクトを表示させる。

sourceWidth, displayWidthなどは指定しなくてもデフォルト値が幅640、高さ480で設定される。

 var arToolkitSource = new THREEx.ArToolkitSource({
        //ウェブカメラの場合
       sourceType: 'webcam',
       
       //画像の場合
       // sourceType : 'image',
       // sourceUrl : '../../data/images/img.jpg',      

       // 動画の場合
       // sourceType : 'video',
       // sourceUrl : '../../data/videos/headtracking.mp4',
       
        //生成されるdomエレメントのwidth,height(width="XXX")
       sourceWidth: 100,
       sourceHeight: 100,
       
       //生成されるdomエレメントの表示サイズ(style.width="XXX")
       displayWidth:200,
       displayHeight:200
 });
 
 // 初期化することで、ArToolkitSourceオブジェクトが描画される。
 arToolkitSource.init(function onReady() {
        //初期化時に実行する処理
        //画面のリサイズ処理を行う
      arToolkitSource.onResize(renderer.domElement)
 })

sourceTypeを webcamを選択した場合、 mediaDevices APIに対応しているブラウザである必要があります。 現状はchromeFirefoxが対応しています。iOSchromeは対応してません。

mediaDevices API

mediaDevicesは接続された利用可能な映像/音声デバイスの情報が取得できます。

MediaDevices.getUserMedia

カメラや画面共有機能、マイクを起動して、入力と共にビデオトラックやオーディオトラックを含むMediaStreamを提供します。

MediaDevices.getUserMedia({ audio: true, video: true }).then(function(stream){
    //MediaStreamオブジェクトが返却される
});
MediaDevices.enumerateDevices

システム上で利用できる入出力メディアデバイスの情報を取得できます。

MediaDevices.enumerateDevices().then(function(devices){
    //利用できる入出力メディアデバイス情報が返却される
});

ArToolkitContextを作成

マーカーを読み込む為のカメラ設定。

var arToolkitContext = new THREEx.ArToolkitContext({
     cameraParametersUrl: 'images/camera_para.dat',
     detectionMode: 'mono',
     imageSmoothingEnabled: true,
     maxDetectionRate: 60,
     canvasWidth: arToolkitSource.parameters.sourceWidth,
     canvasHeight: arToolkitSource.parameters.sourceHeight,
    })

//初期化
 arToolkitContext.init(function onCompleted() {
    //射影行列をコピー
    camera.projectionMatrix.copy(arToolkitContext.getProjectionMatrix());
})
パラメーター 内容 補足
debug デバック用のキャンバスを表示 true / false
cameraParametersUrl カメラのパラメーターファイルのパス。設定するファイルにはカメラのレンズ設定のようなものが入っています。
detectionMode マーカー検出用モード ‘color’, ‘color_and_matrix’, ‘mono’, ‘mono_and_matrix'のいずれか。初期値はcolor_and_matrix
matrixCodeType detectionModeでmatrixタイプを選択した場合に設定 3x3, 3x3_HAMMING63, 3x3_PARITY65, 4x4, 4x4_BCH_13_9_3, 4x4_BCH_13_5_5のいずれか。初期値は3x3
imageSmoothingEnabled CanvasRenderingContext2D.imageSmoothingEnabledの設定 初期値はfalse
maxDetectionRate マーカー検出用のレート。大きくすると検出がスムーズになり、マーカー移動時になめらかに表示される 初期値は60
canvasWidth マーカー検出用の画像解像度。高いほうが検出されやすくなる。 初期値は640
canvasHeight マーカー検出用の画像解像度。高いほうが検出されやすくなる。 初期値は480

ArMarkerControlsを作成

マーカー用のコントーローラーを作成。

//パラメーター
var parameters = {
    size : 1,
    type : 'pattern',
    patternUrl : 'sample.pat',
    barcodeValue : null,
    changeMatrixMode : 'modelViewMatrix',
}

//object3Dを作成
//このオブジェクトにマーカー上に表示するオブジェクを格納する
var markerRoot = new THREE.Group
scene.add(markerRoot)

//インスタンスを作成
//第1引数 arToolkitContext
//第2引数 object3D
//第3引数 パラメーター
var artoolkitMarker = new THREEx.ArMarkerControls(arToolkitContext, markerRoot, parameters)
 
パラメーター 内容 補足
size マーカーのサイズ

1を設定

10を設定

type マーカーのタイプ ‘pattern’, ‘barcode’, ‘unknown’ のいずれかを選ぶ
patternUrl typeで'pattern'を選んだ場合に指定する、パターンファイルのパス
barcodeValue typeで'barcode'を選んだ場合に指定する、バーコードファイルのパス
changeMatrixMode ‘modelViewMatrix’, ‘cameraTransformMatrix'のいずれかを選択
minConfidence マークの認識度?? 0~1の値を設定。初期値は0.6

ARで表示するオブジェクト作成

ArMarkerControlsに連携したobject3Dに表示するオブジェクトを追加する。

var geometry = new THREE.CubeGeometry(1, 1, 1);
var material = new THREE.MeshNormalMaterial({
     transparent: true,
     opacity: 0.5,
     side: THREE.DoubleSide
});

var mesh = new THREE.Mesh(geometry, material);
mesh.position.z = geometry.parameters.height / 2
markerRoot.add(mesh);

arToolkitContextをアップデート

arToolkitContextをアップデートし続ける。

animate();

function animate(nowMsec) {
   
     requestAnimationFrame(animate);

     arToolkitContext.update(arToolkitSource.domElement)
     
}

その他

オリジナルのマーカーを作る場合は下記を使用すると楽。

https://github.com/jeromeetienne/AR.js/blob/master/data/marker-generator/MGO2.swf

まとめ

表示するだけなら、ライブラリのおかげでそれほど難しくありませんでした。

Three.jsのカメラについて

カメラの定義方法

カメラの基本的な定義方法は下記。 ※レンダーとシーンの作成は割愛

//カメラのパラメーター
var CAMERA_PARAMETER = { 
  fovy: 60,
  aspect: width / height,
  near: 0.1,
  far: 10.0,
  x: 3.0, 
  y: 2.0,
  z: 5.0, 
  lookAt: new THREE.Vector3(0.0, 0.0, 0.0)
 };
 
 //カメラのインスタンスを作成
 camera = new THREE.PerspectiveCamera(
            CAMERA_PARAMETER.fovy,
            CAMERA_PARAMETER.aspect,
            CAMERA_PARAMETER.near,
            CAMERA_PARAMETER.far
        );
 
 //カメラの位置を設定   
 camera.position.x = CAMERA_PARAMETER.x;
 camera.position.y = CAMERA_PARAMETER.y;
 camera.position.z = CAMERA_PARAMETER.z;
 
 //カメラの注視点を設定
 camera.lookAt(CAMERA_PARAMETER.lookAt); 
 
 //レンダーの第2引数で渡す
 renderer.render(scene, camera);

カメラの種類

three.jsにはカメラが2種類あります。

透視投影(PerspectiveCamera)

透視投影とは、近くのものは大きく、遠くのものほど小さく見える通常目で見る状態に近い表現。

new THREE.PerspectiveCamera(fov, aspect, near, far);
透視投影のパラメータ
fov aspect near far
視野角(0~90) アスペクト比 撮影範囲の手前の距離。nearよりも近くにあるものはレンダリングされない 撮影範囲の最大距離。farより遠くのものはレンダリングされない

平行投影(OrthographicCamera)

オブジェクトの大きさが位置によって変化しない状態。どこからみても同じ大きさになる。

 new THREE.OrthographicCamera(left, right, top, bottom, near , far ); 
平行投影のパラメータ
left right top bottom near far
カメラ中心座標からの左側の長さ カメラ中心座標からの右側の長さ カメラ中心座標からの上側の長さ カメラ中心座標からの下側の長さ 撮影範囲の手前の距離。nearよりも近くにあるものはレンダリングされない 撮影範囲の最大距離。farより遠くのものはレンダリングされない

カメラの中心座標を決める

中心座標というよりも、空間の中での上方向をきめます。

設定値は1、-1、0のいずれかを設定します。

 camera.up.set(x, y, z); 

camera.up.set(1, 0, 0)を設定した場合。

camera.up.set(0, 1, 0)を設定した場合。

camera.up.set(0, 0, 1)を設定した場合。

イマイチ使い所がわかりませんでした。

SELECT文だけの修飾メモ

SELECT文だけの修飾

DISTINCT

検索結果の中で重複した行を取り除いてくれます。 用途としては、データの種類を取得する場合などに使用します。

SELECT DISTINCT 列名
FROM テーブル名

下記のテーブルがあるとした場合、 DISTINCT を使用して、nameの種類を取得できます。

◎userテーブル
name
sato
kato
suzuki
sato
kato
sato

下記で実行。

SELECT DISTINCT name
FROM user
◎結果
name
sato
kato
suzuki

ORDER BY

検索結果の並び替えに使用します。 並び替える列は、列名、もしくは列番号で指定します。

SELECT 列名 
FROM テーブル名
ORDER BY 列名(列番号) 並び順(ASC もしくは DESC。デフォルトはASC)

ASC ・・・ 昇順 DESC ・・・降順

◎userテーブル
age
20
25
19
30
45
50

下記で実行。

SELECT age
FROM user
ORDER BY age DESC
◎結果
age
50
45
30
25
20
19

複数行で並べ替える

ORDER BYをカンマで区切ることで、複数の条件で並び替えができることができます。

SELECT 列名 
FROM テーブル名
ORDER BY 列名1(列番号) 並び順 , 列名2(列番号) 並び順

上記の場合は列名1で並び替えたあと、さらに列名2で並び替えます。

◎userテーブル
age height
20 170
20 160
19 170
30 160
20 180
50 170

下記で実行。

SELECT * 
FROM user
ORDER BY age DESC, height DESC
◎結果
age height
50 170
30 160
20 180
20 170
20 160
19 170

SELECT

SELECT文はテーブルの先頭から、取得数をしていしてデータを取り出す。 取得開始の位置も設定できる。

SELECT 列名
FROM テーブル名
LIMIT 取得行数 OFFSET 先頭から除外する行数
◎userテーブル
age
20
25
19
30
45
50

下記で実行。

SELECT age
FROM user
LIMIT 2 OFFSET 2
◎結果
age
30
25

集合演算子

同じ構造の複数のテーブルのデータを扱う場合に集合演算子を使う。 集合演算子を使う場合は、それぞれのテーブルの列数と、データの型を一致させておく必要があります。

もし、列数が合わない場合などはNULLなどを追加して揃えて数を一致させる。

UNION(和集合)

UNIONはテーブルと、テーブルの検索結果の足し合わせに使います。 ALLキーワードを付けると重複した行もそのまま表示されます。 ALLキーワードがついていない場合は、重複した行は1行で表示されます。

SELECT 列名
FROM テーブルA
UNION (ALL)
SELECT 列名
FROM テーブルB
◎userAテーブル
age
20
25
19
30
45
50
◎userBテーブル
age
50
65
70

下記で実行

SELECT age
FROM userA
UNION
SELECT age
FROM userB
◎結果

ALLキーワードを使ってないので重複している「50」は1行にまとめて表示される。

age
20
25
19
30
45
50
65
70
EXCEPT / MINUS(差集合)

EXCEPTはそれぞれのテーブルの検索結果の差分を求められます。 Oracle DBの場合はMINUSを使います。

SELECT 列名
FROM テーブルA
EXCEPT (ALL)
SELECT 列名
FROM テーブルB
注意

EXCEPTで注意が必要なのは、SELECTの順番です。

先に指定したのが検索結果から、後に指定した検索結果省くので、 先に指定したのがテーブルのデータが基準となります。

A - B と B - Aは結果が異なります。

INTERSECT(積集合)

INTERSECTは2つの検索結果の共通する行を集めます。

SELECT 列名
FROM テーブルA
INTERSECT (ALL)
SELECT 列名
FROM テーブルB

まとめ

SELECT文はSQLでよく使われる命令のようなので、 やれることも多いです。 目的のデータを正しい形で取得できるように練習が必要そうです。

three.jsのライトについてのメモ

three.jsのライトについて

three.jsでは他のオブジェクトとと同じようにライトも追加することができます。

【ライト追加前】

【ライト追加後】

上記のように、ライトは明るさもですが、影の描画に大きく影響します。

ライトの追加方法

ライトのオブジェクトは下記のように追加。

 //シーンを準備
 var scene = new THREE.Scene();

//ライトを作成
 var light = new THREE.DirectionalLight(0xffffff);
 
 //シーンに追加
 scene.add(light);

ライトの種類

ディレクショナルライト(平行光源)

平行光源とは、無限に遠いところからシーン全体に降り注ぐ光の光源のことです。

コードにすると下記になります。

//hex:光の色(16進数)
//intensity:光の強さ(float)

THREE.DirectionalLight ( hex, intensity )

アンビエントライト(環境光)

どの面に対しても一様に照らすライト。 ディレクショナルライトは光が当たっていない箇所が発生するが、アンビエントライトは全体を明るくする場合に使える。全体を明るくしますのでビエントライトによる影はできません。

コードにすると下記になります。

//hex:光の色(16進数)
//intensity:光の強さ(float)
THREE.AmbientLight ( hex, intensity )

ポイントライト(点光源)

全方位に光を発することができる光源。電球などのイメージに近い。 ポイントライトによる影はできません。

コードにすると下記になります。

//hex:光の色(16進数) 
//intensity:光の強さ(float)
//distance : 光源からの距離による減衰係数(float)
THREE.PointLight ( hex, intensity, distance )

スポットライト

一点に向けて光を当てる光源。その名の通りスポットライトのような感じ。 スポットライトによる影ができる。

//hex:光の色(16進数) ,
//intensity:光の強さ(float) , 
//distance : 光源からの距離による減衰係数(float), 
//angle: 照らす範囲角の半分(単位はラジアンでMath.PI/2を越えないように), 
//exponent:指数関数的な減衰係数(多くすると中心から外側に向かってすぐ暗くなる)
THREE.SpotLight ( hex, intensity, distance, angle, exponent )
   

ライトとマテリアルの関係

ライトを使用する場合はシェイプ側のマテリアルの設定にも注意が必要です。 マテリアルの中にはライトを受ける(反射できる)ことができるタイプとできないタイプがあります。

まとめ

上手くライトの描画の違いを表現できませんでした。 マテリアルとの相性とかもあるのかもしれません。

参考

Three.jsの基本⑤