はじめに
この度、「MFG GPUプログラミングコンテスト 2025」にて、制作した「水彩画フィルター」が 大賞(最優秀賞) を頂きました!
コンテストの結果発表・総評等はこちら。他の方々の作品も素晴らしく,大賞に選んでいただいたのには感謝しかないです。
昨年の「U-22プログラミングコンテスト」でのLLMView(経産大臣賞)に続き、GPUプログラミングという自分の興味のある分野で評価をいただけたことは非常に励みになります。本記事では、受賞作品の技術的な解説や、独自の言語「MFG」に触れてみた感想をまとめます。
MFG GPUコンテストとは?
MFG GPUプログラミングコンテストは、 MFG(Modern Filter Language for GPU) という独自の言語を用いた画像フィルタ開発コンペです。ペイントツール用の言語なこともあり,MFG Studioのデモ用画像にはイラストもいくつかあって,そっち方向の作品を作るのもアリだったなーって今になったら思えてきました。暇があったらそういうのを作ってみようかなって思っています。
MFG言語はGLSLやHLSLといった既存の言語とは若干異なり,あまり直接的にGPUの性能や効率を考えなくても良いのが魅力かなって思います。ただ,自分の作品とかだと,分散を求めるところの計算量をGreedyに求めてしまうと,処理に結構時間がかかってしまうのでそこら辺のオーバーヘッドは気にしながら作りました。
受賞作品:水彩画フィルター
今回私が制作したのは、入力画像を水彩画風に変換する 「水彩画フィルター」 です。
自分は美術館に行くのが大好きなので,それに関連したものを作ってみようと思ったのがきっかけです。絵画展によく行くので絵画は全般的に好きなのですが,特に好きなのは印象派黎明期のモネの作品ですね。少し時代がズレますが,ターナーの作品もめちゃくちゃ好きです。
この時代の作品は,私たちにとっての写真,写実的な風景に,情緒・作者が景色から受け取った感情を載せて描いてるって感じるんですよね。同じような風景画でも,見たときに受け取る 印象 が全く違うのがめちゃくちゃおもしろいです。理系畑の人間にとっては,画像データに 情緒の情報 を乗せるという少し情報学に近い側面もあるのがおもしろいです。
ターナーの空の表現はどんなに穏やかな空模様でもじっとみてると動いているように感じられるほど躍動感があります。さらに荒れた空には畏怖,穏やかな空には安心(その裏をかいて,嵐の前の静けさ)といった感情まで載っていて,どれだけ長くてもみてられるといいますか…。
こういうのは美術館ルポにとっておいて,今回はそういった,情緒を写真に付随することを最終目標としたフィルターを作ってみました。
フィルターを通した写真は以下の感じです。1枚目はMFGStudioに最初から入っているデモ用の写真で,残りはUnsplashから引用したものです。
かなりいい感じに絵画っぽくなってるのではないでしょうか。この小さめの写真で見ているとぼかしただけのようにも見えるのですが,大きめのモニターに映すとより絵画っぽくなります。ちなみに,著作権の関係でこちらには載せられなかったのですが,インドのジャイプールのパンナ・ミーナ・カ・クンドの写真が一番モネっぽくなります。モネといっても晩年の作品の色味を拾っているような感じになりますが。
技術的な特徴
このフィルターの核となるアルゴリズムには、 桑原フィルター を採用しました。
通常の ガウシアンぼかし などは、エッジ(境界線)までぼかしてしまいますが、桑原フィルターは「周囲の領域のうち、最も分散が小さい領域の平均色を採用する」という処理を行います。これにより、 エッジを保持しながら平滑化する ことができ、水彩画や油絵のような質感を生み出せます。ちなみに,先程言った分散の計算量を減らしたというのはここの話です。この処理はかなり回数繰り返すので,少しのオーバーヘッドが処理時間にかなり効いてきてしまうんですよね。
MFG言語での実装
独自のこだわり:物理ベースの水彩シミュレーション
今回の作品では、単なる見た目の模倣ではなく、 「光と顔料の相互作用」 をコードに落とし込むことに注力しました。できるだけ物理的な見え方に近くなるように工夫しました。
1. 知覚的な色空間「Oklab」の採用
デジタルな色補間特有の「濁り」を避けるため、内部処理はすべて Oklab 色空間 で行っています。人間の知覚に近い等歩度な空間で演算することで、水彩絵具が混ざり合う際の鮮やかさを保ったまま、自然な明度変化を表現できます。
2. 物理法則に基づく透過計算(ランベルト・ベールの法則)
水彩の透明感の正体は、紙の白地での反射光が顔料層を透過して目に届くことにあります。この透過を物理的に計算する手法が,本フィルタで用いている、ランベルト・ベールの法則(Lambert-Beer Law) の法則です。こちらを簡略化して実装しました。完全な余談なのですが,こちらのランベルトベールの法則については,大学入試の化学の問題や大学の基礎化学実験で若干出てきた思い出があります。計算自体が簡単な割に意外といろんなことを説明できるのでちょくちょくいろんなところで出てきます。実際にはもう少し複雑なものなのですが,ここでは簡単に,厚みに対して指数関数的に減少するようにしています。
:顔料の吸収係数(コードではOklab明度の反転で近似)
:顔料の厚み(thickness)
この式に基づき、顔料の厚みに応じて光が減衰する様子を計算することで、塗り重ねた部分の深みと、紙の白さが透ける軽やかさを物理的な整合性を持って両立させています。
↓↓実装はこちら↓↓
let thickness = pigment_density * (1.0 + grain_mask * granulation * 5.0)
let absorption = 1.0 - paint_oklab.x # x は Oklabの明度
let transmittance = 2.7182818 ^ (-absorption * thickness)
let diluted_transmittance = mix(transmittance, 1.0, water_amount)
let paint_L = 1.0 * diluted_transmittance
このように計算しています。前述の透過計算に加えて「水による希釈」を再現するため,transmittanceからdiluted_transmittanceへは
diluted_transmittance = (1.0 - w) * transmittance + w *1.0
と薄めることができるようにしています。wはパラメータとして調整できます。
3. 紙のテクスチャとDomain Warpingによる「滲み」
紙の繊維を FBM(Fractal Brownian Motion) ノイズで生成し、その勾配方向に沿って座標を歪ませる Domain Warping を行うことで、「インクが紙の繊維に沿ってじわっと広がる」挙動を再現しています。ここに関しては完全に専門外だったので,紙の繊維を数式的に表現する色々な手法を調べました。その中で実装が比較的に楽で,いい感じになるのがこちらだったというわけです。
- 繊維が粗いエリアではインクが溜まりやすくなる
- 滲みの方向がランダムではなく、紙の構造に従う
といった、水彩っぽさの源泉になっている部分です。
4. 桑原フィルターによる「筆致」の抽出
前述の 桑原フィルター も、水彩らしさを支える重要な要素です。局所領域ごとに平均値と分散を計算し、「最も分散が小さい領域」の色を採用することで、
- エッジを保持しつつ
- 均一な領域は大胆に平滑化し
写真特有のデジタル感を抑え、アーティストが筆を置いたような筆致を生成しています。これは元々知っていたアルゴリズムだったのですが,こんなにもきれいに絵画っぽさがでるとは思っておらず,最初に実装したときはかなりびっくりしました。
紙のマイクロ構造を考慮した粒状感
特にこだわったのが、紙の凹凸によって顔料が溜まる 粒状感 の表現です。
// 紙の高さから「谷」の部分を計算し、顔料の溜まりをシミュレート
let h = get_paper_height(x, y);
let valley = 1.0 - h;
let grain_mask = valley * valley;
let thickness = pigment_density * (1.0 + grain_mask * granulation * 5.0);
厚み は大まかには深さ の2乗に比例するという近似です。少し深堀りすると,こうしたミクロな紙面の上では重力より表面張力・毛細管現象が支配的になっているため,単純な線形よりもこうした近似の方が粒状感を再現しやすいんですね。
紙のマイクロファセット(微細構造)に応じて顔料の「効き方」を変えることで、
- フラットな領域ではなめらかなグラデーション
- 谷側ではざらっとした粒子感
が自然と現れるようなパラメータ設計にしています。