Tricks of Realtime VFX with Houdini詳細解説 vol.1

こんにちは、セガゲームス龍が如くスタジオの伊地知です。

 去年もHoudiniのお話をこのSEGA TECH Blogで掲載しており、これで二回目となります。

さる2018年12月2日にCEDEC+KYUSHU2018が開催されたのですが

その時私の方で講演させていただいたTricks of Realtime VFX with Houdiniというセッションがありまして

(セッションのスライドはこちらからダウンロード出来ます。)

そのスライドの後半に

「実際にすぐにゲームに出して使えるエフェクトのテクニック」=「トリック」

を3つご紹介させて頂きました。

その3つのトリックを今回のSEGA TECH Blogで詳細に解説させて頂きます。

 

  1. 加工して戻す(RestSOP) ー processAndRest01.hip
  2. 独自シミュレーション(SolverSOP)
  3. 最短経路探索(FindShortestPath)

3ついっぺんにやると果てしなく長いので全部で3回に分け、

今回は1つ目である「加工して戻す」というトリックだけを扱うことにします。

シーンファイルもご利用頂けますしHoudiniの体験版(Apprentice版)でも開けますので

実際にご自身のPC(Windows)、Mac、Linuxで確認しながらご覧になるとご理解いただきやすいでしょう。

(HoudiniはいろんなOSに対応しています!) 

 

今回は初心者の方でも安心の詳細解説となっております。

またwrangleの行数も最低限に抑えてあります。 

操作方法や基礎知識に関しては1年前の前回のHoudiniの回でも

techblog.sega.jp

御紹介させおりますのでその辺が分からない方はそちらも合わせてご覧下さい。

 


1.加工して戻す(RestSOP)

では1つ目のトリックの内容から説明させて頂きます。

Houdiniの新規シーンを開いた想定でお話をさせて頂きます。

ダウンロードして頂いた方はシーンを上から順にデータを見ていって下さい。

まず画面一番左下のボタン

f:id:sgtech:20190224233230p:plain

を押してGlobal Animation Optionsを開きます。

FPS30End256にしてSaveAsDefaultボタンを押します。

f:id:sgtech:20190224233226p:plain

ネットワークエディタのオブジェクトレベルでTabキーを押しTabメニューでgeと押すと候補が出ますのでGeometryを選び作ります。

f:id:sgtech:20190224233347p:plain

f:id:sgtech:20190224233344p:plain

f:id:sgtech:20190224233340p:plain

できたgeo1にダブルクリックやiキーなどでジオメトリレベルに潜ります。

たいていの作業はこのジオメトリレベルで行う事になります。

 

TabメニューからSphereを選択してPrimitiveTypeをPolygon

Frequencyを50にします。

f:id:sgtech:20190224233155p:plain

 

次にTabメニューからUVTextureを出してつなげます。

TextureTypeをPolarFix Boundary Seamsにチェックします。

f:id:sgtech:20190224233149p:plain

この状態でビュー上でSpace+5を押すとUVの状態が確認出来ます。

f:id:sgtech:20190224233143p:plain

UVが01の範囲内からはみ出していると都合が悪いので

TabメニューからUV Transformを出してUV Textureにつなげ

ScaleXの値に 1/$XMAX と入力します。

これはUV値のUの最大値で全体を割るという意味で

こうする事によって01の範囲内に収めます。

f:id:sgtech:20190224233137p:plain

ビュー上でSpace+1を押してPerspectiveに戻します。

TabメニューからPointWrangleを出してUV Transformの出力につなげます。

f:id:sgtech:20190224233131p:plain

ポイントアトリビュートの@rest,@Cd,@Alphaを設定します。 

VEXpressionの欄に下記のコードを記述します。

v@rest = @P;
f@Alpha = 0.0;
@Cd = {1,1,1};

@はアトリビュートという意味でジオメトリのクラス(Point,Vertex,Primitive,Detail)自体に持たせる変数です。@の前のvやfは型を指定しておりvならベクター、fならフロートを定義します。ここではRunOverがPointになっているのでPointのクラスのアトリビュートを制御するという事になります。

@restはポジション(@P)を保持しておく為のアトリビュート。

頂点α(@Alpha)は0で初期化、頂点カラー(@Cd)は白で初期化という意味になります。

TabメニューからPointWrangleを出して先程のPointWrangleにつなげます。

f:id:sgtech:20190224233244p:plain

f@Alpha = 1.0;

VEXpressionの欄にはこの様に記述します。

再度Alphaを1にするのには訳がありますがそれはあとで解説します。

TabメニューからTwist( or Bend)を出して先程のPointWrangleにつなげます。

f:id:sgtech:20190224233240p:plain

Limit Deformation to Capture Regionのチェックを外し

Twistの値を360Capture OriginのZを-1Capture Length2にします。

こうする事で球がZ軸に沿って360度ねじれます。

TabメニューからMountaionを出して先程のBendにつなげます。

f:id:sgtech:20190224233233p:plain

MountainノードのHeight1.73Element Size2.18Scale0.06,0.16,0.06、

OffsetX値を-14に設定しY値は20に設定し1フレーム目Altキーを押しながらクリック256フレーム目18と設定しAltキーを押しながらクリック

これでパラメータの欄が緑色になったのが分かります。

これはアニメーションが設定されていますという事を現しています。

ちなみにShiftを押しながらクリックでアニメーションエディタが開きます。

Max Octaves9Lacunarity2.19Roughness0.407とします。

この状態で再生ボタン(画面左下にあります)を押すか、カーソルキーの↑を

押してみて下さい。

f:id:sgtech:20190224233321g:plain

球のトゲトゲが蠢きながら下に流れていくアニメーションが確認出来ます。

確認出来たら停止ボタン(画面左下にあります)を押すか、Ctrl+↑を押して止めて下さい。

TabメニューからTransformを出して先程のMountainにつなげます。

f:id:sgtech:20190224233317p:plain

RotateX値を31Y値を5Z値を5とし少し回転させます

TabメニューからClipを出して先程のTransformにつなげます。

f:id:sgtech:20190224234332p:plain

先程と同じ要領でDistance1フレーム目-1.25256フレーム目1.36とキーを打ちます。

この状態で再生させると下半分が削れた状態が段々と上に上がっていって

トゲトゲの球が消えていくのが分かります。

TabメニューからPointWrangleを出して先程のClipにつなげます。

f:id:sgtech:20190224234328p:plain

@P = @rest;

VEXPressionの欄にこの1行を入力すると歪められていたポイントのポジションが元に戻ります。

この挙動を不思議に思う方もいらっしゃるかもしれませんがこれは

単純に@restに保持していた座標で元の位置に戻っただけなのです。

しかし消されたポイントは戻りません。

歪んだ状態で水平に切るという事は座標を戻すと水平では無く歪んだ状態で削れていくのです。

↑キーで再生させるとこの様に動作しているのが確認出来るでしょう。

f:id:sgtech:20190224234240g:plain

映像の表現ならここまでで良いですがゲームの場合

ゲーム中で表現する為に工夫が必要です。

 CEDEC+KYUSHU2018では私はテクスチャパターンアニメーションでやりましょうと言ってしまいましたが実はもっと良いαカットオフを用いた手法があります。

シェーダ側でαしきい値を用いて透明にするのですが

そのαをどう出すのかがこのトリックの鍵になります。

2つ目のトリックで紹介するSolverというノードをもうここで使います。

毎フレーム値を加工して蓄積させていく事が出来るノードで評価した結果を

キャッシュしておくことが出来ます。

TabメニューからSolverを出して上から4つ目のpointwrangle1から

左から1番目のところにつなげて左から二番目の入力にpointwrangle3をつなげます。

 

f:id:sgtech:20190224234238p:plain

ダブルクリックしてsolver1の中に潜ります。

 TabメニューからAttributeTransferを出してPrev_Frameを第一入力に

Input_2を第二入力につなげPointsにチェックを付けAlphaを指定します。

f:id:sgtech:20190224234234p:plain

AttributeTransferは第一入力に対し第二入力のアトリビュートを近いものから転写していくノードです。MayaであればTransferAttribute、Softimageをお使いだった方ならGATORを御存知だと思いますがその万能版だと思って頂ければ理解が早いかと思います。(Softimageユーザーの方ならMayaのTransferAttributeなんかと比べられるのは屈辱だと思いますがHoudiniのAttributeTransferからすればどちらも五十歩百歩です。)

SolverSOPの中でこの様なつなぎ方をすると前のフレームの結果に対し現在のフレームの第二入力のアトリビュートを転写するという意味を持ちます。

TabメニューからPointWrangleを出して第一入力にPrev_Frame、第二入力にAttributeTransferをつなげます

f:id:sgtech:20190224233439p:plain

VEXpressionの欄には 

@Alpha += @opinput1_Alpha/256;

 と記述します。

このコードと接続の意味は前のフレームの結果のアルファに対し

第二入力のアルファを256で割った数値を加算するという意味になります。

つまりこの2つのノードの意味は毎フレーム現在のアルファ値の1/256を

加算し続けるということになります。

 

uキーで1つ上の階層に戻って256フレーム目まで進めます。

すると球がこの様に見えているはずです。

f:id:sgtech:20190224233435p:plain

毎フレームアルファ値を累積して256フレーム分貯めた結果がこれです。

これをUV座標に基づいてテクスチャに書き出してやります。

GameDevelopmentToolsetのSimpleBakerを使うやり方が最も簡単でしょう。

GameDevelopmentToolsetのインストールの最も簡単な方法は左上の

GameDevelopmentToolsetタブのUpdateToolsetボタンを押すことです。

f:id:sgtech:20190224233432p:plain

ゲーム会社や映像スタジオ、CGスクールなどでプロキシ環境下であれば

この機能がうまくいかない場合があります。

その際は公式のgithubからダウンロードして手動でインストールして下さい。

GameDevelopmentToolsetがインストールされた状態になったら

TabメニューからGameDev Simple Bakerを出して下さい。

f:id:sgtech:20190224233430p:plain

ここで普通に考えればアルファにチェックを入れれば出力出来るはずなんですが

何故か真っ白になってしまうので一旦@Cdに@Alphaを移して

basecolorとして出力する事になります。

 simple bakerの1つ前にPointWrangleを足して

@Cd = @Alpha;

 記述して頂点カラーにアルファを移してからsimple baker でテクスチャを焼きます。

すると

f:id:sgtech:20190224233426p:plain

この様なテクスチャが焼き上がりますのでPhotoshop上でレベル補正かけたり

トーンカーブで補正かけたりして1~254くらいの値の範囲にしておくと

シェーダに食わせた時の見た目が良い様です。

 

モデルの方も同時に出力しておきます。

 UVを設定したuvtransform1から出すと良いのですがそのままだと

25,000頂点もありかなりメモリを食ってしまいます。

uvtransform1の下にPolyReduceを作ってつなげます。

 Percent To Keep1Vertex Attribute Seams0.4にして頂点数を252にまで

落とします。ゲーム内で綺麗に見える最低限の頂点数であれば Percent To Keepを

いろいろ試してみても良いでしょう。

f:id:sgtech:20190224233533p:plain

TabメニューからROP FBX Outputを出してつなげます。

 Output Fileを指定してSave to Diskボタンを押します。

これでモデルファイルも出力出来ました。

 ではUnity上で確認してみましょう。

普通のスタンダードシェーダでは両面に対応していないので

Create -> Shader -> Standard Surface Shader

で作ったものにちょい足ししたシェーダで表示してみましょう。

Shader "Custom/doubleSidedCutOff" {
    Properties {
        _Color ("Color", Color) = (1,1,1,1)
        _Cutoff ( "Cutoff", Range(0, 1) ) = 0.5
        _MainTex ("Albedo (RGB)", 2D) = "white" {}
        _Glossiness ("Smoothness", Range(0,1)) = 0.5
        _Metallic ("Metallic", Range(0,1)) = 0.0
        _BumpMap ( "Normal Map" , 2D ) = "bump" {}
        _BumpScale ( "Normal Scale" , Range(0,1) ) = 1.0
    }
    SubShader{
        Tags { 
            "Queue" =    "AlphaTest" 
            "RenderType"="TransparentCutout"
        }
        LOD 200
        Cull Off
        CGPROGRAM
        // Physically based Standard lighting model, and enable shadows on all light types
        #pragma surface surf Standard fullforwardshadows alphatest:_Cutoff

        // Use shader model 3.0 target, to get nicer looking lighting
        #pragma target 3.0

        sampler2D _MainTex;
        sampler2D _BumpMap;

        struct Input {
            float2 uv_MainTex;
        };

        half _Glossiness;
        half _Metallic;
        half _BumpScale;
        fixed4 _Color;

        // Add instancing support for this shader. You need to check 'Enable Instancing' on materials that use the shader.
        // See https://docs.unity3d.com/Manual/GPUInstancing.html for more information about instancing.
        // #pragma instancing_options assumeuniformscaling
        UNITY_INSTANCING_BUFFER_START(Props)
            // put more per-instance properties here
        UNITY_INSTANCING_BUFFER_END(Props)

        void surf (Input IN, inout SurfaceOutputStandard o)
        {
            // Albedo comes from a texture tinted by color
            fixed4 c = tex2D (_MainTex, IN.uv_MainTex) * _Color;
            o.Albedo = c.rgb;
            // Metallic and smoothness come from slider variables
            o.Metallic = _Metallic;
            o.Smoothness = _Glossiness;
            o.Alpha = c.a;

            fixed4 n = tex2D( _BumpMap, IN.uv_MainTex);
            o.Normal = UnpackScaleNormal(n, _BumpScale);
        }
        ENDCG
    }
    FallBack "Transparent/Cutout/VertexLit"
    
}

このシェーダをアサインしたマテリアルを用意して適当なテクスチャを貼ってあげます。

その際テクスチャのアルファに先程生成したテクスチャを入れ込んでおきます。

するとこの様に表示されます。

youtu.be

 

@restに座標を保持して戻す時に削れたpointが戻らないのは当然としても

生成されたpointがちゃんと良い感じの場所に戻ってくれるのは凄いと思います。

中で一体どういう処理が走ってるんでしょうか?気になります。 

累積アルファのカットオフはテクスチャパターンアニメーションでやるより

遥かに質も向上しメモリも削減出来る賢いやり方なので

覚えておいて損は無いでしょう。

 

さて今回はここまでです、いかがでしたでしょうか?
ゲームに出力する部分は様々な手法を考慮し工夫する必要があります。

現状ビルボードが主流のゲームエフェクトですがこんなトリックを用いる事で思いがけない表現をプレイヤーの方々にお見せし感動体験を演出する事も可能になるのです。

ゲームエンジン側の知識もフル動員すればもっと凄いものが表現出来るでしょう。

勉強する事は山程ありますし考えなければいけない事もいっぱいあります。

ですがやりたい表現が達成出来た時の喜びもまたひとしおです。

次回のHoudiniの記事もお楽しみに。 

 

この記事に興味を持って頂けた方は弊社で私達と一緒に働いてみませんか?

弊社ではHoudiniに興味を持って取り組めるような人を募集しています!
我こそはという方、興味のある方は以下のリンクを是非クリックしてみてください。

 
sega-games.co.jp

 

©SEGA

 

Powered by はてなブログ