<- Back

●フラクタルを作る


CGの醍醐味のひとつとして、スクリプトを使って数学的な美しさをもった形状を作ることが挙げられます。別の記事で螺旋を作成しましたが、今回はスクリプトで作る形状の代表格とも言える「フラクタル」です。下の画像は球によるフラクタル形状です。左から順に、分岐数3、分岐数4、分岐数5となっています。

   

そしてこちらは球を立方体に置き換えたもの。



スクリプトでフラクタルを作るには「再帰」という方法を用います。
「再帰」とは、ある関数の中でそれ自身を呼び出すというものです。MayaのMELスクリプトで書くとこんな感じになります。

proc int fractal(string $objects[], int $gen, float $scale) {

   string $duplicated[], $objectsB[];

   if($gen == 0) {
      return 0;
   }

   else {

      for($obj in $objects) {
         $duplicated = `duplicate $obj`;
         $objectsB = `stringArrayCatenate $objectsB $duplicated`;
         scale -r $scale $scale $scale $duplicated[0];
      }
      $gen = $gen-1;
      fractal($objectsB, $gen, $scale);
      clear $objectsB;
      return 1;

   }
   
}
この関数は基本的な部分を抜き出したものなので、これをそのまま実行しても上の画像のようなものはできません。重要なのは、この"fractal"という関数の中で、同じ"fractal"という関数を使用しているということです(下から5行目)。このやり方は、間違えると無限ループに陥ってMayaがフリーズしてしまうので注意が必要です。ここでは変数"$gen"が0になったら関数から抜け出すという処理にしてあります。

関数fractalは引数として、フラクタルの元となるオブジェクトの名前($objects)、分岐させる数($gen)、分岐ごとにどのくらいオブジェクトを小さくするか($scale)の3つの値をとります。そして分岐数が0でなければ、$objectsの数だけそれを複製し、スケールをかけ、複製されたオブジェクトの名前を配列$objectsBに格納します。この$objectsBは関数fractalに引き渡され、複製のそのまた複製が生成されるという具合です。

この関数ではオブジェクト1つにつき1個しか複製ができませんが、上の画像では1オブジェクトにつき前後左右上下の6方向に複製を作って、しかるべき位置に移動させています。したがってそのようなスクリプトを組み込む必要があります。ベースのスクリプトができてしまえば、あとは移動、回転、スケールなどの式を色々を変えてみることでバリエーションに富んだ形状を作ることができるでしょう。

画像のフラクタル形状を作ったスクリプトは下になります。1行目で複製させたいオブジェクト名を書きます(既にシーンにあるオブジェクトでなければなりません)。分岐の数($gen)は4になっていますが、僕が自分のPCで実行させたときは、元オブジェクトがプリミティブの球で5が限界でした。実は隠れて見えない部分にも複製が作られており、本当はそれを作らないように考慮すべきなのですが面倒くさかったのでやっていません。したがって$genの値を大きくしてMayaがフリーズしても責任は持ちませんのでご注意ください。

string $objects[] = {"pSphere1"};
float $scale = 0.5;
int $gen = 4;

simpleFractal($objects, $gen, $scale);

proc int simpleFractal(string $objects[], int $gen, float $scale) {

   float $bboxMin[], $bboxMax[], $centerX, $centerY, $centerZ, $worldPivot[];
   string $duplicated[], $objectsB[];

   if($gen ==  0) {
      return 0;
   }

   else {

      for($obj in $objects) {

         $bboxMin = `getAttr ($obj+".boundingBoxMin")`;
         $bboxMax = `getAttr ($obj+".boundingBoxMax")`;
         $centerX = ($bboxMin[0]+$bboxMax[0])/2;
         $centerY = ($bboxMin[1]+$bboxMax[1])/2;
         $centerZ = ($bboxMin[2]+$bboxMax[2])/2;
         $worldPivot = `getAttr ($obj+".rotatePivot")`;

         $duplicated = `duplicate $obj`;
         $objectsB = `stringArrayCatenate $objectsB $duplicated`;
         scale -r $scale $scale $scale $duplicated[0];
         move -a ($bboxMin[0]-$worldPivot[0]) ($centerY-$worldPivot[1])    ($centerZ-$worldPivot[2])    $duplicated[0];

         $duplicated = `duplicate $duplicated[0]`;
         $objectsB = `stringArrayCatenate $objectsB $duplicated`;
         move -a ($centerX-$worldPivot[0])    ($bboxMin[1]-$worldPivot[1]) ($centerZ-$worldPivot[2])    $duplicated[0];

         $duplicated = `duplicate $duplicated[0]`;
         $objectsB = `stringArrayCatenate $objectsB $duplicated`;
         move -a ($centerX-$worldPivot[0])    ($centerY-$worldPivot[1])    ($bboxMin[2]-$worldPivot[2]) $duplicated[0];

         $duplicated = `duplicate $duplicated[0]`;
         $objectsB = `stringArrayCatenate $objectsB $duplicated`;
         move -a ($bboxMax[0]-$worldPivot[0]) ($centerY-$worldPivot[1])    ($centerZ-$worldPivot[2])    $duplicated[0];

         $duplicated = `duplicate $duplicated[0]`;
         $objectsB = `stringArrayCatenate $objectsB $duplicated`;
         move -a ($centerX-$worldPivot[0])    ($bboxMax[1]-$worldPivot[1]) ($centerZ-$worldPivot[2])    $duplicated[0];

         $duplicated = `duplicate $duplicated[0]`;
         $objectsB = `stringArrayCatenate $objectsB $duplicated`;
         move -a ($centerX-$worldPivot[0])    ($centerY-$worldPivot[1])    ($bboxMax[2]-$worldPivot[2]) $duplicated[0];

      }

      $gen = $gen-1;
      simpleFractal($objectsB, $gen, $scale);
      clear $objectsB;
      return 1;

   }

}


<- Back