ローポリモデルでは、スムースシェードで角を滑らかに見せる事が一般的に行われます。ポリゴン数を増やさずにローポリ感を無くすのに有効な手段です。こちらはMayaで作ったモデルです。
しかし、この「スムースシェード有り」のモデル、何だかちょっとダルい感じがしませんか?よくよく見ると、平面の部分が奥に行くに従ってちょっと暗くなっています。完全な平面ではなく、ごくわずかに丸みを帯びている様な…
その理由は3Dソフトの自動計算にあります。角を滑らかにする処理に平面の四辺が影響を受け、四辺の見た目が少し外向きになってしまっているのです。
こちらの図の緑色の線は、それぞれの地点の表示計算上の向きを表しています。
横から見ると一目瞭然です。形状的には垂直・水平な面が、表示計算上はわずかに外側に向いています。
この緑色の線は「法線」と呼ばれます。この「法線」を各平面に対して垂直にしてやれば、平面がきちんと平面に見える様になるはずです。
Mayaには法線の向きを数値入力出来る機能がありますので、それを使って修正してみる事にします。
法線の値はXYZのベクトルで指定します。MayaはY軸が垂直軸なので、例えば上の面の法線であれば、
法線の値はXYZのベクトルで指定します。MayaはY軸が垂直軸なので、例えば上の面の法線であれば、
X=0.0
Y=1.0
Z=0.0
Y=1.0
Z=0.0
という数値にします。六面全てで同様に法線の値を設定します。
ここまでの手順をスクリプトにまとめました。
結果です。ほんの少しの差ですが、平面の丸み感が無くなり印象がピシッとしました。
こういう場合は、下図の二つのベクトルの外積を求めます。ベクトルの外積はベクトルが為す面と垂直なので、それが求める法線となります。ベクトルの外積を計算するというのは何だか難しそうですが、Mayaの場合はcrossというコマンドで即座に答えが出ます。中学レベルの数学すら心もと無い私でも安心です。
Mayaヘルプの解説ページ
後は二つのベクトルの値です。根本の一頂点と先端の二頂点の座標が分かれば二つのベクトルが作れるので、まずはこの面に属する頂点の名前を調べます。
ここからは自動化を見据えてMELスクリプトを使っていきます。
まず、面をMayaで選択しておいてlsコマンドで面の名前を調べます。
Mayaヘルプの解説ページ
後は二つのベクトルの値です。根本の一頂点と先端の二頂点の座標が分かれば二つのベクトルが作れるので、まずはこの面に属する頂点の名前を調べます。
ここからは自動化を見据えてMELスクリプトを使っていきます。
まず、面をMayaで選択しておいてlsコマンドで面の名前を調べます。
ここで、Mayaをお使いの方は「面から頂点名を取得するならpolyListComponentConversionの方が扱いが楽じゃないの?」と疑問に思われるかもしれません。
確かにpolyInfoコマンドだと文字列から数字を取り出したりしなければならず面倒なのですが、これを使うには理由があり、polyListComponentConversionだと頂点の並び順が調べられないのです。
頂点の正しい並び順が分からないとcrossコマンドに渡すベクトルの順番が逆になり、計算した法線が反対を向く可能性があります。なのでpolyInfoコマンドを使う必要があるのです。
さて、頂点の番号が分かった所で座標を調べます。Xformコマンドです。
確かにpolyInfoコマンドだと文字列から数字を取り出したりしなければならず面倒なのですが、これを使うには理由があり、polyListComponentConversionだと頂点の並び順が調べられないのです。
頂点の正しい並び順が分からないとcrossコマンドに渡すベクトルの順番が逆になり、計算した法線が反対を向く可能性があります。なのでpolyInfoコマンドを使う必要があるのです。
さて、頂点の番号が分かった所で座標を調べます。Xformコマンドです。
必要な三頂点の座標を調べ、矢印の先端にあたる座標から根本の座標を引いてベクトルにします。こうして得られた二つのベクトルを、最初に述べたcrossコマンドに渡します。
引数としてはvector型を使います。これは三個のfloatで構成される変数型です。
引数としてはvector型を使います。これは三個のfloatで構成される変数型です。
これで法線ベクトルが計算出来ました。
これを、対象となる面に属する全頂点の法線として設定します。polyNormalPerVertexコマンドを使います。
これを、対象となる面に属する全頂点の法線として設定します。polyNormalPerVertexコマンドを使います。
引数はfloat型で渡すので、.x・.y・.zという形で三個のfloatをvector変数から取り出して使います。ちなみに上記の例の様に括弧に入れてやらないとエラーになります。一度float型変数に入れてから渡すのもありです。
という事で、法線が面に垂直にセット出来ました。
ここまでの手順をスクリプトにまとめました。
スクリプト本体はこちらです。次回も法線加工の話です…
//------------------------------------------------------------------
//NKJ_MakeSelectedFacesLookFlat.mel
//Takahiro Nakajima 2025
//概要:
//スムースシェードが掛かったオブジェクトで、任意のフェイスの各頂点の
//法線を並行にし、そのフェイスが完全に平面に見える様にします。
//使用方法:
//1)このMELスクリプトを実行します。
//ダイアログが開きます。
//2)処理対象のオブジェクトの回転値をリセットします。
//2)処理対象のオブジェクトの回転値をリセットします。
//3)処理対象のフェイスを全て選択します。
//4)ダイアログのボタンを押すと処理が実行されます。
//-------------------------------------------------------
//-------------------------------------------------------
global proc vector NKJ_GetVtxPosVector(string$vtx)
{
//バーテックスの座標をvectorで返します。
//-----------------
//バーテックスの絶対座標を取得
float $vtxPos[] = `xform -q -ws -t $vtx`;
//座標をvectorにする
vector $result = <<$vtxPos[0],$vtxPos[1],$vtxPos[2]>>;
return $result;
}
//-------------------------------------------------------
global proc vector NKJ_GetFaceNormalVector(string$face)
{
//$faceの法線を取得します。
//-----------------
//$faceからオブジェクト名を作成
string $tempList[];
clear $tempList;
tokenize $face "." $tempList;
string $objName = $tempList[0];
//$faceのバーテックス番号を並び順で取得
string $vtxNums[] = `polyInfo -fv $face`;
string $vtxNumList[];
clear $vtxNumList;
tokenize $vtxNums[0] " " $vtxNumList;
//バーテックス名を3個並び順で作成
string $vtxA = $objName + ".vtx[" + $vtxNumList[2] + "]";
string $vtxB = $objName + ".vtx[" + $vtxNumList[3] + "]";
string $vtxC = $objName + ".vtx[" + $vtxNumList[4] + "]";
//上記各バーテックスの絶対座標をvectorで取得
vector $vtxAPos = `NKJ_GetVtxPosVector $vtxA`;
vector $vtxBPos = `NKJ_GetVtxPosVector $vtxB`;
vector $vtxCPos = `NKJ_GetVtxPosVector $vtxC`;
//ベクトルABとベクトルACを作成
vector $vecAB = $vtxBPos - $vtxAPos;
vector $vecAC = $vtxCPos - $vtxAPos;
//ベクトルABとベクトルACの法線を取得し単位ベクトルにする
vector $normal = `cross $vecAB $vecAC`;
$normal = `unit $normal`;
return $normal;
}
//-------------------------------------------------------
global proc NKJ_AlignVtxNormalToFaceNormal(string$face)
{
//$faceの各バーテックスの法線を$faceの法線に合わせます。
//-----------------
//$faceの法線を取得
vector$faceNormal = `NKJ_GetFaceNormalVector $face`;
float $normalX = $faceNormal.x;
float $normalY = $faceNormal.y;
float $normalZ = $faceNormal.z;
//$faceの全頂点を取得
string $vtxList[] = `polyListComponentConversion -tv $face`;
$vtxList = `ls -fl $vtxList`;
//各頂点の法線をセット
string $item;
for($item in $vtxList)
{
polyNormalPerVertex -e -xyz $normalX $normalY $normalZ $item;
}
}
//-------------------------------------------------------
global proc NKJ_AlignVtxNormalToSelectedFaceNormals()
{
//選択している全フェイスで、バーテックスの法線をフェイスの法線に合わせます。
//実行後は選択が解除されます。
string $sel[] = `ls -sl -fl -l`;
select -cl;
string $item;
for($item in $sel)
{
NKJ_AlignVtxNormalToFaceNormal $item;
}
}
//-------------------------------------------------------
//メイン
{
string $title = "NKJ_MakeSelectedFacesLookFlat";
string $titleUI = $title + "_UI";
if(`window -ex $titleUI` == 1)
{
deleteUI $titleUI;
}
window -t $title $titleUI;
columnLayout "cLayout";
text -l $title;
text -l "--------------------";
text -l "This makes faces look flat";
text -l "with keepig their soft edges.";
text -l "1)Reset your object's rotations.";
text -l "1)Reset your object's rotations.";
text -l "2)Select faces you want to edit.";
text -l "3)Click a button below.";
text -l "--------------------";
button -w 200 -c NKJ_AlignVtxNormalToSelectedFaceNormals "Make Selected Faces Look Flat";
window -e -w 210 -h 140 $titleUI;
showWindow $titleUI;
}
//-------------------------------------------------------
//終了













No comments:
Post a Comment