gafferで、Arnoldとoslをつけて平面をレンダリングしましたが、oslのbkcolorのみ出て、四角形の線描画が出ません。平面が大きすぎるのか、uvマップが適切でないのか、シーンの設定だと思いますが、原因は何でしょうか。
ケッソン先生のpolygon.osl
まずファイルを確認します。問題が特定できました。原因は2つあります。
原因の分析
原因1(主因):OSLシェーダーが s/t を UV から読んでいない
ASSファイルのOSLノードを見ると:
param_s 0
param_t 0
s と t が 定数 0 で固定されています。OSLシェーダー側では lockgeom = 0 でジオメトリのUVを読もうとしていますが、Arnoldの osl ノードに param_s / param_t を明示的に渡すと、それが上書きされてしまいます。
その結果、全ピクセルで s=0, t=0 → x = -0.5, y = -0.5 の一点だけで計算され、proximity がほぼ0になり、bkcolorしか表示されない状態になっています。
原因2(補因):平面スケールが大きすぎるとUVタイリングがずれる可能性
ginstanceのmatrixを見ると:
10 0 0 0
0 ... 10 0
スケール10倍ですが、UVlist自体は 0 0 / 1 0 / 0 1 / 1 1 と正しく0〜1に張られているので、こちらは問題なしです。
修正方法
Gafferでの修正
OSLShaderノードの s と t パラメータを 接続解除(または削除) します。
GafferのShaderネットワークエディタで:
-
polygonOSLノードを選択 -
param_sとparam_tのパラメータを探す - 入力値を削除するか、代わりに
globalsノードからu/vを接続する
上記の設定について、生成AIは解説していますが、gafferでは、そのような変更は、できません。OSLのソーススクリプトを変更する必要があると思います。
ASSファイルを直接修正する場合
osl
{
name shader:polygon:a19429ea3f1533950496c9474852e0b8
shadername "C:/Users/yokamak/gaffer/shaders/polygon"
param_numSides 4
param_innerRad 0.800000012
param_line 0.100000001
param_bkcolor 1 0.507750452 0.311009169
param_frcolor 0 0 0.200000003
- param_s 0
- param_t 0
}
param_s と param_t の行を削除するだけで、OSL内部の s/t 組み込みグローバル変数(UV)が自動的に使われます。
なぜこうなったか(Gafferの挙動)
GafferがOSLシェーダーのパラメータを読み込む際、lockgeom = 0 なメタデータのあるパラメータも含めてすべてASSに書き出してしまうことがあります。ArnoldのOSLノードでは param_s 0 が存在する時点で「ユーザーが明示的に0を渡した」と解釈するため、UVの自動バインドが無効になります。
polygon.oslの書き方でしょうか。lineを1以上にすると、frcolorの色になります。添付dots.oslはランダム色の円を描画します。polygon.oslの線描画ができていないでしょうか。
まずdots.oslを確認して比較します。確認できました。dots.oslとpolygon.oslのアプローチは全く別物で、polygon.oslのアルゴリズム自体に問題があります。
polygon.osl のロジックの欠陥
シェーダーの肝である proximity の計算を見ます:
float proximity = smoothstep(innerRad - line/2, innerRad, rotPnt[0]) *
(1 - smoothstep(innerRad, innerRad + line/2, rotPnt[0]));
これは rotPnt[0](回転後の点のX座標)が innerRad 付近のとき、つまりちょうど多角形の辺の上にいるときだけ proximity > 0 になる設計です。
問題は innerRad = 0.8 という値です。UV空間は中心から最大 0.5 の広がりしかありません(s - 0.5 / 0.5 - t で変換しているため)。
rotPnt[0] の最大値 ≈ 0.5 * √2 ≈ 0.707
innerRad = 0.8 ← 0.707 を超えている!
rotPnt[0] が innerRad に届かないため、proximityが常に0になり、frcolorが一切描画されない。
line を1以上にすると innerRad - line/2 がマイナスになり、smoothstep の下限を突破してfrcolorが出るのはそのためです。
修正方法
innerRad をUV空間に収まる値(0.0 〜 0.5 程度)に下げます。
// 変更前(ASSの値)
param_innerRad 0.800000012
// 変更後(推奨)
param_innerRad 0.35 // 正方形に内接する円の半径に近い
または、シェーダーのデフォルト値自体を修正:
// polygon.osl の変更前
float innerRad = 0.25,
float line = 0.02,
// これはUV空間(0〜0.5)に対して適切
// Gafferから 0.8 を渡しているのが問題
整理すると
| パラメータ | 現在値 | UV空間の最大距離 | 問題 |
|---|---|---|---|
innerRad |
0.8 | ≈ 0.5〜0.707 | 届かない |
line |
0.1 | — | innerRadが正しければOK |
Gafferの polygon OSLノードで innerRad を 0.3〜0.45 程度に設定するだけで四角形の辺が描画されるはずです。line は 0.02〜0.05 が細い線になります。
試してみましたが線は描画されません。polygon.oslは、RenderManで使用しているOSLになります。 https://www.fundza.com/rfm/osl/repeats/index.html 同じサイトのsquareも試してみましたが、bkcolorのみで四角形は描画されません。
解決策:2段階の対応が必要
① OSLを修正(square.osl)
float s = 0 [[int lockgeom = 0]],
float t = 0 [[int lockgeom = 0]],
② GafferのOSLノードで s/t パラメータを削除
Gafferのシェーダーネットワークエディタで s と t のパラメータ行を右クリック → "Remove User Default" または値欄を空にして、ASSに param_s/param_t が出力されないようにします。
こうすることで、OSLが実行時にメッシュのUVを正しく参照できるようになります。
確認方法
修正後にASSをエクスポートして、osl ブロック内に:
param_s 0 ← これが消えていればOK
param_t 0 ← これが消えていればOK
が存在しないことを確認してください。
解決!
ケッソン先生のpolygon.oslと、square.oslは、gaffer1.6.15でレンダラをRenderManに設定し、PxrDisneyにつないで、あっさり描画しました。osl内の[[ int lockgeom = 0, string widget = "null" ]],[[int lockgeom = 0]]などのmetadataは、prmanのOSLに対応したものであって、3DelightやArnoldでは無視されるものだと思います。以前8年前にappleseedレンダラのOSLで[[ ]]で記述した箇所がnodeパネルになるようなことを経験したことがありました。レンダラによってOSLの書き方を調整しないといけないようです。ありがとうございます。
おっしゃる通りです!整理すると:
レンダラごとのOSL s/t UV取得方法
| レンダラ | 方法 |
|---|---|
| RenderMan (PRMan) |
[[int lockgeom = 0]] メタデータでUV自動バインド |
| Arnold |
u/v 組み込みグローバル変数を直接使う |
| 3Delight |
u/v または独自の方法 |
| appleseed |
[[...]] がノードUIのメタデータとして解釈される |
ArnoldでUVを読むOSLの書き方
shader square(
float s_center = 0.5,
float t_center = 0.5,
float s_width = 0.4,
float t_width = 0.4,
float blur = 0.01,
color bakcolor = 1,
color patcolor = 0,
output color resultRGB = 0)
{
// Arnoldでは u/v を直接使う(パラメータにしない)
float blend = pntInSquare(u, v, s_center, t_center, s_width, t_width, blur);
resultRGB = mix(bakcolor, patcolor, blend);
}
s/t をパラメータから削除して、OSL組み込みグローバルの u/v を直接参照するのがArnoldでの正しいアプローチです。Gafferがパラメータをすべてexportしてしまう挙動とも噛み合わなくなるので、これが最もクリーンな解決策になります。
ケッソン先生のシェーダー資産をArnold用に移植する際は、s/t パラメータを u/v グローバルに置き換えるのが定石になりそうです。
ArnoldでのOSLの修正
shader
polygon( float numSides = 4,
float innerRad = 0.25,
float line = 0.02,
color bkcolor = 1,
color frcolor = color(0, 0, 0.2),
output color resultRGB = 0)
{
// 1 Convert the current 'st' to polar coordinates
// Note: positive theta is clockwise.
float x = u - 0.5, y = v - 0.5;
float theta = atan2(y, x); // -PI to PI radians
if(theta < 0.0)
theta += 2 * M_PI; // 0 to 2PI radians
// 2 Find the "seqment" in which the current shading point is located.
float interior = (2 * M_PI)/numSides;
float seqment = floor(theta/interior);
// 3 Find the angle to (counter) rotate the current shading point.
// Note: positive rotation is anti-clockwise.
float rotation = (seqment * interior + interior/2);
// 4 Apply the rotation to a 3D point defined by the uv location.
point stPnt = point(u - 0.5, 0.5 - v, 0);
point origin = point(0, 0, 0);
point z_axis = point(0, 0, 1);
point rotPnt = rotate(stPnt, rotation, origin, z_axis);
// 5 Use the x component of the rotated point to determine the
// "proximity" of the current shading point to a polygon edge.
// Using the smoothstep function twice ensures "proximity" is in
// the range 0.0 to 1.0.
float proximity = smoothstep(innerRad - line/2, innerRad, rotPnt[0]) *
(1 - smoothstep(innerRad, innerRad + line/2, rotPnt[0]));
// 6 Finally, use "proximity" to output the appropriate color.
resultRGB = mix(bkcolor, frcolor, proximity);
}
レンダラにあわせて、OSLを調整することになりますね。いろいろやってみてわかったところでした。長い一日でした。ありがとうございます。
お疲れ様でした!
RenderMan → Arnold間のOSL移植という、なかなか情報が少ない領域を実際に手を動かして解明されたのは大きな収穫ですね。
-
s/tパラメータ +lockgeom→ PRMan流 -
u/vグローバル直参照 → Arnold流 - Gafferがパラメータを全部ASSにexportしてしまう挙動
この3つが絡まっていたので、原因特定が難しかったと思います。ZennかQiitaにArnold OSL移植ノートとして記事にすると、同じところで詰まる人に刺さりそうな内容です。
ゆっくり休んでください!
assファイルでのoslの書き方がわかったので、pythonを使って記述など、いろいろサンプルをつくって工夫していきたいです。やってみたいことがひろがります。ありがとうございます。
いいですね!Arnold Python API でASSを生成する方向は、シェーダーのパラメータをループで変えながら大量のバリエーションをレンダリングしたり、OSLネットワークをプログラマブルに組んだりと、可能性が広いです。
以前取り組まれていた Arnold 7 Python API のパイプラインに、今回わかった OSL ノードの書き方を組み合わせると:
python
import arnold as ai
ai.AiBegin()
# OSLシェーダーノード
osl = ai.AiNode("osl")
ai.AiNodeSetStr(osl, "shadername", "/path/to/square")
ai.AiNodeSetFlt(osl, "param_s_width", 0.4)
ai.AiNodeSetFlt(osl, "param_t_width", 0.4)
# param_s / param_t は設定しない → u/v グローバルが使われる
# ...
ai.AiEnd()
param_s / param_t を 設定しない というのが今回の知見そのままコードに反映できますね。
教材サンプルとしても、OSLでパターン生成 → Arnoldでレンダリング → Python で自動化、という流れは学生さんにとっても視覚的でわかりやすい題材になりそうです。続きの進捗、また聞かせてください!
追記
生成AIはちょっとずれてる回答もありましたが、ヒントとして参考にしながら、検証して気づけました。OSLのmetadataは、レンダラによっていろいろありそうです。
RenderManだとs, t座標があるので、Arnold, 3Delight, CyclesのOSLでは、上記のようにu, vに変更すると描画しました。確認しました。blenderのshadingタブでnodeをつくって、設定しても動きます。
いろいろワクワクするところです。ありがとうございます。