Shade3D 公式

12 Bezier Patch [ Shade Labo ]

For Shade 17, Surface subdivision to a polygon mesh is a Bezier Patch, and surface subdivision at rendering is a Gregory Patch!

gregory_patch

For Shade 16, we are not are dealing with rational geometry; however, legacy Curved Surface parts use non-standard blending polynomials not part of NURBS; I have deduced a part of the behavior of the blending polynomials, which will be in another post.

The Gregory Patch uses similar handle judgment criteria as surface subdivision to a polygon mesh.The following script generates the two sets of internal control points for a Gregory Patch:

import labo

scene = xshade.scene()

#  bezier patch を求める自由曲面のブロック指定
m = 0
n = 0

#  選択自由曲面の m, n ブロックの12個の bezier patch 制御点座標を取得
bP = labo.get_bezier_patch_base(xshade, m, n) 	

#  4つの未定義 bezier patch 座標をセット
labo.set_gregory_patch(bP)

#  bezier patch  bP を出力
labo.make_bezier_patch(xshade, bP, 'U')
xshade.scene().select_sister(1)
xshade.scene().active_shapes[0].switch()

#  bezier patch  bP1 を出力
bP1 = labo.get_bezier_patch_base(xshade, m, n)
labo.set_gregory_patch(bP1)
labo.make_bezier_patch(xshade, bP1, 'V')
xshade.scene().select_sister(1)
xshade.scene().select_brother(2)
xshade.scene().place_sister(1)
xshade.scene().select_sister(1)
xshade.scene().active_shapes[0].switch()

Please register the following function into your labo module;

def set_gregory_patch(bP) :
if bP == None :
	return
	
bb = vec3_bounding_box_size(bP)			#  bP の bounding box size
epsilon = bb[3]/100000						#  0 handle 判定しきい値

#  overlap :	patch の一点収束 ( 極 ) 存在状態
pL = []
pL.append(bP[0])										#   第一行
pL.append([bP[0][0], bP[1][0], bP[2][0], bP[3][0]])		#   第一列
pL.append([bP[0][3], bP[1][3], bP[2][3], bP[3][3]])		#   第四列
pL.append(bP[3])										#   第四行

overlap = []
for p in pL :
	result = True
	for i in range(1,4) :
		v = p[i] - p[i - 1]
		if v.abs() > epsilon :
			result = False						
			break
	if result :
		overlap.append(True)				#  一点収束している
	else :
		overlap.append(False)				#  一点収束していない
		

#  4 つの bezier patch 座標をセット ( step 1 )
for [[i, j], [ii, jj], k1, k2] in [  [[0, 0],[1, 1], 0, 1]  ,  [[0, 3],[1, -1], 0, 2]  ,  [[3, 0], [-1, 1], 3, 1]  ,  [[3, 3], [-1, -1], 3, 2]  ] :
	
	p00 = bP[i][j]
	
	p01 = bP[i][j + jj]
	p10 = bP[i + ii][j]
	
	p30 = bP[i + 3*ii][j]
	p31 = bP[i + 3*ii][j + jj]
	
	p03 = bP[i][j + 3*jj]
	p13 = bP[i + ii][j + 3*jj]

	u1 = p01 - p00
	u2 = p31 - p30
	v1 = p10 - p00
	v2 = p13 - p03	
	
	
	#  u, v
	if overlap[k1] and overlap[k2] :					#  行, 列 両方向が一点収束 ( 極 )
		u = vec3(0, 0, 0)
		v = vec3(0, 0, 0)
	
	elif overlap[k1] :									#  行方向が一点収束 ( 極 )
		p20 = bP[i + 2*ii][j]
		v3 = p20 - p00
		r = v3.abs()
		if r > epsilon :
			u = u2*v1.abs()/r
		else :
			u = vec3(0, 0, 0)
		v = v1
		
	elif overlap[k2] :									#  列方向が一点収束 ( 極 )
		p02 = bP[i][j + 2*jj]
		u3 = p02 - p00
		r = u3.abs()
		if r > epsilon :
			v = v2*u1.abs()/r
		else :
			v = vec3(0, 0, 0)
		u = u1
		
	else :											#  行 / 列 方向が一点収束 ( 極 ) ではない
		if u1.abs() > epsilon :						#  行方向 handle あり
			if u2.abs() > epsilon :					#  反対側の行方向 handle あり
				u = (5*u1 + u2)/(6)
			else :									#  反対側の行方向 handle なし
				u = u1
		else :
			u = u2/3
			
		if v1.abs() > epsilon :						#  列方向 handle あり
			if v2.abs() > epsilon :					#  反対側の列方向 handle あり
				v = v1
			else :									#  反対側の列方向 handle なし
				v = v1
		else :
			v = v2/3
	
	bP[i + ii][j + jj] = p00 + u + v	

#  4 つの bezier patch 座標をセット ( step 2 )	
vL = [vec3(0, 0, 0), vec3(0, 0, 0), vec3(0, 0, 0), vec3(0, 0, 0)]		#  座標補正 vector

pL = []
pL.append([bP[0][3], bP[1][3], bP[0][0], bP[1][0], 1, 0, overlap[0]])
pL.append([bP[0][0], bP[0][1], bP[3][0], bP[3][1], 0, 2, overlap[1]])
pL.append([bP[3][0], bP[2][0], bP[3][3], bP[2][3], 2, 3, overlap[3]])
pL.append([bP[3][3], bP[3][2], bP[0][3], bP[0][2], 3, 1, overlap[2]])

for [p1, h1, p2, h2, k1, k2, overlapping] in pL :
	if not overlapping :
		u1 = h1 - p1
		u2 = h2 - p2
		U1 = vec3(u1)
		U2 = vec3(u2)
		if (U1.norm() > epsilon) and (U2.norm() > epsilon) :
			if (U1*U2).abs() > 1e-8 :
				U = U1 - U2
				U.norm()
				L1 = u1.dot(U)
				L2 = u2.dot(U)
				v = -(L1 + L2)/3*U
		
				vL[k1] = vL[k1] + v
				vL[k2] = vL[k2] + v
		
bP[1][1] = bP[1][1] + vL[0]
bP[1][2] = bP[1][2] + vL[1]
bP[2][1] = bP[2][1] + vL[2]
bP[2][2] = bP[2][2] + vL[3]

This is 95% accurate - the exact handle judgment criteria are unknown.
And, here’s the conversion formulae for turning a Gregory Patch into a NURBS surface:

Coordinates

Q00 = P00
Q01 = P00
Q02 = (5P00 + 6P01)/11
Q03 = (2P00 + 15P01 + 6P02 )/23
Q04 = (6P01 + 15P02 + 2P03 )/23
Q05 = (6P02 + 5P03 )/11
Q06 = P03
Q07 = P03
Q10 = P00
Q11 = (7P00 + 3P01 + 3P10)/13
Q12 = (18P111 + 28P00 + 42P01 + 6P02 + 15P10)/109
Q13 = (45P111 + 18P121 + 14P00 + 84P01 + 42P02 + 2P03 + 6P10)/211
Q14 = (18P111 + 45P121 + 2P00 + 42P01 + 84P02 + 14P03 + 6P13)/211
Q15 = (18P121 + 6P01 + 42P02 + 28P03 + 15P13)/109
Q16 = (3P02 + 7P03 + 3P13)/13
Q17 = P03
Q20 = (5P00 + 6P10)/11
Q21 = (18P110 + 28P00 + 15P01 + 42P10 + 6P20)/109
Q22 = (63P110 + 63P111 + 18P121 + 18P210 + 52P00 + 84P01 + 15P02 + 84P10 + 15P20)/412
Q23 = (63P110 + 189P111 + 18P120 + I08P121 + 27P210 + 18P211 + 9P220 + 9P221 + 28P00 + 156P01 +84P02 + 5P03 + 42P10 + 6P13 + 6P20)/768
Q24 = (18P110 + I08P111 + 63P120 + 189P121 + 9P210 + 9P211 + 27P220 + 18P221 + 5P00 + 84P01 +156P02 + 28P03 + 6P10 + 42P13 + 6P23)/768
Q25 = (18P111 + 63P120 + 63P121 + 18P220 + 15P01 + 84P02 + 52P03 + 84P13 + 15P23)/412
Q26 = (18P120 + 15P02 + 28P03 + 42P13 + 6P23)/109
Q27 = (5P03 + 6P13)/11
Q30 = (2P00 + 15P10 + 6P20)/23
Q31 = (45P110 + 18P210 + 14P00 + 6P01 + 84P10 + 42P20 + 2P30)/211
Q32 = (189P110 + 63P111 + 18P120 + 27P121 + 108P210 + 18P211 + 9P220 + 9P221 + 28P00 + 42P01 + 6P02 + 156P10 + 84P20 + 5P30 + 6P31)/768
Q33 = (234P110 + 234P111 + 108P120 + 144P121 + 144P210 + 108P211 + 63P220 + 63P221 + 14P00 + 84P01 + 42P02 + 2P03 + 84P10 + 15P13 + 42P20 + 6P23 + 2P30 + 15P31 + 6P32)/1410
Q34 = (108P110 + 144P111 + 234P120 + 234P121 + 63P210 + 63P211 + 144P221 + 108P221 + 2P00 + 42P01 + 84P02 + 14P03 + 15P10 + 84P13 + 6P20 + 42P23 + 6P31 + 15P32 + 2P33)/1410
Q35 = (18P110 + 27P111 + 189P120 + 63P121 + 9P210 + 9P211 + 108P220 + 18P221 + 6P01 + 42P02 + 28P03 + 156P13 + 84P23 + 6P32 + 5P33)/768
Q36 = (45P120 + 18P220 + 6P02 + 14P03 + 84P13 + 42P23 + 2P31)/211
Q37 = (2P03 + 15P13 + 6P23)/23
Q40 = (6P10 + 15P20 + 2P30)/23
Q41 = (18P110 + 45P210 + 2P00 + 42P10 + 84P20 + 14P30 + 6P31)/211
Q42 = (108P110 + 18P111 + 9P120 + 9P121 + 189P210 + 63P211 + 18P220 + 27P221 + 5P00 + 6P01 + 84P10 + 156P20 + 28P30 + 42P31 + 6P32)/768
Q43 = (144P110 + 108P111 + 63P120 + 63P121 + 234P210 + 234P211 + 108P220 + 144P221 + 2P00 + 15P01 + 6P02 + 42P10 + 6P13 + 84P20 + 15P23 + 14P30 + 84P31 + 42P32 + 2P33)/1410
Q44 = (63P110 + 63P111 + 144P120 + 108P121 + 108P210 + 144P211 + 234P220 + 234P221 + 6P01 + 15P02 + 2P03 + 6P10 + 42P13 + 15P20 + 84P23 + 2P30 + 42P31 + 84P32 + 14P33)/1410
Q45 = (9P110 + 9P111 + 108P120 + 18P121 + 18P210 + 27P211 + 189P220 + 63P221 + 6P02 + 5P03 + 84P13 + 156P23 + 6P31 + 42P32 + 28P33)/768
Q46 = (18P120 + 45P220 + 2P03 + 42P13 + 84P23 + 6P32 + 14P33)/211
Q47 = (6P13 + 15P23 + 2P33)/23
Q50 = (6P20 + 5P30)/11
Q51 = (18P210 + 6P10 + 42P20 + 28P30 + 15P31)/109
Q52 = (18P110 + 63P210 + 63P211 + 18P221 + 15P10 + 84P20 + 52P30 + 84P31 + 15P32)/412
Q53 = (27P110 + 18P111 + 9P120 + 9P121 + 63P210 + 189P211 + 18P220 + 108P221 + 6P10 + 42P20 + 6P23 + 28P30 + 156P31 + 84P32 + 5P33)/768
Q54 = (9P110 + 9P111 + 27P120 + 18P121 + 18P210 + 108P211 + 63P220 + 189P221 + 6P13 + 6P20 + 42P23 + 5P30 + 84P31 + 156P32 + 28P33)/768
Q55 = (18P220 + 18P211 + 63P220 + 63P221 + 15P13 + 84P23 + 15P31 + 84P32 + 52P33)/412
Q56 = (18P220 + 6P13 + 42P23 + 15P32 + 28P33 )/109
Q57 = (6P23 + 5P33)/11
Q60 = P30
Q61 = (3P20 + 7P30 + 3P31)/13
Q62 = (18P211 + 15P20 + 28P30 + 42P31 + 6P32)/109
Q63 = (45P211 + 18P221 + 6P20 + 14P30 + 84P31 + 42P32 + 2P33)/211
Q64 = (18P211 + 45P221 + 6P23 + 2P30 + 42P31 + 84P32 + 14P33)/211
Q65 = (18P221 + 15P23 + 6P31 + 42P32 + 28P33)/109
Q66 = (3P23 + 3P32 + 7P33 )/13
Q67 = P33
Q70 = P30
Q71 = P30
Q72 = (5P30 + 6P31)/11
Q73 = (2P30 + 15P31 + 6P32)/23
Q74 = (6P31 + 15P32 + 2P33)/23
Q75 = (6P32 + 5P33)/11
Q76 = P33
Q77 = P33

Control point weights:

W00, W70, W07, W77 = 0
W01, W71, W06, W76, W10, W60, W17, W67 = 2/7
W02, W72, W05, W75, W20, W50, W27, W57 = 11/21
W03, W73, W04, W74, W30, W40, W37, W47 = 23/35
W11, W61, W16, W66 = 26/49
W12, W62, W15, W65, W21, W51, W26, W56 = 109/147
W13, W63, W14, W64, W31, W41, W36, W46 = 211/245
W22, W52, W25, W55 = 412/441
W23, W53, W24, W 54, W32, W42, W35, W45 = 256/245
W33, W43, W34, W44 = 282/245