はじめに
前回の記事: 【OSL入門】MayaでArnoldのシェーダーを書く①
前回の記事では、MayaのaiOslShaderでOSLを実行する基本手順を解説しました。
今回は、OSLの標準関数と、位置・法線・視線方向を取得するグローバル変数を扱います。
最後に法線と視線の内積を使い、球体の輪郭が明るくなるフレネル風マスクを実装します。
標準関数
前回の記事では、輝度を四則演算により求めました。
しかし、四則演算だけではできることに限界があります。
OSL ではたくさんの関数が用意されており、計算に使用できます。
いくつか例を挙げると、以下のようなものがあります。
| 関数 | 用途 |
|---|---|
| cos(), sin(), acos(), asin(), atan()… | 三角関数を求める関数 |
| pow(), log(), log10(), exp(), sqrt(), mod() | 累乗、対数、平方根等を求める関数 |
| floor(), ceil(), trunc(), round() | 丸め関数(切り捨てや四捨五入など) |
| cross(), length(), distance(), reflect() | ベクトルの外積や長さ、距離などを求める関数 |
| smoothstep(), noise() | エルミート補間やノイズ関数 |
| texture(), substr() | テクスチャや文字列操作などその他色々 |
例えば、floor関数を用いることで、小数を切り捨てることができます。
float value = 4.3;
float result = floor(value); // 結果: 4.0
length関数はベクトルの長さを求めることができます。
vector vec = vector(1.0, 2.0, 3.0);
float result = length(vec); // 結果: 3.741657
実は前回四則演算によって求めた輝度も、luminance()という関数を用いることで簡単に計算することができます。
前回書いたコードをluminance()に置き換えると以下のようになります。
shader sample_shader(
color inputColor = color(1.0, 0.0, 0.0),
output color outColor = 0)
{
float Y = luminance(inputColor); // 変更点
outColor = color(Y);
}
関数の種類やその使い方は OSL のドキュメント(7. Standard Library Functions)で確認できます。
グローバル変数
レンダリングする点のことをシェーディングポイントといいますが、このシェーディングポイントのさまざまな情報はシェーダーを書く上でとても重要です。
例えば法線情報が分かれば、上を向いた面だけ色を変えたり、ライトとの角度を計算して明るさを求めたりできます。
OSL ではシェーディングポイントの情報をグローバル変数を介して取得することができます。
グローバル変数は、自分で宣言しなくても特定の情報を参照できるようにあらかじめ用意された変数です。
例えば、シェーディングポイントの座標は、グローバル変数 P を参照することで取得できます。
OSLはレンダリングするシェーディングポイントごとに計算処理が行われ、そのポイントの座標が変数Pから取得されます。
※ピクセルごとにシェーディングポイントが1つずつとは限りません。アンチエイリアス、反射、間接光などにより、複数回評価される可能性があります。
以下は座標によってレンダリングされる色を変える例です。

shader sample_shader(
output color outColor = 0
){
outColor = color(P);
}
このOSLでは、例えば P が(1.0, 0.0, 0.0)の時、RGB=(1.0, 0.0, 0.0)となり、赤くレンダリングされます。
※ P は0~1の範囲に収まらないことに注意してください。座標値を可視化する簡易例として扱います。
サンプルコードからわかる通り、P は通常の変数とは異なり事前に宣言することなく使用することができます。
代表的なグローバル変数をいくつか例示します。
| 変数名 | 型 | 説明 |
|---|---|---|
| P | point | シェーディングポイントの座標 |
| I | vector | 入射レイからシェーディングポイントに向かうベクトル |
| N, Ng | normal | 法線ベクトル。Ng はスムージングやバンプが反映されない |
| u, v | float | シェーディングポイントのUV座標(厳密にはパラメトリック座標、また、テクスチャの原点は左上隅であることに注意。テクスチャについては次回記事で扱います。) |
グローバル変数はOSL のドキュメント(6.6. Global variables)で確認できます。
フレネル風シェーダー
フレネル反射
フレネル反射とは、光が媒質の境界面へ入射する角度や屈折率によって反射率が変化する現象です。
消灯したスマートフォンの画面を正面から見ると、反射はあまり目立ちませんが、画面を傾けて浅い角度から見ると、周囲の景色が強く映り込みます。
これもフレネル反射による現象です。
本記事では厳密なフレネル式ではなく、標準関数やグローバル変数を使用して、メッシュの面がカメラの方を向いていないほど明るくなるフレネル”風”シェーダーを作成します。
内積
”面がカメラの方向を向いているかどうか”は、内積を計算することで求めることができます。
内積は簡単に言えば、2つの矢印がどのくらい向きが似ているかを求める計算です。
CG 分野において頻出する演算なので覚えておくといいですが、OSL では、標準関数dot()を用いることで簡単に計算することができます。
2つのベクトルを v1, v2 とすると、以下のように書くことで内積を求めることができます。
vector v1 = vector(1.0, 0.0, 0.0);
vector v2 = vector(0.0, 1.0, 0.0);
float d = dot(v1, v2);
v1、v2 の長さが1、つまり単位ベクトルであれば、内積の結果は-1.0から1.0の間の値をとります。
2つのベクトルの向きが真逆に近くなるほど-1.0に、向きが近くなるほど1.0に近づきます。
2つのベクトルが垂直(直角)のとき、内積は0になります。
フレネル風シェーダーでは、シェーディングポイントにおける法線ベクトルとシェーディングポイントから見たカメラの方向ベクトルの内積をとり、これを色として出力します。

これにより、メッシュの面がカメラの方向を向くほど暗くなる表現が可能です。
実装
以下のコードから始めます。
shader fresnel_shader(
output color outColor = 0
){
}
法線ベクトルと、カメラの方向ベクトルは、グローバル変数 N、I で参照することができます。
ただし、I はシェーディングポイントからカメラに向かうベクトルではなく、カメラからシェーディングポイントに向かうベクトルなので、-(マイナス)をつけて逆向きにします。
※正確には、Iは入射レイがシェーディングポイントへ向かう方向であり、反射や屈折などの二次レイではカメラからとは限りません。
-I; // シェーディングポイントからカメラに向かうベクトル
念のため、 I と N は関数 normalize() で明示的に正規化(単位ベクトルに変換)しておきます。
-normalize(I); // カメラに向かう長さが1のベクトル
normalize(N); // 長さが1の法線ベクトル
関数 dot() を使用して、この2つのベクトルの内積を求めます。
dot( normalize(N), -normalize(I) );
また、正規化した法線と視線方向の内積は、-1.0から1.0の範囲になります。
今回はカメラ側を向いている面だけを使用するため、clamp()で0.0から1.0の範囲に制限して変数へ代入します。
負の値は0.0として扱われます。
float d = clamp(dot(normalize(N), -normalize(I)), 0.0, 1.0);
変数 d は、カメラの方を向いている面ほど1.0に近づきます。
R, G, B 値にそのまま d を設定してしまうと、球体の輪郭に近づくほど暗くなってしまうので、 1.0 – d として、色変化を逆にします。
outColor = color(1.0 - d, 1.0 - d, 1.0 - d);
事前に変数に代入して以下のように書くこともできます。
float rim = 1.0 - d;
outColor = color(rim, rim, rim);
さらに、色 C の R、G、B の値が同じ場合は、color(R, G, B) の代わりに、省略して color(C) と書くことができます。
outColor = color(1.0 - d);
ここまでの内容を踏まえて OSL を書くと、以下のようになります。
shader fresnel_shader(
output color outColor = 0
){
float d = clamp(dot(normalize(N), -normalize(I)), 0.0, 1.0);
outColor = color(1.0 - d);
}
前回記事の手順で aiOslShader を作成し、Code 欄へ完成コードを入力してコンパイルします。
outColor を Shading Group または使用するマテリアルへ接続し、球体へ割り当てて Arnold でレンダリングすると、外側に行くほど白くレンダリングされます。

調整用の変数を導入することで、幅を調整できます。
以下のコードでは、標準関数 pow と、調整用の入力 rimPower により、outColor を調整しています。
shader fresnel_shader(
float rimPower = 1.0,
output color outColor = 0
){
float d = clamp(dot(normalize(N), -normalize(I)), 0.0, 1.0);
outColor = color(pow(1.0 - d, rimPower));
}

rimPower には0より大きい値を指定します。
1.0より大きくすると明るい輪郭が狭くなり、0.0より大きく1.0より小さい値にすると広くなります。
まとめ
今回は、OSL の標準関数とグローバル変数を使い、視線方向に応じて輪郭が明るくなるフレネル風マスクを作成しました。
- 標準関数 normalize() や dot() でベクトルの正規化や内積を行う
- グローバル変数 I, N からシェーディングポイントの情報を参照する
次回に続きます。



コメント