Shade3D 公式

12 Bezier Patch [ Shade Labo ]

12 - 1 Bezier Patch と Shade Bezier 曲面


bezier line が 4 つの制御点で定義されたように、bezier surface は 4 x 4 = 16 個の制御点で定義され、これを bezier patch と呼びます。

     [ fig 12 - 1 ]


     [ fig 12 - 2 ]


一方 Shade の bezier 曲面は U, V 各方向に 2 つずつ、計4本の bezier line で構成され、その制御点総数は12個です。

bezier patch 16個中、中央の4つについては、定義されていません。

16個もの変数を楽に制御できるようなユーザーインターフェースはほとんど無理であり、Shade では [ fig 12 - 1 ] の A, B, C, D 4つの制御点を他の制御点によって決定されるようにしています。

つまり、16個の独立変数の内、4つが従属変数として表されるよう別途定義し、12個の独立変数で曲面を定義するように変更している ということです。

また、こうすることで 2つの bezier 曲面が滑らかに連続するようにセットするのが簡単になります。

3 Likes

12 - 2   4つの従属変数


Shade では [ fig 12 - 1 ] の制御点 A の座標は 3 つの座標値 P, H, h を用いて次のように定義されます。( B~D も同様 )

          [ 式 12 - 1 ]


          [ fig 12 - 3 ]


[ script 12 - 1 ] は以上の検証のための script です。

Shade 上で選択された自由曲面の指定するブロックの bezier patch を線形状群として出力するもので、[ 式 12 - 1 ] で求めたものと、DXF ファイル経由で得たものの2種類を出力し、比較します。


検証結果
               [ fig 12 - 4 ]


< script 実行の前に >

bezier patch の16個の制御点の内、従属変数とした A, B, C, D 4つの制御点座標を得る機能は script には用意されていませんが、DXF エクスポートで「 自由曲面としてエクスポート 」すると、16個の制御点座標を入手できます。

この script を実行する前に予め DXF エクスポートのプロパティ設定を次の要領でマニュアルで行っておく必要があります。

  • メニュー > ファイル > エクスポート >DXF を選択

  • DXFエクスポート設定欄の「 ベジェ曲面 」にチェックを入れる

  • OK ボタンを押す

  • 保存のためのダイアログが現れるので「 キャンセル 」ボタンを押す

一度このプロパティを設定しておけば、Shade を再起動してもしばらくはその設定が継続されたままになります。

     

     [ fig 12 - 4 ]


[ script 12 - 1 ]

import labo

#  bet_bezier_patch(bP as vec3 matrix)
#  bz 内の4つの未定義 bezier patch 座標をセット
#
def set_bezier_patch(bP) :
	for [[i, j], [ii, jj]] in [[[1, 1],[-1, -1]],[[1, 2], [-1, 1]], [[2, 1], [1, -1]], [[2, 2], [1, 1]]] :
		p0 = bP[i + ii][j + jj]
		p1 = bP[i + ii][j]
		p2 = bP[i ][j + jj]
		bP[i][j] = p1 + p2 - p0
			


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 座標をセット
set_bezier_patch(bP)

#  選択自由曲面の m, n ブロックの bezier patch を DXF 経由で取得
bP2 = labo.get_bezier_patch_by_DXF(xshade, m, n)

scene.create_part()
labo.make_bezier_surface(xshade, bP)		#  当該ブロックを自由曲面として出力
	
#  bezier patch  bP を出力
labo.make_bezier_patch(xshade, bP, 'bezier patch')

#  bezier patch  bP2 を出力
labo.make_bezier_patch(xshade, bP2, 'bezier patch by DXF')

scene.select_parent(1)

12 - 3 Patch 補正


[ script 12 - 1 ] を球体状の自由曲面に適用すると、極の部分で異なった結果になります。

               [ fig 12 - 4 ]


極部分のコントロールポイントには交差方向のハンドルがありませんから [ 式 12 - 1 ] に従えば、上図中央のようになりますし、DXF で与えられる bezier patch の方が滑らかな球面を構成しそうだということも感覚的に予想できます。

Shade ではハンドルが出ていない場合に補正を加えて赤い patch のように修正します。




この補正は次のようになっています。

  • bezier patch のアンカーポイント P において、そこから伸びるハンドル h0 - P に長さがなく( *1 )、かつ、その交差方向のハンドル h1 - P に長さがある場合( *2 )、奥にあるハンドル H - Q の 1/2 長さのベクトルを h1 に加える。

          [ 式 12 - 2 ]

  • ハンドル長さの 0 判定は次による

     *1 の判定では bounding box size x 1/10000 以下
     *2 の判定では bounding box size x 1/1000 以下


なお、bounding box size( X, Y, Z 方向の box size の和 ) は 12個の制御点座標から求められるもので、xshade.scene().active_shape().bounding_box_size で得られるものとは異なります。

          [ fig 12 - 5 ]


よって bezier patch を求める関数は次のようになり、labo module に登録してあります。


[ scripr 12 - 2 ]

#  get_bezier_patch(xshade as xshade, m as int, n as int, uuid as string = None ) as vec3 matrix
#	自由曲面の指定する区間 m, n ( 0基数 ) の bezier patch 座標を vec3 matrix として返す
#	デフォルトでは選択自由曲面、uuid に Shade で取得した自由曲面の uuid を渡せば、そのデータを取得
#
def get_bezier_patch(xshade, m, n, uuid = None) :
	bP = get_bezier_patch_base(xshade, m, n, uuid) 		#  12 個の bezier pach 座標を取得
	if bP == None :
		return None
		
	bb = vec3_bounding_box_size(bP)						#  bP の bounding box size
	epsilon1 = bb[3]/10000								#  0 handle 判定しきい値
	epsilon2 = bb[3]/1000								#  0 handle 判定しきい値
	
	#  4 つの bezier patch 座標をセット
	for [[i, j], [ii, jj]] in [[[1, 1],[-1, -1]],[[1, 2], [-1, 1]], [[2, 1], [1, -1]], [[2, 2], [1, 1]]] :
		p0 = bP[i + ii][j + jj]
		p1 = bP[i + ii][j]
		p2 = bP[i ][j + jj]

		v1 = p1 - p0
		v2 = p2 - p0
		if (v1.abs() <= epsilon1) and (v2.abs() > epsilon2) :
			p1 = p0 + (bP[i - 2*ii][j] - bP[i - 2*ii][j + jj])/2
		if (v2.abs() <= epsilon1) and (v1.abs() > epsilon2) :
			p2 = p0 + (bP[i][j - 2*jj] - bP[i + ii][j - 2*jj])/2
	
		bP[i][j] = p1 + p2 - p0
			
	return bP

[ script 12 - 3 ] ~ [ script 12 - 5 ] でこれを検証します。
[ script 12 - 3 ]
import labo
from vec3 import *


scene = xshade.scene()
scene.create_part

for h in [4, 4.0001] :		#  h1, h2 : handle  長さ
	#  sample 自由曲面		
	bz = []
	bz.append([[0, 10000, 0], [0, 10000, 5000], [0, 5000, 10000], [0, 0, 10000]])
	bz.append([[h, 10000, 0], None, None, [5000, 0, 10000]])
	bz.append([[5000, 10000, -5000], None, None, [15000, 0, 0]])
	bz.append([[5000, 10000, -5000], [13000, 10000, -5000], [15000, 5000, -5000], [15000, 0, -5000]])
	bz = vec3list(bz)

	#  bz の bounding box size と handle size の比
	bb = labo.vec3_bounding_box_size(bz)
	ratio = h/bb[3]					

	scene.create_part(str(ratio))

	#  sample 自由曲面を出力
	labo.make_bezier_surface(xshade, bz)

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

	#  選択自由曲面の 0, 0 ブロックの bezier patch を DXF 経由で取得
	bP2 = labo.get_bezier_patch_by_DXF(xshade, 0, 0)

	#  bezier patch  bP を出力
	labo.make_bezier_patch(xshade, bP, 'bezier patch')

	#  bezier patch  bP2 を出力
	labo.make_bezier_patch(xshade, bP2, 'bezier patch by DXF')

	scene.select_parent(1)
	
scene.select_parent(1)

[ script 12 - 4 ]
import labo
from vec3 import *


scene = xshade.scene()
scene.create_part

for [h1, h2] in [[4, 40], [4, 40.001], [40, 4], [40.001, 4]] :		#  h1, h2 : handle  長さ
	#  sample 自由曲面		
	bz = []
	bz.append([[0, 10000, 0], [0, 10000, h2], [0, 5000, 10000], [0, 0, 10000]])
	bz.append([[h1, 10000, 0], None, None, [5000, 0, 10000]])
	bz.append([[5000, 10000, -5000], None, None, [15000, 0, 0]])
	bz.append([[5000, 10000, -5000], [13000, 10000, -5000], [15000, 5000, -5000], [15000, 0, -5000]])
	bz = vec3list(bz)

	#  bz の bounding box size と handle size の比
	bb = labo.vec3_bounding_box_size(bz)
	ratio1 = float(h1)/bb[3]					
	ratio2 = float(h2)/bb[3]
	
	if ratio1 >=1e-3 :
		t1 = str(ratio1*1000) + 'e-3'
	else :
		t1 = str(ratio1*10000) + 'e-4'	
	if ratio2 >=1e-3 :
		t2 = str(ratio2*1000) + 'e-3'
	else :
		t2 = str(ratio2*10000) + 'e-4'	

	scene.create_part(t1 + '_' +  t2)

	#  sample 自由曲面を出力
	labo.make_bezier_surface(xshade, bz)

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

	#  選択自由曲面の 0, 0 ブロックの bezier patch を DXF 経由で取得
	bP2 = labo.get_bezier_patch_by_DXF(xshade, 0, 0)

	#  bezier patch  bP を出力
	labo.make_bezier_patch(xshade, bP, 'bezier patch')

	#  bezier patch  bP2 を出力
	labo.make_bezier_patch(xshade, bP2, 'bezier patch by DXF')

	scene.select_parent(1)
	
scene.select_parent(1)

[ script 12 - 5 ]
import labo
from vec3 import *


scene = xshade.scene()
scene.create_part

for h in [5.321, 5.322] :		#  h1, h2 : handle  長さ
	#  sample 自由曲面		
	bz = []
	bz.append([[0, 10000, 0], [0, 10000, 5000], [0, 5000, 10000], [0, 0, 10000]])
	bz.append([[h, 10000, 0], None, None, [5000, 0, 10000]])
	bz.append([[5000, 10000, -5000], None, None, [15000, 0, 0]])
	bz.append([[5000, 10000, -5000], [13000, 10000, -5000], [15000, 5000, -5000], [15000, 0, -5000]])
	bz = vec3list(bz)

	scene.create_part()

	#  sample 自由曲面を出力
	labo.make_bezier_surface(xshade, bz)
	
	#  自由曲面を回転
	scene.move_object([0, 0, 0], None, [0, -115, 0], None)
	scene.move_object([0, 0, 0], None, [40, 0, 0], None)
	scene.move_object([0, 0, 0], None, [0, 0, 48], None)

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

	#  選択自由曲面の 0, 0 ブロックの bezier patch を DXF 経由で取得
	bP2 = labo.get_bezier_patch_by_DXF(xshade, 0, 0)

	#  bezier patch  bP を出力
	labo.make_bezier_patch(xshade, bP, 'bezier patch')

	#  bezier patch  bP2 を出力
	labo.make_bezier_patch(xshade, bP2, 'bezier patch by DXF')

	#  bz の bounding box size と handle size の比
	bb = labo.vec3_bounding_box_size(bP)
	ratio = h/bb[3]
	scene.select_parent(1)
	scene.active_shape().name = str(ratio)
	
scene.select_parent(1)

EN:
There seems to be more going on with curved surface generation than what is shown in this post, especially since the release of Shade3D 17.1.

In short, I detect noticeable deviations from freeform surfaces obtained by Equation 12-1 and the polygon output from Shade. None of the vertices of the polygon output lie on the surfaces obtained from Equation 12-1, save for the boundary curves. Given a surface with an average bounding box of 950 mm, the surface obtained by Equation 12-1 deviates from the vertices of Shade’s polygon output by as much as 5mm.

JA:
特にShade3D 17.1がリリースされて以来、この記事で示されているものよりも湾曲したサーフェスの生成が多く発生しているようです。

つまり、式12-1で得られた自由曲面からの目立つ偏差とシェードのポリゴン出力を検出します。 ポリゴン出力の頂点のいずれも、式12-1で得られたサーフェス上にはなく、境界曲線のために保存されていません。 平均外接矩形が950mmの表面が与えられると、式12-1によって得られた表面は、シェードのポリゴン出力の頂点から5mmだけずれている。

Hi, thank you for your advice.

I confirmed the problem, and found the below.



Shade pythn script problem ( Shade 16, 17, 18 )

Shade script “xshade.scene().save_DXF()” doesn’t support file DXF export setting properties.

The check of “Bezier curved surface” doesn’t work.

I will revise related scripts.

pic%201



Shade 17 / 18 changed Bezier patch base

< Shade 15 / 16 >
Bezier patches by DXF file and rendering base are different.

< Shade 17 / 18 >
Bezier patches by DXF file and rendering base are the same, but the patches are different between Shade 15/16 and Shade 17/18.


I guess that Shade 17/18 changed DXF file library and rendering patch library due to NURBS support.

It takes time, but I will examine the Bezier Patch and report it.

1 Like

I also noticed that curved surfaces render different than if they were converted to polygon mesh objects. Converting to a polygon mesh at higher subdivision levels reveals
Both are rendered at subdivision level 4 in Shade 12.

I guess results like these, when converting to a polygon mesh, is what prompted the change to the surface generation algorithm in Shade 17.1?

Ja, I’ve recognized the problem of surface subdivision at rendering.

We have two major problems to be solved.

One is bezier patch interpolate alogorithm of Sade 17 and the other is surface subdivision algorithm at rendering.

The former is already found out.

The algorithm is quite difficult and need illustration to explain.

pic%2016

The latter isn’t just for Shade 17, but for any Shade versions.

I have no idea yet.

Anyway I’m already writing a draft of new Shade Labo topic, please give me some time.

1 Like

Some topics will be posted. ( sorry, in Japanese )



< 12 - 4 Specifications change of Shade 16 and later >

It shows summary of specifications change and modified labo module function.

  • Shade 16 threshold value for zero handle
  • Shade 17 threshold value for zero handle
  • Shade 17 bezier patch interpolate algorithm

Modified function ( labo module )

get_bezier_patch ( xshade as xshade, m as int, n as int, uuid as string = None, legacy as bool = None )

     legacy : specify bezier patch interpolate algorithm

     legacy = True : Shade 15 base
     legacy = False : Shade 17 base
     legacy = None : as per current Shade version ( if Shade 16, Shade 15 base is applied )



< 12 - 5 New script for Shade 16 and later >

Shade 16 and later don’t support DXF file export setting properties.

get_bezier_patch_by_DXF_2( ) is shown as a substitute of get_bezier_patch_by_DXF( ).

The script require some manual operations.

  • At first, export DXF file by manual
  • Run the script
  • Script requests input of DXF file path



< 12 - 6 Verification of patch interpolate algorithm ( Shade 16 ) >

< 12 - 7 Patch interpolate algorithm ( Shade17 and later ) >

< 12 - 8 Verification of patch interpolate algorithm ( Shade17 and later ) >


= = = = = = = = = = = = = = = = = = = = = = = = = = =

Shade Bezier surface subdivision at rendering is different from normal method.

It takes much time to clarify the Shade method and I will report a new topic 16.5 Bezier Surface Subdivision at Rendering


Shade 15 ( flat shading )      rendering of brazier surface / bezier patch subdivision / both

Shade_15%201 Shade_15%202 Shade_15%203


Shade 17 ( flat shading )      rendering of brazier surface / bezier patch subdivision / both

Shade_17%201 Shade_17%202 Shade_17%203


bezier surface

1


polygon mesh

black : subdivision at rendering
red : normal patch subdivision

left : Shade 15 / right : Shade 17

2 3

1 Like

12 - 4  Shade 仕様の変更に関して ( Shade 16 以降 )



< Shade 16>


12 - 3 項で述べた patch 補正の際の 長さのない handle の判定基準が次のように変更されています。

      *1  当該ハンドルの長さの 0 判定
      *2  交差ハンドルの長さの 0 判定


     Shade 15

     レンダリング時のポリゴン分割

          判定時の基準座標:  world 座標
          *1 :  bounding box size x 1e-4 未満
          *2:   bounding box size x 1e-4 未満

     DXF file 出力時

          判定時の基準座標: local 座標
           *1 :   bounding box size x 1e-4 以下
           *2 :   bounding box size x 1e-3 以下


     Shade 16

     レンダリング時のポリゴン分割

          判定時の基準座標: world 座標
          *1 :   bounding box size x 1e-6 未満
          *2 :   bounding box size x 1e-6 未満

     DXF file 出力時

          判定時の基準座標: local 座標
           *1 :   bounding box size x 1e-6 以下
           *2 :   bounding box size x 1e-5 未満


Shade 16 用の Patch 補正検証 script は 12 - 6 項を参照。



< Shade 17 以降>


bezier patch 中央4点の 内挿法 及び 長さのない handle 判定基準 が全面的に変更されており、詳細は 12 - 7 項を参照。



< labo module 改訂 ver. 1.7 >


labo に登録している自由曲面から bezier ptch を取得する関数 get_bezier_patch( ) を改訂し、Shade 17 ベースの patch も得られるようにしました。


labo.get_bezier_patch ( xshade as xshade, m as int, n as int, uuid as string = None, regacy as bool = None )

      legacy :   内挿基準を指定

     regacy = True :       Shade 15 ベース
     regacy = False :       Shade 17 ベース
     regacy = None :       使用する Shade version に合わせる( Shade 16 の場合は Shade 15 ベース )

     長さのない handle 判定基準を レンダリング時のポリゴン分割 のそれに合わせる




/ / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / /




12 - 5   Script 不具合 / 変更への対策 ( Shade 16 以降 )



Shade 16 以降 で 12 - 2 項 < script 実行の前に > で述べた DXF export property のマニュアルによる 事前の設定 が効かなくなっており、DXF 経由の bezier patch 取得が不可 となっています。( Mac で確認 )

#  選択自由曲面の 0, 0 ブロックの bezier patch を DXF 経由で取得
bP2 = labo.get_bezier_patch_by_DXF(xshade, 0, 0)			#  Shade 16 以降で不具合

さらに Shade 18 から DXF file の header 構成が変更されています。

そこで新たな関数 get_bezier_patch_by_DXF_2( ) を用意しました。( labo module には未登録 )

この関数は次の手順で使用し、vec3 matrix で与えられる bezier patch を取得します。



1) 事前にマニュアル操作により DXF file を出力

        出力 property を次のように設定

     

      [ fig 12 - 6 ]



2) script 実行

3) script が開く DXF file path 入力欄 に path を入力

     pic%2015      [ fig 12 - 7 ]




[ script 12 - 6 ]      Shade 16 用 検証用 script

まず 自由曲面をマニュアルで DXF file に出力し、その自由曲面を選択したまま script を実行して path を入力、DXF file 経由の bezie patch と 自由曲面をポリゴン分割したものが出力される。

#  get_bezier_patch_by_DXF_2(m as int, n as int) as vec3 matrix	
#
# 	自由曲面の指定する区間 m, n ( 0基数 )の bezier patch 座標を 予め保存しておいた DXF file 経由で vec3 matrix として返す
	
def get_bezier_patch_by_DXF_2(m, n) :	
	import os

	scene = xshade.scene()
	
	bP = labo.get_bezier_patch_base(xshade, m, n, None)	 #  選択自由曲面の区間 m, n の bezier patch 座標を matrix として取得
	if bP == None :
		return None
		
		
	#  dialog uuid は Online GUID Generator により生成		https://www.guidgenerator.com
	dialog = xshade.create_dialog_with_uuid('ab2a1365-0b51-4cf2-8577-c42d5881d3da')

	file_path = dialog.append_string('DXF file path')

	if dialog.ask('DXF file path') :
		path = dialog.get_value(file_path)				#  予めマニュアルで保存しておいた DXF file の path を取得
	else :
		return None
	

	if not os.path.exists :
		print ' 指定した file が見つからない - get_bezier_patch_by_DXF_2'
		return None
	else :
		f = open(path)									#  DXF file 読み込み
		t = f.read()
		f.close()

	
	if shade.version >= 510000 :						#  Shade 18 以降
		idx1 = 51
		idx2 = 59
		k = 63
	else :												#  Shade 17 まで
		idx1 = 11
		idx2 = 19
		k = 23

	s = t.split()
	if (s[idx1] !='16') or (s[idx2] != 'VERTEX') :
		print 'DXF エクスポートの設定として、「ベジェ曲面」にチェックを入れておいて下さい。'
		return None
	else :
		bP = matrix()
		for i in range(4) :
			v = []
			for j in range(4) :
				v.append(vec3(float(s[k]), float(s[k + 4]), -float(s[k + 2])))	#  DXF file の座標は傾いている
				k = k + 12
			bP.append(v)

		return bP



import labo
from vec3 import *
from matrix import *


scene = xshade.scene()
obL = []

#  選択自由曲面の copy を作成、ポリゴン分割を実施
ob = scene.active_shape()
scene.copy_object(None, None, None, None)
ob2 = scene.active_shape()
ob2.convert_to_polygon_mesh_with_subdivision_level(2)
ob3 = scene.active_shape()


#  選択自由曲面の 0, 0 ブロックの bezier patch を DXF 経由で取得
ob.activate()
bP = get_bezier_patch_by_DXF_2(0, 0)

#  bezier patch  bP を出力
if bP != None :
	labo.make_bezier_patch(xshade, bP, 'bezier patch by DXF')
	obL.append(scene.active_shape())

	
obL.append(ob3)
scene.active_shapes = obL



/ / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / /




12 - 6   長さのない Handle 判定基準 検証( Shade 16 )



12 - 3 項に記した [ script 12 - 3 ] ~ [ script 12 - 5 ] では sample 形状の出力と bezier patch 出力を同時に行っていますが、ここでは sample 形状の出力のみの script とします。

これらの script で形状出力し、マニュアルで DXF file を出力、上記の [ script 12 - 6 ] で検証します。

「 長さのない handle 判定 」が 12 - 4 項で示したように DXF file と ポリゴン分割で異なっていることが示されます。



[ script 12 - 3a ]     sample 形状出力

import labo
from vec3 import *


scene = xshade.scene()
obL = []

for h in [0.039, 0.04, 0.041] :		#  h : handle  長さ
	#  sample 自由曲面		
	bz = []
	bz.append([[0, 10000, 0], [0, 10000, 5000], [0, 5000, 10000], [0, 0, 10000]])
	bz.append([[h, 10000, 0], None, None, [5000, 0, 10000]])
	bz.append([[5000, 10000, -5000], None, None, [15000, 0, 0]])
	bz.append([[5000, 10000, -5000], [13000, 10000, -5000], [15000, 5000, -5000], [15000, 0, -5000]])
	bz = vec3list(bz)

	#  bz の bounding box size と handle size の比
	bb = labo.vec3_bounding_box_size(bz)
	ratio = h/bb[3]					

	ob = scene.create_part(str(ratio))

	#  sample 自由曲面を出力
	labo.make_bezier_surface(xshade, bz)

	
	scene.select_parent(1)
	obL.append(ob)
	
scene.active_shapes = obL


[ script 12 - 4a ]     sample 形状出力

import labo
from vec3 import *


scene = xshade.scene()
obL = []

for [h1, h2] in [[0.039, 0.39], [0.039, 0.4], [0.039, 0.039], [0.039, 0.04]] :		#  h1, h2 : handle  長さ
	#  sample 自由曲面		
	bz = []
	bz.append([[0, 10000, 0], [0, 10000, h2], [0, 5000, 10000], [0, 0, 10000]])
	bz.append([[h1, 10000, 0], None, None, [5000, 0, 10000]])
	bz.append([[5000, 10000, -5000], None, None, [15000, 0, 0]])
	bz.append([[5000, 10000, -5000], [13000, 10000, -5000], [15000, 5000, -5000], [15000, 0, -5000]])
	bz = vec3list(bz)

	#  bz の bounding box size と handle size の比
	bb = labo.vec3_bounding_box_size(bz)
	ratio1 = float(h1)/bb[3]					
	ratio2 = float(h2)/bb[3]
	
	if ratio1 >1e-6 :
		t1 = str(ratio1*100000) + 'e-5'
	else :
		t1 = str(ratio1*1000000) + 'e-6'	
	if ratio2 >1e-6 :
		t2 = str(ratio2*100000) + 'e-5'
	else :
		t2 = str(ratio2*1000000) + 'e-6'

	ob = scene.create_part(t1 + ' - ' +  t2)

	#  sample 自由曲面を出力
	labo.make_bezier_surface(xshade, bz)

	
	scene.select_parent(1)
	obL.append(ob)
	
scene.active_shapes = obL


[ script 12 - 5a ]     sample 形状出力

import labo
from vec3 import *


scene = xshade.scene()
obL = []

h1 = 0.04*1.325
h2 =  0.04001*1.335


for h in [h1, h2] :		#  h1, h2 : handle  長さ
	#  sample 自由曲面		
	bz = []
	bz.append([[0, 10000, 0], [0, 10000, 5000], [0, 5000, 10000], [0, 0, 10000]])
	bz.append([[h, 10000, 0], None, None, [5000, 0, 10000]])
	bz.append([[5000, 10000, -5000], None, None, [15000, 0, 0]])
	bz.append([[5000, 10000, -5000], [13000, 10000, -5000], [15000, 5000, -5000], [15000, 0, -5000]])
	bz = vec3list(bz)

	ob = scene.create_part()

	#  sample 自由曲面を出力
	labo.make_bezier_surface(xshade, bz)
	
	#  自由曲面を回転
	scene.move_object([0, 0, 0], None, [0, -115, 0], None)
	scene.move_object([0, 0, 0], None, [40, 0, 0], None)
	scene.move_object([0, 0, 0], None, [0, 0, 48], None)

	#  選択自由曲面の 0 0 ブロックの bezier patch 制御点座標を取得 ( 中央4点は除く )	
	bP = labo.get_bezier_patch_base( xshade, 0, 0)

	#  bz の bounding box size と handle size の比
	bb = labo.vec3_bounding_box_size(bP)
	ratio = h/bb[3]
	
	scene.select_parent(1)
	scene.active_shape().name = str(ratio)
	
	obL.append(ob)
	
scene.active_shapes = obL



/ / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / /




12 - 7   Bezier Patch 内挿法( Shade 17 以降 )



Shade 17 より、bezier patch 中央4点の 内挿法 及び 長さのない handle 判定基準 が全面的に変更されています。

     pic%2016      [ fig 12 - 8 ]




< bezier patch script >


labo に登録している自由曲面から bezier ptch を取得する関数 get_bezier_patch( ) を改訂、Shade 17 ベースの patch も得られるようにしました。


labo.get_bezier_patch ( xshade as xshade, m as int, n as int, uuid as string = None, regacy as bool = None )

      legacy :   内挿基準を指定

     regacy = True :       Shade 15 ベース
     regacy = False :       Shade 17 ベース
     regacy = None :       使用する Shade version に合わせる( Shade 16 の場合は Shade 15 ベース )

     長さのない handle 判定基準を レンダリング時のポリゴン分割 のそれに合わせる




< 長さのない handle 判定基準 >


     判定時の基準座標:

           レンダリング時のポリゴン分割 : world 座標
           DXF file 出力時 : local 座標


     判定基準:

           bounding box size x 1e-5 以下




< Patch 内挿法 - Step 1 >

< Case - 1 >

     pic%2017
                pic%2018      [ fig 12 - 9 ]


< Case - 2 , 3 >

     pic%2019
                pic%2020      [ fig 12 - 10 ]


< Case - 4 >

     pic%2021
                pic%2022      [ fig 12 - 11 ]


< Case - 4 > において、| U1 | / | U3 | には limitter が与えられていない。

よって 0 < | U3 | << | U1 | なるとき、| U1 | / | U3 | は極めて大きな値となり、 P11 座標は bezier patch の bounding box 外の遙か遠くに位置することになってしまう。




< Patch 内挿法 - Step 2 >

     


     [ fig 12 - 12 ]






よって bezier patch を求める関数は次のようになり、labo module に登録してあります。

[ scripr 12 - 7 ]

#  set_bezier_patch_2(bP as vec3 matrix, bb as float list)
#	中央4点の control point 座標未定義の patch base bP の未定義座標をセット ( Shade 17 以降 )
#
def set_bezier_patch_2(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 = (5*v1 + v2)/6
				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]



/ / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / /




12 - 8   Patch 内挿法 検証 ( Shade 17 以降 )



[ script 12 - 9 ] ~ [ script 12 - 13 ] で sample 形状を出力し、マニュアルで DXF file を出力、[ script 12 - 8 ] で検証します。

[ script 12 - 8 ]       検証用 script

import labo
from vec3 import *
from matrix import *



#  get_bezier_patch_by_DXF_2(m as int, n as int) as vec3 matrix	
#
# 	自由曲面の指定する区間 m, n ( 0基数 )の bezier patch 座標を 予め保存しておいた DXF file 経由で vec3 matrix として返す
	
def get_bezier_patch_by_DXF_2(m, n) :	
	import os

	scene = xshade.scene()
	
	bP = labo.get_bezier_patch_base(xshade, m, n)	 #  選択自由曲面の区間 m, n の bezier patch 座標を matrix として取得
	if bP == None :
		return None
		
		
	#  dialog uuid は Online GUID Generator により生成		https://www.guidgenerator.com
	dialog = xshade.create_dialog_with_uuid('ab2a1365-0b51-4cf2-8577-c42d5881d3da')

	file_path = dialog.append_string('DXF file path')

	if dialog.ask('DXF file path') :
		path = dialog.get_value(file_path)			#  予めマニュアルで保存しておいた DXF file の path を取得
	else :
		return None
	

	if not os.path.exists :
		print ' 指定した file が見つからない - get_bezier_patch_by_DXF_2'
		return None
	else :
		f = open(path)									#  DXF file 読み込み
		t = f.read()
		f.close()

	
	if shade.version >= 510000 :						#  Shade 18 以降
		idx1 = 51
		idx2 = 59
		k = 63
	else :												#  Shade 17 まで
		idx1 = 11
		idx2 = 19
		k = 23

	s = t.split()
	if (s[idx1] !='16') or (s[idx2] != 'VERTEX') :
		print 'DXF エクスポートの設定として、「ベジェ曲面」にチェックを入れておいて下さい。'
		return None
	else :
		bP = matrix()
		for i in range(4) :
			v = []
			for j in range(4) :
				v.append(vec3(float(s[k]), float(s[k + 4]), -float(s[k + 2])))	#  DXF file の座標は傾いている
				k = k + 12
			bP.append(v)

		return bP

	
	
		

scene = xshade.scene()
obL = []

#  選択自由曲面の copy を作成、ポリゴン分割を実施
ob = scene.active_shape()
scene.copy_object(None, None, None, None)
ob2 = scene.active_shape()
ob2.convert_to_polygon_mesh_with_subdivision_level(2)
obL.append(scene.active_shape())


#  選択自由曲面の 0, 0 ブロックの bezier patch を DXF 経由で取得
ob.activate()
bP = get_bezier_patch_by_DXF_2(0, 0)
#bP = labo.get_bezier_patch_by_DXF(xshade, 0, 0)			#  Shade 18 で script の不具合が解消されたら、これが使用可能に

#  bezier patch  bP を出力
if bP != None :
	labo.make_bezier_patch(xshade, bP, 'bezier patch by DXF')
	obL.append(scene.active_shape())
	
#  選択自由曲面の 0 0 ブロックの bezier patch 制御点座標を取得
ob.activate()
bP2 = labo.get_bezier_patch(xshade, 0, 0, None, False) 		#  Shade 17 ベースの patch	
#bP2 = labo.get_bezier_patch(xshade, 0, 0, None, True) 		#  Shade 15 ベースの patch
#bP2 = labo.get_bezier_patch(xshade, 0, 0) 					#  使用する Shade version に合わせた patch

#  bezier patch  bP2 を出力
labo.make_bezier_patch(xshade, bP2, 'bezier patch')
obL.append(scene.active_shape())

	
scene.active_shapes = obL


[ script 12 - 9 ]       sample 形状出力

import labo
from vec3 import *


scene = xshade.scene()
obL = []

scene.create_part()

#  sample 自由曲面
scale = scene.user_to_native_unit
x = 1500*scale
z = 1000*scale
hx = 1000*scale
hz = 2000/3.*scale
bb = 2*x + 2*z					#  bounding box size

for h in [hx/2, hx/5, bb*0.000011, bb*0.00001] :			#  h : handle 長さ
	bz = []
	bz.append([[-x, 0, -z], [-x + h, 0, -z], [x - hx, 0, -z], [x, 0, -z]])
	bz.append([[-x, 0, -z + hz], None, None, [x, 0, -z + hz/3]])
	bz.append([[-x, 0, z - hz], None, None, [x, 0, z - hz]])
	bz.append([[-x, 0, z], [-x + hx, 0, z], [x - hx, 0, z], [x, 0, z]])
	bz = vec3list(bz)
	
	#  bz の bounding box size と handle size の比
	bb = 2*x + 2*z
	ratio = h/bb				

	ob = scene.create_part(str(ratio))
	
	#  sample 自由曲面を出力
	labo.make_bezier_surface(xshade, bz)
	
	scene.select_parent(1)
	obL.append(ob)
	
scene.active_shapes = obL


[ script 12 - 10 ]       sample 形状出力

i mport labo
from vec3 import *

scene = xshade.scene()
obL = []

scene.create_part()

#  sample 自由曲面
scale = scene.user_to_native_unit
x = 1500*scale
z = 1000*scale
hx = 1000*scale
hz = 2000/3.*scale
bb = 2*x + 2*z					#  bounding box size

for [h1, h2] in [[bb*0.00001, bb*0.00001] , [bb*0.000011, bb*0.00001] , [bb*0.00001, bb*0.000011] , [bb*0.000011, bb*0.000011]] :			#  h1, h2 : handle 長さ
	bz = []
	bz.append([[-x, 0, -z], [-x + h1, 0, -z], [x - hx, 0, -z], [x, 0, -z]])
	bz.append([[-x, 0, -z + h2], None, None, [x, 0, -z + hz]])
	bz.append([[-x, 0, z - hz], None, None, [x, 0, z - hz]])
	bz.append([[-x, 0, z], [-x + hx, 0, z], [x - hx, 0, z], [x, 0, z]])
	bz = vec3list(bz)
	
	#  bz の bounding box size と handle size の比
	bb = 2*x + 2*z
	ratio = h/bb				

	ob = scene.create_part(str(ratio))
	
	#  sample 自由曲面を出力
	labo.make_bezier_surface(xshade, bz)
	
	scene.select_parent(1)
	obL.append(ob)
	
scene.active_shapes = obL


[ script 12 - 11 ]       sample 形状出力

import labo
from vec3 import *


scene = xshade.scene()
obL = []

scene.create_part()

#  sample 自由曲面
scale = scene.user_to_native_unit
x = 1500*scale
z = 1000*scale
hx = 1000*scale
hz = 2000/3.*scale


for h in [0.795, 0.785] :			#  h : handle 長さ
	bz = []
	bz.append([[-x, 0, -z], [-x + h, 0, -z], [x - hx, 0, -z], [x, 0, -z]])
	bz.append([[-x, 0, -z + hz], None, None, [x, 0, -z + hz]])
	bz.append([[-x, 0, z - hz], None, None, [x, 0, z - hz]])
	bz.append([[-x, 0, z], [-x + hx, 0, z], [x - hx, 0, z], [x, 0, z]])
	bz = vec3list(bz)

	ob = scene.create_part()
	
	#  sample 自由曲面を出力
	labo.make_bezier_surface(xshade, bz)
	
	scene.move_object([0, 0, 0], None, [0, -115, 0], None)
	scene.move_object([0, 0, 0], None, [40, 0, 0], None)
	scene.move_object([0, 0, 0], None, [0, 0, 48], None)
	
	#  回転後の bounding box size と handle size の比
	bz2 = labo.get_bezier_patch_base(xshade, 0, 0)
	bb = labo.vec3_bounding_box_size(bz2)
	ratio = h/bb[3]

	scene.select_parent(1)
	ob.name = (str(ratio))
	obL.append(ob)
	
scene.active_shapes = obL


[ script 12 - 12 ]       sample 形状出力

import labo
from vec3 import *


scene = xshade.scene()
#obL = []

scene.create_part()

#  sample 自由曲面
scale = scene.user_to_native_unit
x = 1500*scale
z = 1000*scale
hx = 1000*scale
hz = 2000/3.*scale
#bb = 2*x + 2*z					#  bounding box size

bz = []
bz.append([[-x, 0, -z], [-x + hx/2, 0, -z], [x - hx*1.5, 0, -z], [x, 0, -z]])
bz.append([[-x, 0, -z + hz], None, None, [x, 0, -z + hz/3]])
bz.append([[-x*1.25, 0, z - hz], None, None, [x*1.8, 0, z - hz*1.5]])
bz.append([[-x, 0, z], [-x + hx*1.5, 0, z], [x - hx/2, 0, z], [x, 0, z]])

#  sample 自由曲面を出力
labo.make_bezier_surface(xshade, bz)


[ script 12 - 13 ]       sample 形状出力

import labo
from vec3 import *


scene = xshade.scene()
obL = []

scene.create_part()

#  sample 自由曲面
scale = scene.user_to_native_unit
x = 1000*scale
z = 1000*scale
bb = 2*(x + z)								#  bounding box size

for d in [bb*0.0000099, bb*0.00001] :
	bz = []
	bz.append([[-d, 0, -z], [0, 0, -z], [0, 0, -z], [d, 0, -z]])
	bz.append([[-x*2/3, 0, 0], None, None, [x*2/3, 0, 0]])
	bz.append([[-x, 0, z*2/3], None, None, [x, 0, z*2/3]])
	bz.append([[-x, 0, z], [-x/3, 0, z], [x/3, 0, z], [x, 0, z]])
	bz = vec3list(bz)
	
	#  bz の bounding box size と handle size の比
	ratio = d/bb				

	ob = scene.create_part(str(ratio))
	
	#  sample 自由曲面を出力
	labo.make_bezier_surface(xshade, bz)
	
	scene.select_parent(1)
	obL.append(ob)
	
	
for d in [bb*0.0000099/2, bb*0.00001/2] :
	bz = []
	bz.append([[-d*1.5, 0, -z], [-d, 0, -z], [d, 0, -z], [d*1.5, 0, -z]])
	bz.append([[-x*2/3, 0, 0], None, None, [x*2/3, 0, 0]])
	bz.append([[-x, 0, z*2/3], None, None, [x, 0, z*2/3]])
	bz.append([[-x, 0, z], [-x/3, 0, z], [x/3, 0, z], [x, 0, z]])
	bz = vec3list(bz)
	
	#  bz の bounding box size と handle size の比
	ratio = d/bb				

	ob = scene.create_part(str(ratio))
	
	#  sample 自由曲面を出力
	labo.make_bezier_surface(xshade, bz)
	
	scene.select_parent(1)
	obL.append(ob)
	
	
scene.active_shapes = obL
1 Like

A lot to unpack :smiley:

I tested the new labo.set_bezier_patch_2 function and verified that it works.
However, I find a difference with the polygon output of Shade 17 and the new function.

The sample surface I attached has case 2 at both cross curves, where P(10)-P(00)=0 and P(13)-P(03)=0

wing1_patch_m2_n8.shd (207.1 KB)

My report is just for modeling and it shows correct patch interpolate algorithm supported by Shade 17.

Rectangle polygon shapes in your sample file wing1_patch_m2_n8.shd have small difference.

It is due to the difference between normal bezier patch subdivision and bezier surface subdivision at rendering, mentioned before.

It remains unsolved.

orange : normal patch subdivision
green : subdivision at rendering

test.shd

For the upcoming topic 16-5 Bezier Surface Subdivision at Rendering, we might be dealing with significantly more control points, possibly a degree 16 patch, if I had to guess. From my field tests in Autodesk Alias, a surface of degree 9 has too few internal control points to replicate the final surface.

Aligning the circled region…

Produces a wacky, incorrect result everywhere else:

The attached scene helped me rule out rational control points. The V-direction in this scene for any subdivision level is perfectly straight, and changing the weight for any control point breaks alignment with the V-direction of the rendered result.

I await to see if you also came to the same conclusion.

subdivision_test_8.shd (167.3 KB)

I’m sorry, I can’t understand what you want to say.

I don’t have Autodesk, so I can’t verify its internal logic

In Alias, you can hand-edit the internal control points like so:
Aliasでは、次のように内部の制御点を手動で編集できます。

The above is the same test scene attached in the previous post.
The white curves will never match the brown curves using only a tertiary bezier surface.
This is what led me to consider using de Casteljau’s algorithm to increase the number of control points to help obtain the final surface.
上記は前の記事に添付されているのと同じテストシーンです。
白い曲線は、3次ベジェ曲面のみを使用している茶色の曲線とは一致しません。
これが、最終的な曲面を得るのを助けるために制御点の数を増やすためにde Casteljauのアルゴリズムを使用することを私に考えさせたものです。

Well, I understand Autodesk can control patch position manually, and you try to use de Casteljau’s algorithm.

I can guess your trial, but I don’t know any Autodesk function and user interface at all.

So, I can’t judge your trial is correct or not.

If you would like to continue this topic, please use just Shade only.

実を言うと、Shade内のパブリック関数を使用してソリューションを推測する方法はありません。 Pythonはレンダリング時にサーフェイスの細分化に関するものを呼び出すことはできません(Loaded Bezier Surface)。 Loaded Bezier Surfaceを処理するShade SDK内の2つのメンバー関数(calculate_internal_pointsとcalculate_blending_weights)は、レンダラーによってのみ内部使用されているため、座標データも最終サーフェスの形状パラメーターに関する値も出力できません。

次の点を除いて、Loaded Bezier Surfaceについてわかっていることはほとんどありません。

  1. Shadeは16個の内部標準点の配列を計算します。レンダリングパッチベースをShade 16(レガシー)からShade 17(Gregory)に変更すると、16点配列の座標が変更されるかどうかは不明です。

  2. Shadeは、16の内部制御点に基づいてサーフェスを「荷重」します。

Loaded Bezier Surfaceの機能をまだ再現していない場合は、役に立つと思われる情報はありますか?

現時点でそれ以上の支援を提供できない場合はお詫び申し上げます。

To tell you the truth, I see no way to deduce the solution just using the public functions within Shade. Python can’t invoke anything relating to surface subdivision at rendering (Loaded Bezier Surface). The two member functions within the Shade SDK that deal with the Loaded Bezier Surface (calculate_internal_points & calculate_blending_weights) are for internal use by the renderer only, and thus can’t output the coordinate data nor values relating to the shape parameters of the final surface.

What is known about the Loaded Bezier Surface is very little, except the following:

  1. Shade calculates an array of 16 internal control points. It is unknown if changing the rendering patch base from Shade 16(legacy) to Shade 17(Gregory) changes the coordinates of the 16 point array.

  2. Modifies, or stresses, the surface based on the 16 internal control points.

If you haven’t yet reproduced the functionality of the Loaded Bezier Surface, is there any information you have discovered that might help?

I apologize that I cannot provide further assistance at this time.

1 ) Subdivision Coordinate Values

Yes, we officially have no way to get the subdivision coordinate values at rendering.

But if an assumption is right, we can get it.

Assumption :

When a curved surface is converted to a polygon mesh, the polygon mesh vertex coordinate is decided by surface rendering subdivision method.

Verification :

Please refer to the sample file

sample 1.shd (81.1 KB)

  • Make a curved surface

  • Convert the surface to a polygon mesh by Shade 15 and 18
    Subdivision level is “Fine”

          pic%201

Curved Surface

     pic%202

Converted Polygon mesh ( Shade 15 )

     pic%203

Converted Polygon mesh ( Shade 18 )

     pic%204

Two polygon mesh shapes are different.

    pic%205



Chose surface and polygon mesh and see quick rendering image by Shade 15 and 18.

Confirm two browser position cases, one is that the surface position is up the polygon mesh, and the other is below.

In the case of two same shape objects in the same position, rendering image shows the object set to below in the Shade browser.

Please note that “<” is added to the surface name. ( It means the rendering subdivision level is “Fine” )

The surface and polygon mesh shapes are almost the same.



Shade 15      surface : upper     polygon mesh : lower

     pic%206

Shade 15      polygon mesh : upper     surface : lower

     pic%207

Shade 18      surface : upper     polygon mesh : lower

     pic%208

Shade 18      polygon mesh : upper     surface : lower

     pic%209



Thus, the assumption is probably right.

Even if not so, two shapes are almost the same.





2 ) Patch Interpolate Algorithm of Shade 17 ~ 19 ( mentioned at para. 12 - 7 )

I suppose the algorithm may be available at rendering.

Please refer to the next sample.

This sample is made by Shade 18.

sample 2.shd (49.5 KB)



I got a surface coordinate value at parameter s = 0.5 and t = 0.5 by the script 12 - 14 using the algorithm.

This surface center position is the same as polygon mesh center vertex. ( red point in the above figure )

In the case of other parameter values, they are different as you know.

Even if an equal coordinate in just one point, It is hard to judge that is coincidental.

It suggest the interpolate algorithm is available for surface subdivision to a polygon mesh.

And the surface subdivision to a polygon mesh is probably the same as subdivision at rendering, as mentioned above.



[ script 12 - 14 ]

import labo
from vec3 import *
from matrix import *

	
#  my_convert_to_polygon_mesh(bP as vec3 matrix, name as text, level as int)
#
#	bP で与えられる曲面を 指定する level で細分化されたポリゴンメッシュとして出力

def my_convert_to_polygon_mesh(bP, name, level) :	
	scene.create_surface_part(name)
	ob = scene.active_shape()
	ob.disclosed = False
	
	delta = 0.5**level
	n = 2**level + 1

	scene.begin_creating()
	
	for i in range(n) :
		pL = []
		t = i*delta
		for j in range(n) :
			s = j*delta
			p = labo.bezier_surface_position(bP, s, t)
			pL.append(p)
		
	
		scene.begin_line(None, False)
		for p in pL :
			scene.append_point(p, None, None, None, None)
		scene.end_line()
	
	scene.end_creating()
	ob.activate()
	ob.convert_to_polygon_mesh_with_subdivision_level(0)
	
	
		

scene = xshade.scene()
level = 1

	
#  選択自由曲面の 0 0 ブロックの bezier patch 制御点座標を取得
bP = labo.get_bezier_patch(xshade, 0, 0) 					#  使用する Shade version に合わせた patch


# bP で与えられる曲面を 指定する level で細分化されたポリゴンメッシュとして出力
my_convert_to_polygon_mesh(bP, 'subdivision by script', level)

こんにちは、私は説明されているようにサンプルファイルをチェックし、そして同じことを見つけました。 グレゴリーパッチアルゴリズムでは、u = v = 0.5で与えられる座標は、レンダリングで細分割を使用した場合と同じです。
Hi, I checked the sample files as described, and found the same; For the Gregory Patch algorithm, the coordinates given by u=v=0.5 remain the same when using subdivision at rendering

ポリゴンオブジェクトに変換するための組み込みの細分化プリセット(Coarse … Very Fine)を使用することはレンダリング時の曲面細分化と同じであることを確認できますが、自由曲面パーツの場合、Shadeは異なるサーフェス法線を使用します。これらのサーフェス法線が局面に対して正確かどうか、または曲面の不規則性を隠すための「法線マップ」の結果であるかどうかは不明です。
I can confirm that using the built-in subdivision presets (Coarse…Very Fine) invokes surface subdivision at rendering, but for a Curved Surface Part, Shade uses different surface normals. It is unknown whether these surface normals are accurate to the surface, or are a result of a “normal map” to hide any surface irregularities.

Sample file for inspection:
normals_rendering.shd (1.6 MB)

私はShade 15ベースを使うことを選択しました。違いはShade 15ベースで見るのがはるかに簡単です。
I have chosen to use the Shade 15 base; the difference is much easier to see with the Shade 15 base.

Very Fineプリセットを使用したサーフェス法線の違い:
Surface normals difference at Very Fine:

normals_rendering

単純な拡散マテリアルを使用しても、ポリゴンメッシュの表面の不規則性は簡単に見えます。
Even using a simple diffuse material, the surface irregularities of the polygon meshes are easily visible:

normals_diffuse

これが曲面部品とポリゴンオブジェクトの唯一の違いです。それらの形状は同じです。
This is the only difference between the Curved Surface Parts and the polygon objects; their geometry is identical.

また、レンダリングで細分割を使用した場合に点s = t = 0.5が不変であることは偶然ではなく、これまでに得られた最大のヒントです。私はファイル "test.shd"に戻り、全289個の頂点について法線細分化とレンダリング細分化の違いを比較して、全体的な表面摂動がどのように見えるかを調べました。結果は次の添付のExcelスプレッドシートにあります。

I also believe that the point s=t=0.5 remaining invariant when using subdivision at rendering is no accident, and is the biggest hint that we’ve received thus far. I went back to the file “test.shd” and compared the difference between normal subdivision and rendering subdivision for all 289 vertices to see what the overall surface perturbations looked like. The results are in the following attached Excel spreadsheet:

test_shd_difference.zip (30.5 KB)

最後のチャートは、観測された違いと非常によく一致しています。
The final chart corresponds quite closely with the observed difference:

残念ながら、私はGregory Patchベースを使用するShadeバージョンを持っていないので、私はその面で立ち往生しています。
通常のサブディビジョンとレンダリングのサブディビジョンの違いを比較するためにいくつかのテストサーフェスを生成することは役に立ちますか?たぶん十分に高い細分割レベルでの違いは、我々がまだ知らない補間数学を決定するのを助けることができますか?

Unfortunately I don’t have a Shade version that uses the Gregory Patch base, so I’m stuck on that front.
Would it be helpful to generate some test surfaces to compare the difference between normal subdivision and rendering subdivision? Maybe the differences, at a sufficiently high subdivision level, can help determine the interpolation math that we don’t yet know?

Unfortunately qual coordinate at s = t = 0.5 is just gotten by Shade 17 and later.

I have no plan to examine old Shade’s rendering subdivision method.