さやちゃんぐbotブログ

シェーダーで遊んだりするブログです。

p5.jsでレイマーチングを描きましょうー。

やあやあやあやあさやちゃんぐbotだよ。

日本全国のjs書きのみなさま、お待たせしました!

 

今日はp5.jsを使ってレイマーチングをやってみましょう!!

f:id:sayachang_bot:20191027193746p:plain

 

今回実装したコード全文は以下のリンクからご参照ください。

https://scrapbox.io/api/code/sayachang/p5.js/p5RayMarching.js

p5.jsはブラウザ開いてコードと数式を書くとお絵描きできちゃう素敵サイトです。

使い始めるには、これを開いてさくっとユーザ登録しちゃいましょう。

p5js.org

 

さて、はじめて触るツールではまずHello Worldをやるものですが、数式で絵を出すならレイマーチングをやることがHello Worldになりますね!!

今の時代、文字を表示することだけがHello Worldではないと言えるでしょう!

 

ということでやっていきましょう。

setup関数でキャンパスサイズを指定するようなので、適当なかんじの数値を書いてみます。

createCanvas(640, 400);

あとはdraw関数にどうレイマーチングを実装するのか?ということだけを考えればいいわけです。

 

フラグメントシェーダーでレイマーチングを実装する場合は、2次元座標をベースにカメラ、マーチングループ、法線算出をやればいいのでこのあたりの考え方は同じようにやってみます。

違うのは座標pを与えればあとはよろしく並列に全ピクセルを処理してくれるわけではないので、縦横で入れ子のループを書いていけば良いハズです。

for(x=0;x<640;++x){
for(y=0;y<400;++y){
let d=de(x,y,z);
if(d>0.001) {
stroke(color(0,0,0));
} else {
stroke(setCol(x,y,z,lx,ly,lz));
}
point(x,y);
}
}

 

どう色を出すの?というあたり、リファレンスを見ればそれっぽい関数が見つかります。

https://p5js.org/reference/

strokeで色を決めて、pointで1ピクセル塗れるのでこれをループで呼びます。

deで形状をモデリングし、レイが衝突した場合にsetColでライティングと色を決めるという構造にします。このあたりは一般的なレイマーチングと同様。

 

次にdeを書き進めてみます。

function de(x,y,z){
let p = createVector(x,y,z);
let sphere = createVector(400,100,50);
let d = p5.Vector.dist(sphere,p);
return d-100;
}

座標やベクトルは浮動小数点3つで扱えば良いですが、p5ではcreateVector関数があるのでこれを利用します。

リファレンスによるとベクトル計算の便利メソッドがいろいろあるようなので、これを使っていけば動きそうです。

球を定義してあげて、キャンパスの座標との距離を求めます。サイズを今回は100として、これを引き算した値を返してあげればレイマーチングとしての作りになります。

 

最後に法線算出とライティングをまとめて書いてしまいましょう。

function normD(x,y,z){
const e=0.000001;
let m=de(x,y,z);
let mx=de(x-e,y,z);
let my=de(x,y-e,z);
let mz=de(x,y,z-e);
let n=[e+m-mx,e+m-my,e+m-mz];
return n;
}

function setCol(x,y,z,lx,ly,lz){
let n=normD(x,y,z);
let N=createVector(n[0],n[1],n[2]);
N.normalize();
let L=createVector(lx,ly,lz);
L.normalize();
let dt=p5.Vector.dot(N,L);
if(dt<0)dt=0;
return color(240*dt,230*dt,210*dt);
}

 

法線算出はちょっぴり座標をずらして呼び出したもので引き算をするという、ありふれた手法です。軽くしたいので6回呼び出しではなく4回呼び出しで済むやり方を使っていますが、よくわからんという場合は詳細をiq神のサイトなどを探してみてください。

法線を求めたら、あとはライトとの内積を取るだけです。ベクトルの正規化はnormalize関数、ドットプロダクトはdot関数が使えます。

dotの0.0から1.0の範囲だけ拾い上げて色を決定すれば完成です。

 

いかがでしたでしょうか。

ループをぶん回していてさすがに軽いものではないですが、p5.jsで作る一枚絵でレイマーチングの手法が使えることがわかっただけでも収穫かもしれません。

 

js書きのみなさまも、レイマーチングを始めてみると楽しいのではないでしょうか。

ではでは。