Mayaでブレンドシェイプのアトリビュートをキャラクタセットに追加する方法。
1)空のキャラクタセットを作成し、それを現在のキャラクタセットにしておく。
2)シェイプエディタあるいはChannelBoxのインプットでブレンドシェイプ全部にキーを打つ。
3)グラフエディタを開くと、左のカラムにキーを打ったアトリビュートと現在のキャラクタセットが表示されているので、アトリビュートを選択してキャラクタセットにドラッグ&ドロップする。
How to add blend shape's attributes to a character set in Maya
1)Make an empty character set. (It automatically becomes a current character set.)
2)Make blend shape's keys in ShapeEditor or ChannelBox's input column.
3)Open Graph Editor. In a left column, you see the blend shape's attributes you made keys on. You also see a current character set in the column. Select attributes, then drag & drop them to the characterset to add the attributes.
Wednesday, May 20, 2026
Mayaでブレンドシェイプをキャラクタセットに追加する/How to add blend shape's attributes to a character set in Maya
Tuesday, January 20, 2026
Mayaのビューポートでオブジェクトの原点は表示出来るのか?
3Dソフトのオブジェクトは、空間内での位置を定義する一点を持ちます。3D空間内でのこの点の位置が、そのオブジェクトの座標とされます。
ここではこの点を「オブジェクトの原点」と呼ぶ事にします。
ところで、Mayaのビューポートでオブジェクトを見てもそれらしい表示は見当たりません。
Mayaでオブジェクトの原点を見る事は出来るのでしょうか?
確認するだけなら方法は考え付きます。例えば、オブジェクトの座標を数値入力でXYZ=0にすれば、ワールドの原点の位置がオブジェクトの原点の位置と重なります。

また、オブジェクトにロケータをペアレントし、ロケータの座標をXYZ=0にすればオブジェクトの原点の位置にロケータが置かれます。
ですが、確認だけの為にシーンに手を加えるのは出来れば避けたいです。オブジェクトの原点をシンプルに表示する方法は無いものでしょうか?
思いつくのはMoveツールを起動すると表示されるマニピュレーターです。これはオブジェクトの原点に表示されている様に見えます。Attribute EditorのPivotsペイン内にあるチェックボックスを使えば、基点(ピボット)の位置を表示したままにする事も出来ます。
思いつくのはMoveツールを起動すると表示されるマニピュレーターです。これはオブジェクトの原点に表示されている様に見えます。Attribute EditorのPivotsペイン内にあるチェックボックスを使えば、基点(ピボット)の位置を表示したままにする事も出来ます。
それでは他のインジケーターはどうでしょうか?
オブジェクトの全てのアトリビュートをlistAttrコマンドで調べ、表示に関係しそうな物をsetAttrで全てオンにしてみます。
すると、オブジェクトの原点の辺りに十字のマークが表示されました!これはHandleと呼ばれる物です。これで解決でしょうか?
残念ながら、Handleも数値入力で移動させられます…。ちなみにこのHandleはオブジェクト選択に使えますので、下図の様にオブジェクトから離せばオブジェクト外から選択が可能になります。これは何かに使えそうな機能ですね。
といった所で、結局今回はオブジェクトの原点を表示させるやり方を見つける事は出来ませんでした。無いって事なのでしょうか…。
最後に、上の図でXYZが付いているインジケーターはLocalAxisと呼ばれる物です。字義通りならこれがオブジェクトの原点になりそうな物ですが、どうもRotate Pivotの位置に置かれるみたいです。コンストレインにはこの位置が使われていると思われます。
Monday, January 12, 2026
Mayaで法線を編集する (3)
法線編集の話題も最終回です。
前回は特定のオブジェクトの方向に(正確には逆方向ですが)法線を向ける話でした。こんな感じです。
全ての法線が緑の十字オブジェクトの方を向いている
さて、回転させた平面をZ軸方向から見るとこんな感じです。
ここまで来れば両直線の始点と終点の座標を使い、連立一次方程式で交点が計算出来ます。Z座標は両直線の中間値にします。
最後にその座標に対して先ほどの回転を逆に実行し、最終的な焦点の座標を得ます。
スクリプトはこちらです。
//--------------------------------------------------------------------------------
ですが実際のモデリングではこの作業だけでは足りません。逆に、特定の法線の向きに合うオブジェクトの座標を決める必要もあります。つまり、二つの法線の焦点を見つけるという事です。
法線の焦点の座標を求めるには?二つの法線はいわゆるねじれの位置にある事もありますので、数学的には「二直線の最短距離」を求める計算を使います。こちらのページにある図が分かりやすいです。
【高校数学C】ねじれの位置にある2直線間の最短距離(共通垂線) | 受験の月
正直私には計算式が全く理解出来ませんが、この図の「共通垂線」の絶対角度を求めるには初回で話したMayaのcrossコマンドが使えます。Mayaのマニュアルを参照して頂ければ分かりますが、もう見たまんまです。
Maya Creative ヘルプ | cross | Autodesk
さて、共通垂線の絶対角度を得た後はこんな感じに計算します。
下図の赤と青の直線が二つの法線の延長線、白の線が共通垂線です。
【高校数学C】ねじれの位置にある2直線間の最短距離(共通垂線) | 受験の月
正直私には計算式が全く理解出来ませんが、この図の「共通垂線」の絶対角度を求めるには初回で話したMayaのcrossコマンドが使えます。Mayaのマニュアルを参照して頂ければ分かりますが、もう見たまんまです。
Maya Creative ヘルプ | cross | Autodesk
さて、共通垂線の絶対角度を得た後はこんな感じに計算します。
下図の赤と青の直線が二つの法線の延長線、白の線が共通垂線です。
この二つの直線が属する平面を考えてみます。
この二つの平面がZ軸方向を向く様に全体を回転させます。
「回転させる」とさらっと言いましたが、これはあくまで計算上の話なのでMayaのオブジェクトとして回転させる事は出来ません。なのでどうするかというと、回転後の頂点座標を計算してその位置に各頂点を移動させます。数学的に計算するのは大変そうですが、Mayaのrotコマンドを使うとすぐに答えが出ます。
Maya Creative ヘルプ | rot | Autodesk
ちなみにX軸回転とY軸回転の二回に分けて計算します。それぞれの軸で回転方向を調べる必要があるからです。両直線をYZ・XZ各平面に投影した上でcrossコマンドで法線を出し、各平面の垂直軸と正負を比較して回転方向を決めます。(一度にやる方法があるかも知れませんが…)
Maya Creative ヘルプ | rot | Autodesk
ちなみにX軸回転とY軸回転の二回に分けて計算します。それぞれの軸で回転方向を調べる必要があるからです。両直線をYZ・XZ各平面に投影した上でcrossコマンドで法線を出し、各平面の垂直軸と正負を比較して回転方向を決めます。(一度にやる方法があるかも知れませんが…)
さて、回転させた平面をZ軸方向から見るとこんな感じです。
ここまで来れば両直線の始点と終点の座標を使い、連立一次方程式で交点が計算出来ます。Z座標は両直線の中間値にします。
最後にその座標に対して先ほどの回転を逆に実行し、最終的な焦点の座標を得ます。
エッジを加えたせいで角張った様になった見た目も…
焦点を決めて法線を加工するときれいな表示に。
(左右両サイドそれぞれで焦点を決めました。)
(左右両サイドそれぞれで焦点を決めました。)
スクリプトはこちらです。
//--------------------------------------------------------------------------------
//--------------------------------------------------------------------------------
//GetNormalCrossing
//Takahiro Nakajima 2026
//二つの機能があります。
//A)選択している二つのバーテックスの焦点の座標を取得します。
//B)選択している全てのバーテックスの法線のルートをその焦点に向けます。
//使い方:
//1)このスクリプトを実行します。ダイアログが出ます。
//2)オブジェクトの回転をリセットします。
//3)焦点を取得したい場合は、二つの頂点を選んで上のボタンを押すと
//ダイアログのカラムに焦点の座標値が入ります。
//(法線が平行な場合は0が入ります)
//4)焦点に向けたい場合は、法線を動かしたい頂点を全て選んで下のボタンを押します。
//--------------------------------------------------------------------------------
global proc float[] getLinesCrossing(vector$S1,vector$E1,vector$S2,vector$E2)
{
//二直線L1とL2が最短距離になる点P1とP2の平均座標をfloat配列で返します。
//もし二直線が平行だった場合は返り値のアドレス3に1が入ります(それ以外は0)。
//L1の始点はS1、終点はE1
//L2の始点はS2、終点はE2
float $crossing[];
clear $crossing;
//二直線の法線nmlを取得
vector $V1 = $E1 - $S1;
vector $V2 = $E2 - $S2;
vector $nml = `cross $V1 $V2`;
//nmlがZ軸と一致する回転角(rad)を取得
float $angles[] = `getRotAngleForCrossingNormalToZaxis $nml`;
//二直線の始点と終点を回転させ、XY平面と平行にする
$S1 = `rotationNomalAlignToZaxis $S1 $angles 0`;
$E1 = `rotationNomalAlignToZaxis $E1 $angles 0`;
$S2 = `rotationNomalAlignToZaxis $S2 $angles 0`;
$E2 = `rotationNomalAlignToZaxis $E2 $angles 0`;
//XY平面上での交点の平均座標を取得
float $rotatedXY[] = `NKJ_GetCrossingOf2Lines $S1 $E1 $S2 $E2`;
//逆回転させて元に戻す(二直線が平行の場合はそのまま)
if($rotatedXY[2] == 1)
{
$crossing = {0,0,0,1};
}
else
{
float $crossingZ = (($S1.z) + ($S2.z)) / 2.0;
vector $crossingVec = <<$rotatedXY[0],$rotatedXY[1],$crossingZ>>;
$crossingVec = `rotationNomalAlignToZaxis $crossingVec $angles 1`;
float $corssingVeX = $crossingVec.x;
float $corssingVeY = $crossingVec.y;
float $corssingVeZ = $crossingVec.z;
$crossing = {$crossingVec.x,$crossingVec.y,$crossingVec.z, 0};
}
return $crossing;
}
//--------------------------------------------------------------------------------
global proc float[] getRotAngleForCrossingNormalToZaxis(vector$crossingNormal)
{
//$crossingNormalがZ軸と一致する回転角(rad)を返します。
//返り値のアドレス0がX回転、アドレス1がY回転になります。
//各軸のベクトル
vector $xAxis = <<1,0,0>>;
vector $yAxis = <<0,1,0>>;
vector $zAxis = <<0,0,1>>;
//$crossingNormalをX軸方向から見たベクトルを作成
float $crossingNormalY = $crossingNormal.y;
float $crossingNormalZ = $crossingNormal.z;
vector $XcrossingNormal = <<0,$crossingNormalY,$crossingNormalZ>>;
//上記ベクトルとZ軸との法線を取得し、法線がXプラスかXマイナスかで
//回転方向を決める
float $xDirection = 1.0;
vector $normalAndZaxisCrossing = `cross $XcrossingNormal $zAxis`;
if(($normalAndZaxisCrossing.x)<0.0)
{
$xDirection = (-1.0);
}
//上記ベクトルとZ軸との角度を取得し、回転方向を掛ける
float $xRot = `angle $XcrossingNormal $zAxis` * $xDirection;
//$crossingNormalを回転させる
vector $crossingNormalXRotated = `rot $crossingNormal $xAxis $xRot`;
//回転させた$crossingNormalとZ軸との法線を取得し、法線がYプラスか
//Yマイナスかで回転方向を決める
float $yDirection=1.0;
vector $rotNormalAndZaxisCrossing = `cross $crossingNormalXRotated $zAxis`;
if(($rotNormalAndZaxisCrossing.y)<0.0)
{
$yDirection=(-1.0);
}
//回転させた$crossingNormalとZ軸との角度を取得し、回転方向を掛ける
float $yRot = `angle $crossingNormalXRotated $zAxis` * $yDirection;
//結果を返す
float $result[] = {$xRot,$yRot};
return $result;
}
//------------------------------------------------------------
global proc vector rotationNomalAlignToZaxis(vector$crossingNormal,float$angles[],int$reverse)
{
//$reverse == 0 >> 行き
//$reverse == 1 >> 帰り
vector $result;
vector $xAxis = <<1,0,0>>;
vector $yAxis = <<0,1,0>>;
if($reverse == 0)
{
vector $crossingNormalRot1 = `rot $crossingNormal $xAxis $angles[0]`;
$result = `rot $crossingNormalRot1 $yAxis $angles[1]`;
}
else
{
vector $crossingNormalRot1 = `rot $crossingNormal $yAxis ($angles[1] * (-1.0))`;
$result = `rot $crossingNormalRot1 $xAxis ($angles[0] * (-1.0))`;
}
return $result;
}
//------------------------------------------------------------
global proc float[] NKJ_GetlinearGraphParams(float$point1[],float$point2[])
{
//point1とpoint2を通る一次方程式グラフの傾きと高さ(y=ax+bのaとb)を返します。
//もしグラフが垂直であれば、返り値のアドレス2に1が入ります(初期値は0)。
float $result[];
clear $result;
if(($point2[0] - $point1[0]) != 0.0)
{
float $a = ($point2[1] - $point1[1]) / ($point2[0] - $point1[0]);
float $b = $point1[1] - ($a * $point1[0]);
$result = {$a,$b,0};
}
else
{
$result = {0,0,1};
}
return $result;
}
//------------------------------------------------------------
global proc float[] NKJ_GetCrossingOf2Lines(vector$S1,vector$E1,vector$S2,vector$E2)
{
//二つの直線のXY平面上での交点を返します。
//もし二直線が平行な場合、返り値のアドレス2に1が入り、結果は{0,0,1}になります。
//$S1と$E1は、直線1上の二つの点のXYZ座標です。
//$S2と$E2は、直線2上の二つの点のXYZ座標です。
float $resultX = 0;
float $resultY = 0;
float $parallel = 0;
//引数から二つの直線それぞれの始点と終点のXY座標を取り出して配列に入れる
float $S1X = $S1.x;
float $S1Y = $S1.y;
float $E1X = $E1.x;
float $E1Y = $E1.y;
float $S2X = $S2.x;
float $S2Y = $S2.y;
float $E2X = $E2.x;
float $E2Y = $E2.y;
float $line1SXY[] = {$S1X,$S1Y};
float $line1EXY[] = {$E1X,$E1Y};
float $line2SXY[] = {$S2X,$S2Y};
float $line2EXY[] = {$E2X,$E2Y};
//line1とline2のaとb(傾きと高さ)を取得
float $param1[] = `NKJ_GetlinearGraphParams $line1SXY $line1EXY`;
float $param2[] = `NKJ_GetlinearGraphParams $line2SXY $line2EXY`;
//もし両方とも垂直な場合(交点無し)
if(($param1[2]==1)&&($param2[2]==1))
{
$resultX = 0;
$resultY = 0;
$parallel = 1;
}
//もし傾きが同じ場合(交点無し)
else if($param1[0] == $param2[0])
{
$resultX = 0;
$resultY = 0;
$parallel = 1;
}
//もしline1だけが垂直な場合
else if(($param1[2]==1)&&($param2[2]==0))
{
$resultX = $S1.x;
$resultY = ($param2[0] * $resultX) + $param2[1];
}
//もしline2だけが垂直な場合
else if(($param1[2]==0)&&($param2[2]==1))
{
$resultX = $S2.x;
$resultY = ($param1[0] * $resultX) + $param1[1];
}
//その他の場合
else
{
$resultX = ($param2[1] - $param1[1]) / ($param1[0] - $param2[0]);
$resultY = ($param1[0] * $resultX) + $param1[1];
}
float $result[] = {$resultX,$resultY,$parallel};
return $result;
}
//------------------------------------------------------------
global proc float[] NKJ_GetLinearGraphParamsFromVtxs(string$vtx1,string$vtx2)
{
//vtx1とvtx2を通る直線をXY平面で見た時のグラフの傾きと高さ(y=ax+bのaとb)を返します。
//もしグラフが垂直であれば、返り値のアドレス2に1が入り、配列のサイズが3になります。
float $point1[] = `xform -q -a -ws -t $vtx1`;
float $point2[] = `xform -q -a -ws -t $vtx2`;
float $result[] = `NKJ_GetlinearGraphParams $point1 $point2`;
return $result;
}
//-------------------------------------------------------
global proc NKJ_GetNormalCrossing()
{
//選択頂点を取得
string $sel[] = `ls -sl -fl -l`;
//選択頂点の座標を取得
float $vtx1Pos[] = `xform -q -a -ws -t $sel[0]`;
float $vtx2Pos[] = `xform -q -a -ws -t $sel[1]`;
//選択頂点の法線を取得
float $vtx1Nml[] = `polyNormalPerVertex -q -xyz $sel[0]`;
float $vtx2Nml[] = `polyNormalPerVertex -q -xyz $sel[1]`;
//法線先端の座標を取得
float $vtx1NmlX = $vtx1Nml[0] + $vtx1Pos[0];
float $vtx1NmlY = $vtx1Nml[1] + $vtx1Pos[1];
float $vtx1NmlZ = $vtx1Nml[2] + $vtx1Pos[2];
float $vtx2NmlX = $vtx2Nml[0] + $vtx2Pos[0];
float $vtx2NmlY = $vtx2Nml[1] + $vtx2Pos[1];
float $vtx2NmlZ = $vtx2Nml[2] + $vtx2Pos[2];
//選択頂点と法線先端の座標をベクトルにする
vector $vtx1SVec = <<$vtx1Pos[0],$vtx1Pos[1],$vtx1Pos[2]>>;
vector $vtx2SVec = <<$vtx2Pos[0],$vtx2Pos[1],$vtx2Pos[2]>>;
vector $vtx1EVec = <<$vtx1NmlX,$vtx1NmlY,$vtx1NmlZ>>;
vector $vtx2EVec = <<$vtx2NmlX,$vtx2NmlY,$vtx2NmlZ>>;
//交点の座標を取得
float $crossingPosXYZ[] = `getLinesCrossing $vtx1SVec $vtx1EVec $vtx2SVec $vtx2EVec`;
if($crossingPosXYZ[3]==1)
{
clear $crossingPosXYZ;
$crossingPosXYZ = {0,0,0,1};
}
//カラムにセット
floatField -e -v $crossingPosXYZ[0] "NKJ_GetNormalCrossing_UI|cLayout|positionXField";
floatField -e -v $crossingPosXYZ[1] "NKJ_GetNormalCrossing_UI|cLayout|positionYField";
floatField -e -v $crossingPosXYZ[2] "NKJ_GetNormalCrossing_UI|cLayout|positionZField";
}
//-------------------------------------------------------
global proc NKJ_SetNormalCrossing()
{
//選択しているバーテックスの法線の角度を
//UIから取得した座標が焦点になる様に修正します。
//現在の選択からバーテックスリストを取得
string $vtxList[] = `ls -sl -fl -l`;
//焦点座標を取得
float $focusX = `floatField -q -v "NKJ_GetNormalCrossing_UI|cLayout|positionXField"`;
float $focusY = `floatField -q -v "NKJ_GetNormalCrossing_UI|cLayout|positionYField"`;
float $focusZ = `floatField -q -v "NKJ_GetNormalCrossing_UI|cLayout|positionZField"`;
float $locatorPos[] = {$focusX,$focusY,$focusZ};
//選択解除
select -cl;
//各バーテックスに対してループ処理
string $item;
for($item in $vtxList)
{
//バーテックスの座標を取得
float $vtxPos[] = `xform -q -a -ws -t $item`;
//法線として使う値を計算
float $x = $vtxPos[0] - $locatorPos[0];
float $y = $vtxPos[1] - $locatorPos[1];
float $z = $vtxPos[2] - $locatorPos[2];
//正規化
vector $normal = <<$x,$y,$z>>;
$normal = `unit $normal`;
//法線を設定
polyNormalPerVertex -xyz ($normal.x) ($normal.y) ($normal.z) $item;
}
}
//------------------
//メイン
{
string $title = "NKJ_GetNormalCrossing";
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 gets a focus of 2 vtxs";
text -l "normals, or aims selected vtxs";
text -l "normals to the focus.";
text -l "1)Reset rotations.";
text -l "2) Select 2 vtxs then click upper";
text -l "button to get their focus values.";
text -l "3) Select vtxs then click lower";
text -l "button to aim them to the focus.";
text -l "--------------------";
button -w 200 -c "NKJ_GetNormalCrossing" -l "Get Nml Focus Of Selected 2 Vtxs" getCrossingButton;
floatField -w 100 -v 0.0 positionXField;
floatField -w 100 -v 0.0 positionYField;
floatField -w 100 -v 0.0 positionZField;
button -w 200 -c "NKJ_SetNormalCrossing" -l "Set Nmls On Selected Vtxs" setCrossingButton;
window -e -w 210 -h 250 $titleUI;
showWindow $titleUI;
}
//-------------------------------------------------------
//終了
Friday, January 9, 2026
Mayaで法線を編集する (2)
引き続き法線についてです。
前回、スムースシェーディングでローポリ感を軽減する話をしました。
こんな感じですね。
カクカクした見た目が…
ポリゴン数を増やした部分が段差の様な見た目になってしまいました…。
加工前と後の法線を比較してみましょう。
加工部分の法線を延長してみると、球の中心からずれているのがわかります。
ずれている法線の角度を修正し、延長線が球の中心を通る様にすれば段差の様な見た目は解消されます。
今回は、任意のオブジェクトの方角に法線を傾けるMELを作成し修正してみました。
球の中心に別のオブジェクト(ロケータ)を配置し、とりあえず全部の法線に対して実行。
段差状に見えていた箇所がきれいになりました。
普通ならガタつくこの様な形状も滑らかに出来ます。
MELはこちらです。次回も法線の話です。
//------------------
//------------------
global proc NKJ_AimSelectedVtxsNormalsToSelectedObj()
{
//選択しているバーテックスの法線の角度を、根本側が
//別のオブジェクトの方向を正しく向く様に修正します。
//(つまり、法線の根本方向の延長線上に、焦点となる
//オブジェクトが存在する様になります。)
//(つまり、法線の根本方向の延長線上に、焦点となる
//オブジェクトが存在する様になります。)
//使用方法:
//1)このスクリプトを実行します。ダイアログが出ます。
//2)法線角度を修正したいオブジェクトの回転をリセットします。
//3)法線角度を修正したいバーテックスを全て選択します。
//4)焦点にしたい別のオブジェクトをOutlinerで追加選択します。
//5)ダイアログのボタンをクリックすると処理が行われます。
//------------------
//現在の選択からバーテックスリストを取得
string $vtxList[] = `ls -sl -fl -l -typ "float3"`;
//現在の選択からエイム対象オブジェクト(transform)を取得
string $locatorList[] = `ls -sl -l -typ "transform"`;
//ロケータの座標を取得
float $locatorPos[] = `xform -q -a -ws -t $locatorList[0]`;
//選択解除
select -cl;
//各バーテックスに対してループ処理
string $item;
for($item in $vtxList)
{
//バーテックスの座標を取得
float $vtxPos[] = `xform -q -a -ws -t $item`;
//法線として使う値を計算
float $x = $vtxPos[0] - $locatorPos[0];
float $y = $vtxPos[1] - $locatorPos[1];
float $z = $vtxPos[2] - $locatorPos[2];
//正規化
vector $normal = <<$x,$y,$z>>;
$normal = `unit $normal`;
//法線を設定
polyNormalPerVertex -xyz ($normal.x) ($normal.y) ($normal.z) $item;
}
}
//------------------
//メイン
{
string $title = "NKJ_AimNormalsToObj";
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 aims selected vertices normals";
text -l "roots to another object.";
text -l "1)Reset object rotations.";
text -l "1)Select vertices you want to edit.";
text -l "2)Add-select another object";
text -l "in Outliner.";
text -l "3)Click a button below.";
text -l "--------------------";
button -w 200 -c NKJ_AimSelectedVtxsNormalsToSelectedObj "Aim";
window -e -w 210 -h 140 $titleUI;
showWindow $titleUI;
}
//-------------------------------------------------------
//終了
Thursday, January 8, 2026
Mayaで法線を編集する (1)
ローポリモデルでは、スムースシェードで角を滑らかに見せる事が一般的に行われます。ポリゴン数を増やさずにローポリ感を無くすのに有効な手段です。こちらはMayaで作ったモデルです。
しかし、この「スムースシェード有り」のモデル、何だかちょっとダルい感じがしませんか?よくよく見ると、平面の部分が奥に行くに従ってちょっと暗くなっています。完全な平面ではなく、ごくわずかに丸みを帯びている様な…
その理由は3Dソフトの自動計算にあります。角を滑らかにする処理に平面の四辺が影響を受け、四辺の見た目が少し外向きになってしまっているのです。
こちらの図の緑色の線は、それぞれの地点の表示計算上の向きを表しています。
横から見ると一目瞭然です。形状的には垂直・水平な面が、表示計算上はわずかに外側に向いています。
この緑色の線は「法線」と呼ばれます。この「法線」を各平面に対して垂直にしてやれば、平面がきちんと平面に見える様になるはずです。
Mayaには法線の向きを数値入力出来る機能がありますので、それを使って修正してみる事にします。
法線の値はXYZのベクトルで指定します。MayaはY軸が垂直軸なので、例えば上の面の法線であれば、
法線の値はXYZのベクトルで指定します。MayaはY軸が垂直軸なので、例えば上の面の法線であれば、
X=0.0
Y=1.0
Z=0.0
Y=1.0
Z=0.0
という数値にします。六面全てで同様に法線の値を設定します。
これを、対象となる面に属する全頂点の法線として設定します。polyNormalPerVertexコマンドを使います。
ここまでの手順をスクリプトにまとめました。
結果です。ほんの少しの差ですが、平面の丸み感が無くなり印象がピシッとしました。
こういう場合は、下図の二つのベクトルの外積を求めます。ベクトルの外積はベクトルが為す面と垂直なので、それが求める法線となります。ベクトルの外積を計算するというのは何だか難しそうですが、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で構成される変数型です。
そして、crossコマンドで取得した値をunitコマンドで長さ1にします(正規化)。通常、ポリゴンの法線は長さ1が使われるからです。これで法線ベクトルが計算出来ました。
これを、対象となる面に属する全頂点の法線として設定します。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;
}
//-------------------------------------------------------
//終了
Thursday, August 29, 2024
画面をキャプチャーして反転表示する
以下のサイトを参考にさせて頂きました。
Pythonの画像処理ライブラリPillow(PIL)の使い方 | note.nkmk.me
【完全版】たった10ステップ!PythonのPillowライブラリで画像処理をマスターしよう | ちょこっとプロ! (chocottopro.com)
PythonでPillow(PIL)モジュールを使用しスクリーンショットを撮る | Men of Letters(メン・オブ・レターズ) – 論理的思考/業務改善/プログラミング (kazuuu.net)
Python, Pillowで画像の一部をトリミング(切り出し/切り抜き) | note.nkmk.me
以下、Windows11での手順です。
まずPythonをインストールし、続いてPillowというライブラリをインストールします。
次に新規テキストファイルにこちらのスクリプトを記入し、拡張子.pyでセーブします。
from PIL import Image
from PIL import ImageGrab
pic = ImageGrab.grab()
pic = pic.transpose(Image.FLIP_LEFT_RIGHT)
pic = pic.crop((500,100,1420,980))
pic.show()
そのファイルをダブルクリックするとWindows PowerShellで実行され、左右反転したスクリーンキャプチャー画像が表示されます。
このスクリプトではクロップされた画像になりますが、クロップが必要無い場合は5行目を削除します。
なお、このままだとスクリーンキャプチャーにWindows PowerShell自体のウィンドウが映り込んでしまうので、邪魔にならない様にWindows PowerShellの設定で起動サイズを小さくし、軌道位置を固定しておきます。(もっと良いやり方がありそうですが…)
Subscribe to:
Posts (Atom)









































