Processingでスライムを描画してみた
Hello New Year !! (挨拶)
新年一回目の更新です!
今回は, 先日投稿したツイートが思いのほかいいねがあったのでソースコードを紹介しておきたいと思います!
ジェネラティブすらいむ#Processing #creativecoding #generativeart pic.twitter.com/vhBHEbJCDM
— Yuz (@Eddie_Le_Garden) 2020年1月7日
ソースコード
float verNoise; float vertex = 8; //最大頂点数 float viscosity = 30; //粘度 color col = #03B9FF; void setup() { size(500, 500); noStroke(); } void draw() { background(255); slime((int)(1*vertex-1)+1, abs(sin(frameCount*0.1)-0.5)*viscosity); verNoise+=.005; } void slime(int ver, float vis) { //ver頂点数 vis粘度 pushMatrix(); translate(width/2, height/2); rotate(frameCount*.01); fill(col); beginShape(); for (int theta=0; theta<360; theta++) { float r = 150+sin(radians(theta*ver))*vis; vertex(cos(radians(theta))*r, sin(radians(theta))*r); } endShape(CLOSE); popMatrix(); }
主にはslime関数により描画処理が行われており, グローバル変数の値を変えることでスライムの描画を調節します.
変数vertexはスライムが伸びた際の最大頂点数を示し, 変数viscosityの値を大きくすると伸び具合 (粘度)が設定できます.
↑viscosity=60の場合
割と単純なソースコードですが, プロパティを変えるだけで色々な描画結果になるのでぜひお試しください~!!
HSVでグリッドな床シェーダーを作ってみた
かなり久しぶりの投稿です...!
今回は, 先月の学内プロコン用に作ったシェーダーを作った際の工夫を書いてきます!
今年のプロコンに出したプロジェクト
— Yuz (@Eddie_Le_Garden) 2019年11月3日
モーキャプで積み木ゲーム#unity pic.twitter.com/TEh0bxTf3q
イメージとしてはplaneオブジェクトにグリッド線の描画し, さらにBloomエフェクトを設定して色相を動的に変更していく感じです
※本シェーダーではPost Processing Stack を必要とします
Surfaceシェーダ作成
実際に書いたシェーダーが下記になります↓
Shader "Custom/HsvGrid" { Properties{ _GridThickness("Grid Thickness", Float) = 0.1 _GridSpacing("Grid Spacing", Float) = 9.0 _Freq("frequency", Range(1,3)) = 1 _Bloom("Bloom", float) = 7 } SubShader{ Tags{ "Render" = "Opaque" } LOD 200 CGPROGRAM #pragma surface surf Standard fullforwardshadows #pragma target 3.0 struct Input { float3 worldPos; }; //HSB→RGB fixed3 hsv2rgb(fixed3 c) { fixed4 K = fixed4(1.0, 2.0 / 3.0, 1.0 / 3.0, 3.0); fixed3 p = abs(frac(c.xxx + K.xyz) * 6.0 - K.www); return c.z * lerp(K.xxx, clamp(p - K.xxx, 0.0, 1.0), c.y); } float _GridThickness; //グリッド線の太さ float _GridSpacing; //グリッドの間隔 float _Freq; //色相が変化する早さ float _Bloom; //Bloomエフェクト用の係数 void surf(Input IN, inout SurfaceOutputStandard o) { float _Hue = _Time * _Freq; //色相 //グリッド線 o.Albedo = (frac(IN.worldPos.x / _GridSpacing) < _GridThickness || frac(IN.worldPos.z / _GridSpacing) < _GridThickness) ? hsv2rgb(fixed3(_Hue % 1, 1, 1))*_Bloom : fixed3(0, 0, 0); } ENDCG } FallBack "Diffuse" }
これをPlaneオブジェクトに適用します↓
グリッド線のジャギー対策
この時点だと上記のキャプチャから遠くのグリッド線にジャギーが生じてしまうことが確認できます
今回はこれを, むろ小径さん(@murosyoukei)のツイートを参考に改善していきます
シェーダーでグリッドを表現しようとすると遠くの方でジャギる現象を
— むろ小径 (@murosyoukei) 2019年10月8日
グリッドの太さを深度値で変化させるようにして解決する試み pic.twitter.com/Tc5EIk00e8
このツイートを基に改善したシェーダーがこちらです↓
Shader "Custom/HsvGrid" { Properties{ _GridThickness("Grid Thickness", Float) = 0.1 _GridSpacing("Grid Spacing", Float) = 9.0 _Freq("frequency", Range(1,3)) = 1 _Bloom("Bloom", float) = 7 _Center("Center", Vector) = (0,0,0,0) //変更点1 _Range("Renge", Float) = 0.05 //変更点2 } SubShader{ Tags{ "Render" = "Opaque" } LOD 200 CGPROGRAM #pragma surface surf Standard fullforwardshadows #pragma target 3.0 struct Input { float3 worldPos; }; //HSB→RGB fixed3 hsv2rgb(fixed3 c) { fixed4 K = fixed4(1.0, 2.0 / 3.0, 1.0 / 3.0, 3.0); fixed3 p = abs(frac(c.xxx + K.xyz) * 6.0 - K.www); return c.z * lerp(K.xxx, clamp(p - K.xxx, 0.0, 1.0), c.y); } float _GridThickness; float _GridSpacing; float _Freq; float _Bloom; float4 _Center; //変更点1 float _Range; //変更点2 void surf(Input IN, inout SurfaceOutputStandard o) { float _Hue = _Time * _Freq; //色相 float _DepthThick = (distance(fixed3(_Center.x, 0, _Center.z), IN.worldPos)*_Range); //変更点3 //グリッド線 o.Albedo = (frac(IN.worldPos.x / _GridSpacing) < _GridThickness + _DepthThick //変更点4 || frac(IN.worldPos.z / _GridSpacing) < _GridThickness + _DepthThick) ? hsv2rgb(fixed3(_Hue % 1, 1, 1))*_Bloom : fixed3(0, 0, 0); } ENDCG } FallBack "Diffuse" }
変更点1, 2ではそれぞれ, MainCameraの位置とその距離に応じてグリッド線を太くするための係数を示します
surf関数では実際にMainCameraとの距離を求め, _DepthThick (変更点3) に加算する太さを代入しています
各プロパティの値は大体こんな感じに設定しました↓
次に, 上記のシェーダーにMainCameraの座標を渡すスクリプトを作成します
using System.Collections; using System.Collections.Generic; using UnityEngine; public class PosShader : MonoBehaviour { [SerializeField] Renderer mat; void Update() { mat.material.SetVector("_Center", transform.position); } }
このスクリプトをMainCameraにアタッチし, インスペクターから"mat"にPlaneオブジェクトを指定します.
これで実行すると大体こんな感じになるかと思います↓
以前のグリッドシェーダーにMainCameraの座標を渡すように改善しました!#Unity pic.twitter.com/c5CiumzYvq
— Yuz (@Eddie_Le_Garden) 2019年12月6日
右のGame画面からも視点が線に近づくにつれ細くなっていることが確認できると思います.
まとめ
完全にジャギーを消すにはさらに色々と改善しなければならない点はあると思いますが, とりあえず簡単な動きだけ実装しました
今後何かいい方法が思いついたらまた投稿したいと思います!
今回, むろ小径さんにはジャギーの改善以外にも色々お世話になりました
本当にありがとうございます...!
下記参考
hsva-unity/HSVRangeShader.shader at master · greggman/hsva-unity · GitHub
[備忘録]Processingで簡単なテクスチャを作ってみた
Hello Autumn !! (挨拶)
少しずつ日没も早まり秋っぽくなってきましたね!
今日も変わらず研究室ではUnityでプログラムを書いてました (´・ω・)
そのなかで, 今日はUI用のテクスチャを自作する必要があったのですが, Photo ShopやGimpなどは少し扱ったことがある程度で個人的に少し抵抗があるんですよね...
「何か他に慣れ親しんだもので簡単に済ませられないかな...」 と考えたところ
「せや、Processingで作ろう!」
という考えに行きついたため, その方法を備忘録的にまとめておきたいと思います!
ということで下記が簡単なコードです
PGraphics img; int density = 300; int r = 200; void setup() { size(512, 512); background(0); img = createGraphics(width, height); img.beginDraw(); //テクスチャへの描画開始 img.translate(width/2, height/2); img.strokeWeight(8); //ふわふわ具合 for (int i=0; i<density; i++) { float angle = 360*i/density; for (int j=0; j<r; j++) { img.stroke(255, map(j, 0, r, 255, 0)); img.point(cos(angle)*j, sin(angle)*j); } } img.endDraw(); //テクスチャへの描画終了 image(img, 0, 0); //画像をアプレットに表示 img.save("texture.png"); //テクスチャを保存 }
PGraphics型のimgが実際に作成するテクスチャで, createGraphics関数により初期化されています
テクスチャに描画処理を行うにはPGraphics型のインスタンス名.beginDraw()と.endDraw()の間に処理を記述していきます
また, テクスチャに書き込む際の描画処理は「PGraphics型のインスタンス名.関数名()」にする必要があります
例: ellipse(50,50,50,50) → img.ellipse(50,50,50,50)
そして最後に, 「インスタンス名.save("ファイル名"); 」でテクスチャを画像として保存します
これでできたテクスチャがこれです. (見やすいように背景を黒くして置いてあるが実際は透明)
Unityに入れてみるとこんな感じ
今回はあえて簡単な描画にしましたがもっとProcessingっぽいものを書き出すと楽しいと思います!
まぁ、特別こだわりがない限りシェーダーやPhoto Shopでやった方がいいと思いますけど (;´・ω・)
情報科学生のクリエイティブコーディング入門 part.3
更新が遅くなってしまいました... 汗
今回はProcessingでデジタル時計を作っていきたいと思います.
ソースコードは下記のとおりです.
int num = 7; //花びらの枚数 PFont font; //フォント void setup() { size(500, 500); background(0, 10, 20); strokeWeight(5); rectMode(CENTER); colorMode(HSB,360,100,100); font = loadFont("DSEG7ClassicMini-Regular-48.vlw"); //フォントファイル textAlign(CENTER); //中央揃え textFont(font,48); //文字サイズの設定 } void draw() { pushMatrix(); translate(width/2, height/2); noStroke(); fill(209,99,7,10); //RGB(0,10,20) rect(0,0,width-1,height-1); sinFlower(); popMatrix(); display(); } //sin波模様 void sinFlower(){ rotate(frameCount*0.01); float r = sin(frameCount*0.05)*width/2; //sin波 stroke(map(abs(r),0,width/2,0,360),99,99); //中心からの距離で色を決定 for (int i=0; i<num; i++) { float angle = radians(360*i/num); point(cos(angle)*r, sin(angle)*r); } } //時刻の表示 void display(){ String hour = String.format("%02d",hour()); String minute = String.format("%02d",minute()); String second = String.format("%02d",second()); String ms = String.format("%02d",20+millis()/6000); fill(209,99,7); noStroke(); rect(width/2,height/2,255,60); fill(0,0,99,50); text("88:88:88",width/2,height/2+25); fill((map((frameCount*0.7)%359,0,width/2,0,359)),99,99); text(hour+":"+minute+":"+second,width/2,height/2+25); }
フォントの設定
Processingでは hour(), minute(), second() 関数を利用することで簡単に現在時刻を取得することができます.
今回, 時刻を表示するフォントはこちらを利用させていただきました.
www.keshikan.net
このフォントは数字が0~9まで同じ大きさで作られているため, サイズによるずれが生じない点がとてもよかったです.
ファイルをダウンロード・解凍したら, そのなかの "DSEG7ClassicMini-Regular.ttf" ファイルをインストールしましょう.
windowsでのインストール方法は下記を参考にします.
インストールができたらProcessingを起動します.
ツール>フォントの作成を開き, インストールしたフォントを探します.
今回はサイズを48にしておき "OK"を押下します.
すると, 現在のスケッチのフォルダ内に"data"というフォルダができ, その中にフォントのvlmファイルが生成されます.
追記: インストールしたフォントが見つからない場合は, フォントをインストールする際に, "すべてのユーザーに対してインストール"を選択することで解決することがあります.
(僕はそうでした)
下記のように実行するとインストールしたフォントが使用できてることが確認できます.
PFont font; //フォント void setup() { size(500, 200); background(0); strokeWeight(5); font = loadFont("DSEG7ClassicMini-Regular-48.vlw"); //フォントファイル textAlign(CENTER); //中央揃え textFont(font, 48); //文字サイズの設定 text("hello world", width/2, height/2); }
実行結果
模様
今回はsin波を描画し, rotate()で回転させることで模様を描画しています.
また, 下記のコードを追加で記述することで簡単に模様を変えることができるので色々なバリエーションが楽しめます.
//三角波模様 void triangleFlower(){ rotate(frameCount*0.01); float r = triangleWave(90,frameCount*0.5)*width; stroke(map(abs(r),0,width/2,0,360),99,99); //中心からの距離で色を決定 for (int i=0; i<num; i++) { float angle = radians(360*i/num); point(cos(angle)*r, sin(angle)*r); } } //三角波 float triangleWave(float wl, float t) { return abs((t+wl/2)%wl/wl-0.5); }
模様を変えるにはdraw()内のsinFlower()をtriangleFlower()に書き換える必要があります.
他にもパーリンノイズを用いたり, frameCountに乗算する値を変えるだけで簡単に模様が変わるので, ぜひ好みに遊んでみてもらえればと思います!
最近はTwitterで#つぶやきProcessingというタグでコードをツイートすることが流行っているみたいです.
基本的にコードは1ツイートで収まるようにするので, なかなか文字制限が難しいところではありますがすごく面白いので積極的に参加していきたいですね!
情報科学生のクリエイティブコーディング入門 part.2
今回はHSBカラーモデルを用いたプログラムの例を挙げていきたいと思います.
そもそも, HSBカラーとは何なのかは以下のページをみて頂くのが良いかと思います.
このブログのヘッダー画像もHSBカラーモデルによって描かれています.
そしてソースコードは以下の通りです.
int pixel = 5; //背景の粗さ int num = 80; //円の数 Circle[] list = new Circle[num]; void setup() { size(720, 300); noStroke(); colorMode(HSB, 360, 100, 100, 100); //カラーモードをHSBに変更 for (int i=0; i<list.length; i++) list[i] = new Circle(); /*背景*/ for (int i=0; i<width; i+=pixel) { for (int j=0; j<height; j+=pixel) { fill(map(i, 0, width, 0, 360), 99, map(j, 0, height, 0, 40)); //座標の位置から色を決定する rect(i, j, pixel, pixel); } } blendMode(ADD); for (Circle c : list) c.draw(); } class Circle { float x, y; //平面座標 int size; //円の大きさ(直径) Circle() { x = random(width); //0~720(画面の横幅)内でランダムに決定する y = random(height); //0~300(画面の縦幅)内でランダムに決定する size = (int)random(20, 50); } //円の描画処理 void draw() { fill(map(x, 0, width, 0, 360), 99, 99, 40); //x座標の位置から自身の色を決定する ellipse(x, y, size, size); } }
colorMode
ProcessingではデフォルトでカラーモードをRGBに設定しています.
ProcessingでHSBカラーモデルを使用するにはcolorMode関数を用います.
第1引数に HSB, 第2引数に 色相(H)の範囲, 第3引数に彩度(S)の範囲, 第4引数に明度(B)の範囲, 第5引数にアルファ値 (透明度)の範囲を指定します.
今回は
colorMode(HSB, 360, 100, 100, 100);
とあるので, 0~360度にかけて色が一周するようにHSBを設定しています.
HSBを適用すると以降, fill()やstroke(), background()で色を指定する際は (H, S, B) もしくは (H, S ,B ,A) と引数を与えます.
サンプルとして以下のプログラムを実行してみると色が時間(フレーム) 経過と共に変化していくのが確認できると思います.
void setup() { size(300, 300); colorMode(HSB, 360, 100, 100, 100); //カラーモードをRGBからHSBに変更 background(209, 99, 7); //RGB( 0, 10, 20) noStroke(); } void draw() { fill(frameCount%360, 99, 99); //色彩(H)が0~360の範囲で繰り返す ellipse(width/2, height/2, 100, 100); }
実行結果
また, 彩度(S)を色を下げることで白っぽく, 明度(B)を下げることで黒っぽく色が変化するのもHSBの特徴です.
map
map関数とは map(a, b, c, d, e)のとき, 値aを b~cの範囲から d~eの範囲に変えることができる関数です.
(『Processing クイックリファレンス 算術関数』引用)
今回は
/*背景*/ for (int i=0; i<width; i+=pixel) { for (int j=0; j<height; j+=pixel) { fill(map(i, 0, width, 0, 360), 99, map(j, 0, height, 0, 40)); //座標の位置から色を決定する rect(i, j, pixel, pixel); } }
とあるように, 画面いっぱいに大きさ pixel の四角を並べた上で, それぞれ x方向に進むほど0~360の範囲で色相を決定し, y方向に進むほど0~40の範囲で明度を決定しています.
また, Circleクラスでも同様に描画する円の色相をそれぞれのx座標から決定しています. (明度は99で固定)
blendMode
ProcessingではblendModeにより, 色が重なったときの表示について指定しています.
デフォルトではBLENDと設定されており, 色が重なったとき後に描画される色で上書きされるようになっています. (アルファ値が設定されていない場合)
今回は
blendMode(ADD);
と設定しており, 色が重なった部分が白っぽくなります.
詳しくは以下のページを見てもらうのが分かりやすい思います.
aa-deb.hatenablog.com
まとめ
- HSBカラーモデルを扱うことでカラフルな色の表現がしやすくなる
- map関数によりある範囲における値を任意の範囲内で置き換えることができる
- blendModeはいいぞ
大したプログラムではない割に文章で説明しようとすると分かりづらくなってしまいました... 汗
分かりづらいところがあったら気軽にコメントください (;'∀')
情報科学生のクリエイティブコーディング入門 part.1
夏休みになっても別段遊びに行くことがない休暇を送っております... 泣
今回ご紹介するのは僕が初めてクリエイティブコーディングを意識して書いたプログラムです.
そして以下がソースコードです.
//Main int num = 200; //星の数 Star[] stars = new Star[num]; void setup() { size(500, 500); background(0, 10, 20); //背景色 noStroke(); //インスタンスの生成 for (int i=0; i<stars.length; i++) stars[i] = new Star(); } void draw() { //軌跡を残す fill(0, 10, 20, 20); rect(0, 0, width, height); translate(width/2, height/2); //原点の変更 rotate(frameCount*0.01); //原点から回転 for (Star star : stars) star.draw(); } //Starクラス class Star { float x, y, size; //x座標, y座標, 大きさ int col; //星の色 Star() { x = random(-width/2, width/2); y = random(-height/2, height/2); size = random(1, 3); col = color(0, random(30, 255), 255); //水色~青の範囲からランダムに決定 } //描画処理 void draw() { fill(col); ellipse(x, y, size, size); } }
Starクラスの説明
class Star { float x, y, size; //x座標, y座標, 大きさ int col; //星の色 Star() { x = random(-width/2, width/2); y = random(-height/2, height/2); size = random(1, 3); col = color(0, random(30, 255), 255); //水色~青の範囲からランダムに決定 } //描画処理 void draw() { fill(col); ellipse(x, y, size, size); } }
Starクラスはそれぞれの座標と大きさ, 色を属性として持っています.
コンストラクタ内でそれぞれの属性をランダムに決定しています.
(x座標, y座標の範囲が上記のようになる利用は後ほど説明します)
Mainプログラム
frameCount
frameCountとはプログラム実行時から実行したフレームの数を示します
void draw(){
println(frameCount);
}
Processingではデフォルトでフレームレートを60に設定してあるので, 上記のように実行することで 1, 2, 3... と一秒間に60回カウントします.
translate()
通常Processingで原点は実行画面左上を示しますが, translate()を使用することで引数として与えたx座標, y座標の位置を原点して設定することができます.
今回は(width/2, height/2)を指定しているので実行画面の中心座標を原点として設定しています.
よって以降, プログラムでは座標として原点 (0,0)を指定すると実行画面の中心座標を示すようになります.
そのため, コンストラクタで各インスタンス の座標を指定する際はx座標を(-width/2 ~ width/2), y座標を(-height/2 ~ height/2)の範囲で与えることで画面にまんべんなく星を描画することができます.
rotate()
また, rotate()では原点を中心に引数として与えた角度 (弧度法) 分, 実行画面を回転させることができます.
個人的なイメージとしては画用紙の原点にピンを刺し, それを中心に画用紙を回転させる感じです.
今回は実行画面の中心座標を原点とし, frameCount*0.01を引数として与えています. (frameCountに0.01を乗算することで回転する早さをゆっくりにしている)
軌跡の残し方
Mainプログラム内のdraw()では
fill( 0, 10, 20, 20); rect( 0, 0, width, height);
との記述があります.
fill()内の4番目の引数は色のアルファ値 (透明度)を示しており, 255で不透明, 0で見えなくなります.
今回は4番目の引数に20を指定しているのでとても色の薄い背景色で四角を画面いっぱいに描画しています.
これにより, 毎フレーム透明な背景色のフィルムが重なるように古い描画結果がゆっくりと塗りつぶされていきます.
おわりに
以上で簡単にですがこのプログラムの説明はおわりになります.
文章で説明するのが苦手で分かりづらい点もあると思いますが, それぞれの記述がどのような役割を持つのかはその行をコメントアウトしながら確認してもらうと良いかと思います.
来週は夏コミですね!
まだ行くかは検討中ですが, 色んなクリエイターの方々集まる場なので一度行ってみたいです! \\└(‘ω’)┘////
情報科学生のクリエイティブコーディング入門 part.0
Hello Summer Vacation ! (挨拶)
前期の講義も終わり何となく夏休みの実感が出てきました (;´・ω・)
僕は毎年, 学科内のSAなどで後輩たちのプログラミングの講義に携わらせて頂いてるのですが, それももう3年目になります... (遠い目)
僕の通う大学では1年のうちはProcessingというプログラミング言語を通してオブジェクト指向や簡単なアルゴリズムなどを学びはじめます.
Processingは簡単に言うと描画処理に長けた言語なのですが, JavaやPythonなどと比べて構文も簡単で環境構築も容易な点が初学者には良いみたいですね!
実際に他の大学でも同じようにProcessingから入門することがあるみたいです.
しかし, 大学の講義内ではProcessingのメディア・アートとしての側面までは説明されないので僕自身, クリエイティブコーディングというものを知ったのは2年生の秋頃でした... (講義内では某猫型ロボットを描画するくらい)
そこで, 主に学内でメディア・アートに興味を持ってくれた後輩が見てくれることを期待して (紹介するとは言ってない) , 次回から過去に僕が作ったプログラムの紹介とその仕組みについて解説したいと思います.
また, 他にも僕と同じようにProcessingで絵を描いたことがなかった方が, この記事を通してクリエイティブコーディングに興味を持ってくれたら嬉しいなと思います!
ということで, 明日から頑張ります...!