On this page |
概要 ¶
KarmaはHoudiniの新しいレンダラーで、Mantraの代わりになるように設計されています。
レンズシェーダは、レンダラーによって実行されるcvex
関数を定義して、レイトレーサーから送信される光線の原点と方向を生成します。
Karmaでのレンズシェーダは、光線の方向と原点だけでなく、光線のカラー(色合い)、(シャッター)時間での光線の配置、光線のクリップ範囲も定義することができます。
Houdiniには、既製のKarma Physical LensレンズシェーダVOPが用意されています。 このVOPには、ローリングシャッター、色収差、ティルト/シフトに限らず他にも多くのレンズ効果のパラメータが備わっています。 上級ユーザは、好きなように完全にVEXでカスタムシェーダ関数を記述することができます。
How to ¶
To... | Do this |
---|---|
Karma Physical Lensノードをセットアップする |
|
VEXソースコードからレンズシェーダノードを作成する |
|
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でクリップ距離を調整してください。 他の投影では、クリップ範囲の解釈が異なる場合があります。
float &lambda
色収差から選択された波長。内部使用のみ。
vector &P
ここには、送信される光線の原点を3D空間で設定します。
vector &I
ここには、送信される光線の方向をベクトルで設定します。
vector &tint
ここには、送信される光線のカラー/寄与度を設定します。
vector2 &jitter
ここには、あなたがピクセルに適用した相対ジッター(水平と垂直)を設定します。
あなた独自でx
とy
を生成する場合(以下参照)は、この変数を出力する必要があります。
既存のMantraレンズシェーダ関数との下位互換用に以下の引数がサポートされています。 Karma用にゼロからレンズシェーダを記述している場合は、これらの引数を使用するのは止めてください 。代わりに上記に載せている引数のみを使用してください。
Karmaでは、dofx
とdofy
の可能領域、“絞り(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シェーダでは、ユーザがシェーダに渡す引数はzoom
とfocus
です。
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; }
このレンズシェーダは、通常の遠近法カメラと完全に一致するはずです。
正方形ボケ ¶
古い円形ボケにうんざりするのが好きでないなら、あなたは正方形ボケが欲しいことでしょう。
しかし、dofx
とdofy
は簡単に正方形にマップされないので、独自にそれらを生成することになります。
Karmaからピクセルのseed
とsampleindex
をいただいてから、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座標を反転します。 -
x
とy
のパラメータを使用しない場合は、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; } }