【 2. 断面形状を線形状の始端にセットし掃引する 】 †
1)正面図の原点を中心にして断面形状を作成する
2)線形状の始端の接線ベクトルを求める
3)接線ベクトル方向の緯度経度を求める
4)接線ベクトルの緯度経度を基に断面形状を回転し、線形状の始端位置に移動する。
これにより線形状始端位置に線形状の接線方向に垂直な面内に断面形状をセットすることができる
5)script command を用いて断面形状を掃引する
《 2.1 線形状の接線ベクトルを求める 》 †
anchor point と anchor point に挟まれた一つの区間毎に bezier parameter t( 0 <= t <= 1 )によって与えられる位置での接線単位ベクトルを求めることができます。
t = 0 でその区間の始端での接線方向が、t = 1 でその区間の終端での接線方向が求まります。
///////////////////////////////////////////////////
接線単位ベクトルを求める擬似コード
///////////////////////////////////////////////////
BL_Tangent(p() as vec3, t as float) as vec3 は bzier parameter t( 0 <= t <= 1 )における接線単位ベクトルを返します。
引数
p(0)〜p(3):Bezier 制御点座標
p(0):anchor point i
p(1):out handle i
p(2):in handle i + 1
p(3):anchor point i + 1
t:bezier parameter( 0 <= t <= 1 )
返り値
接線単位ベクトル
dim J0, J1, J2, J3 as float
dim v as vec3 // 接線単位ベクトル( 返り値 )
dim length as float
J0 = -3*(1 - t)^2
J1 = 3(1 - t)^2 - 6*t*(1 - t)
J2 = 6*t*(1 - t) - 3*t^2
J3 = 3*t^2
v.x = J0*p(0).x + J1*p(1).x + J2*p(2).x + J3*p(3).x
v.y = J0*p(0).y + J1*p(1).y + J2*p(2).y + J3*p(3).y
v.z = J0*p(0).z + J1*p(1).z + J2*p(2).z + J3*p(3).z
length = Sqrt(v.x^2 + v.y^2 + v.z^2)
v.x = v.x/length // 単位ベクトル化
v.y = v.y/length
v.z = v.z/length
return v
《 2.2 ハンドル長さがない場合の接線ベクトルの求め方 》 †
ハンドルが出ていない bezier 曲線の場合、bezier parameter 基準では端点での接線ベクトルを求めることができません。
この場合には、t = 0 あるいは t = 1 として求めずに、t = epsilon, t = 1 - epsilon( epsilon は微小の数 )として終端近傍の接線ベクトルを求め、終端の接線ベクトルの代用とします。
epsilon は次のように考えて色々と自分で実験して適切な大きさを見つけてください。
1) 期待される真の接線ベクトルの方向を実用上問題のない精度内で近似できるほどに小さいこと
2) 数値の丸め誤差によってあらぬ方向の接線ベクトルに計算されてしまわないほどに大きいこと
特に 2 )の条件には十分な注意が必要です。
ハンドル長さがない線形状に対して t = 0 or 1 として求めると、2.1項の接線ベクトルを求める計算では 長さ = 0 のベクトル として計算され、単位ベクトル化の段階で 分母 = 0 となってエラーとなってしまいます。
また、 ハンドル長さがない線形状に対して t = epsilon or t = 1 - epsilon としても epsilon が過度に小さい場合、分母が 0 になることはないものの、float の精度から避けられない数値の丸め誤差によって、とんでもない方向の接線ベクトルが計算されてしまいます。
epsilon の大きさは、意外に大きな値にしなければなりませんが、それでも 1)の条件を満たす epsilon は得られます。
色々なケースで実験して epsilon の大きさを決定してください。
自分で納得のいく数値が見つかれば、それが「 正解 」です。
「 ハンドル長さがない = 直線 」ではありませんので御注意を。
inhandle には長さが無く、outhandle には長さがあるケースも考えななければいけません。
bezier 曲線はハンドル長さがある( 四つの独立した制御点がある )ことを前提にしています。
ですから「 ハンドル長さがない 」という特殊ケースで解が求まらないというのは色々な場面で顔を出します。
一方 Shade のモデリングではハンドル長さのない bezier 曲線は特殊なものではなく、一般的なものとして頻繁に使用されますので、bezier 曲線を使ったロジックを組み立てた場合、それがハンドルなしでも成立するのか否かを確認しておくことはとても大事なことです。
///////////////////////////////////////////////////
接線単位ベクトルを求める擬似コード
///////////////////////////////////////////////////
BL_Tangent(p, t) ( 2.1項参照 )により接線単位ベクトルが与えられる。
dim p(3) as vec3 // 四つの制御点座標
dim v as vec3
dim t as float // bzier parameter t( 0 <= t <= 1 )
dim epsilon as float // bezier parameter 補正項
dim tanV as vec3 // 接線単位ベクトル
v = p(1) - p(0)
if v.x = 0 and v.y = 0 and v.z = 0 then // inhandle 長さがなかった
t = Max(t, epsilon) // Max() は二つの引数の内、大なる方を返す関数
end if
v = p(2) - p(3)
if v.x = 0 and v.y = 0 and v.z = 0 then // outhandle 長さがなかった
t = Min(t, 1 - epsilon)) // Min() は二つの引数の内、小なる方を返す関数
end if
tanV = BL_Tangent(p, t)
《 2.3 接線ベクトルの緯度経度を求める 》 †
単位ベクトル v の方向を表す緯度経度を求めます。
緯度経度は次のように定義します。
・原点を中心とした球を考え、X-Z 平面と球との交差線が赤道
・球とY-Z 平面の +Z 側の面との交差線が子午線
・緯度:北緯が + 方向
・経度:東経が + 方向
///////////////////////////////////////////////////
緯度経度を求める擬似コード
///////////////////////////////////////////////////
LatitudeLongitude(v as vec3) as float, as float は 単位ベクトル v の方向を表す緯度経度( radian角 )を返します。
引数
v:単位ベクトル
返り値
緯度と経度
dim latitude, longitude as float // 緯度, 経度( 返り値 )
dim L as float
dim pi as float // π( 円周率 )
if v.x = 0 and v.z = 0 then // Y軸上にある場合
longitude = 0 // 経度
if v.y > 0 then // Yが正方向なら
latitude = pi/2 // 緯度
else
latitude = -pi/2 // Yが負方向なら
end if
else
L = Sqrt(v.x*v.x + v.z*v.z)
longitude = Acos(v.z/L) // 経度
if v.x < 0 then // Xが負ならば
longitude = -longitude
end if
latitude = Asin(v.y) // 緯度
end if
return latitude, longitude
《 2.4 断面形状を線形状の始端位置にセットする 》 †
1)掃引断面形状を原点を中心として X-Y 平面上に作成する
2)掃引中心線線形状の始端における接線単位ベクトルの緯度経度を求める。
3)緯度経度にもとづいて、掃引断面形状を script command で回転する
4)掃引中心線線形状の始端位置まで掃引断面形状を script command で移動する
5) script command で掃引を実行する
1)において掃引断面形状が閉じている場合、その面法線の向きが必ず +Z 方向を向くように作成しておきます。
3)の回転では、X-Y 平面に描いた断面形状の「 -Z 」方向を掃引中心線始端の接線ベクトルの向きに一致させます。
上記の二つのルールは重要です、こうしておけば、掃引体の面法線は必ず外側を向くように統一することができます。
3)では script command で次のように操作します。
・最初に原点を中心として X 軸まわりに「 - 緯度 」回転( 符号に注意 )
・ついで原点を中心として Y 軸まわりに 「 経度 + π(180度)」回転
4)では掃引中心線線形状始端のアンカーポイント座標を求めて、3 )で回転された掃引断面形状を script command で座標値分だけ移動します。
これにより、掃引中心線始端位置に 1)で掃引断面形状を描いたときの原点位置を持ってくることになります。