Metasequoia から mental ray 3.14 standalone 用の .mi ファイルを出力する自作エクスポータを検証していたところ、地面の平面が対角線でくっきり明暗に分かれるという現象に遭遇しました。
最初は法線の問題だと確信していました。実際、法線の問題も「あった」のですが、それを直しても症状は消えず、真犯人はまったく別の場所——ライト定義の origin 0 0 0 という一行でした。
原因の切り分け過程が教材的に面白かったので、記録として残します。
環境
- mental ray 3.14 standalone(Windows、
rayコマンド直叩き) - Metasequoia 4.7+ の Python スクリプトで
.miを直接出力 - ライティングは
mia_physicalsun+mia_physicalsky+ finalgather
DCC ツールを介さず、テキストの .mi ファイルを直接読み書きする、いつものコードファースト構成です。
症状
200×200 の平面(三角形2枚)にテクスチャを貼ってレンダリングすると、画面の下半分が暗くなります。境界は平面の対角線上。直射が当たっていない側は physicalsky からの間接光だけで照らされるため、うっすら水色に見えます。
object "obj_0"
...
group
-100 0 -100
100 0 -100
-100 0 100
100 0 100
...
p "" 0 1 2
p "" 3 4 5
end group
end object
「三角形2枚の平面が、対角線を境に明暗が分かれる」——この字面だけ見れば、誰でも法線か巻き方向を疑うと思います。私も疑いました。
仮説1:法線(巻き方向)——半分正解、でも真犯人ではない
p 文の頂点順から幾何法線を外積で検算すると、実際に**両方の三角形が下向き(-Y)**でした。
原因はエクスポータにあり、Metasequoia の面は「表から見て時計回り」、mental ray は「反時計回りが表」という規約の違いを変換していなかったためです。エクスポータ側で以下の2点を修正しました。
# 面法線:外積のエッジ順を入れ替えて mi 側の表向きに
face_n.append(vnorm(vcross(vsub(pos[fa.index[2]], pos[fa.index[0]]),
vsub(pos[fa.index[1]], pos[fa.index[0]]))))
# コーナー出力を逆順にして p 文の巻き方向を反転
for j in range(fa.numVertex - 1, -1, -1):
これは正しい修正で、再出力した .mi の幾何法線は両三角形とも +Y になりました。ところが——症状はまったく変わりませんでした。
mapを外して、単色を入れてレンダリング
決定的な手がかり:境界線がメッシュの対角線と一致していない
ここで転機になったのが、明暗の境界線の座標を正確に読んだことです。
再出力した .mi を展開すると、三角形の分割線(2枚の共有辺)は (100,0,100)–(-100,0,-100) でした。ところが実際の明暗の境界は (-100,0,100)–(100,0,-100)、つまり逆の対角線です。
三角形単位の問題(法線、UV、マテリアル)であれば、境界は必ずメッシュの辺に沿います。辺と無関係な直線で明暗が分かれるということは、原因はジオメトリではなく、空間中の連続的な何かが x + z = 0 の平面で切り替わっている、ということになります。
真犯人:origin + direction は「原点付き平行光」
エクスポータが出力していたライト定義はこうでした。
light "sun_light"
"mia_physicalsun" ( "on" on, "multiplier" 1.0,
"y_is_up" on,
"rgb_unit_conversion" 0.0001 0.0001 0.0001 )
direction -1 -1 -1
origin 0 0 0
end light
mental ray のライトは、.mi 文法上の指定の組み合わせで型が決まります。
-
originのみ → 点光源 -
directionのみ → 無限遠の平行光 -
origin+direction+spread→ スポットライト - そして
origin+direction(spread なし)→ 原点付き平行光
最後の「原点付き平行光」は、origin を通り direction に垂直な平面を発光面として、その前方の半空間だけを照らします。照射条件は次のとおりです。
(P - origin) · direction ≥ 0
今回は origin = (0,0,0)、direction = (-1,-1,-1) なので、照らされるのは x + y + z ≤ 0 の半空間だけ。地面 y = 0 上ではちょうど x + z ≤ 0 の側だけが直射を受け、境界線は x + z = 0——観測された (-100,0,100)–(100,0,-100) の直線と完全に一致します。
「なんとなく安全そうだから」と書いていた origin 0 0 0 が、シーンのど真ん中に見えないクリッピング平面を立てていたわけです。動作していた既存の手書きシーンと見比べると、そちらの太陽光には origin 行がありませんでした。差分は最初からそこにあったのに、法線を疑っている間は目に入りませんでした。
修正と検証
修正は origin 0 0 0 の削除だけです。
light "sun_light"
"mia_physicalsun" ( ... )
direction -1 -1 -1
end light
再レンダリングで対角線の明暗は消え、平面全体が均一に照らされました。
面白いのはここからで、この挙動は逆手に取って実証できます。origin を消す代わりに origin 50 0 -50 などに変えてレンダリングすると、明暗の境界線が平面上を平行移動します。境界は常に direction に垂直で origin を通るので、origin を動かすたびにクリッピング平面が動くのがレンダリング画像で直接観察できます。「ドキュメントの記述をレンダリングで確かめる」授業ネタとして、そのまま使えそうです。
振り返り:症状が同じでも原因は複数ある
今回のバグは3層になっていました。
- 巻き方向の未変換(MQO 時計回り → mi 反時計回り)——実在したバグ。修正は正しかったが、症状の主因ではなかった
-
未使用の法線ベクトル——
v N t M形式でn参照を出さないオブジェクトでも法線をベクトルリストに書き込んでいた。実害はないがファイルを汚す -
origin+directionの半空間クリッピング——真犯人
「対角線で明暗が分かれる」という一つの症状に対して、もっともらしい容疑者(法線)が実際に有罪だったせいで、真犯人の発見が遅れました。抜け出すきっかけは、境界線の座標を厳密に読んで、メッシュの構造と照合したことです。「だいたい対角線」ではなく「どちらの対角線か」まで詰めると、仮説が一気に絞れます。
レンダラのデバッグでは、症状の見た目で原因を推定するより、境界・座標・数値を1つずつ検算するほうが結局速い——いつもの結論に、また一つ実例が増えました。
まとめ
- mental ray の light で
originとdirectionを併記すると(spread なし)、origin を通る垂直平面より前方の半空間しか照らさない「原点付き平行光」になる - 無限遠の太陽光が欲しいなら
directionのみを書く。origin 0 0 0を「とりあえず」書いてはいけない - 明暗の境界がメッシュの辺と一致するか否かで、ジオメトリ起因かどうかを切り分けられる
- Metasequoia → mental ray では面の巻き方向の変換(逆順出力)が必要
ありがとうございます。




