pg11
テーマ アートとプログラム
2019年度向けメモ:
P5.jsを新エディタに移行する。旧エディタ上ではコードのぷれびゅうが出来ないのかも。2018.11.29の時点でExamplesに掲載のコードがブラウザで実行されない状態。2018.12.05復旧確認
2019.2.21 CanTree のサイトに接続できない。ドメイン名からIPアドレスがでてこない。-> 2019.11.26復旧確認。
ジェネラティブアート
今回取り組む分野。
映像・サウンド・画像などを生成するルールを決め、主にコンピュータでプログラムを実行して作品を制作する。
人工的なパターン:
編み物や布の柄・壁紙・レンガやタイルの敷き詰めなど
規則的で幾何学的な図形
プログラムで数学的規則を元に描画する
ネットで見かけた作品例)
Twitterで見かけたモーショングラフィックス ←この方が作成したモーフィングCGの記事: Processing で簡単モーフィング @deconbatch
自然界のパターン:
動物の毛皮や魚の体表の紋様・雪の結晶・植物の形・地形・樹木の形など
規則的な操作の積み重ねにより表現することも可能
不規則性がある
パターン生成に利用される理論の例
自己相似図形
フラクタル図形
カオス理論
反応拡散系
補足)
自然なノイズについて:自然な不規則性に関する理論
パーリンノイズを理解する @POSTDcc
フラクタル図形
ネットで調べてみよう。
ウィキペディア フラクタル コッホ曲線 や シェルピンスキーギャスケット の生成過程のアニメーションを確認する。
フラクタルツリー の画像検索
フラクタルツリー をパラメーター調整で生成
※ 枝の比率を100%にすると、画面を覆う模様を生成できる。
※ 枝の広がりは 45度 や 60度 など、360度を単純な整数で分割した角度は規則的な模様を生成する
※ 枝の広がりを 0度~180度までスライドすると、全角度でツリーを生成し、アニメーション表示
ネタ テレビアニメのOPで使われていた
都市の自動生成 Wave Function Collapse
迷路の自動生成 Wave Function Collapse
他にもプログラムによる自動生成について調べてみよう。
ダンジョンの自動生成
迷路の自動生成
家具のデザインの自動生成
キャラクターデザインの自動生成
模様の自動生成(布の柄・CGテクスチャなど)
木の自動生成 Blenderや他のCGソフト、Unityなどのゲームエンジンでも利用可能
景観の自動生成 Terragenなど様々なソフトがある。Terragen4が実習室PCにインストール済み。
※演習の注意
ブラウザは、 Google Chrome を使用してください。
インターネットエクスプローラー(IE)では、課題プログラムが動作しない、作成したプログラムが消える などの不具合が発生します。
課題提出
課題1 か 課題2 か 課題3 の3の中から 2つ以上(全部でもOK)の課題に取り組み、作成したCGをスクリーンショットで WebClass にアップロードする。
応用例:自然景観CGの作成(課題1 または 課題2 の代わりにこれに取り組んでもOK)
授業では、以下の課題3を主に進める。他の課題は、残りの時間または宿題で進める。
課題3
p5.js によるアニメーションとサウンド作成
demo(音がでます。ボリューム調整に注意)
作成準備
p5.js をクリックして P5.jsのサイトを開く。
p5.js は、メディアアート系プログラミング言語 Processing(略称 p55) を JavaScript と HTML5によりブラウザで利用可能にしたものです。
※英語版サイトはExamplesに掲載のコードが動かないようです。2018.11.29の時点では。 日本語版サイトはこちら 2018.12.05復旧確認。 2019年度は日本語版サイトは使用しない。
Start creating with the p5 Editor! をクリックしてコードの入力と実行画面を開く。
Examples から一部解説(課題とは別)
Interactivity 1 マウスに反応するプログラムの例。クリックで丸い図形を表示
Oscillator Frequency サウンドのサンプル(※音声有り。要ボリューム調整) マウスの位置xy座標に応じて音が変化
Random ランダムな太さの線を引き続ける
作成手順
アニメーション処理の基本形 のプログラムを開いて、下記のコード例を参考にして修正していく。
メニューの File → Examples → 検索窓から animate を検索
マウスをクリックした位置に円の座標を移動するコード↓を追加する。場所は元のコードの最下行に。
// When the user clicks the mouse
function mousePressed() {
x = mouseX;
y = mouseY;
}
↓の赤い部分の追加修正により、緑のドットを弾として発射する丸に変更する。
※追加修正する代わりに、 コード全体をコピペ して入れ替えてもよい。
// Where is the circle
let x, y;
let mx, my;
function setup() {
createCanvas(720, 400);
noSmooth();
mx = 0;
my = 0;
// Starts in the middle
x = width / 2;
y = height;
}
function draw() {
background(0);
// Draw a circle
stroke(50);
fill(100);
ellipse(mx,my, 24, 24);
// Jiggling randomly on the horizontal axis
x = x + random(-1, 1);
// Moving up at a constant speed
y = y - 1;
// Reset to the bottom
if (y < 0) {
y = height;
}
// Draw white points
stroke(0,255,0);
strokeWeight(4);
point(x, y);
}
// When the user clicks the mouse
function mousePressed() {
mx = mouseX;
my = mouseY;
x = mx;
y = my;
}
三角波の音源(osc)を追加する。
音の高さ(周波数 freq)を 緑のドットの x 座標に対応させる。
音の大きさ(amp)を 同じく y 座標に対応させる。
※追加修正する代わりに、 コード全体をコピペ して入れ替えてもよい。
// Where is the circle
let x, y;
let mx, my;
let osc;
function setup() {
createCanvas(720, 400);
noSmooth();
mx = 0;
my = 0;
// Starts in the middle
x = width / 2;
y = height;
osc = new p5.TriOsc(); // set frequency and type
osc.amp(.5);
osc.start();
}
function draw() {
background(0);
// Draw a circle
stroke(50);
fill(100);
ellipse(mx,my, 24, 24);
// Jiggling randomly on the horizontal axis
x = x + random(-1, 1);
// Moving up at a constant speed
y = y - 1;
// Reset to the bottom
if (y < 0) {
y = height;
}
// Draw green points
stroke(0,255,0);
strokeWeight(4);
point(x, y);
// change oscillator frequency based on mouseX
let freq = map(x, 0, width, 40, 880);
osc.freq(freq);
let amp = map(y, 0, height, 0.1, 1.0);
osc.amp(amp);
}
// When the user clicks the mouse
function mousePressed() {
mx = mouseX;
my = mouseY;
x = mx;
y = my;
}
残りの時間の実験内容:
・音の触れ幅を変化させてみよう random(-1,1) を random(-10,10) に修正
・音の触れ幅を偏らせてみよう random(-1,1) を random(-4, 5) に修正
・弾のスピード(yの変化量)を -1 から別の値に変えて速度を上げてみよう(例 -3)。
・音の大きさ(アンプ)や高さ(周波数)は、map関数でドットのxy座標から変換して設定している。
map( 参照座標 , 参照元最小値, 参照元最大値, 変換先最小値, 変換先最大値)
x座標の 0 ~ width(画面サイズ) に対して、周波数を 40~880 まで変更する場合、
map(x, 0, width, 40, 880);
ヒント:周波数を2倍にすると、1オクターブ音程が上昇する。
※コードを修正して結果を確認したら、課題提出の準備をする。
その他の調整例:
・残像効果。画面更新の際に、背景色で塗りつぶすbackground(0)の代わりに、半透明の黒で塗りつぶすように変更。
function draw() {
//background(0);
fill(0,0,0,30);
rect(0,0,width,height);
・微調整
画面外に消えたドット(サンプルコード Animation に追加した最初の緑の点)が
画面の下端にループ(heightの位置)して登場するようになっている
↓
マウスをクリックした位置(円の位置)へループするように修正。
// Reset to the bottom
if (y < 0) {
y = my;
}
課題3の提出
p5.js で作成したプログラムが動いている様子が分かるタイミングで、スクリーンショットを撮る。
※ 弾の動きが早いので、 キーボードの PrintScreen キー を押してメモリーにスクショを記録
→ ペイントブラシ(スタート→検索→pbrush)に貼り付け
を利用するとよい。
例)
応用例)
・多数の弾を表示する座標を記録するため、配列(Array)で、1000個のメモリを準備。
・マウスを押したときに、マウスのxy座標を弾の位置として記録。
pop()で配列から一番古い弾の座標を削除。
push()で配列にマウスクリックをしたときに発射した弾の座標を記録。
・記録された全ての弾を移動させ、表示するための for ループの追加。
・音の周波数(高さ) freq は y に対応 var freq = map(y, 0, height, 40, 880);
音の大きさ amp は x に対応 var amp = map(x, 0, width, 1, .01);
// Where is the circle
let x, y;
let mx, my;
let osc;
let ax = new Array(1000);
let ay = new Array(1000);
function setup() {
createCanvas(720, 400);
noSmooth();
mx = 0;
my = 0;
// Starts in the middle
x = width / 2;
y = height;
osc = new p5.TriOsc(); // set frequency and type
osc.amp(.5);
osc.start();
}
function draw() {
background(0);
// Draw a circle
stroke(50);
fill(100);
ellipse(mx,my, 24, 24);
// Jiggling randomly on the horizontal axis
x = x + random(-10, 10);
// Moving up at a constant speed
y = y - 1;
// Reset to the bottom
if (y < 0) {
y = height;
}
// Draw white points
stroke(0,255,0);
strokeWeight(4);
point(x, y);
// change oscillator frequency based on mouseX
let freq = map(y, 0, height, 40, 880);
osc.freq(freq);
let amp = map(x, 0, width, 1, .01);
osc.amp(amp);
for(var i=0; i<ax.length; i++) {
ax[i] = ax[i] + random(-1, 1);
ay[i] = ay[i] - 1;
point(ax[i], ay[i]);
}
}
// When the user clicks the mouse
function mousePressed() {
mx = mouseX;
my = mouseY;
x = mx;
y = my;
ax.pop();
ay.pop();
ax.unshift(mx);
ay.unshift(my);
}
・残像効果。画面更新の際に、背景色で塗りつぶす代わりに、半透明の黒で塗りつぶすように変更。
function draw() {
//background(0);
fill(0,0,0,30);
rect(0,0,width,height);
・弾を連射するように変更する。mousePressed() 内の以下の4行を、 draw() の内部に移動する。
ax.pop();
ay.pop();
ax.unshift(mx);
ay.unshift(my);
・微調整:
音を出すタイミングを発射位置に合わせる。
osc2.freq(map(ax[my-y-1], 0, width, 40, 880));
・ドットのx座標の変化を時間に対して周期的にする。
y の値をcos関数で処理すればよい。
以下のコードでは -10 ~ 0 ~ +10 ~ 0 ~ -10 のループになる。
ax[i] = ax[i] + 10*cos(200*PI*y/height);
ay[i] = ay[i] - 5;
や
ax[i] = ax[i] + 3*cos(8*PI*y/height);
ay[i] = ay[i] - 3;
数値の組み合わせによっては、このようなパターンも生成可能。(この例で使用した数値の組み合わせは不明。各自で探ってみよう。)
(ヒント) 画面の高さは 400ピクセル。cosの中の式は、上の例で 8 の値をwと書くと、
w*PI*y/height
になる。
さらに、PIはπの値で固定、yは時間に応じて変化する。y以外の時間で変化しない値は、height=400 なら w/400*π となる。
三角関数 cos の性質から、w = 800 や 400 や 200 の時には弾は特殊な軌跡を描く。
400をちょうど割り切ることが出来る100や50の時もやや特殊な軌跡となる。
特殊な軌跡となる値から少しずれた値、799 や 99 等を指定すると面白い。
例)
ax[i] = ax[i] + 50*cos(395*PI*y/height);
ay[i] = ay[i] - 2;
変化しない軌跡を描くコード。赤い数字の部分を調整してみよう。
ax[i] = ax[i] + 5*cos(2*map(ay[i],0,0.5*height,-PI,PI));
ay[i] = ay[i] - 2;
おまけ:音をさらに追加する。追加する音の高さは、x座標の配列から時間に応じて読み出した値。yの変化量は -1 程度に抑え目にしておく。
変数追加
let osc,osc2;
setup()に音源の追加
osc2 = new p5.TriOsc(); // set frequency and type
osc2.amp(.5);
osc2.start();
draw()に周波数変更コードの追加。
位置は draw()の最後の方でunshift()のコード(弾を追加するコード)の下に以下の1行を張り付ける。
osc2.freq(map(ax[y], 0, width, 40, 880));
draw()にあるyの値を変更するコードを調整
y = y - 1;
余力のある人向け
P5.jsのサンプルから Multiple Particle Systems を改造してみよう。
解説:
パーティクルの初期値を決めているコード
// A simple Particle class
var Particle = function(position) {
this.acceleration = createVector(0, 0.05);
this.velocity = createVector(random(-1, 1), random(-1, 0));
this.position = position.copy();
this.lifespan = 255.0;
};
パーティクルの寿命を決めているコード
// Method to update position
Particle.prototype.update = function(){
this.velocity.add(this.acceleration);
this.position.add(this.velocity);
this.lifespan -= 2;
};
パーティクルの種類(Particle と CrazyParticleの2種類)を選択するコード
ParticleSystem.prototype.addParticle = function () {
// Add either a Particle or CrazyParticle to the system
if (int(random(0, 2)) == 0) {
p = new Particle(this.origin);
}
else {
p = new CrazyParticle(this.origin);
}
this.particles.push(p);
};
パーティクルの形を描画するコード
CrazyParticle.prototype.display=function() {
// Render the ellipse just like in a regular particle
Particle.prototype.display.call(this);
// Then add a rotating line
push();
translate(this.position.x, this.position.y);
rotate(this.theta);
stroke(255,this.lifespan);
line(0,0,25,0);
pop();
}