見出し画像

fxhash Tips (p5.js編)

fxhashはTezosブロックチェーンにおいて、ジェネラティブアートをNFTとして扱うプラットフォームです。

近頃は日本からfxhashへ参入する方も増えてきています。これからfxhashでp5.jsを用いたジェネラティブアートを出品する方の役に立てばと思い、出品をしてきた中で得た知見を書き残しておきます。

ここに記載した内容はあくまでも一例です。これが正解というものではありません。あくまでも困った時の補助の役割として使っていただければと思います。


fxhashのはじめ方についてはkusakariさんの記事で出品までの手順を丁寧に説明されているので、とても参考になります。


乱数の固定

fxhashでは各トークンで異なる描写をさせ、同トークンでは毎回同じ描画をさせる必要があります。そのため、乱数を各トークンで変えつつ、同トークンで同じ乱数となるように固定させるます。

fxrand()を用いる

乱数の固定にfxhashで用意しているfxrand()を用いる方法があります。fxrand()は各トークンのhash値を元にして乱数を生成します。各トークンで異なる乱数、同トークンでは同一乱数が実現されます。

p5.jsでは乱数生成にrandom()を用いますが、それを全てfxrand()に置き換えます。

fxrand()はrandom()と同様に0〜1のfloat値を返します。下の2つのコードは同等の機能になります。

let num0 = random();
let num1 = random(10);
let num2 = int(random(50, 100));
let num0 = fxhash();
let num1 = fxhash() * 10;
let num2 = int(50 + fxhash() * 50);

それぞれ
 num0は0〜1のランダムな小数
 num1は0〜50のランダムな小数
 num2は50〜100のランダムな整数
を生成します。

配列から一つランダムに選ぶ様な場合は次の様になります。

let num3 = random([0.1, 5, 1000]);
let set_num = [0.1, 5, 1000];
let num3 = set_num[floor(fxrand() * set_num.length)];

fxhash()で置き換えることは可能ですが、これは少々手間が増えてしまいます。この方法でも良いですが、p5.jsのコードを簡素化できるメリットを失ってしまうのは気になります。

p5.jsのコードをなるべく、そのまま扱うのが下の方法になります。

randomSeed()を用いる

p5.jsで乱数を固定する方法としてrandomSeed()があります。これをfxhashでの各トークンの動作を考慮して乱数を固定します。

randomSeed()のシード値にfxrand()を用います。

let seed = fxrand() * 100000;

function setup(){
  randomSeed(seed);

  let num0 = random();
  ...
}

setup()内でrandomSeed()で乱数を固定すれば、それ以降は普段のp5.jsの記述と変わらずrandom()を用いることができるのはメリットになります。

ただし、seedの生成をfxrand() * 100000としていますが、これは根拠はなく筆者が感覚的に決めています。100000より大きい値、小さい値に設定することもできます。


noise()の固定

乱数と同様にnoise()も各トークンで異なる描写をさせ、同トークンでは毎回同じ描画をさせる必要があります。

p5.jsでnoise()を固定する方法としてnoiseSeed()があります。これをfxhashでの各トークンの動作を考慮して乱数を固定します。

noiseSeed()のシード値にfxrand()を用います。

let seed = fxrand() * 100000;

function setup(){
  noiseSeed(seed);

  ...
  let num0 = noise(x, y);
  ...
}

setup()内でnoiseSeed()でnoise()を固定すれば、それ以降は普段のp5.jsの記述をそのまま用いることができます。

randomSeed()を用いる時と同様で、seedの生成で100000以外の値で設定することもできます。


キャンバスサイズを可変にする

fxhashではキャンバスの表示サイズは見る側の環境に依存するため、描画するキャンバスサイズは可変にしておくのが良いと思われます。(固定キャンバスサイズがダメというわけではないです。)

p5.jsのwindowWidth, windowHeightを用いてキャンバスサイズを可変にします。

縦横同サイズ

fxhashのプレビューではいわゆる正方形の縦横が同サイズが表示されます。そこに合わせてキャンバスサイズを正方形としておく方法があります。

function setup(){
  minCanvasSize = min(windowWidth, windowHeight);
  createCanvas(minCanvasSize, minCanvasSize);
  ...
}

function draw(){
  ...
}

setup()でwindowWidth, windowHeightの小さい方を一辺の長さとしたキャンバスを生成します。

横長サイズ(縦長サイズ)

横長サイズのキャンバスを生成する方法です。縦長の場合は縦横を読み替えて下さい。

アスペクト比16:9の横長のキャンバスの例です。

function setup(){
  let ratio = 9/16; // 16:9
  let setHeight = windowWidth * ratio;
  if(setHeight < windowHeight){
   createCanvas(windowWidth, setHeight);
  } else{
   let setWidth = windowHeight / ratio;
   createCanvas(setWidth, windowHeight);
  }
  ...
}

function draw(){
  ...
}

ウィンドウサイズがキャンバスのアスペクト比よりも横長か縦長かで挙動を分けています。

cssを使ってキャンバスを中央に寄せておくとfxhashでのプレビューも整います。

body {
   background-color: #d0d0d0;
   margin: 0;
}

canvas {
   position: absolute;
   left: 50%;
   margin-right: -50%;
   top: 50%;
   transform: translate(-50%, -50%);
}

分かりやすくHTML側の背景をグレーにしてます。fxhashで見ると下の様になります。


その他

今後も随時追記していきます。


作例

ランダムな円を描く静止画

ランダムな円を描く静止画の作例です。100個の円をランダムな位置に配置します。

let seed = fxrand() * 1000000;

function setup() {
  initialize();
  noLoop();
}

function draw(){
  background(random(100));

  for(let i=0;i<100;i++){
    fill(rand_color + 60*noise(i), 30, 100);
    ellipse(random(width), random(height), size);
  }
}

function initialize(){
  minCanvasSize = min(windowWidth, windowHeight);
  createCanvas(minCanvasSize, minCanvasSize);
  size = 0.1 * minCanvasSize;

  randomSeed(seed);
  noiseSeed(seed);
  colorMode(HSB);

  rand_color = random(360);
}

function windowResized(){
  initialize();
  redraw();
}

function keyPressed(){
  if( key == "S" || key == "s" ){
    save("save.png");
  }

  if( key == "F" || key == "f" ){
    let fs = fullscreen();
    fullscreen(!fs);
  }
}

fxhash対応を考慮した部分は以下になります。
 トークン毎に円の色や位置、背景色が変わる
 キャンバスサイズをウィンドウサイズの変化に合わせる(ウィンドウサイズ変更時に描画をはじめからやりなおす)
 キー入力で画像保存とフルスクリーンに対応

上記コードの別の書き方。ウィンドウサイズの変化時にsetup()を呼び出すのも一つの方法です。

let seed = fxrand() * 1000000;

function setup() {
  minCanvasSize = min(windowWidth, windowHeight);
  createCanvas(minCanvasSize, minCanvasSize);
  size = 0.1 * minCanvasSize;

  randomSeed(seed);
  noiseSeed(seed);
  colorMode(HSB);

  rand_color = random(360);

  background(random(100));

  for(let i=0;i<100;i++){
    fill(rand_color + 60*noise(i), 30, 100);
    ellipse(random(width), random(height), size);
  }
}

function windowResized(){
  setup();
}

function keyPressed(){
  if( key == "S" || key == "s" ){
    save("save.png");
  }

  if( key == "F" || key == "f" ){
    let fs = fullscreen();
    fullscreen(!fs);
  }
}

mintする際のtipsもまとめているので、こちらも参考にしてみて下さい。


この記事が気に入ったらサポートをしてみませんか?