5.2 3Dプログラミング2

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

前節では基本的なオブジェクトの作成、座標系の移動、回転、拡大縮小を学びました。この節では、ライティングの演出やカメラの設定を体験します。

5.2.1 ライト

ここでは、processingの3D空間上でライトを使ってみます。ライトといっても、環境光、方向光、スポットライト、ハイライトなど様々な要素がありますが、まずは、一番基本的なライトです。

2016_09_28_19_59
図5.2-a
リスト5.2-a
float theta = 0.0;    //角度

//1秒で1回転するように30で割る。度数法だと12°
//更に6で割ると1周期6秒
float rad = (TWO_PI/30.0)/6;

void setup() {
  size(400, 400, P3D);
  frameRate(30);  //1秒30フレーム
  noStroke();
}

void draw() {
  background(0);
  lights();    //基本ライトを設置

  //立体の中心を画面中央に移動
  translate(width/2, height/2); 
  rotateX(theta);  //X軸に対してtheta分だけ回転
  rotateY(theta);  //Y軸に対してtheta分だけ回転
  box(150, 150, 150);  //150x150x150pxの立方体を描画

  theta += rad;    //時間を進める
  if (theta > TWO_PI) theta = 0.0;    //1周期分終わったら原点に戻る
}

これで、陰影が付いたことが確認できました。

5.2.2 環境光

次に環境光(ambient light)を再現してみます。環境光は方向性を持たない光です。全体をまんべんなく照らしているので、オブジェクトに陰影は付きません。構文は次の形になります。

//ambientLight(赤, 緑, 青)
ambientLight(red, green, blue);
ambient_light
図5.2-b
2016_09_28_20_08
図5.2-c
リスト5.2-b
float theta = 0.0;    //角度
 
//1秒で1回転するように30で割る。度数法だと12°
//更に6で割ると1周期6秒
float rad = (TWO_PI/30.0)/6;
 
void setup() {
  size(400, 400, P3D);
  frameRate(30);  //1秒30フレーム
  noStroke();
}
 
void draw() {
  background(0);
  ambientLight(127, 0, 0);  //赤の環境光
 
  //立体の中心を画面中央に移動
  translate(width/2, height/2); 
  rotateX(theta);  //X軸に対してtheta分だけ回転
  rotateY(theta);  //Y軸に対してtheta分だけ回転
  box(150, 150, 150);  //150x150x150pxの立方体を描画
  
  theta += rad;    //時間を進める
  if (theta > TWO_PI) theta = 0.0;    //1周期分終わったら原点に戻る
}

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

Processingが提供しているライトのタイプは方向光、点光源やスポットライトなどそれぞれ特色があり、それらを組み合わせて使うこともできます。

●方向光(directionalLight)

方向光(directional light)は一定の方向を持ったライトで、物体をまんべんなく照らします。nx, ny, nzでは、光が向かっている方向を-1.0 ~ 1.0の間で指定します。

//directionalLight(赤, 緑, 青, x方向, y方向, z方向)
directionalLight(red, green, blue, nx, ny, nz);
directional_light
図5.2-d
リスト5.2-c
void setup() {
  size(400, 400, P3D);
  noStroke();
}

void draw() {
  background(0);

  //立体の中心を画面中央にする
  translate(width / 2, height / 2);

  //directionalLight(赤, 緑, 青, x方向, y方向, z方向);
  //x, y, z方向は光源が照らす方向。-1.0 ~ 1.0
  directionalLight(0, 255, 0, 1.0, 1.0, 0.0);
  
  sphere(75);  //球を描画
}

●点光源(pointLight)

pointLightは電球のように一箇所から拡散する光です。

//pointLight(赤, 緑, 青, x座標, y座標, z座標);
pointLight(red, green, blue, x, y, z);
point_light
図5.2-e
リスト5.2-d
void setup() {
  size(400, 400, P3D);
  noStroke();
}

void draw() {
  background(0);

  //立体の中心を画面中央にする
  translate(width / 2, height / 2);

  //pointLight(赤, 緑, 青, x座標, y座標, z座標);
  pointLight(255, 0, 0, -200, 0, 200);
  
  sphere(75);  //球を描画
}

●スポットライト(spotLight)

spotLightはその名の通り、一定方向に強い光をあてるライトです。directionalLightやpointLightと比べると、細かい設定が可能です。

spotLight(red, green, bule, //赤, 緑, 青
          x, y, z,          //光源のx, y, z座標
          nx, ny, nz,       //光の方向
          angle,            //スポットの角度(ここでは20°)
          concentration);   //光の集まり度合い。数値が小さいと強く一点に集まる
spot_light
図5.2-f

次のサンプルでは、3種類のライトを切り替えることができます【リスト5.2-c】。

図5.2-g
リスト5.2-e
float angle = 0.25;  //スポットの角度
float diffusion = 0.5;  //光が拡散する度合い

void setup() {
  size(400, 400, P3D);
  noStroke();
}

void draw() {
  background(0);

  //立体の中心を画面中央にする
  translate(width / 2, height / 2);

  spotLight(0, 0, 255, //赤, 緑, 青
    0, -200, 200, //光源のx, y, z座標
    0, 1.0, -1.0, //光源が照らす方向。-1.0 ~ 1.0
    angle, //スポットの角度
    diffusion);          //光が拡散する度合い

  sphere(75);  //球を描画
}

void keyPressed() {

  if (key == CODED) {
    if (keyCode == UP)
      angle += 0.05;    //スポットの角度を拡大
    else if (keyCode == DOWN)
      angle -= 0.05;    //スポットの角度を縮小
    else if (keyCode == RIGHT)
      diffusion += 5.0;  //拡散度合いを拡大
    else if (keyCode == LEFT)
      diffusion -= 5.0;  //拡散度合いと縮小
  }
}

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

様々な種類のライトがありましたが、次は光源の鏡面反射成分を指定するlightSpecular()と、オブジェクトの鏡面反射成分を設定するspecular()を使います。似た名称で混同しやすいのですが、反射成分が光源とオブジェクトのどちらに属しているかの違いです。

//lightSpecular(赤, 緑, 青)
lightSpecular(red, green, blue);   //光源の鏡面反射成分

//specular(赤, 緑, 青)
specular(red, green, blue);        //オブジェクトの鏡面反射成分
10_6_15__1_35_AM
図5.2-h

次のコードを実行してみましょう【リスト5.2-d】。

リスト5.2-f
boolean lightFlag = false;

void setup() {
  size(400, 400, P3D);
  background(0);
  noStroke();
}

void draw() {
  background(0);

  ambientLight(10, 10, 10);    //環境光

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

  //directionalLight 光源の鏡面反射成分を設定
  lightSpecular(255, 255, 255);

  //方向光を設定
  directionalLight(255, 255, 255, -1, 1, -1);

  //反射面の強いハイライトを設定
  if (lightFlag == true)specular(255, 255, 255);
  else specular(50, 50, 50);    //反射面の弱いハイライトを設定

  sphere(100);
  popMatrix();
}

//lightSpecularの切り替え用フラッグ
void mousePressed() {
  if (lightFlag == true) lightFlag = false;
  else lightFlag = true;
}

クリックでspecularの値が変わります。例えばspecular(50, 50, 50)の場合、ハイライトは緩やかになり、specular(255, 255, 255)の場合はハイライトが強くなります。

次に、ふたつの球それぞれにspecular()を設定してみます。

2016_09_29_0_16
図5.2-i
リスト5.2-g
float angle = 0.0;
 
void setup() {
  size(400, 400, P3D);
  background(0);
  noStroke();
}
 
void draw() {
  background(0);
 
  ambientLight(20, 20, 20);    //環境光を当てる
  //光の鏡面反射成分(ハイライト)を設定
  lightSpecular(255, 255, 255);
  //方向光を設定
  directionalLight(100, 100, 100, 0, 1, -1);
 
  //左の球
  pushMatrix();
  translate(100, height/2, 0);
  specular(255, 0, 0);  //オブジェクトの鏡面反射成分
  sphere(50);
  popMatrix();
 
  //右の球
  pushMatrix();
  translate(300, height/2, 0);
  specular(0, 0, 255);  //オブジェクトの鏡面反射成分
  sphere(50);
  popMatrix();
}

5.2.5 光沢

金属のような光沢はshininess()で設定します。

2016_09_29_0_19
図5.2-j
リスト5.2-h
float angle = 0.0;
 
void setup() {
  size(400, 400, P3D);
  background(0);
  noStroke();
}
 
 
void draw() {
  background(0);
 
  ambientLight(20, 20, 20);    //環境光を当てる
  //光の鏡面反射成分(ハイライト)を設定
  lightSpecular(255, 255, 255);
  //方向光を設定
  directionalLight(100, 100, 100, 0, 1, -1);
 
  //左の球
  pushMatrix();
  translate(100, height/2, 0);
  specular(200, 200, 200);  //オブジェクトの鏡面反射成分を設定
  shininess(5.0);    //オブジェクトの光沢を設定
  sphere(50);
  popMatrix();
 
  //右の球
  pushMatrix();
  translate(300, height/2, 0);
  specular(200, 200, 200);  //オブジェクトの鏡面反射成分を設定
  shininess(1.0);    //オブジェクトの光沢を設定
  sphere(50);
  popMatrix();
}

5.2.6 カメラ

ここではカメラを設置して、空間の見え方を設定します。
標準のカメラはcamera()を使います。視点と中心点、天地がどの方向か(通常はYが天)を指定します。

//camera(視点X, 視点Y, 視点Z, 中心点X, 中心点Y, 中心点Z, 天地X, 天地Y, 天地Z)
camera(eyeX, eyeY, eyeZ, centerX, centerY, centerZ, upX, upY, upZ);
2016_09_29_3_09
図5.2-k

コードは【リスト5.2-g】になります。camera関数の中の値を変えてどのように見え方が変化するか確認しましょう。

リスト5.2-i
void setup() {
  size(400, 400, P3D);
  noFill();
  stroke(0);
}
 
void draw() { 
  background(255);
 
  //基準点を画面中央
  translate(width/2, height/2, 0);
 
  camera(90.0, -100.0, 300.0, // 視点X, 視点Y, 視点Z
         0.0, 0.0, 0.0, // 中心点X, 中心点Y, 中心点Z
         0.0, 1.0, 0.0); // 天地X, 天地Y, 天地Z
 
  box(150);
}

5.2.7 ortho()

パースペクティブ(遠近)がつかない立体です。

//ortho(左, 右, 下, 上);
ortho(left, right, bottom, top);

//ortho(左, 右, 下, 上, 近い面までの距離、遠い面までの距離);
ortho(left, right, bottom, top, near, far);
2016_09_29_3_16
図5.2-l
リスト5.2-j
void setup() { 
  size(400, 400, P3D);
  noFill();
  stroke(0);

  //ortho(左、右、下、上)
  ortho(-width/2, width/2, -height/2, height/2);
}

void draw() {  
  background(255);

  //基準点を画面中央
  translate(width/2, height/2, 0);
  rotateX(-PI/9.0);
  rotateY(-PI/9.0);
  box(150);
}

5.2.8 frustum()

パースペクティブが付いた空間になります。次の形で使います。

frustum(左、右、下、上、近い面までの距離、遠い面までの距離)
frustum(left, right, bottom, top, near, far);

【図5.2-m】はその構造を図解したものです。

image61
図5.2-m OpenGL Programming 公式サイトより引用
2016_09_29_3_51
図5.2-n
リスト5.2-k
float scale = 5;  //視点の範囲の拡大率
float aspect;  //画面の縦横比

void setup() {
  size(400, 400, P3D);
  noFill();
  stroke(0);

  //画面の縦横比が変わってもオブジェクトが歪まないように比率を計算
  aspect = float(width)/float(height);

  //frustum(左、右、下、上、近い面までの距離、遠い面までの距離)
  frustum(-scale*aspect, scale*aspect, -scale, scale, 10, 500);
}

void draw() {
  background(255);

  //基準点を画面中央。z軸方向には-100
  translate(width/2, height/2, 0);
  rotateX(-PI/9.0);  //-20°回転
  box(150);
}

5.2.9 perspective()

perspective()はfrustum()とほぼ同じ機能ですが、x座標の視野角(fov)を設定できるのが特徴で、fovの設定を変えると遠近感のつき方が変化します。次の形で使います。

//perspective(視野角、縦横の比率、近い面までの距離、遠い面までの距離) 
perspective(foxy, aspect, zNear, zFar);

【図5.2-o】はその構造を図解したものです。

image62
図5.2-o OpenGL Programming 公式サイトより引用

サンプルは、視野角を非常に広い90度にしているために、遠近感が強くなっています。一般的には、30度から60度ぐらいまでが適切かもしれません。

2016_09_29_4_02
図5.2-p
リスト5.2-l
float transZ = 0.0;
float angle = 45.0;

void setup() {
  size(400, 400, P3D);
  noFill();
  stroke(0);

  //perspective(視野角、縦横の比率、近い面までの距離、遠い面までの距離)
  perspective(radians(angle), float(width)/float(height), 100.0, 800.0);
}

void draw() {
  background(255);

  translate(width/2, height/2, transZ);  //基準点を画面中央、z座標を設定
  rotateX(radians(-30.0));  //-30°回転
  box(150);
}

//何らかのキーが押された時に実行される
void keyPressed() {
  background(255);

  //1を押したら視野角45度、2を押したら視野角90度
  switch(key) {
  case '1':
    transZ = 0.0;
    angle = 45.0;
    perspective(radians(angle), float(width)/float(height), 100.0, 800.0);

    println("1");
    break;
  case '2':
    transZ = 140.0;
    angle = 90.0;
    perspective(radians(angle), float(width)/float(height), 100.0, 800.0);
    println("2");
    break;
  }
}