3Dプログラミング2

      3Dプログラミング2 はコメントを受け付けていません

前節では基本的なオブジェクトの作成、座標系の移動、回転、拡大縮小を学びました。この節では、Processingの3D空間でライトを使い、立体の見え方や質感を変える方法を学びます。特に、specular()lightSpecular()の違いは混乱しやすいので、物体側とライト側の設定として分けて理解します。

1. 基本の3D表示

Processingで3Dを使うには、size()の3つ目にP3Dを書きます。まずは、回転する箱を表示してみましょう。

リスト1
void setup() {
  size(400, 400, P3D);
  noStroke();
}

void draw() {
  background(0);

  translate(width/2, height/2, 0);
  rotateX(frameCount * 0.01);
  rotateY(frameCount * 0.01);

  box(150);
}

frameCountは、画面が描き直されるたびに増える数です。frameCount * 0.01を回転角度に使うと、少しずつ回転するアニメーションになります。

5.2.2 ライト

3Dでは、ライトを使うと立体に陰影がつきます。陰影がつくことで、面の向きや奥行きがわかりやすくなります。まずは一番簡単なlights()を使います。

リスト2
void setup() {
  size(400, 400, P3D);
  noStroke();
}

void draw() {
  background(0);
  lights();

  translate(width/2, height/2, 0);
  rotateX(frameCount * 0.01);
  rotateY(frameCount * 0.01);

  fill(180);
  box(150);
}

lights()は、Processingが用意している基本的なライトをまとめて使う命令です。細かい設定をしなくても、立体に明るい面と暗い面ができます。

ポイント:3Dで立体感を出したいときは、まずlights()を入れてみましょう。

5.2.3 環境光

環境光(ambient light)は、方向を持たない光です。全体を均一に照らすため、強い陰影は出ません。画面全体の暗さを少し明るくしたいときに使います。

構文

ambientLight(赤, 緑, 青);
リスト3
void setup() {
  size(400, 400, P3D);
  noStroke();
}

void draw() {
  background(0);
  ambientLight(80, 80, 80);

  translate(width/2, height/2, 0);
  rotateX(frameCount * 0.01);
  rotateY(frameCount * 0.01);

  fill(200);
  box(150);
}

ambientLight()は、影をくっきり作るライトではありません。「全体の明るさを底上げする光」と考えるとわかりやすいです。

5.2.4 その他のタイプのライト

Processingには、環境光以外にもいくつかのライトがあります。ここでは、方向光と点光源を扱います。

●方向光(directionalLight)

方向光は、一定方向から来る光です。太陽のように、遠くから同じ方向に向かって照らす光だと考えるとわかりやすいです。

構文

directionalLight(赤, 緑, 青, x方向, y方向, z方向);
リスト4
void setup() {
  size(400, 400, P3D);
  noStroke();
}

void draw() {
  background(0);

  directionalLight(255, 255, 255, -1, 1, -1);

  translate(width/2, height/2, 0);
  rotateX(frameCount * 0.01);
  rotateY(frameCount * 0.01);

  fill(180);
  box(150);
}

directionalLight()の最後の3つの値は、光の方向を表します。最初は細かく考えすぎず、値を変えると明るくなる面が変わる、ということを確認しましょう。

●点光源(pointLight)

点光源は、電球のように一点から広がる光です。光源の位置を変えると、明るくなる場所も変わります。

構文

pointLight(赤, 緑, 青, x座標, y座標, z座標);
リスト5
void setup() {
  size(400, 400, P3D);
  noStroke();
}

void draw() {
  background(0);

  pointLight(255, 255, 255, mouseX, mouseY, 200);

  translate(width/2, height/2, 0);
  fill(120, 170, 255);
  sphere(100);
}

このサンプルでは、マウスの位置に合わせて光源が動きます。球の明るい部分がマウスに追従することを確認してください。

5.2.5 ハイライトとオブジェクトの色

ここでは、lightSpecular()specular()を使って、光沢のある表面を作ります。この2つは名前が似ているため混同しやすいですが、担当しているものが違います。

命令担当意味
lightSpecular()ライト側ライトが持っているハイライト用の光の色を決める
specular()物体側物体がどの色・強さで光を反射するかを決める

一言で言うと、lightSpecular()は「光る側」の設定、specular()は「光を受けて反射する側」の設定です。

重要:光沢は、ライト側と物体側の両方が関係して見えます。ライトにハイライト成分があり、物体がそれを反射する設定になっているとき、表面にピカッとした光沢が出ます。

構文

lightSpecular(赤, 緑, 青);  // ライト側のハイライト色
specular(赤, 緑, 青);       // 物体側の反射色
リスト6
void setup() {
  size(400, 400, P3D);
  noStroke();
}

void draw() {
  background(0);

  ambientLight(30, 30, 30);
  lightSpecular(255, 255, 255);
  directionalLight(255, 255, 255, -1, 1, -1);

  translate(width/2, height/2, 0);

  fill(80, 140, 255);
  specular(255, 255, 255);
  shininess(30);

  sphere(100);
}

このサンプルでは、ライト側のハイライト色を白にし、物体側も白く反射するように設定しています。そのため、球の表面に白い光沢が現れます。

● specular()の値を変える

次のサンプルでは、左の球は光沢が弱く、右の球は光沢が強くなります。ライト側の設定は同じで、物体側のspecular()だけを変えています。

リスト7
void setup() {
  size(400, 400, P3D);
  noStroke();
}

void draw() {
  background(0);

  ambientLight(30, 30, 30);
  lightSpecular(255, 255, 255);
  directionalLight(255, 255, 255, -1, 1, -1);

  translate(130, height/2, 0);
  fill(80, 140, 255);
  specular(30, 30, 30);
  shininess(30);
  sphere(60);

  translate(140, 0, 0);
  fill(80, 140, 255);
  specular(255, 255, 255);
  shininess(30);
  sphere(60);
}

specular()の数値が大きい右の球の方が、ハイライトがはっきり見えます。これは、物体がライトのハイライト成分を強く反射しているからです。

● lightSpecular()の色を変える

次のサンプルでは、ライト側のハイライト色を赤っぽくしています。

リスト8
void setup() {
  size(400, 400, P3D);
  noStroke();
}

void draw() {
  background(0);

  ambientLight(30, 30, 30);
  lightSpecular(255, 0, 0);
  directionalLight(255, 255, 255, -1, 1, -1);

  translate(width/2, height/2, 0);
  fill(80, 140, 255);
  specular(255, 255, 255);
  shininess(30);

  sphere(100);
}

lightSpecular(255, 200, 80)にすると、ハイライトが少し黄色っぽくなります。つまり、lightSpecular()は「反射して見えるライトの色」に影響します。

5.2.6 光沢

shininess()は、光沢の鋭さを決める命令です。値が小さいとハイライトは広がり、値が大きいとハイライトは小さく鋭くなります。

リスト9
void setup() {
  size(400, 400, P3D);
  noStroke();
}

void draw() {
  background(0);

  ambientLight(30, 30, 30);
  lightSpecular(255, 255, 255);
  directionalLight(255, 255, 255, -1, 1, -1);

  fill(80, 140, 255);
  specular(255, 255, 255);

  translate(130, height/2, 0);
  shininess(3);
  sphere(60);

  translate(140, 0, 0);
  shininess(80);
  sphere(60);
}
見え方
shininess()の値が小さいハイライトが広く、やわらかい
shininess()の値が大きいハイライトが小さく、鋭い

5.2.7 カメラ

camera()を使うと、3D空間をどこから見るかを設定できます。視点、見る中心、上下方向を指定します。

構文

camera(視点X, 視点Y, 視点Z,
       中心X, 中心Y, 中心Z,
       上方向X, 上方向Y, 上方向Z);
リスト10
void setup() {
  size(400, 400, P3D);
  noFill();
  stroke(0);
}

void draw() {
  background(255);

  camera(200, -150, 300,
         0, 0, 0,
         0, 1, 0);

  rotateY(frameCount * 0.01);
  box(150);
}

最初は、camera()の前半3つが「カメラの位置」、真ん中3つが「どこを見るか」、最後の3つが「どちらを上にするか」と覚えておけば十分です。

5.2.8 ortho()

ortho()を使うと、遠近感がつかない表示になります。遠くのものも近くのものも、大きさが変わらずに表示されます。図面やアイソメトリック風の表現に向いています。

リスト11
void setup() {
  size(400, 400, P3D);
  ortho();
}

void draw() {
  background(255);
  lights();

  translate(width/2, height/2, 0);
  rotateX(-0.5);
  rotateY(0.6);

  fill(180);
  box(150);
}

5.2.9 perspective()

perspective()を使うと、遠くのものが小さく見える通常の3Dらしい表示になります。Processingでは初期状態でも遠近感のある表示になっていますが、perspective()を使うと視野角などを調整できます。

構文

perspective(視野角, 画面の縦横比, 近い面までの距離, 遠い面までの距離);
リスト12
void setup() {
  size(400, 400, P3D);
  noStroke();
}

void draw() {
  background(255);
  lights();

  // マウスの位置で視野角を変える
  float fov = map(mouseX, 0, width, PI/8, PI/2);

  // perspective(視野角, 縦横比, 近くの限界, 遠くの限界)
  perspective(fov, float(width)/float(height), 10, 1000);

  translate(width/2, height/2, 0);
  rotateX(-0.4);
  rotateY(frameCount * 0.01);

  fill(180);

  // 手前の箱
  pushMatrix();
  translate(-100, 0, 100);
  box(80);
  popMatrix();

  // 中央の箱
  pushMatrix();
  translate(0, 0, 0);
  box(80);
  popMatrix();

  // 奥の箱
  pushMatrix();
  translate(100, 0, -200);
  box(80);
  popMatrix();
}

授業では、まずortho()と通常表示の違いを確認できれば十分です。frustum()は細かく設定できる反面、初学者には理解が難しいため、発展項目として扱うとよいでしょう。

まとめ

命令役割
lights()基本ライトをまとめて入れる
ambientLight()全体を均一に照らす
directionalLight()一定方向から照らす
pointLight()一点から広がる光を作る
lightSpecular()ライト側のハイライト色を決める
specular()物体側の反射・光沢の色を決める
shininess()光沢の鋭さを決める
camera()3D空間を見る位置を決める
ortho()遠近感のない表示にする
perspective()遠近感のある表示を調整する

今日の最重要ポイント:lightSpecular()はライト側、specular()は物体側です。光沢は、ライトがハイライト成分を持ち、物体がそれを反射することで見えるようになります。