GenerativeArtNFTで使ったアート画像生成ロジックをメモします。
はじめに
こんにちは、@bioerrorlogです。
先日、@hoosan16さん@ClankPanさんと参加したDfinityのハッカソンの一環として、Generative ArtのNFTを作成しました。
※ハッカソンで提出したメインプロダクト - Flippyがデモ用途で利用するNFT実装です。
本記事では、このGenerativeArtNFTのアート画像生成ロジックをまとめておきます。
Generative ArtでNFTアート画像生成
やりたいこと
TokenIdをinputとして無限で多様なGenerative Artを生成する、というのがやりたいことです。
アプローチとしては、TokenIdのhash値からパラメータを抽出し、Generative Artを生成するようにしました。 Generative Artの生成にはp5.jsを使っています。
アート模様はパーリンノイズをベースにして、カラーもhash値に応じて変わるようにする、というのが当初のアイデアです。
元バージョン: Reactコンポーネントとしてのプロトタイプ
もともとはフロントサイドで画像生成する想定だったので、Reactコンポーネントの形でプロトタイプを実装しました。
import React from 'react'; import p5 from 'p5'; import md5 from 'md5'; const sketch = (p: p5) => { const TokenId = 1 // pseudo TokenId const hashedNftSeed = md5(String(TokenId)) console.log(hashedNftSeed) const getSlicedNum = (x: number, y: number) : number => { return parseInt(hashedNftSeed.slice(x, y), 16) } const lineLengthSeed = getSlicedNum(0, 2) + 1; // 1~257 const strokeColorV1 = getSlicedNum(2, 4); // 0~255 const strokeColorV2 = getSlicedNum(4, 6); // 0~255 const strokeColorV3 = getSlicedNum(6, 8); // 0~255 const noiseScale = (getSlicedNum(8, 11) % 1000) / 10000; //0.0001~0.1000 const rotateSeed = getSlicedNum(11, 13); // 0~255 const backgroundColorV1 = getSlicedNum(15, 17); // 0~255 const backgroundColorV2 = getSlicedNum(17, 19); // 0~255 const backgroundColorV3 = getSlicedNum(19, 21); // 0~255 const drawIterateVal = getSlicedNum(21, 26) + 100000; // 100000~1000000 const randomSeedVal = getSlicedNum(26, 29); // 0~4096 const noiseSeedVal = getSlicedNum(29, 32); // 0~4096 p.setup = () => { p.createCanvas(500, 500).parent('p5sketch'); p.background(backgroundColorV1, backgroundColorV2, backgroundColorV3, 255); p.randomSeed(randomSeedVal); p.noiseSeed(noiseSeedVal); for (let i = 0; i < drawIterateVal; i++){ const x = p.random(p.width); const y = p.random(p.height); const noiseFactor = p.noise(x*noiseScale, y*noiseScale); const lineLength = noiseFactor * lineLengthSeed; p.push(); p.translate(x, y); p.rotate(noiseFactor * p.radians(rotateSeed)); p.stroke(strokeColorV1, strokeColorV2, strokeColorV3, 3); p.strokeWeight(1); p.line(0, 0, lineLength, lineLength); p.pop(); } } } // ref. https://discourse.processing.org/t/instance-mode-creating-two-canvas/14121 new p5(sketch); const DemoNft: React.FC = () => { return ( <div id = "p5sketch"> {/* p5 instance will be created here --> */} </div> ); } export default DemoNft;
※TokenId
がハードコードされているのは、プロトタイプだったからということでご容赦を。。
TokenId
をhash化した上で、アート画像を生成するための各パラメータをhash値のsliceから割り当てる、という方法をとってみました。
発想はシンプルですが、それぞれのTokenId
から全く異なるテイストのアート画像を生成することに成功しています。
出力アート画像例:
最終バージョン: ts-nodeで実行
上記をベースとしつつも、修正した上で最終プロダクトに利用しています。
- Reactコンポーネントではなく、ts-nodeスクリプトとして実行できる形にする
- アート画像生成ロジック/出力結果を軽くする
フロントエンド側で都度アート画像生成をさせるのは処理が重すぎるので、あらかじめ生成したアート画像をCanisterにアップロードするよう設計を変更しました。
それでもまだ表示速度に問題があったので、生成画像サイズを軽くする変更を加えています。
最終的なソースコードはこちら: github.com
おわりに
以上、GenerativeArtNFTで使ったアート画像生成ロジックをメモしました。
久しぶりにGenerative Artなコードを書きましたが、普通のエンジニアリングとはまた違った試行錯誤が求められて、やはり楽しいですね。
調べてみると、最近はまたいろいろなクリエイティブコーディングツールがでてきているようです。 Rustで書けるnannouなんかも面白そう。
Generative Artや人工生命は以前から興味の対象でしたので、また遊びがてら書いていきたいところです。
[関連記事]
参考
GitHub - Japan-DfinityInfoHub/generative-art-nft
GitHub - Japan-DfinityInfoHub/nft-barter
NFTの絵柄を生成するコード · Issue #2 · Japan-DfinityInfoHub/generative-art-nft · GitHub
GitHub - bioerrorlog/p5-in-react: PoC for demo NFT implementation