2018年8月19日(日) 13:02 JST

中身の詰まった滑らかなメビウスの輪 -GDLメモ vol.5

  • 2011年3月 8日(火) 16:26 JST
  • 投稿者:

前回のGDLメモではプリミティブ要素を使った滑らかなモデルの記述の仕方について紹介しました。その中で、厚さのないメビウスの輪をエッジの重複がないようにモデリングすると、不自然な箇所が出てきてしまうことがわかりました。確かに厚さのないものは現実世界には存在しませんから、処理がうまくいかないのでしょう。今回は掃引体を使って厚さのあるメビウスの輪、しかも中身が詰まった滑らかなモデルを描いてみましょう。

GDLメモでは、vol.3からプリミティブ要素を扱ってきました。プリミティブ要素を使ってエッジの重複がないようにモデリングすると中身の詰まったモデルに、面(PGON)の設定を行うことで滑らかな面が描けるのでしたね。さっそくモデリングをおこなってみましょう。GDLメモ vol.2の掃引体のコードを参考に作成します。下コードの★のところからが本題です。少し掃引体についておさらいをすると、掃引体のコードでは、下図のような4本の輪(色の付いたもの)を計算し、それぞれ配列に記録しています。配列に記録したそれぞれの輪の頂点を呼び出しながら面を描いていくことでモデリングを行っています。プリミティブ要素の場合も大まかな流れは同じです。掃引体のコードでPLANEを使ってモデリングしていたところをプリミティブ要素に書き換えます。

DIM x0[], y0[], z0[]
DIM x1[], y1[], z1[]
DIM x2[], y2[], z2[]
DIM x3[], y3[], z3[]
DIM x4[], y4[], z4[]

n = 32		!! 分割数
r = 100		!! 半径
w = 15		!! 長方形横
h = 5		!! 長方形縦

th = 360	!! 1周360°
ro = 180	!! で半ひねり

!! スイープパスの生成
FOR i=1 to n+1
	x0[i] = r * COS( (th/n) * (i-1)) : y0[i] = r * SIN( (th/n) * (i-1)) : z0[i] = 0
NEXT i

xx1 =  w : yy1 =  0 : zz1 =  h
xx2 = -w : yy2 =  0 : zz2 =  h
xx3 = -w : yy3 =  0 : zz3 = -h
xx4 = w : yy4 = 0 : zz4 = -h

FOR i=1 to n+1
	l = COS((ro/n)*(i-1)) * xx1 - SIN((ro/n)*(i-1)) * zz1
	z = SIN((ro/n) * (i-1)) * xx1 + COS((ro/n) * (i-1)) * zz1
	y = (l+r) * SIN((th/n)*(i-1))
	x = (l+r) * COS((th/n)*(i-1))
	z1[i] = z + z0[i] : x1[i] = x : y1[i] = y

	l = COS((ro/n)*(i-1)) * xx2 - SIN((ro/n)*(i-1)) * zz2
	z = SIN((ro/n) * (i-1)) * xx2 + COS((ro/n) * (i-1)) * zz2
	y = (l+r) * SIN((th/n)*(i-1))
	x = (l+r) * COS((th/n)*(i-1))
	z2[i] = z + z0[i] : x2[i] = x : y2[i] = y

	l = COS((ro/n)*(i-1)) * xx3 - SIN((ro/n)*(i-1)) * zz3
	z = SIN((ro/n) * (i-1)) * xx3 + COS((ro/n) * (i-1)) * zz3
	y = (l+r) * SIN((th/n)*(i-1))
	x = (l+r) * COS((th/n)*(i-1))
	z3[i] = z + z0[i] : x3[i] = x : y3[i] = y

	l = COS((ro/n)*(i-1)) * xx4 - SIN((ro/n)*(i-1)) * zz4
	z = SIN((ro/n) * (i-1)) * xx4 + COS((ro/n) * (i-1)) * zz4
	y = (l+r) * SIN((th/n)*(i-1))
	x = (l+r) * COS((th/n)*(i-1))
	z4[i] = z + z0[i] : x4[i] = x : y4[i] = y
NEXT i

!! ★ここまでは「掃引体」のコードと同じです。
!! 配列に保存されている頂点座標を使って、プリミティブ要素でモデリングしていきましょう。

!! 頂点の宣言を行います。
!! それぞれの輪の先頭のIndexをInd_vxに記録しておきます。
FOR i = 1 TO n
	VERT x1[i], y1[i], z1[i]
NEXT i
Ind_v1 = 1
FOR i = 1 TO n
	VERT x2[i], y2[i], z2[i]
NEXT i
Ind_v2 = Ind_v1 + n
FOR i = 1 TO n
	VERT x3[i], y3[i], z3[i]
NEXT i
Ind_v3 = Ind_v2 + n
FOR i = 1 TO n
	VERT x4[i], y4[i], z4[i]
NEXT i
Ind_v4 = Ind_v3 + n

FOR i = 1 TO n - 1
	EDGE Ind_v1 + (i - 1), Ind_v1 + i, -1, -1, 0		!! v1(n)とv1(n + 1)を繋ぐ ①
	EDGE Ind_v1 + (i - 1), Ind_v2 + i, -1, -1, 2		!! v1(n)とv2(n + 1)を繋ぐ ②
	EDGE Ind_v1 + (i - 1), Ind_v2 + (i - 1), -1, -1, 2	!! v1(n)とv2(n)を繋ぐ ③
NEXT i
!! 最後のエッジはn 番目と1番目を繋ぎます。
i = n 
EDGE Ind_v3 + (n - 1), Ind_v1, -1, -1, 0
EDGE Ind_v3 + (n - 1), Ind_v2, -1, -1, 2
EDGE Ind_v3 + (n - 1), Ind_v4 + (n - 1), -1, -1, 2
!! このエッジ群の先頭のIndexをInd_e1に記録しておきます。
Ind_e1 = 1

!! 同じように他の輪に関してもエッジを作成していきます。
FOR i = 1 TO n - 1
	EDGE Ind_v2 + (i - 1), Ind_v2 + i, -1, -1, 0		!! v2(n)とv2(n + 1)を繋ぐ ④
	EDGE Ind_v2 + (i - 1), Ind_v3 + i, -1, -1, 2		!! v2(n)とv3(n + 1)を繋ぐ ⑤
	EDGE Ind_v2 + (i - 1), Ind_v3 + (i - 1), -1, -1, 2	!! v2(n)とv3(n)を繋ぐ ⑥
NEXT i
EDGE Ind_v4 + (n - 1), Ind_v2, -1, -1, 0
EDGE Ind_v4 + (n - 1), Ind_v3, -1, -1, 2
EDGE Ind_v4 + (n - 1), Ind_v1 + (n - 1), -1, -1, 2
Ind_e2 =  Ind_e1 + 3 * n

FOR i = 1 TO n - 1
	EDGE Ind_v3 + (i - 1), Ind_v3 + i, -1, -1, 0
	EDGE Ind_v3 + (i - 1), Ind_v4 + i, -1, -1, 2	
	EDGE Ind_v3 + (i - 1), Ind_v4 + (i - 1), -1, -1, 2
NEXT i
EDGE Ind_v1 + (n - 1), Ind_v3, -1, -1, 0
EDGE Ind_v1 + (n - 1), Ind_v4, -1, -1, 2
EDGE Ind_v1 + (n - 1), Ind_v2 + (n - 1), -1, -1, 2
Ind_e3 =  Ind_e2 + 3 * n

FOR i = 1 TO n - 1
	EDGE Ind_v4 + (i - 1), Ind_v4 + i, -1, -1, 0
	EDGE Ind_v4 + (i - 1), Ind_v1 + i, -1, -1, 2	
	EDGE Ind_v4 + (i - 1), Ind_v1 + (i - 1), -1, -1, 2
NEXT i
EDGE Ind_v2 + (n - 1), Ind_v4, -1, -1, 0
EDGE Ind_v2 + (n - 1), Ind_v1, -1, -1, 2
EDGE Ind_v2 + (n - 1), Ind_v3 + (i - 1), -1, -1, 2
Ind_e4 =  Ind_e3 + 3 * n

!! 上下面
!! エッジを繋いで面を作成していきます。
!! エッジの定義では、一つのループ毎に三本のエッジを作成しました。なので、
!! Ind_e1 + 3 * (i - 1)、Ind_e1 + 3 * (i - 1) + 1、Ind_e1 + 3 * (i - 1) + 2は、下図でいう①、②、③に相当します。
!! (Ind_e2 + 3 * (i - 1)、Ind_e2 + 3 * (i - 1) + 1、Ind_e2 + 3 * (i - 1) + 2は、下図でいう④、⑤、⑥に相当します。
SmoothFlag= 2 !! 0 || 2
FOR i = 1 TO n - 2
	PGON 3, 0, SmoothFlag, Ind_e1 + 3 * (i - 1), Ind_e1 + 3 * i + 2, -(Ind_e1 + 3 * (i - 1) + 1)
	PGON 3, 0, SmoothFlag, Ind_e1 + 3 * (i - 1) + 1, - ( Ind_e2 + 3 * (i - 1)), -(Ind_e1 + 3 * (i - 1) + 2)
	PGON 3, 0, SmoothFlag, Ind_e3 + 3 * (i - 1), Ind_e3 + 3 * i + 2, -(Ind_e3 + 3 * (i - 1) + 1)
	PGON 3, 0, SmoothFlag, Ind_e3 + 3 * (i - 1) + 1, - ( Ind_e4 + 3 * (i - 1)), -(Ind_e3 + 3 * (i - 1) + 2)
NEXT i
!! n-1番目とn番目は、1番目からn-2番目までと異なる処理によって面を作成します。
!! 一周回ると180度ねじれるため、頂点群v1でできた輪は頂点群v3とつながるかたちで面になります。
i = n - 1
PGON 3, 0, SmoothFlag, Ind_e1 + 3 * (i - 1), Ind_e3 + 3 * i + 2, -(Ind_e1 + 3 * (i - 1) + 1)
PGON 3, 0, SmoothFlag, Ind_e1 + 3 * (i - 1) + 1, - ( Ind_e2 + 3 * (i - 1)), -(Ind_e1 + 3 * (i - 1) + 2)
PGON 3, 0, SmoothFlag, Ind_e3 + 3 * (i - 1), Ind_e1 + 3 * i + 2, -(Ind_e3 + 3 * (i - 1) + 1)
PGON 3, 0, SmoothFlag, Ind_e3 + 3 * (i - 1) + 1, - ( Ind_e4 + 3 * (i - 1)), -(Ind_e3 + 3 * (i - 1) + 2)
i = n
PGON 3, 0, SmoothFlag, Ind_e1 + 3 * (i - 1), Ind_e1  + 2, -(Ind_e1 + 3 * (i - 1) + 1)
PGON 3, 0, SmoothFlag, Ind_e1 + 3 * (i - 1) + 1, - ( Ind_e2 + 3 * (i - 1)), -(Ind_e1 + 3 * (i - 1) + 2)
PGON 3, 0, SmoothFlag, Ind_e3 + 3 * (i - 1), Ind_e3  + 2, -(Ind_e3 + 3 * (i - 1) + 1)
PGON 3, 0, SmoothFlag, Ind_e3 + 3 * (i - 1) + 1, - ( Ind_e4 + 3 * (i - 1)), -(Ind_e3 + 3 * (i - 1) + 2)

!! 側面
SmoothFlag= 0 ! 0 || 2
FOR i = 1 TO n - 2
	PGON 3, 0, SmoothFlag, Ind_e2 + 3 * (i - 1), Ind_e2 + 3 * i + 2, -(Ind_e2 + 3 * (i - 1) + 1)
	PGON 3, 0, SmoothFlag, Ind_e2 + 3 * (i - 1) + 1, - ( Ind_e3 + 3 * (i - 1)), -(Ind_e2 + 3 * (i - 1) + 2)
	PGON 3, 0, SmoothFlag, Ind_e4 + 3 * (i - 1), Ind_e4 + 3 * i + 2, -(Ind_e4 + 3 * (i - 1) + 1)
	PGON 3, 0, SmoothFlag, Ind_e4 + 3 * (i - 1) + 1, - ( Ind_e1 + 3 * (i - 1)), -(Ind_e4 + 3 * (i - 1) + 2)
NEXT i
i = n - 1
PGON 3, 0, SmoothFlag, Ind_e2 + 3 * (i - 1), Ind_e4 + 3 * i + 2, -(Ind_e2 + 3 * (i - 1) + 1)
PGON 3, 0, SmoothFlag, Ind_e2 + 3 * (i - 1) + 1, - ( Ind_e3 + 3 * (i - 1)), -(Ind_e2 + 3 * (i - 1) + 2)
PGON 3, 0, SmoothFlag, Ind_e4 + 3 * (i - 1), Ind_e2 + 3 * i + 2, -(Ind_e4 + 3 * (i - 1) + 1)
PGON 3, 0, SmoothFlag, Ind_e4 + 3 * (i - 1) + 1, - ( Ind_e1 + 3 * (i - 1)), -(Ind_e4 + 3 * (i - 1) + 2)
i = n
PGON 3, 0, SmoothFlag, Ind_e2 + 3 * (i - 1), Ind_e2 + 2, -(Ind_e2 + 3 * (i - 1) + 1)
PGON 3, 0, SmoothFlag, Ind_e2 + 3 * (i - 1) + 1, - ( Ind_e3 + 3 * (i - 1)), -(Ind_e2 + 3 * (i - 1) + 2)
PGON 3, 0, SmoothFlag, Ind_e4 + 3 * (i - 1), Ind_e4 + 2, -(Ind_e4 + 3 * (i - 1) + 1)
PGON 3, 0, SmoothFlag, Ind_e4 + 3 * (i - 1) + 1, - ( Ind_e1 + 3 * (i - 1)), -(Ind_e4 + 3 * (i - 1) + 2)

BODY 2

中身の詰まったモデルを描くときはエッジが重複していてはいけませんから上図のように、一回のループで三本のエッジを作成していきます。図中の数字(①など)は、コード中の数字と対応しています。PGONのところの記述が少しややこしいですね。エッジの重複がないようにしているため、ピンクの面を作成する場合、n番目のループで宣言された①、②のエッジと、n+1番目のループで宣言された③のエッジが必要になります。このコードによって、中身の詰まった滑らかなメビウスの輪がモデリングできます。直方体からこのメビウスの輪を減算すると、下図のようなイメージが得られます。中身が詰まったモデルになっていることが確認できます。

さて、実はこのコードでは、メビウスの輪の側面は滑らかなモデルになっていません。側面のポリゴンを作成するところではPGONのstateが「0」になっています(SmoothFlag = 0)。ためしに側面も滑らかモデルにしようとすると下図左のようになってしまいます。稜線がなくなっていますね。中身が詰まるようにモデリングを行う場合、エッジが重複しないようモデリングする必要があります。エッジが重複しないということは、ある面と面とが稜線を形成する場合、それぞれの面で使われている(稜線となっている)エッジは同じもの(同じindex)であり、そのエッジの始点と終点(頂点)も同じもの(同じindex)ということになります。頂点は一つ(だけ)法線をもつことから、上下面、側面ともに滑らかにする場合は、上下面と側面の境界(稜線)も滑らかになってしまうというわけです。中身は空になってしまいますが、上下面、側面で、それぞれ異なる頂点からエッジを宣言し、面を作成すると下図右のように上下面、側面ともに滑らかなモデルを得ることができます。