rn.log

備忘録など

【Unity C#】エフェクト用のらせんメッシュを作る

はじめに

お遊びで、エフェクト用のらせんメッシュをUnity C# で作ってみました。
今回の記事では、Unity C# でらせんメッシュを作る方法を解説したいと思います。

f:id:r-ngtm:20210115205521p:plain:w480
Unity C# で動的に作成したメッシュ

ソースコード

らせんメッシュのC#スクリプトは、GitHubリポジトリにて公開しています。SpiralMeshGenerator.csというスクリプトが当該のスクリプトになります。

github.com

Unity 上でメッシュを作るメリット

エフェクト用のメッシュを用意する時、通常はHoudiniやBlenderといった外部のDCCツールを使ってエフェクト用の3Dモデルを作成することになります。
しかし、DCCツールとUnityを行ったり来たりするのはコストがかかります。

Unity上でメッシュを作れる場合、Unity上で結果を見ながら形状を調整できるため、低コストでエフェクト用のメッシュを作ることが可能となります。



作り方解説

STEP1 : らせんのカーブを作る

らせんを作る考え方

らせん上の点の方位角を  \theta と置いたとき、らせん上の点の座標Pは (cos k \theta, \theta, sin k \theta) のような形で表されます。

f:id:r-ngtm:20210115185818p:plain:w480
らせん
/// <summary>
/// カーブに沿ったポイントを作成
/// </summary>
private static void ComputeCurvePoints(SpiralMeshGenerator generator, out Vector3[] points)
{
    float curveTimeMin = generator.radiusCurve[0].time;
    float curveTimeMax = generator.radiusCurve[generator.radiusCurve.length - 1].time;

    points = new Vector3[generator.curveDivsV]; // らせん上のポイントの座標を保存するための配列
    for (int i = 0; i < generator.curveDivsV; i++)
    {
        float t = (float) i / (generator.curveDivsV - 1);
        float radian = t * 2f * Mathf.PI * generator.loops;
        float radius = generator.radiusCurve.Evaluate(Remap(t, 0f, 1f, curveTimeMin, curveTimeMax));  // らせんの半径はAnimationCurveで管理している
        float x = Mathf.Cos(radian) * radius;
        float y = generator.height * t;
        float z = Mathf.Sin(radian) * radius;
        points[i] = new Vector3(x, y, z);
    }
}

半径rのコントロール

高さ \thetaでの らせんの半径 r(\theta) は AnimationCurve でコントロールできるようにしています。

f:id:r-ngtm:20210115192431p:plain:w240
らせんの半径r
float radius = generator.radiusCurve.Evaluate(Remap(t, 0f, 1f, curveTimeMin, curveTimeMax));  // らせんの半径はAnimationCurveで管理している

これにより、AnimationCurveを変えることでらせん形状のバリエーションを出せるようになります。

f:id:r-ngtm:20210115204435p:plain:w320
様々ならせん形状



STEP2 : らせんに沿ってメッシュを作成する

らせんメッシュの作り方

STEP1 でらせんを計算しました。

f:id:r-ngtm:20210115194751p:plain:w280
らせんのライン

らせんの各ポイントから外側へ向かうベクトル  r を計算します。

f:id:r-ngtm:20210115212315p:plain:w280
外側へ向かうベクトル r

ベクトル  r にそってメッシュを広げることで、らせんメッシュを作ることができます。

f:id:r-ngtm:20210115212710p:plain:w280
らせんメッシュ


このベクトル  r外積を利用して計算することができます。

外積を利用する

らせんに沿うような接線ベクトルを  \vec t 、y軸上向きのベクトルを  \vec uと置きます。

らせんの外側を向くベクトル  \vec r外積を利用して計算できます。

 \vec r = \vec t \times \vec u

f:id:r-ngtm:20210115010540p:plain:w320
外側方向のベクトル r

STEP3 : らせんメッシュをねじる

 \vec r, \vec  u を軸とみなす

STEP2で、二つのベクトル  \vec r  \vec  u を計算しました。
ここで、これらのベクトルを座標軸と考えて、以下のような点Pを取ることを考えます。

 \vec P = cos \theta \cdot \vec r + sin  \theta \cdot \vec u
f:id:r-ngtm:20210115011833p:plain:w320

 \vec r, \vec  u を利用して、メッシュ頂点を計算する

この点Pをメッシュの頂点として利用すると、らせんメッシュをらせんに沿ってひねったような軌跡を描きます。

f:id:r-ngtm:20210115201110p:plain:w240
点Pを取る
f:id:r-ngtm:20210115201713p:plain:w240
点Pをメッシュ頂点として利用
f:id:r-ngtm:20210115213052p:plain:w240
結果

この \vec r, \vec  uは、線形代数学では直交基底ベクトルと呼ばれたりもします。

【シェーダーグラフメモ その56】内積を使って任意方向のグラデーションを作る

UV座標と2次元ベクトルの内積 = グラデーション

以下のシェーダーグラフを見てください。

UV座標とベクトル(1, 0.5) の内積をとると、(1, 0.5)の方向へ増加するグラデーションができます。

f:id:r-ngtm:20210102165822p:plain
UVとベクトルの内積

数式による解説

UV座標  p = (x, y) と ベクトル  (1, 0.5)内積を計算すると、  x + 0.5 y になります。

この式がある色の値 g をとると考えた場合、以下のような直線の方程式を得ます。

x + 0.5 y = g

グラデーションの色の値

gの値を変えながら、グラデーションの上に直線x + 0.5y = g を引いてみると以下のようになります。

f:id:r-ngtm:20210102161631p:plain


このグラデーションは直線  x + 0.5y = g (0 \leq  g \leq 1.5) が表す領域であるとも言えます。

ちなみに、直線 x + 0.5y = g はベクトル  (1, 0.5) と垂直になっています。

内積に使用するベクトルは正規化したほうが良い

内積に使用するベクトルの長さが変化すると、内積結果は変わってしまいます。

f:id:r-ngtm:20210102164043p:plain
ベクトルの長さによって内積結果が変わる

内積に使用するベクトルは Normalizeで長さを1にした方が良いでしょう。

f:id:r-ngtm:20210102164314p:plain
Normalizeを使う

3次元の内積

3次元座標と3次元ベクトルの内積をとると、これもベクトル方向へ増加するグラデーションになります。

f:id:r-ngtm:20210102162320p:plain
3次元座標と3次元ベクトルの内積

ベクトル(1, 2, 3)の正規化 と3次元座標の内積をとると、(1, 2, 3) の方向へ増加するグラデーションになります。

f:id:r-ngtm:20210102162707p:plain
ベクトル(1, 2, 3) の方向へ増加するグラデーション

3次元座標とベクトルの内積は平面の方程式を作る

ベクトル(1, 2, 3)と3次元座標(x, y, z)内積x + 2y +3zになります。

この式がある色の値gをとると考えた場合、以下の方程式を得ます。
x + 2y +3z = g

これはベクトル(1, 2, 3)に垂直な平面の方程式になっています。

g = 0 の部分は、Unity上のオブジェクトでは以下のように対応します。
f:id:r-ngtm:20210102165228p:plain

2020年を振り返る

 はじめに


こんにちは。普段はUnityエンジニアとして勤めているかもそば(@rn49rn49)と申します。

2020年も残すところあと僅か。

来年は丑(うし)年なので、乳でもたくさん飲みたいと思います。

 

今回の記事では、2020年をふりかえりたいと思います。

 

   

活動01. 技術アウトプット

2020年はブログ記事をいくつか書きました。

ゲームグラフィックスに関係する記事が多いです。

 

技術記事 

【Universal RP】SRP Batcher対応の積雪シェーダーを書いてみた - Qiita

 

【Unity】C# Job System + Burstで波動方程式を実装し、 ShaderGraphで水を描画する - Qiita

 

【シェーダーグラフメモ その55】ボロノイを利用した、水中コースティクス表現 - rn.log

 

Unityエフェクトレシピ02 - 六角形シールド表現 (2/2) - rn.log

 

独学用途にGASを覚えたりもしました。 

【GAS】Googleスプレッドシートに並べたクイズをランダム出題する (zenn.dev)

 

 

活動02. Unity1週間ゲームジャム

2020年はUnity1週間ゲームジャムに2回参加しました。

 

テーマ : あける

空中でボールを蹴りながらゴールに入れるゲームを作りました。

ShaderGraphを使って画面を盛ったり、Cinemachineでカメラをグリグリ動かしています。

エフェクト用にもShaderGraphを実は使っています。

www.youtube.com

 

Unity1週間ゲームジャム「空中キックボール」開発記録|かもそば|note

 

 

技術的な話

ゲームの裏ではテストコードを書いたり、Zenjectを使ってDIしたり、NCMB使ってランキング実装したり、RenderererFeatureを使って描画をコントロールしたり、と技術面でいろいろと勉強になりました。

 

 

 

テーマ : 密

レーザーを撃つゲームを作りました

ShaderGraphやエフェクトで画面を盛っています。

ブログ

【ゲーム制作】Unity1Week(お題:密)に参加しました - rn.log

 

活動03. エフェクトセミナー登壇

ご縁があって、Born Digital さんでエフェクトセミナーを発表させていただきました。

HoudiniとUnity ShaderGraphで作る Sci-Fi シールド表現(ボーンデジタルユーザー限定) – Born Digital サポート

 

発表したエフェクトはブログにて紹介しています。

Unityエフェクトレシピ02 - 六角形シールド表現 (2/2) - rn.log (hatenablog.com)


活動04. ゲームエフェクトを学び、そして辞める

ゲームエフェクトに興味があったため、

去年の冬から今年の9月ごろにかけて、

FLYPOTさんのエフェクト講座でゲームエフェクト制作を学んでいました。

エフェクト講座 | FLYPOT.LLC

 

しかし、9月頃にはゲームエフェクトからはいったん距離を置くことにしました。

 

 

ゲームエフェクトを辞めた理由

・作りたいものがなくなった(作ってみたかったものは趣味で作りきった)

・趣味としてエフェクトを作るのは楽しいが、職業アーティストになりたいわけではなかった

プログラマーとしての業務経験をエフェクトに活かす道が見えなかった

・今後のキャリアを考える上で、エフェクト制作スキルはあまり重要ではないように思えてきた

いろいろな業界で役に立つスキルが欲しい(エフェクト制作スキルは非ゲーム業界では役に立たなさそうに思います)

・ゲームエフェクト以外にも興味が沸いた

作ったエフェクト一部紹介

 

 

 

活動05. おめシス(うんちゃん)のゲームを作った

今年の9月 ~ 11月にかけて、NEWVIEW CYPHERというイベントに参加してゲームを作りました。

newview.design

 

 

かもそば = おめシスのファン

実は私かもそば、VTuberとして活動しているおめシス(おめがシスターズ)のファンでございます。

YouTubeに上がっているおめシスの動画はほぼ全て観ました。

www.youtube.com

 

 

イベント参加特典 = おめシスがゲームを遊んでくれる

NEWVIEW CYPHERというイベントに参加してゲームを提出すると、

「おめシスがゲームを遊んでコメントをくれる」という特典がありました。

 

おめが団としてはこれはもう参加するしかない! ということで光の速さで参加を決めました。 (おめが団 = おめシスのファンを指す言葉)

 

 

 

 

作ったゲーム : うんちゃんタワー

おめシスのうんちゃんを使ったVRゲーム「うんちゃんタワー」を制作いたしました。

制作ツールはSTYLYUnity になります。

www.youtube.com

 

 

賞をいただきました

制作したうんちゃんタワーですが、ありがたいことにベストプレイヤー賞を頂きました。

 

うんちゃんタワーは下記リンクから遊べます。 (遊ぶには、OculusQuestなどのVRバイスが必要です)

gallery.styly.cc

 

活動06. GLSLレイマーチング

GLSLレイマーチングを利用して、映像的な表現を作る趣味に1か月ほどハマっていました。 

 

 

制作した作品はNEORTにまとめています。

neort.io

 

自分で書いたGLSLレイマーチングより、既存のレンダラーを使ったほうが短い工数で品質の高い絵が出せてしまうため、続ける必要性を感じなくなってきています。

 

活動07. ポートフォリオを整理した

ポートフォリオが長年放置されていましたが、重い腰を上げて整理しました。

職務経歴もしれっと載せていたりします。

rngtm.github.io

 

ポートフォリオの作成にはGitBookを使っており、作り方は別の記事にてまとめています。

r-ngtm.hatenablog.com

 

 

2020年のまとめ

・GLSLレイマーチングを通してレンダリングへの理解が深まった

・技術インプット・アウトプットがあまりできていなかった

VRゲームを作るのはけっこう楽しい 

・ゲームエフェクトは趣味としてやる分には楽しい

 

2021年の目標

目標1 : 自分の武器を見つける

今の自分の興味のあるキーワードに+αして、自分だけの武器を身に着けられたらいいと思っています。

・シェーダー

・ゲームエフェクト(VFX)

レンダリング

・数学

あたりのキーワードに興味があるので、この領域を広めて良ければ良いなと思います。

 

目標2 : スキルアップ

自分の持っている手札の領域を広め、より良い物作りができるようなスキルを身に着けることを2021年の目標にしたいと思います。

・DIなどの設計の知識を身に着けてより良いプログラムを書けるようにする

・数学・物理・アルゴリズムなどへの知識を深め、アプリ実装力を向上させる

・シェーダーやレンダリングパイプラインへの理解を深め、表現の実装力を向上させる

・CPU/GPUなどの低レイヤーへの理解を深め、パフォーマンスの良い実装ができるよ

うにする

・コミュニケーション力の向上

などなど

 

今までは技術ばかりを見ていましたが、2021年はより広い視点を持てるようになりたいとも思います。

 

2020年はコミュニティ活動があまりできていなかったので、2021年は何かしらの形でコミュニティに還元していきたいなぁとも思います。

勉強会登壇したい

 

目標3 : アウトプットのチャンネルを増やす

今まではTwitterかブログの二つでしか情報を発信していませんでした。

それまでとは別のチャンネルで情報を発信出来たら良いなぁと思います。

技術書を出してみたい

 

目標4 : 転職

2021年の4つ目の目標は転職です。

転職のモチベーションとしては、

・新しいことにチャレンジしてみたい (新しい刺激が欲しい)

・自分の専門性を高めたい (自分だけの武器が欲しい)

収入を上げたい (同じ会社にいても給料は上がりにくい)

 

現在考えている業界は xR系、ハイカジュ系あたりでしょうか

(シェーダーを組むスキルや、ゲーム開発経験が役に立ちそう)

 

趣味でシェーダーを組んで得た知識が現職の会社ではあまり活用できていないのが、

ちょっとだけフラストレーションが溜まっていたりします。

 

 

2020年に買った本

2020年は数学/グラフィックス系の本をたくさん買ったと思います。

(8割くらい積み本になっているので、ちゃんと読まなきゃ...)

 

UniRx/UniTask完全理解 より高度なUnity C#プログラミング | 打田 恭平 |本 | 通販 | Amazon

 

リアルタイムレンダリング 第4版 (Real Time Rendering Fourth Edition 日本語版) | Tomas Akenine-Moller, Eric Haines, Naty Hoffman, 髙橋 誠史, 今給黎 隆, 加藤 諒, 中本 浩 |本 | 通販 | Amazon

 

Amazon.co.jp: コンピュータグラフィックス [改訂新版] eBook: コンピュータグラフィックス編集委員会: Kindleストア

 

 生成 Deep Learning ―絵を描き、物語や音楽を作り、ゲームをプレイする | David Foster, 松田 晃一, 小沼 千絵 |本 | 通販 | Amazon

 

その数式、プログラムできますか? | アレクサンダー・A・ステパノフ, ダニエル・E・ローズ, 株式会社クイープ | コンピュータ・IT | Kindleストア | Amazon

 

ゲームを動かす技術と発想 R | 堂前 嘉樹 | Kindle本 | Kindleストア | Amazon

 

実例で学ぶゲーム3D数学 | Fletcher Dunn, Ian Parberry, 松田 晃一 |本 | 通販 | Amazon

 

Unityでわかる!ゲーム数学 | 加藤 潔 | コンピュータ・IT | Kindleストア | Amazon

 

イラストでわかる物理現象 CGエフェクトLab. CGWORLD (シージーワールド) | 近藤啓太 | コンピュータ・テクノロジー | Kindleストア | Amazon

 

VRが変える これからの仕事図鑑 | 赤津 慧, 鳴海 拓志 | 社会・政治 | Kindleストア | Amazon

 

ミライをつくろう! VRで紡ぐバーチャル創世記 | GOROman, 西田 宗千佳 |本 | 通販 | Amazon

 

 

Unity デザイナーズ・バイブル | 森 哲哉, 秋山 高廣, 室星 亮太, 石塚 淳一, 轟 昂, 牙竜, コポコポ, すいみん, ツバネ, ryosios, トライタム, やまたくさん, クロイニャン, ズゴゴ, karukaru, Maruton, monmoko, 大下 岳志, 時任 友興, 佐藤 英一 |本 | 通販 | Amazon

 

エフェクトグラフィックス 動き・流れ・質感の表現カタログ | 松岡 伸治 |本 | 通販 | Amazon

 

SubstanceDesigner入門 | ぽこぽん丸。, ktk.kumamoto |本 | 通販 | Amazon

 

その他 

r-ngtm.hatenablog.com

【Unity1Week/あける】シェーダー解説 (ShaderGraph)

 

 はじめに

1週間ゲームジャムUnity1Weekに参加したので、記事を書きたいと思います。

Unity 1週間ゲームジャム | フリーゲーム投稿サイト unityroom

 

作ったゲーム

空中でボールを蹴りながらゴールに入れるゲームを作りました。

空中キックボール | フリーゲーム投稿サイト unityroom

 

www.youtube.com

www.youtube.com

 

 

今回の記事

実装したシェーダー表現を4つほど紹介したいと思います。

  1. スクリーン座標を利用したスキャンライン表現
  2. 物陰に隠れない点線
  3. 輪郭線が太い3D矢印
  4. 六角形Sci-Fiシールド

 

環境

Unity2019.4.15f1

Universal RP 7.3.1

 

表現1 : スクリーン座標を利用したスキャンライン

www.youtube.com

 

 

スキャンライン(走査線)とは

モニターの走査線のような模様のことスキャンラインと呼びます。

f:id:r-ngtm:20201228183753p:plain

スキャンラインのON/OFF

スキャンラインの作り方 (ShaderGraph)

スクリーン座標(Screen Positionノード)のY成分を利用すると、横縞を作ることができます。

f:id:r-ngtm:20201228184359p:plain

ScreenPositionを利用したスキャンライン

 

ゲームで使用する際は、スキャンラインをSubGraphとして定義し、汎用的に使えるようにしています。

f:id:r-ngtm:20201228184544p:plain

SubGraphとしてスキャンラインを定義

 

シールド表現のスキャンライン

f:id:r-ngtm:20201228185544g:plain

スキャンライン

 

フレネル効果にスキャンラインを加算しています。

f:id:r-ngtm:20201228184746p:plain

 

ゴール表現にもフレネル+スキャンライン表現を利用しています。

f:id:r-ngtm:20201228190815p:plain

 

矢印模様のスキャンライン

f:id:r-ngtm:20201228190151g:plain

矢印表現のスキャンライン

モデルUVから矢印模様を作成し、そこにスキャンラインを乗算しています。

f:id:r-ngtm:20201228190525p:plain

スキャンラインを乗算

 

表現2 : 物陰に隠れない点線表現

今回のゲームでは、物陰に隠れない点線を作成しました。

f:id:r-ngtm:20201228192000g:plain

3Dモデルの裏に隠れない点線

 

Forward Renderer Dataを利用する

Universal Render Pipelineの Forward Rendererにて

「Overlayレイヤーの不透明オブジェクトはDepthテストを常に成功させる」

という設定を行いました。

 

これによってOverlayレイヤーのオブジェクトは手前のオブジェクトの裏に隠れなくなります。

 

以下はForwardRendererの設定例です。

 

f:id:r-ngtm:20201228192534p:plain

ForwardRendererの設定

 

点線オブジェクトの設定

点線オブジェクトは Overlay というレイヤーに設定します。

f:id:r-ngtm:20201228192717p:plain

点線オブジェクトのレイヤー

 

結果

点線は常に表示されるようになります。

f:id:r-ngtm:20201228192000g:plain

3Dモデルの裏に隠れない点線

 

表現3 : 輪郭が太い矢印の表現

今回のゲームでは以下のような矢印を作成しました。

矢印の3Dモデル2つを配置しています。

f:id:r-ngtm:20201228193424g:plain

矢印表現

矢印の3Dモデル(Houdini)

矢印の3Dモデルは、Houdiniを利用して作成しています。

f:id:r-ngtm:20201228194103p:plain

Houdiniで作成した矢印モデル 2つ

矢印モデルのマテリアルにはUnityの Universal Render Pipeline / Unlit シェーダーを適用します。

 

矢印はOverlayレイヤーにする

大きいモデルの中に小さいモデルを埋め込むと、通常だと内側のモデルは隠れてしまいます。

f:id:r-ngtm:20201228195801p:plain

 

矢印モデルのDepthテストを無視することにより、内側のモデルが見える状態にできます。

f:id:r-ngtm:20201228195915p:plain

 

f:id:r-ngtm:20201228195702p:plain

 

 

描画順のコントロール

内側のオレンジ色モデルのマテリアルはPriority = -1、

外側の白色の矢印モデルはPriority = 0 に設定します。

 

これにより、白色のモデルが描画された後にオレンジ色のモデルが描画されます。

f:id:r-ngtm:20201228194623p:plain

白色の矢印はPriority = -1 に設定

f:id:r-ngtm:20201228194808p:plain

オレンジ色の矢印はPriority = 0 に設定

 

表現4 : 六角形シールド表現

今回のゲームでは、以下のような六角形のシールド表現を作成しました。

サッカーボール状の3Dモデルにシェーダーで色を付けています。

f:id:r-ngtm:20201228200941g:plain

六角形シールド表現

 

 

3Dモデルの作成(Houdini)

シールド表現用の3DモデルはHoudiniで作成しました。

作り方は過去記事に載せているので、興味があればご覧ください。

Unityエフェクトレシピ 02 - 六角形シールド表現 (1/2) - rn.log (hatenablog.com)

 

f:id:r-ngtm:20201228201244p:plain

六角形シールド用3Dモデル

シェーダーで色を付ける

六角形シールドに使用したシェーダーは以下になります。

f:id:r-ngtm:20201228202223p:plain

六角形表現で使用しているシェーダー

 

 

【Unity2020.2 / VFX Graph】HDRPサンプルの蝶のVFXを読んでみる

はじめに

Unity 2020.2 HDRP のサンプルプロジェクトを見ると、

きれいなお部屋の中を歩き回ることができます。

f:id:r-ngtm:20201220090636j:plain

 

このお部屋、竹の根元に蝶々がいます。かわいい。

f:id:r-ngtm:20201220093011g:plain

蝶々

 

この蝶々はVFX Graph で作られています。

f:id:r-ngtm:20201220093308p:plain

VFX Graph

 

今回は この VFX Graphを読んでいきたいと思います。

 

Inspector上の設定

Visual Effect コンポーネントを見ると、以下のような設定になっています。

f:id:r-ngtm:20201220093710p:plain

Visual Effect コンポーネント

 

Rendering Layer Mask

Rendering Layer Maskの部分が Mixed... になっているのが気になります。

f:id:r-ngtm:20201220093900p:plain

Rendering Layer Mask

f:id:r-ngtm:20201220093954p:plain

二つのLayerのチェックがONになっている

Rendering Layer Maskを変えてみる

蝶々を拡大すると、以下のような表示になっています。

f:id:r-ngtm:20201220094639p:plain

初期設定

 

Rendering Layer Mask を Decal Layer default のみ有効にすると、白っぽい感じになります。

f:id:r-ngtm:20201220094555p:plain

Light LayerDefault を有効にすることで、VFXの描画結果がライトの影響を受けるようになるみたいです。

 

Light LayerDefaultのみをONにすると以下のような表示になります。

f:id:r-ngtm:20201220094918p:plain

Light LayerDefaultのみに設定



Properties

続いては、Propertiesを見てみます。

f:id:r-ngtm:20201220095208p:plain

Properties

パラメータは以下のようなものを表しているようです。

 

プロパティ 意味
Count 蝶々の生成数
Radius 蝶々を生成する位置の半径
Wing Animation Speed 蝶々の羽ばたくアニメーションの速さ

VFX Graphのプロパティからアニメーションを制御できるのが興味深いと思いました。

 

VFX Graphの中を見てみる

次は、VFX GraphアセットButterFliesの中を覗いていきます。

VFX Graph

コンテキスト

ButterFlies アセットの中身は以下のようになっています。

VFX Graph の 4つのコンテキスト
コンテキスト やっている内容
Spawn 蝶の生成
Initialize 蝶の初期化
Update 蝶を動かす
Output 蝶を画面に出す

 

Spawn

Spawn コンテキストでは、 Single Burst ブロックが使用されています。

VFX開始時に、一度だけ Count 分の蝶を生成しています。

Spawn コンテキスト

 

Initialize Particle

その次のInitialize Particle コンテキストは以下のようになっています。

Initialize Particle コンテキスト

Initialize Particle コンテキストには、以下のようなブロックが接続されています。

ブロック やっている内容
Add Position (Shape: Sphere) 蝶の初期位置を、球内部のランダムな位置に設定する
Set Direction 蝶の向きを設定
Set Tex Index 蝶のテクスチャ番号設定
Add Velocity from Direction & Speed (Random Direction) 速度を足す

 

Add Position

蝶の初期位置を、球内部のランダムな位置に設定しています。

 

Set Direction

SetDirectionブロックでは、位置と外積を利用して、球に沿って下方向へ向かうような向きを設定しています。

やっていることがやや複雑なので紐解いていきます。

SetDirectionブロック

Set Directionへ設定しているベクトルデータ(Houdiniで可視化)

 

 

 

Get Attribute : position current

Get Attribute は パーティクルが持つ情報(Attribute)を取得するノードです。

今回は position currrent(現在の座標)を取得しています。

 

今回、蝶(パーティクル)は球の内部にまばらに存在しています。

Houdiniを使って可視化してみると、以下のようになるでしょうか。

球の内部にまばらに存在する点

 

Normalize (Vector3)

Normalizeはベクトル長を1にする(正規化)を行うノードです。

Normalize で 座標を正規化

 

座標にNormalizeをかけると、点は半径1の球の表面に並びます。

 

Cross Product (Vector 3)

Cross Product は ベクトルの外積を行うノードです。

Cross Product

 

外積結果を黄色い線として、表示してみると以下のようになります。

時計周りの向きになります。

 


さらにこのベクトルを、元の座標と外積しています。

座標とベクトルの外積

ベクトルの計算結果は以下のようになります。

球に沿って下方向に向かうベクトルになりました。

ノードだけを見るとなかなかイメージがつかみにくいですが、可視化すると分かりやすいですね。

最終的に得られるベクトル

 

このベクトルは、Set Direction ブロックへ入力されます。

つまり、蝶は最初は下を向いているということになります。

Set Direction へ入力

 

実際のノードと、ベクトルの可視化を並べてみました。

ノードとベクトルの対応

 

Set Tex Index

Set Tex Index ブロック

この蝶々の元素材は以下のようなテクスチャシートになっています。

蝶々の元素材

 

Set Tex Indexブロックでは、テクスチャシートの左から何番目のテクスチャを使うか、という番号を設定した物になります。

 

Add Velocity from Direction & Speed (Random Direction)

このブロックは、パーティクルのDirectionの方向に対して、ランダムな速度を加算しています。

先ほど、Set Direction ブロックにて以下のようなベクトルを設定していたので、

球に沿って下方向に対して、長さ0.2 ~ 1.0 のランダムな速度が足されることになります。

Add Velocity ブロック

 

加算される速度の向き

以上で Initialize Particle コンテキストは終わりとなります。

次に Update Particle コンテキストを見ていきます。

 

 

Update Particle コンテキスト

Update Particle コンテキスト

Updateコンテキストでは、以下のようなブロックが接続されています。

 

ブロック やっている内容
Force 力を足す
Turbulence 速度に乱流を与える
Set Velocity 速度が0.5を下回らないように制限を加える
Set Scale.XY パーティクルの大きさの設定

Set Pivot.X

メッシュのピボットを設定 (メッシュの回転の中心位置を決める)

 

Force

Forceブロックでは、蝶にかかる外力を設定しています。

Forceブロック

 

PositionとDirectionの外積

PositionとDirectionの外積

ノードだけ見てもよくわからないので、Houdiniを使って可視化してみます。

 

Direction

Initialize コンテキストにて設定したDirectionですが、

時間が経過するとパーティクルは移動し、以下のようにバラバラな向きになります。

どのパーティクルも、下方向になっています。

 

 

PositionとDirectionの外積をとると、球に沿ったベクトルになります。

つまり、原点(0,0,0)の周りをグルグル回るようなベクトルになります。

別の角度から見た場合

 

横方向ベクトルへランダム値を乗算

ここで求めた横方向ベクトルに、パーティクルごとのランダムな数値を乗算し、

速度に揺らぎを持たせています。

ランダム値の乗算

中心へ向かう引力

先ほどにベクトルに対して、座標(position) の符号を反転したものを加算しています。

これは原点へ向かって引き寄せる力になります。

符号反転した座標を加算


座標は正規化せずに符号反転したものを使っているので、

パーティクルが原点から離れるほど強い引力になり、

パーティクルが原点に近づくと弱い引力になります。

中心へ向かう引力

最終的には以下のようなベクトルが外力となります。

横方向ベクトルを灰色、 中心へ向かうベクトルを黄色で表しています。

中心へ向かう引力(黄色) と 回転の力(灰色)

 

Turbulence

Turbulenceブロックではパーティクルに乱流を与えるもので、

蝶の動きに揺らぎを与えます。

Turbulenceブロック

Set Velocity

Set Velocityはパーティクルの速度を上書きするものです、

パーティクルの速さ(VelocityのLength)が0.5を下回らないように制限を与えています。

Set Velocity

Set Scale / Set Pivot

ここでは、パーティクルのスケールやピボットを設定しています。

0.45や0.6という謎の数字が登場しますが、アートの都合によってこのような数値になったのだと思います。

Set Scale / Set Pivot

 

Set Pivotを外してみた

通常の蝶はこのような表示になっていますが、

蝶の表示

Set Pivot を無効にすると、以下のようないびつな表示になってしまいます。

 

 

以上でUpdate Particleは終わりとなります。

次にOutput 系のコンテキストを見ていきます。

 

Output コンテキスト

Output Particle Lit Quad

 

Orient Adnvanced (羽の向きの設定)

パーティクルの向きの設定は Orient Adnvanced ブロックで行っています。

向きの設定

 

Axis Z、Axis Y の挙動について

Axis Z や Axis Y を設定した際のメッシュの向きは以下のようになります。

メッシュの面はAxisZを向き、メッシュの上方向はAxisYに一致するようにメッシュ自体が回転します

 

Z軸を変えた時のパーティクルの向き

 

f:id:r-ngtm:20201220160138p:plain

Y軸を変えた時のパーティクルの向き

ちなみに、パーティクルのローカルのX軸(赤い線)は, Axis Z と Axis Y外積に一致しています。

パーティクルのX方向はZ, Y の外積に一致

 

蝶の羽の向き

今回の蝶パーティクルのAxis は以下のように設定されています。

  • Axis Z : 進行方向に対して横向き
  • Axis Y : 上方向

蝶の羽メッシュとAxisの対応関係

向きの設定

 

蝶のはばたき

蝶の羽ばたきは、Add Potision や Set Angle.Y によって実装されています。

Y方向の位置変化(Add Position) + 羽の回転(Set Angle.Y) をSine波で動かすことで羽ばたきを表現しています。

Add Position / Set Angle.Y

 

羽ばたきの実装

 

羽ばたき

Sine波が大きくなると、羽が大きく広がり、蝶も早く移動するような実装になっています。

Angle.Y = 0 だと、羽が完全に閉じた状態になります。

Angle.Y = 0の場合

 

Set Angle.X

最後のSet Angle.X は、羽のX回転を設定しています。

Set Angle.X

以下のような軸で回転します。

回転の軸

 

関連

Unity 2020.2 TECH ストリームがダウンロード可能になりました

Unity 2020.2 TECH Stream is now available for download (unity3d.com)

【Unity2020.2】URP向けのグレースケールPostEffectを作る

はじめに

Unity 2020.2 の Universal RP 向けのグレースケールポストエフェクトを作ってみました。

f:id:r-ngtm:20201219214342j:plain:w480
グレースケールをかける前
f:id:r-ngtm:20201219214403j:plain:w480
グレースケールをかけた後

環境

Unity 2020.2
Universal RP 10.2.2

ポストエフェクトに必要なもの

Universal RP でポストエフェクトを実装する際、以下のようなデータを扱うことになります。
・Universal Render Pipeline Asset
・Forward Renderer Data
・ポストエフェクト用シェーダー (.shader)
・RendererFeature (C#スクリプト)

ポストエフェクト実装の流れ

1. ポストエフェクト用シェーダー (.shader)を作る
2. ポストエフェクトを実行するRendererFeatureを作る
3. ForwardRendererDataを作成し、RendererFeatureをそこへ登録する
4. Universal Render Pipeline アセットにForwardRendererDataを登録
5. シーンのCameraコンポーネントから、使用したいForwardRendererDataを選択
6. 画面にグレースケールがかかるようになる

イメージとしては以下のようになります。

f:id:r-ngtm:20201219221843p:plain:w640
Forward
f:id:r-ngtm:20201219222456p:plain:w640
グレイスケールを実装したRendererFeature

グレースケールエフェクトを作ってみる

1 : ポストエフェクト用シェーダー

画面色のR, G, B からグレースケールを計算するシェーダーを作成します。

今回は CIE XYZ を利用してグレースケールを計算しました。

Gray = 0.2126 * R + 0.7152 * G + 0.0722 * B

参考 : グレースケール画像のうんちく - Qiita

以下のシェーダーを Grayscale.shader としてUnityプロジェクト内に保存します。

Shader "PostEffect/Grayscale"
{
    Properties
    {
        [HideInInspector] _MainTex ("Texture", 2D) = "white" {}
    }
    SubShader
    {
        Pass
        {
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag
            #include "UnityCG.cginc"

            struct appdata
            {
                float4 vertex : POSITION;
                float2 uv : TEXCOORD0;
            };

            struct v2f
            {
                float2 uv : TEXCOORD0;
                float4 vertex : SV_POSITION;
            };

            sampler2D _MainTex;
            float4 _MainTex_ST;

            v2f vert (appdata v)
            {
                v2f o;
                o.vertex = UnityObjectToClipPos(v.vertex);
                o.uv = TRANSFORM_TEX(v.uv, _MainTex);
                return o;
            }

            fixed4 frag (v2f i) : SV_Target
            {
                fixed4 col = tex2D(_MainTex, i.uv);
                return dot(col.rgb, fixed3(0.2126, 0.7152, 0.0722));
            }
            ENDCG
        }
    }
}

2 : RendererFeature

次に、カメラのレンダリング結果にグレースケールを適用するRendererFeatureを実装します。
以下のスクリプトGrayscale.cs としてUnityプロジェクト内に保存します。

using UnityEngine;
using UnityEngine.Rendering;
using UnityEngine.Rendering.Universal;

public class Grayscale : ScriptableRendererFeature
{
    [System.Serializable]
    public class GrayscaleSetting
    {
        // レンダリングの実行タイミング
        public RenderPassEvent renderPassEvent = RenderPassEvent.AfterRenderingTransparents;
    }

    /// <summary>
    /// Grayscale実行Pass
    /// </summary>
    class GrayScalePass : ScriptableRenderPass
    {
        private readonly string profilerTag = "GrayScale Pass";

        public Material grayscaleMaterial; // グレースケール計算用マテリアル

        public override void Execute(ScriptableRenderContext context, ref RenderingData renderingData)
        {
            var cameraColorTarget = renderingData.cameraData.renderer.cameraColorTarget;

            // コマンドバッファ
            var cmd = CommandBufferPool.Get(profilerTag);

            // マテリアル実行
            cmd.Blit(cameraColorTarget, cameraColorTarget, grayscaleMaterial);

            context.ExecuteCommandBuffer(cmd);
        }
    }

    [SerializeField] private GrayscaleSetting settings = new GrayscaleSetting();
    private GrayScalePass scriptablePass;

    public override void Create()
    {
        var shader = Shader.Find("PostEffect/Grayscale");
        if (shader)
        {
            scriptablePass = new GrayScalePass();
            scriptablePass.grayscaleMaterial = new Material(shader);
            scriptablePass.renderPassEvent = settings.renderPassEvent;
        }
    }

    public override void AddRenderPasses(ScriptableRenderer renderer, ref RenderingData renderingData)
    {
        if (scriptablePass != null && scriptablePass.grayscaleMaterial != null)
        {
            renderer.EnqueuePass(scriptablePass);
        }
    }
}

3 : Forward Renderer Data の作成

Forward Renderer Data を作成します。

f:id:r-ngtm:20201219215943p:plain

Forward Renderer Data は以下のような名前にしてみました。

f:id:r-ngtm:20201219220111p:plain
作成した Forward Renderer Data

4 : Forward Renderer Data を PIpeline Asset へ登録

Univesal Render Pipeline Asset へ STEP1で作成したForward Renderer Data を登録します。

f:id:r-ngtm:20201219220209p:plain
Forward Renderer Data を Pipeline Assetへ登録

5 : Forward Renderer Data に Grayscale を登録

Forward Renderer Data に Grayscale を登録します。

Add Renderer Feature ボタンをクリックします。

f:id:r-ngtm:20201219220520p:plain
Add Renderer Feature ボタンをクリックして、Renderer Feature を登録

ドロップダウンメニューからGrayscale を選択します。

f:id:r-ngtm:20201219220626p:plain
Grayscale RendererFeatureを選択
f:id:r-ngtm:20201219220726p:plain
登録された Grayscale

6 : カメラの設定

CameraコンポーネントのRenderer にて、 STEP2で PIpeline Asset に登録したForward Renderer Data を 選択します。

f:id:r-ngtm:20201219220828p:plain
CameraコンポーネントのRenderer の設定
f:id:r-ngtm:20201219220209p:plain
STEP2 で 登録したForward Renderer Data

結果

画面にグレースケールエフェクトが適用されます。

f:id:r-ngtm:20201219214403j:plain
画面にグレースケールエフェクトが適用される

【Unity 2020.2】物陰に隠れた部分を描画する

 

はじめに

今回は、Unity2020.2で物陰に隠れた部分を描画する方法を紹介します。

URPのForward Renderer Dataを設定するだけで実装可能です。

f:id:r-ngtm:20201216085052g:plain

物陰に隠れた部分を

環境

Unity2020.2

Universal RP 10.2.2

 

物影を水色にする方法

Unityが不透明のオブジェクトの描画を終えた後、

手前のオブジェクトの裏に隠れている領域だけを青く塗りつぶしています。

f:id:r-ngtm:20201216131309p:plain

不透明オブジェクトを描画

 

f:id:r-ngtm:20201216131344p:plain

物陰に隠れている領域だけを青く塗りつぶす

 

実装方法

次に、Unity2020.2でこれを実装する方法をご紹介します。

 

STEP1 : 影を描画させたいオブジェクトのLayerを設定

今回はPlayerという名前のレイヤーを作成し、球体のレイヤーをPlayerにします。

f:id:r-ngtm:20201216090003p:plain

隠れた部分描画をさせたいオブジェクト(Sphere)

f:id:r-ngtm:20201216090209p:plain

レイヤーをPlayerに設定

STEP2 : Materialを用意

物陰を青く塗りつぶすときに使用するMaterialを用意します。

今回は以下のような水色のUnlitマテリアルを用意しました。

f:id:r-ngtm:20201216085601p:plain

Unlitマテリアル

マテリアルの名前は Color にしておきます。

f:id:r-ngtm:20201216095602p:plain

作成したマテリアル



STEP3 : Forward Renderer Data  

ForwardRenderer.assetを編集すると、Universal RPの描画ロジックがカスタマイズできます。

 

URPテンプレートでプロジェクトを作成している場合、Settingsフォルダの中にいます。

f:id:r-ngtm:20201216085732p:plain

ForwardRendererData.asset

 

 

STEP4 : Forward Renderer Data に影の描画パスを追加

今回はForwardRenderer.asset に 2つのRender Objects を登録して、以下のように設定してみました。

f:id:r-ngtm:20201216133710p:plain

 

Render Objects の追加方法

Inspectorの下の方にある Add Renderer Feature ボタンから Render Objects は登録できます。

f:id:r-ngtm:20201216095816p:plain

RendererFeature追加ボタン

f:id:r-ngtm:20201216133823p:plain

Render Objects を追加




結果

物陰に隠れている部分は、Colorマテリアルで描画され、水色になります。

f:id:r-ngtm:20201216094806p:plain

物陰に隠れている部分がColorマテリアルで描画されている

 

Color(Render Objects)を外すと、以下のような表示になります。

f:id:r-ngtm:20201216094855p:plain

Colorマテリアルの描画を外した場合

特許

特許が既に登録されているそうです(来年切れる?)

https://www.j-platpat.inpit.go.jp/web/PU/JPB_3637031/83F99CD3BABFA1C42D5F2FA913BC1CF9

関連

【Unity】LWRPで、壁で遮られて見えないキャラクターをシルエット表示するのが超簡単にできた - テラシュールブログ (hateblo.jp)