Houdini 19.0 Solaris

Karma用レンズシェーダの作成

On this page

概要

KarmaはHoudiniの新しいレンダラーで、Mantraの代わりになるように設計されています。

レンズシェーダは、レンダラーによって実行されるcvex関数を定義して、レイトレーサーから送信される光線の原点と方向を生成します。

Karmaでのレンズシェーダは、光線の方向と原点だけでなく、光線のカラー(色合い)、(シャッター)時間での光線の配置、光線のクリップ範囲も定義することができます。

Houdiniには、既製のPhysical LensレンズシェーダVOPが用意されています。 このVOPには、ローリングシャッター、色収差、ティルト/シフトに限らず他にも多くのレンズ効果のパラメータが備わっています。 上級ユーザは、好きなように完全にVEXでカスタムシェーダ関数を記述することができます。

How to

To...Do this

Physical Lensノードをセットアップする

  1. LOPネットワーク(例えば、/stage)内で、レンダリングに使用したいカメラをセットアップします。

  2. そのカメラノードの横で、Material Networkノードを作成します。

  3. そのMaterial Networkノードの中に入って、Physical Lensノードを作成します。

    このPhysical Lensノード上のパラメータを使用して、ローリングシャッター、色収差、ティルト/シフトなどの効果をセットアップします。

  4. 上位階層に戻ってカメラノードを選択します。 Karma タブをクリックします。

    • Use Lens Shader の隣りにあるドロップダウンメニューを“Set or Create”に設定してから、 Use Lens Shader を有効にします。

    • Lens Shader VOP の隣りにあるドロップダウンメニューを“Set or Create”に設定してから、テキストフィールドの隣にあるノード選択アイコンをクリックして開いたウィンドウを使用して、先ほど作成したPhysical Lensノードを探して選択します。

  5. Karmaを使用してシーンビューアをカメラ視点に切り替えると、Karmaはあなたが設定したPhysical Lensシェーダを使用してシーンをレンダリングするはずです。

VEXソースコードからレンズシェーダノードを作成する

  1. cvexシェーダ用ソースコードを.vflファイル(例えば、my_lens_shader.vfl)として記述します。以下のサンプルを参照してください。

  2. ソースコード内にVEXプラグマを使用することで、そのコードからノードインターフェースが生成されるようにすることができます(例えば、ノード上に人が読めるラベルを置いたり、パラメータの見た目と範囲を設定することができます)。

  3. vccを使用してVEXコードをVOPアセットにコンパイルします:

    vcc -O vop -l my_lens_shader.hda my_lens_shader.vfl
    
  4. 生成された.hdaファイルがHoudini起動時に読み込まれるようにするために、それをHOUDINIPATH/otlsのどこか(例えば、$HOUDINI_USER_PREF_DIR/otls/my_lens_shader.hda)に配置します。

  5. Houdiniで、そのコンパイル済みのVOPアセットのインスタンスを作成します。

  6. カメラノードで、 Use Lens Shader を有効にし、 Lens Shader VOP にそのレンズシェーダノードのノードパスを設定します。

VOPネットワークからレンズシェーダノードを作成する

  1. Material Networkノードの中で、CVEX Shader Builder VOPを作成します。

  2. そのCVEX Shader Builder VOPの中に入って、レンズシェーダを定義したVOPネットワークを作成します。

    Parameter VOPBind VOPを使用することで、入力パラメータとエクスポートパラメータを定義することができます。

  3. そのCVEX Shader Builder VOPからデジタルアセットを作成します(CVEX Shader Builder VOPを右クリックして Create Digital Asset を選択します)。 ノード名とノードラベルとHDAの保存先を設定して Accept を押すと、その新しいアセットのType Propertiesウィンドウが開くので、 Save タブをクリックして、 Save Cached Code を有効にします。そして Accept を押します。

    (現在のところ、USDにはレンズシェーダを直接レンダラーに渡す方法が用意されておらず代わりに外部.hdaファイルの参照と引数値を含んだ暗号された文字列をレンダラーに渡すことになるので、レンズシェーダを.hdaファイルに格納することが必要になります。)

  4. カメラノードで、 Use Lens Shader を有効にし、 Lens Shader VOP にそのレンズシェーダノードのノードパスを設定します。

レンズシェーダの引数

引数のほとんどは、単に様々なカメラパラメータの値をシェーダに渡すだけです。

int ix

X軸のピクセル座標。

int iy

Y軸のピクセル座標。

float focal

カメラの焦点距離(Focal Length)。

float focus

カメラのフォーカス範囲(Focus Distance)。

float fstop

カメラのF-Stop(f/NのN)。

float aperture

カメラの絞り幅。

float orthowidth

カメラの正投影幅。

vector4 viewport

NDC(正規デバイス座標)空間の{xmin, xmax, ymin, ymax}で表現されたカメラウィンドウ。

int seed

現行ピクセル固有のシード値。

int sampleindex

このピクセルが処理された回数( パスインデックス とも言います)。

vector4 datawindow

矩形で表現されたデータウィンドウ(空間はNDCではなくピクセル座標)。

float aspect

表示ウィンドウのアスペクト比(幅を高さで割った値)。

int xres

表示ウィンドウのピクセル幅。

int yres

表示ウィンドウのピクセル高さ。

int isRHS

RHS(Right-Hand Space:右手空間)で指定された関数に渡された値を解釈するかどうか。 シェーダがこの引数を受け取らない場合、Houdiniは常に左手空間で値を渡します。

この引数はRHSで動作するKarmとの互換性を良くするためにあり、移植性を高くします。

float &Time

光線が送信されたシャッター時間内のポイントを指定した(0.0から1.0の)単位時間。

渡された値と異なる値を出力するのは、ローリングシャッターや他の種類のモーションブラー効果の設定次第です。

vector2 &clippingrange

関数に渡された値は、(レンズシェーダが割り当てられたカメラから測定される)ニアクリップ距離とファークリップ距離を表現します。

あなたの関数でこれらの値を変更することで、光線のニア/ファーの制限(光線が移動可能な最小距離と最大距離)を変更することができます。

遠近法のトランスフォームを使用する場合は、距離の整合性を保つために光線方向のZでクリップ距離を調整してください。 他の投影では、クリップ範囲の解釈が異なる場合があります。

vector &P

ここには、送信される光線の原点を3D空間で設定します。

vector &I

ここには、送信される光線の方向をベクトルで設定します。

vector &tint

ここには、送信される光線のカラー/寄与度を設定します。

vector2 &jitter

ここには、あなたがピクセルに適用した相対ジッター(水平と垂直)を設定します。 あなた独自でxyを生成する場合(以下参照)は、この変数を出力する必要があります。

下位互換の引数

既存のMantraレンズシェーダ関数との下位互換用に以下の引数がサポートされています。 Karma用にゼロからレンズシェーダを記述している場合は、これらの引数を使用するのは止めてください 。代わりに上記に載せている引数のみを使用してください。

Karmaでは、dofxdofyの可能領域、“絞り(aperture)”の領域(実際には、CoCことCircle of Confusion:許容錯乱円径)はカメラの焦点距離とF-Stopに基づいています。

(Houdiniのカメラには絞りの高さと幅のパラメータがありますが、これは物理カメラの絞りではありません。色んな意味で、Houdiniのカメラは実際のセンサーサイズを表現し、あるいは、それが指定した寸法のスクリーンと考えることができるので、つまり、焦点距離だけ離れたところからピクセルを飛ばしています。)

float x

(ジッターされた)現行ピクセルのX座標で、カメラのビューポートのNDC値の範囲内に収まります。

float y

(ジッターされた)現行ピクセルのY座標で、カメラのビューポートのNDC値の範囲内に収まります。

float dofx

DOFオフセットのX座標で、(0, 0を中心に配置された)絞りのディスクサイズからサンプリングされます。

float dofy

DOFオフセットのY座標で、(0, 0を中心に配置された)絞りのディスクサイズからサンプリングされます。

int &valid

関数が実行された後に、光線が有効だったかどうかを示した1または0をここに設定します。 これを0に設定することは寄与度を{0, 0, 0}に設定することと同じです。

サンプル

単純な投影のサンプル

被写界深度を使ったプロジェクションマップを行なう単純なレンズシェーダ。

このサンプルでは、Mantraスタイルの引数を使用しています。 Mantraシェーダでは、ユーザがシェーダに渡す引数はzoomfocusです。 Karmaでfocusを指定しなかった場合、Karmaはカメラからのフォーカス範囲(Focus Distance)を渡します。

cvex simplelens (
    // 入力
    float x = 0;
    float y = 0;
    float Time = 0;
    float dofx = 0;
    float dofy = 0;
    float aspect = 1;

    // 出力
    export vector P = 0;
    export vector I = 0;

    // シェーダ引数
    float zoom = 1;
    float focus = 1;
) {
    // 方向ベクトルを設定します。
    I = set(x * aspect / zoom, y / zoom, 1.0);
    // 位置を設定します。
    P = set(dofx, dofy, 0);
    // フォーカス範囲の方向の焦点を遠くにします。
    // Pがどこにあっても、ピクセルの光線はいつでも同じ場所のフォーカス平面に当たります。
    I *= focus;
    I -= P;
}

このサンプルでは、焦点距離と絞りの幅を使用して画角とズームを計算しているのではなく、代わりに明示的にzoomパラメータを使用しています。

標準投影

カメラの情報をシェーダに渡すことができるので、Karmaの標準投影に一致する投影を構築することができます。

cvex simplelens (
    // 入力
    float x = 0;
    float y = 0;
    float Time = 0;
    float dofx = 0;
    float dofy = 0;
    float aspect = 1;
    // カメラ
    float focus = 1;
    float focal = 1;
    float fstop = 0;
    float aperture = 1; // これは水平絞り(メートル)に合致することに注意してください。

    // 出力
    export vector P = 0;
    export vector I = 0;
) {
    // カメラのプロパティを使用して方向ベクトルを設定します。
    I = set( x * aperture * 0.5 * aspect / focal,
         y * aperture * 0.5 / focal,
         1.0 );
    // Set position
    P = set(dofx, dofy, 0);
    // フォーカス範囲の方向の焦点を遠くにします。
    // Pがどこにあっても、ピクセルの光線はいつでも同じ場所のフォーカス平面に当たります。
    I *= focus;
    I -= P;
}

このレンズシェーダは、通常の遠近法カメラと完全に一致するはずです。

正方形ボケ

古い円形ボケにうんざりするのが好きでないなら、あなたは正方形ボケが欲しいことでしょう。

しかし、dofxdofyは簡単に正方形にマップされないので、独自にそれらを生成することになります。 Karmaからピクセルのseedsampleindexをいただいてから、random_brjを使用して、それらに基づいて均一な分布の数列を取得します。

cvex simplelens (
    // 入力
    float x = 0;
    float y = 0;
    float Time = 0;
    // カメラ
    float focus = 1;
    float focal = 1;
    float fstop = 0;
    float aperture = 1; // これは水平絞り(メートル)に合致することに注意してください。
    float aspect = 1;
    // ランダムな数列生成用
    int seed = 0;
    int sampleindex = 0;

    // 出力
    export vector P = 0;
    export vector I = 0;
) {
    // カメラのプロパティを使用して方向ベクトルを設定します。
    I = set( x * aperture * 0.5 * aspect / focal,
         y * aperture * 0.5 / focal,
         1.0 );

    // 0-1単位ボックス内でランダムな被写界深度(DoF)ポイントを取得します。
    vector2 dof = random_brj(seed, sampleindex);
    float diameter = focal / fstop; // aperture/CoC直径を取得します。
    dof = (dof - 0.5) * diameter; // 単位ボックスを中心に配置して直径を設定します。
    P = set(dof.x, dof.y, 0);

    // フォーカス範囲の方向の焦点を遠くにします。
    // Pがどこにあっても、ピクセルの光線はいつでも同じ場所のフォーカス平面に当たります。
    I *= focus;
    I -= P;
}

これは、random_brjを使用してレンズシェーダで均一な分布の数列を生成するサンプルです。 ただし、忘れないでほしいことが1つあります: 複数の乱数を生成する場合、すべての乱数に対して同じシードを使用したくないものの、数列の基底としてピクセル固有のシードを 使用したくないです 。 上記のサンプルでは、単純に生成した乱数毎にピクセルシードと定数シードをXOR(排他的論理和)しています。

XとYの生成

以下は、レンズシェーダ内でrandom_brjを使用して完全にXとYを生成するサンプルです。

// ジッターと被写界深度(DoF)を生成するための単純なランダムシード
#define JITTER_SEED     0x98A208B1
#define DOF_SEED        0xA8B2440D

cvex simplelens (
    // Inputs
    int ix = 0;
    int iy = 0;
    // カメラ
    float focus = 1;
    float focal = 1;
    float fstop = 0;
    float aperture = 1;
    float aspect = 1;
    // ランダムな数列生成用
    int seed = 0;
    int sampleindex = 0;
    // スクリーン情報
    vector4 datawindow = 0;
    vector4 viewport = { -1, 1, -1, 1 };
    int xres = 0;
    int yres = 0;
    int isRHS = 0;

    // 出力
    export vector P = 0;
    export vector I = 0;
    export vector2 jitter = 0; // XとYを生成する場合は、これを返す必要があることに注意してください。
) {
    // データウィンドウを参照してピクセルを取得します。
    int rx = ix + int(datawindow[0]);
    int ry = iy + int(datawindow[2]);
    // ジッターを返す必要があります。
    jitter = random_brj(seed ^ JITTER_SEED, sampleindex);
    float x = float(rx) + jitter.x;
    float y = float(ry) + jitter.y;

    // NDCにマップします。
    x = efit(x, 0, float(xres), viewport[0], viewport[1]);
    y = efit(y, 0, float(yres), viewport[2], viewport[3]);

    // カメラのプロパティを使用して方向ベクトルを設定します。
    I = set( x * aperture * 0.5 * aspect / focal,
         y * aperture * 0.5 / focal,
         -1.0 ); // RHSが想定されていることに注意してください。

    // 0-1単位ボックス内でランダムな被写界深度(DoF)ポイントを取得します。
    vector2 dof = random_brj(seed, sampleindex);
    float diameter = focal / fstop; // aperture/CoC直径を取得します。
    dof = (dof - 0.5) * diameter; // 単位ボックスを中心に配置して直径を設定します。
    P = set(dof.x, dof.y, 0);

    // フォーカス範囲の方向の焦点を遠くにします。
    // Pがどこにあっても、ピクセルの光線はいつでも同じ場所のフォーカス平面に当たります。
    I *= focus;
    I -= P;

    if (!isRHS)
    {
        P.z = -P.z;
        I.z = -I.z;
    }
}
  • このサンプルは、データウィンドウ、ビューポート、XとYの解像度、isRHSなどの新しいKarma引数を使用しています。

  • これらの引数は、ピクセルポジションをNDCのXとYにマップすることができます。

  • このサンプルでは、右手空間(RHS)を想定し、isRHSがFalseならZ座標を反転します。

  • xyのパラメータを使用しない場合は、jitterを出力する必要があります。その場合にjitterを出力しなかければ、未定義の挙動になります。

色合い

Mantraレンズシェーダとは違って、Karmaレンズシェーダは光線に色合いを付けることができるので、色収差や アナグリフ3D などの効果を作成することができます。

このサンプルでは、カメラにアナグリフカメラのような挙動をさせます。 典型的には、これは2つのカメラを作成し、両目の間隔を空けて(約65mm)、片目を赤、もう片目をシアンにし、それら2枚の画像を重ね合わせることで行なっていました。

このレンズシェーダは、それを1つのカメラだけで実現することができます。 これは、光線の半分を左目の位置からシアンの色合いで送信し、残り半分を右目の位置から赤の色合いで送信します。 (これを動作させるのにはテクニックがあって、実際に3Dの被写界深度を取得するためにシェーダが光線の焦点を合わせる必要があります。)

このサンプルのデモファイルが$HFS/houdini/help/files/anaglyph.hip.gz$HFS/houdini/help/files/anaglyphlens.hdaにあります。

// 指定したシードとXOR(排他的論理和)されるランダムシード
#define JITTER_SEED         0x98A208B1
#define COINTOSS_SEED       0xA8B2440D

cvex
anaglyphlens(
    // 入力
    int ix = 0;
    int iy = 0;
    // カメラ
    float focus = 1;
    float focal = 1;
    float fstop = 0;
    float aperture = 1;
    float aspect = 1;
    // ランダムな数列生成用
    int seed = 0;
    int sampleindex = 0;
    // スクリーン情報
    vector4 datawindow = 0;
    vector4 viewport = { -1, 1, -1, 1 };
    int xres = 0;
    int yres = 0;
    int isRHS = 0;

    // 出力
    export vector P = 0;
    export vector I = 0;
    export vector tint = 1;
    export vector2 jitter = 0; // XとYを生成する場合は、これを返す必要があることに注意してください。

    // シェーダ引数
    float dist = 0.065;
    vector rcolor = { 1, 0, 0 };
    int enablecustomleft = 0;
    vector lcolor = { 0, 1, 1 };
    )
{
    // データウィンドウを参照してピクセルを取得します。
    int rx = ix + int(datawindow[0]);
    int ry = iy + int(datawindow[2]);
    // ジッターを返す必要があります。
    jitter = random_brj(seed ^ JITTER_SEED, sampleindex);
    float x = float(rx) + jitter.x;
    float y = float(ry) + jitter.y;

    // NDCにマップします。
    x = efit(x, 0, float(xres), viewport[0], viewport[1]);
    y = efit(y, 0, float(yres), viewport[2], viewport[3]);

    // カメラのプロパティを使用して方向ベクトルを設定します。
    I = set( x * aperture * 0.5 * aspect / focal,
         y * aperture * 0.5 / focal,
         -1.0 ); // RHSが想定されていることに注意してください。

    vector use_lcolor = lcolor;
    if (!enablecustomleft)
        use_lcolor = 1 - rcolor; // 右目の色の補色を取得します。

    float cointoss = random_brj(seed ^ COINTOSS_SEED, sampleindex);
    cointoss = fit01(cointoss, 0, 2);
    if (cointoss < 1)
    {
        tint = rcolor;
        P.x = 0.5 * dist;
    }
    else if (cointoss < 2)
    {
        tint = use_lcolor;
        P.x = -0.5 * dist;
    }

    // 上記と同じフォーカス...
    I *= focus;
    I -= P;

    if (!isRHS)
    {
        P.z = -P.z;
        I.z = -I.z;
    }
}

ローリングシャッター

cvex
rollingshutter(
    // 入力
    int ix = 0;
    int iy = 0;
    // カメラ
    float focus = 1;
    float focal = 1;
    float fstop = 0;
    float aperture = 1;
    float aspect = 1;
    // ランダムな数列生成用
    int seed = 0;
    int sampleindex = 0;
    // スクリーン情報
    vector4 datawindow = 0;
    vector4 viewport = { -1, 1, -1, 1 };
    int xres = 0;
    int yres = 0;
    int isRHS = 0;

    // エクスポートされる入力
    export float Time = 0;

    // 出力
    export vector P = 0;
    export vector I = 0;
    export vector2 jitter = 0; // XとYを生成する場合は、これを返す必要があることに注意してください。

    // シェーダ引数
    float blurriness = 0.05; // ブラー量(0-1)
) {
    // データウィンドウを参照してピクセルを取得します。
    int rx = ix + int(datawindow[0]);
    int ry = iy + int(datawindow[1]);
    // ジッターを返す必要があります。
    jitter = random_brj(seed ^ JITTER_SEED, sampleindex);
    float x = float(rx) + jitter.x;
    float y = float(ry) + jitter.y;

    // NDCにマップします。
    x = efit(x, 0, float(xres), viewport[0], viewport[1]);
    y = efit(y, 0, float(yres), viewport[2], viewport[3]);

    // カメラのプロパティを使用して方向ベクトルを設定します。
    I = set( x * aperture * 0.5 * aspect / focal,
         y * aperture * 0.5 / focal,
         -1.0 ); // RHSが想定されていることに注意してください。

    // ローリングシャッターを実装します:
    // 光線のY軸の位置に基づいて光線の時間位置をマップします。
    float time_offset = fit(y, viewport[2], viewport[3], 0, 1);
    // Timeは0から1の乱数で、それを制限することでblurrinessをシャッター長より短く制限します。
    Time = Time * blurriness + time_offset;

    // 上記と同じフォーカス...
    I *= focus;
    I -= P;

    if (!isRHS)
    {
        P.z = -P.z;
        I.z = -I.z;
    }
}

Solaris

USD

ジオメトリ

  • SOPをUSDに取り込む方法

    HoudiniがSOPジオメトリをUSDに変換する方法、その工程を制御する方法の詳細。

  • Component Builder

    Component Builderツールは、マテリアル、バリアント、ペイロード、レイヤーをサポートし、SOPからUSDモデルを作成するためのネットワークスニペットを配置します。

レイアウト

  • Editノード

    ビューア内でインタラクティブにPrimsをトランスフォームさせます。物理衝突を使用して、プロップを現実的に配置することができます。

  • Layoutノード

    インスタンス化されたUSDアセットをシーンに取り込むツールが備わっています。個々にコンポーネントを配置したり、カスタマイズ可能なブラシを使って色々な方法でコンポーネントをペイント/スキャッターしたり、既存のインスタンスを編集することができます。

  • カスタムレイアウトブラシ

    Layout LOPの挙動をカスタマイズして利用可能なレイアウトブラシデジタルアセットの作成方法。

シェーディング

  • シェーダフレームワーク

    シェーダノードのUSDプリミティブへの変換を含む、Solarisシェーディングフレームについて説明しています。

  • KarmaでのMaterialXの使い方

    HoudiniにはMaterialXシェーダノードに呼応させたVOPノードが用意されています。これらのノードを使用してシェーダネットワークを構築したり、既存のMaterialXシェーダをインポートすることで、(HoudiniのUSDレンダラーの)KarmaでMaterialXシェーダノードを利用することができます。

  • /solaris/materialx_nodes

Karmaレンダリング

チュートリアル