yoppa.org


immediate bitwave

人工言語入門 A 2010

Processingによる3Dプログラミング

今日の内容

Processingで3DCGプログラミング

  • 3Dの座標系
  • OpenGLとは
  • 2Dのアニメーションを3Dに拡張してみる
  • 3D座標で図形を描く
  • 視点の移動
  • 3Dのを用いた高度なアニメーション

コンピュータで3Dを表現するには

コンピュータ画面で3Dを表現したい

  • コンピュータのディスプレイは、2D (縦横に並んだピクセル)
  • 奥行は擬似的に表現するしかない
  • 画面に、立体や空間などの3次元の存在を投影して描画する = 3DCG (3次元グラフィックス)

2次元平面に3次元の存在を投影するには、様々な数学的な知識が必要

  • Processingではこうした演算を自動的に行うことが可能
  • 3次元の座標系をそのまま使用できる
  • 高速な表示のためのライブラリ(OpenGL)も標準で使用可


コンピュータ画面での3Dを表示する

3D空間の座標系

X (幅)、 Y (高さ) に加えて、奥行を表現する座標軸 Z が加わる。


3Dの座標系

3Dプログラミング基本

まず2Dの図形を回転するプログラムを作成してみる

  • translateで画面の中心を座標の原点に
  • 以下の処理を繰り返す
    • 背景描画
    • rotateで回転
    • rectを描画
    • 回転する角度を更新
float rot=0;

void setup(){
    size(400,400);
    frameRate(60);
    smooth();
    fill(63,127,255);
    stroke(255);
    //四角形を中心を原点に描画するモードに
    rectMode(CENTER);
}

void draw(){
    background(0);
    //画面の中心に原点(0,0)を移動する
    translate(width/2,height/2);
    //画面の中心を軸にrotだけ回転
    rotate(rot);
    //四角を描く
    rect(0,0,200,200);
    //角度を更新
    rot += 0.06;
}

現状の回転する四角形のプログラム

  • 2D的な視点では平面上で回転している、これを3D的な視点に変更してみる
  • 現状では、Z軸を中心軸としてXY平面上に置いてある物体が回転している
  • では軸をZ軸ではなく、他の軸(X軸、Y軸)にすると果してどうなるのか

回転する軸を指定してrotateする関数

rotateX(angle);  // X軸を中心に回転
rotateY(angle);  // Y軸を中心に回転軸を中心に回転
rotateZ(angle);  // Z軸を中心に回転軸を中心に回転

3D座標を用いたプログラミングをする際の注意点

レンダラー (描画の際の方式) を指定する必要がある

  1. P3D – Processing専用の3D描画エンジン
  2. OpenGL – 3Dグラフィックスのためのプログラムインターフェイス、高速に3D画像を描画できる

P3Dを使用する場合

  • size関数に以下の指定をする
size (幅, 高さ, P3D); // size関数にP3Dの指定

OpenGLを使用する場合

  • プログラムの先頭でOpenGLのライブラリを読み込み
  • size関数にOPENGLの指定をする
import processing.opengl.*; // OpenGLライブラリの読み込み
size(幅, 高さ, OPENGL);     // size関数にOPENGLの指定

OpenGLを使用した3次元空間での回転

x軸を中心に回転

import processing.opengl.*;

float rot=0;
void setup(){
    size(400,400,OPENGL);
    frameRate(60);
    fill(63,127,255);
    stroke(255);
    rectMode(CENTER);
}

void draw(){
    background(0);
    translate(width/2,height/2);
    //X軸を中心に回転させる
    rotateX(rot);
    rect(0,0,200,200);
    rot += 0.06;
}

x,y,z軸をそれぞれ回転

import processing.opengl.*;

// x, y, z それぞれの軸での回転角度
float rotX, rotY, rotZ;

void setup(){
    size(400,400,OPENGL);
    frameRate(60);
    fill(63,127,255);
    stroke(255);
    rectMode(CENTER);
}

void draw(){
    background(0,0,20);
    translate(width/2,height/2);
    //X軸を中心に回転
    rotateX(rotX);
    //Y軸を中心に回転
    rotateY(rotY);
    //Z軸を中心に回転
    rotateZ(rotZ);
    //四角形を描く
    rect(0,0,200,200);
    //それぞれの軸の回転角度を更新
    rotX += 0.02;
    rotY += 0.03;
    rotZ += 0.05;
}

3D図形の描画デモ

四角形を3D空間にタイル状に敷き詰める

  • for文を2重に入れ子にして、X,Y方向にグリッド状に四角形を敷きつめていく
  • ただし個々の四角形はすこしだけ斜めに傾けて立体感を強調する
  • マウスの位置によって、座標全体を回転してみる
  • まずは単純に描画してみる
import processing.opengl.*;

void setup() {
  size(400, 400, OPENGL);
  noStroke();
  fill(255, 190);
}

void draw() {
  background(0);
  translate(width / 2, height / 2, 0);

  //マウスの位置で座標全体を回転する
  rotateX(mouseX / 200.0);
  rotateY(mouseY / 100.0);

  //四角形描画を中心を原点に
  rectMode(CENTER);

  //敷きつめるタイルの一片の長さ
  int dim = 18;

  //XY平面を正方形でタイリング
  for (int i = -height / 2; i < height / 2; i += dim) {
    for (int j = -width / 2; j < width / 2; j += dim) {
      pushMatrix();
      translate(i, j);
      rotateX(radians(30));
      rotateY(radians(30));
      rect(0, 0, dim, dim);
      popMatrix();

    }
  }
}

ライティング

Processinには様々なライティングの方法を用いることができる

  • ambientLight() – 環境光
  • directionalLight() – 一定方向から差し込む平行光
  • pointLight() – 点光源
  • spotLight() – スポットライト

様々な光を活用しながら、複雑なライティングをしてみる

import processing.opengl.*;

void setup() {
  size(400, 400, OPENGL);
  noStroke();
  fill(255, 190);
}

void draw() {
  //環境光
  ambientLight(63, 31, 31);
  //平行光
  directionalLight(255, 255, 255, -1, 0, 0);
  //点光源
  pointLight(63, 127, 255, mouseX, mouseY, 200);
  //スポットライト
  spotLight(100, 100, 100, mouseX, mouseY, 200, 0, 0, -1, PI, 2);

  background(0);
  translate(width / 2, height / 2, -20);

  rotateX(mouseX / 200.0);
  rotateY(mouseY / 100.0);

  int dim = 18;
  for (int i = -height / 2; i < height / 2; i += dim * 1.4) {
    for (int j = -width / 2; j < width / 2; j += dim * 1.4) {
      pushMatrix();
      translate(i, j);
      rotateX(radians(30));
      rotateY(radians(30));
      box(dim, dim, dim);
      popMatrix();
    }
  }
}

カメラ

Processingでは何も指定していない時にはカメラ(視点)の場所は、中心点(0, 0, 0)。しかし、camera() 関数を使用することで、視点をコントロールすることができる

camera() 関数

  • camera(eyeX, eyeY, eyeZ, centerX, centerY, centerZ, upX, upY, upZ)
    • eyeX, eyeY, eyeZ – 視点の位置
    • centerX, centerY, centerZ – 注視する中心位置
    • upX, upY, upZ – カメラの向き

マウスの位置でカメラの位置をコントロールしてみる

import processing.opengl.*;

void setup() {
  size(400, 400, OPENGL);
  noStroke();
  fill(255, 190);
}

void draw() {
  background(0);

  ambientLight(63, 31, 31);
  directionalLight(255, 255, 255, -1, 0, 0);
  pointLight(63, 127, 255, mouseX, mouseY, 200);
  spotLight(100, 100, 100, mouseX, mouseY, 200, 0, 0, -1, PI, 2);

  //カメラを定義、マウスの位置でカメラの位置が変化する
  camera(mouseX, mouseY, 200, width / 2.0, height / 2.0, 0, 0, 1, 0);

  translate(width / 2, height / 2, -20);
  int dim = 18;
  for (int i = -height / 2; i < height / 2; i += dim * 1.4) {
    for (int j = -width / 2; j < width / 2; j += dim * 1.4) {
      pushMatrix();
      translate(i, j);
      rotateX(radians(30));
      rotateY(radians(30));
      box(dim, dim, dim);
      popMatrix();
    }
  }
}

3D空間でのアニメーション

少しずつスピードを変化させながら回転する立方体を作成してみる

import processing.opengl.*;

float a;
int NUM = 128;
float offset = PI / NUM;
color[] colors = new color[NUM];

void setup() {
  size(400, 400, OPENGL);
  noStroke();
  colorMode(HSB, 360, 100, 100, 100);
  frameRate(30);
  for (int i = 0; i < NUM; i++) {
    colors[i] = color(i * 2 + 100, 70, 100, 25);
  }
  lights();
}

void draw() {
  background(0);

  ambientLight(63, 31, 31);
  directionalLight(255, 255, 255, -1, 0, 0);
  pointLight(63, 127, 255, mouseX, mouseY, 200);
  spotLight(100, 100, 100, mouseX, mouseY, 200, 0, 0, -1, PI, 2);

  translate(width / 2, height / 2, -20);

  rotateX(mouseX / 200.0);
  rotateY(mouseY / 100.0);

  for (int i = 0; i < NUM; i++) {
    pushMatrix();
    fill(colors[i]);
    rotateY(a + offset * i);
    rotateX(a / 2 + offset * i);
    rotateZ(a / 3 + offset * i);
    box(width / 2);
    popMatrix();
  }

  a += 0.01;
}

サンプルファイルのダウンロード

今日とりあげた全てのサンプルは下記からダウンロードしてください。