p5.jsのWebGLでカメラを回してみる

## はじめに

p5.jsで3Dの描画をしようという話です.

## 導入方法

```createCanvas(400, 400);``` の部分を ```createCanvas(400, 400, WEBGL);``` と書くだけです.

WebGLに関して深く知ろうとしても難しいだけなので, 一旦はこう書けば良いのだと覚えるだけで良い気がします.

## 何か描いてみる

```box(100);``` と ```draw```の中に書けば, 立方体を書くことができます.

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2236620/d8da0118-dd5f-307e-1d0f-dc0f35610cf0.png)

こんな感じになると思います.

ただ, これでは3D感がわからないので, カメラを動かしてみたいと思います.

```draw``` の中に ```orbitControl();``` を描いてみてください.

``` JavaScript
function setup() {
  createCanvas(400, 400, WEBGL);
}

function draw() {
  background(220);
  
  orbitControl();
  
  box(100);
}
```

これでキャンバスの中でマウスをクリックしながらグリグリすることでカメラを動かせるようになります.

割と3Dな感じになってきたのではないかと思います.

また, 塗りとか枠線などについては2Dの時と同様にして, ```fill(200, 100, 50);``` や　```stroke(30, 50, 190);``` のようにして指定できます.

## 座標に少し注意

2Dと大きく違うのが座標系で, 中心が```(0, 0, 0)``` になるように座標があります.

そして, ```box```をはじめとして3D上で書く基本図形には, 引数に座標を指定するものがありません.

```box(100);``` の時に書いた```100```は1辺の長さで, 座標をしめすものではありません.

そこで, 座標を移動させる時に使うのが, ```translate();```という関数です.

ひとまず, こんな感じで使います.

``` JavaScript
function setup() {
  createCanvas(400, 400, WEBGL);
}

function draw() {
  background(220);
  
  orbitControl();
  
  fill(200, 100, 50);
  
  translate(100, 0, 0);
  box(100);
}
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2236620/4f89f8e8-50a0-c90b-ca88-cd4074596ab2.png)

これで立方体が右に```100```移動しました.

右というのはx軸方向ということで, ```translate();``` の引数は1つめから順番にx軸, y軸, z軸の方向に座標系を動かす量を示しています.

例えば, 

``` JavaScript
function setup() {
  createCanvas(400, 400, WEBGL);
}

function draw() {
  background(220);
  
  orbitControl();
  
  fill(200, 100, 50);
  
  translate(50, -100, 0);
  box(100);
}
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2236620/3c8eab1a-796f-9e0b-9848-c9ffdf10d13e.png)

## 2つ以上オブジェクトを置く時に注意

例えば, 右と左に立方体を置こうとしたら, こんな感じに書くと思います.

``` JavaScript
function setup() {
  createCanvas(400, 400, WEBGL);
}

function draw() {
  background(220);
  
  fill(200, 100, 50);
  
  translate(100, 0, 0);
  box(100);
  
  translate(-100, 0, 0);
  box(100);
}
```

しかし, これの実行結果はこんな感じになります.

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2236620/7ca95a79-e341-7696-3b71-71544219dc3b.png)

これは, 1つめの```translate()```の座標の移動がそれ以下のものにも影響しているからです.

この世界丸ごと右に動かしてから, 左に同じだけ動かしているので, 元のところに戻ってきて2つめの立方体は中心に来たみたいな感じです.

そこで, ここでは```push()```と```pop()```を使います.

使い方はシンプルで, その世界が始まってほしい場所に```push()```を描いて, 終わってほしい場所に```pop()```を書きます.

``` JavaScript
function setup() {
  createCanvas(400, 400, WEBGL);
}

function draw() {
  background(220);
  
  fill(200, 100, 50);
  
  push(); // 右に100移動する世界のはじまり
  translate(100, 0, 0);
  box(100);
  pop(); // もとの原点が真ん中にある世界に戻る
  
  push(); // 左に100移動する世界のはじまり
  translate(-100, 0, 0);
  box(100);
  pop(); // もとの原点が真ん中にある世界に戻る
}
```

つまり, こんな感じです.

対応するように挟んであげれば良いってわけです.

## カメラを使ってみる

回転などを描いてもいいのですが, 立方体だけで楽しいものの中にカメラワークがあるので紹介します.

こんな感じで書くとカメラを指定できます.

``` JavaScript
function setup() {
  createCanvas(400, 400, WEBGL);
}

function draw() {
  background(220);
  
  camera(0, 500, 500, 0, 0, 0, 0, 1, 0); // カメラの追加
  
  fill(200, 100, 50);
  
  box(100);
}
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2236620/96862b04-b5f6-71b9-a001-fd6910f20e56.png)

少し手前で上から見たような絵になりました.

```camera```の引数が9つあるのですが, これらは3つごとのグループになっています.

はじめから3つ, つまりここでいうところの ```0, 500, 500``` はカメラの位置です.
カメラの座標を, x, y, zの順番に指定してます.

真ん中の3つ, ここでいうところの ```0, 0, 0``` はカメラの見ている座標です.
カメラの視線の先の座標を, x, y, zの順番に指定してます.

最後の3つ, ここでいうところの ```0, 1, 0``` はカメラの持ち方です.
カメラを持っていいる人の立っている向きを表すベクトルのを, x, y, zの順番に指定してます.
これは正直難しいので, 一旦 ```0, 1, 0``` で良いと思います.

## Tips. 媒介変数表示とカメラ

こんな感じで書いてあげます.

``` JavaScript
function setup() {
  createCanvas(400, 400, WEBGL);
}

function draw() {
  background(220);
  
  let t = frameCount/100; // 時間
  camera(500, 0, 500, 0, 0, 0, 0, 1, 0);
  
  fill(200, 100, 50);
  
  box(100);
}
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2236620/a005e087-db25-e28b-4d4c-712e3168e2c5.png)

すると, 斜めから見た感じになると思います.

```let t = frameCount/100;``` は時間を表してます.
まだ使っていません.

で, ここから高校で習った三角関数を使って, カメラを回していきます.

手始めに, 立方体の周りをぐるぐると回転させてみましょう.

``` JavaScript
function setup() {
  createCanvas(400, 400, WEBGL);
}

function draw() {
  background(220);
  
  let t = frameCount/100;
  camera(500*cos(t), 0, 500*sin(t), 0, 0, 0, 0, 1, 0); // 三角関数をかけた
  
  fill(200, 100, 50);
  
  box(100);
}
```

時間で回転していくので, ここでは省略しますが, 回転し始めたと思います.

```sin```と```cos```をこんな感じで使ってあげると動くってわけです.

そして, この```sin```と```cos```で円がかけるよみたいな式の表し方を媒介変数表示と言ったりします.

[参考になりそうな動画](https://www.try-it.jp/chapters-7149/sections-7215/lessons-7216/) 

まあ, 理解は特に必要なくて, 高校でやった気がするなあ, ふーーんくらいで思っておいて, ここではそれを使ってカメラを動かしてみます.

めっちゃ良いサイトがありまして, [媒介変数表示された有名な曲線７つ](https://manabitimes.jp/math/898)というのがこちらにまとめられています.

ここからうまく使ってあげましょう.

1つアステロイドは円の式のそれぞれの値を3乗してあげればキラキラの時に使うイラストの形みたいに動きそうなので, 使ってみます.

``` JavaScript
function setup() {
  createCanvas(400, 400, WEBGL);
}

function draw() {
  background(220);
  
  let t = frameCount/100;
  camera(500*cos(t)**3, 0, 500*sin(t)**3, 0, 0, 0, 0, 1, 0);
  
  fill(200, 100, 50);
  
  box(100);
}
```

上から俯瞰してみた時にアステロイドの形になるようにカメラが動いたのではないでしょうか.

割とこれが楽しいので, ぜひ他の曲線でも使ってみてください !! という紹介でした.

``` JavaScript
function setup() {
  createCanvas(400, 400, WEBGL);
}

function draw() {
  background(220);
  
  let t = frameCount/100;
  // 円
  camera(500*sin(t), 0, 500*sin(t), 0, 0, 0, 0, 1, 0);
  
  // アステロイド
  // camera(500*sin(t)**3, 0, 500*sin(t)*3, 0, 0, 0, 0, 1, 0);
  
  // リサージュ
  // camera(500*sin(t*3), 0, 500*sin(t*4+1), 0, 0, 0, 0, 1, 0);
  
  // 対数螺旋
  // camera(500*2.71**t*cos(t), 0, 500*2.71**t*sin(t), 0, 0, 0, 0, 1, 0);
  
  // インボリュート
  // camera(500*(cos(t)+t*sin(t)), 0, 500*(sin(t)-t*cos(t)), 0, 0, 0, 0, 1, 0);
  
  fill(200, 100, 50);
  
  box(100);
}
```

ぜひぜひ, いろんな図形もおいてみて楽しんでください !!