takumifukasawa’s blog

WebGL, Shader, Unity, UE4

【Unity】ディゾルブシェーダーの境界からパーティクルを出す

f:id:takumifukasawa:20210826112609g:plain

※ 「どういうアプローチで実装したか」を中心としたメモになるので、恐縮ですが具体的な実装はリポジトリを見ていただけると早いかもしれません。

↓ 作ったものをアップしたリポジトリになります。static な mesh と skinned mesh のどちらにも対応しています。

github.com

f:id:takumifukasawa:20210824233111g:plain

f:id:takumifukasawa:20210824233114g:plain

紙に火をつけると、どんどん炎が広がり紙が燃えていき、やがて消えてなくります。

ディゾルブシェーダーの境界にあたる部分は、燃えている場所・焦げている場所にあたると思います。

その境界からなにかしらのパーティクルを出してみたいなと考えていて、今回その仕組みを実装してみました。

f:id:takumifukasawa:20210824233119p:plain

環境

  • unity version: 2020.3.6f1

  • pipeline: URP

ポイント

VFX Graph でパーティクルを放出する位置はメッシュの頂点ではなく、ディゾルブの境界のワールド座標になるようにCompute Shaderで計算をしています。

放出する位置をメッシュの頂点位置にすると、頂点数が少ないときに「放出されている位置はメッシュの頂点」というのが分かりやすくなってしまう場合があります。

今回は「ディゾルブの境界からパーティクルを放出する」ことが目的なので、Compute Shaderで「ディゾルブの境界のワールド座標」を求める形にしました。

流れ

  1. ディゾルブの境界から放出する VFX Graph パーティクルは、Position from Mapを使って放出位置をテクスチャから参照する

  2. Compute Shader で、ランダムにサンプルしたポリゴン内における、ランダムな位置のワールド座標・「ディゾルブの境界率(後述)」を計算

  3. ワールド座標・「ディゾルブの境界率」をテクスチャに格納

  4. テクスチャを VFX Graph に渡す

  5. 「ディゾルブの境界率」を透明度として用い、境界から放出されるパーティクルは見えるように、境界外から放出されるパーティクルは見えないように

Compute Shader の中身はこちらになります。

github.com

デモでは法線情報もテクスチャに格納して渡しています。

テクスチャのデータ

もしディゾルブの境界になっている位置の情報だけをすべてテクスチャに格納していく形だと、格納されるデータは必ずディゾルブの境界情報になっている必要があるので、再帰的な処理が必須です。

そこで、テクスチャに格納するデータはメッシュのランダムな位置におけるパーティクル用の情報としてしまいます。デモでは「ワールド座標」「法線」「ディゾルブの境界率」が格納された3つのテクスチャを作成しています。

こうすることでテクスチャのデータのすべてがディゾルブの境界ではなくなるデメリットはありますが、再帰的な計算をしなくてよくなるという計算負荷的なメリットがあります。

例えば各種テクスチャのサイズが64x64でもピクセル数が4096あるので、ディゾルブの境界情報が必ず埋まるようにはなっていなくとも、ある程度の数のディゾルブの境界情報が埋まる状態を保つことができれば十分なはずと考えました。

実際、こちらのキャプチャは各種テクスチャのサイズは32x32になっていて、情報の少なさ(データの足りなさ)は感じられませんでした。

f:id:takumifukasawa:20210824233111g:plain

ディゾルブの境界率

では、これらのテクスチャを使ってどうやって境界からパーティクルを放出するように見せるのか。

まず、ディゾルブの境界率は一言でいうと「境界かどうか」です。今回の実装では「境界かどうか」ではなく「どれぐらい境界に近いか」を計算していて実際格納されるデータは0~1の間なので「境界率」という言い方をしています。

境界率がなぜ必要かというと、境界からのみパーティクルを放出するように見せるためです。

実は、パーティクルはディゾルブの境界以外からも放出されていて、ディゾルブの境界率をテクスチャに格納してVFX Graphに渡し、境界率に応じて透明度を調整しています。

例えば下の画像のように、メッシュは消えているにも関わらず見えていないパーティクルがたくさん放出されています。

f:id:takumifukasawa:20210825200505p:plain

境界率によって透明度を調整することで、境界から放出されているパーティクルは見えて、境界以外から放出されているパーティクルは見えないというのを実現しています。

しかし、この方法には欠点があります。画像のように見えないパーティクルも放出されてしまっているのが無駄な点です。

テクスチャとディゾルブの度合いの関係

前述のように、テクスチャに格納するデータはメッシュのランダムな位置におけるパーティクル用の情報になります。

どういうデータが格納されるかを可視化してみました。左から「ワールド座標」「法線」「ディゾルブの境界率」のテクスチャになります。

↓ 全体のディゾルブ度合い: 見えている状態 f:id:takumifukasawa:20210825190337p:plain

↓ 全体のディゾルブ度合い: 半分ぐらい消えている状態 f:id:takumifukasawa:20210825190330p:plain

↓ 全体のディゾルブ度合い: 完全に消えている状態 f:id:takumifukasawa:20210825190334p:plain

このように、メッシュが消えているかどうかに関わらずワールド座標と法線は必ずテクスチャに格納されています。


最後に改めてリポジトリのリンクを貼っておきたいと思います。

github.com

【Shell】Python2, Python3 のどちらのバージョンでも HTTP Server を立ち上げる Shell Script

簡易サーバーを立ち上げる際は python の HTTP Server を使うことが多いのですが、python2系とpython3系で微妙にコマンドが違います。

それぞれのバージョンで HTTP Server 用のモジュールが異なるためです。

具体的には、python3系だと python -m http.server 8000、python2系だと python -m SimpleHTTPServer 8000 で立ち上げることになります。

pyenv などの関係で立ち上げたいディレクトリごとに python のバージョンが違うと上記のコマンドも変える必要があり、少しややこしいです。

そこで、pythonのバージョン(2系か3系)に関わらず HTTP Server を立ち上げるスクリプトをつくりました。Mac限定です。

これを起動したいルートに配置し、ダブルクリックで起動します。すると http://localhost:8000 でアクセスできるようになります。

メジャーバージョンを取得して、2系か3系かでコマンドを出し分けています。

【Javascript】素のjsでシングルトンクラスを実装する(ver. 2021/7)

jsでシングルトン的なことを実現する場合、module や import / export を使用できる環境下では例えばこのように export でインスタンスを渡すことでシングルトン的な振る舞いをさせることができます。

module.exports = new Klass();

export default new Klass();

最近、ブラウザ上で実行する素のjsでシングルトン的な機能が欲しい場面があり、クラスを使ったいわゆるシングルトンを作ってみようと思いました。

新しいAPIを試してみたいと思いMDNを漁っていたところ、private class fields が主要ブラウザ(chrome,firefox,safari)の各最新バージョンの「ほとんど」で実装されているみたいだったので使ってみることにしました。

「ほとんど」と書いたのは、 firefoxで private class fields を使うことができるのは v90 からで 2021/7/7時点のfirefox最新版v89系だとまだ使用出来ないためです。ただ、数週間のうちにv90系が出るらしいのと、今回は仕事で使う用途ではないので実験的に組み込んでみる判断にしました。

developer.mozilla.org

class Singleton {
  static #instance;
  num;

  static get instance() {
    if(!Singleton.#instance) {
      throw "no instanced";
    }
    return Singleton.#instance;
  }

  constructor(num = 0) {
    if(Singleton.#instance) {
      return Singleton.#instance;
    }
    Singleton.#instance = this;
    this.num = num;
  }

  up() {
    this.num++;
  }
}

...

const s = new Singleton();
s.up();
s.up();
console.log(s.num); // 2

...

const ss = Singleton.instance;
// ↑ const ss = new Singleton(); でも同じインスタンスが返ってくる

ss.up();
ss.up();
console.log(ss.num); // 4

ちなみに、private class field が # を使って表す理由はこの記事がとてもわかりやすかったです。「# しか使うことが出来なかった」そうです。

blog.jxck.io

【Javascript】ビット演算で2の累乗判定する関数

2の累乗判定をするときに、ビット演算だとかなりシンプルになることを知ったのでそのメモになります。

こちらがその関数です。

const isPowerOfTwo = x => (x & (x - 1)) === 0

原理は以下のようなものです。

数値が2の累乗の場合は必ず最上位ビットのみが1になります。 その数値に1を引くとすべてのビットが反転するので、元の数値-1をした数値論理積 & を使うと 0 になる性質を利用しています。

2 ... (10 & 01) === 0 => 00 === 0 => true
3 ... (11 & 10) === 0 => 10 === 0 => false
4 ... (100 & 011) === 0 => 000 === 0 => true
5 ... (101 & 100) === 0 => 100 === 0 => false
8 ... (1000 & 0111) === 0 => 0000 === 0 => true

【vscode】MacのUnity開発でC#の補完が動かなくなったので対処

MacvscodeでUnity C#の補完が効かなくなったので原因を探りました。

起こっていたこと

環境は、 mac OSbig sur, unity は 2019.4.25f です。

vscode の Omnisharp Log を見てみると、大きく2つのエラーが発生していました。

  • Could not load file or assembly 'Microsoft.CodeAnalysis.Workspaces ...

  • OmniSharp server load timed out. Use the 'omnisharp.projectLoadTimeout' setting to override the default delay (one minute).

どうやら読み込めていないモジュールがあり、読み込みのタイムアウトが起こっているようです。

解決

まず2つ目のエラーに着目して、 omnisharp.projectLoadTimeout の時間をデフォルトの60秒よりも長い設定にしても変わらずでした。

最終的に、vscode の拡張の C# for Visual Studio Code のバージョンを最新の 1.23.12 から 1.23.11 に戻すことで直りました。

f:id:takumifukasawa:20210618235857p:plain

必要な assembly がなかった関係でロードのタイムアウトが起こっていたようです。

【Unity】Timelineで枠外に隠れたカーブの全体を表示するショートカット

現象

Timeline の graph view で key の位置やイージングの調整をしたいとき、下の画像のようにカーブ全体が表示されないという現象が度々発生して困ることが多くありました。

f:id:takumifukasawa:20210309222434p:plain

[Edit] -> [Shortcuts...] を覗いてみても、カーブ全体を表示するようなショートカットはありませんでした。

f:id:takumifukasawa:20210309222458p:plain

解決

F キーを押すことでカーブの全体表示がされるようになります。

FキーはScene View などで特定のオブジェクトにフォーカスする用途で頻繁に使いますが、Timeline のカーブにも同様の処理がされるようです。

f:id:takumifukasawa:20210309222509p:plain

Timeline に限らず、VFX Graph などでも特定のノード・ブロックにフォーカスしたいときはFキーを使うので、どのTabかを問わず「何かが隠れている or 小さく表示されている」->「全体表示する or フォーカスする」という挙動をさせたいときにはひとまずFキーを押してみるのは有用かもしれません。

【Node.js】画像群を一括圧縮するスクリプト

画像圧縮をする際、jpegminiやimagealphaなどのアプリを使ったりPCにインストールしたcli経由で行うことが多かったのですが、これらの方法だと画像圧縮の方法がプロジェクトの各人に依存してしまうという問題があります。 そこで、画像圧縮ツールもnpmで管理する形だと人に依存しない & スクリプトを使い回すことができるなと思ったので、nodeスクリプトを作成してみました。

ディレクトリ指定をするとそのディレクトリ以下を再帰的に、単一ファイル指定だとそのファイルのみを圧縮するようにしました。pngとjpgに対応しています。

圧縮には imagemin を用いました。ファイルにもよりますが、100近いファイルを圧縮したところ全体で3分の1ぐらいまでに減りました。

github.com