iA


人工言語入門 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);
    }
}

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;  
} 

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

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