C++ ときどき ごはん、わりとてぃーぶれいく☆

USAGI.NETWORKのなかのひとのブログ。主にC++。

Real Unreal Engine C++ 2017-12 (part-3/5)

はてなブログの記事あたりの容量制限のため前の部分 §1.9.3 までは前の記事でどうぞ→Real Unreal Engine C++ 2017-12 (part-2/5) - C++ ときどき ごはん、わりとてぃーぶれいく☆

1.9.4. Fmath の真価

前項では FMath について少し残念な結果も見えたが、ともあれ、これまでの下層の実装の上に、 UE4/C++er が実際に多くの場合に数学関数のために触れる FMath が実装される。

API Reference を見ると冒頭の Inheritance Hierarchy でも FPlatformMath から派生している事がわかる。この FPlatformMath は前項のようにプラットフォームごとに実装が特殊化されており、その基底型として FGenericPlatformMath 、また数学関数の最下層としては本節の始めの項のように C++ の CRT 互換層も #include している。

さて、それでは FMath 型には何が定義されているかというと、主に線形幾何、そして範囲操作、補間、角度単位の変換などの関数群、そして区間ベクター、回転、四元数、行列、任意精度整数、球面座標、IEEE754/binary16 実装など、主に線形幾何と補間、そして GPU 互換性が必要な場合向けの型が含まれる。

FMath の機能詳細の全貌を明らかとする事は本記事の目的から逸脱する事、また FMath の規模が大きい事、 std 慣れした C++er にとって特に互換性のある標準機能は無く、さしあたりこうした機能群が UE4FMath に存在するという予備知識が得られれば十分である事などを理由に、本項では関数の概要を紹介するに留める。

効果 UE4
int32値 n を与え [0..n] に一様分布する乱数を得る
注意: C++ CRT 線形合同法擬似乱数生成器ラッパーを内部的に使用。
RandHelper
FRandRange と同じ。 int32 版と誤用されないようオーバーロードしている。 RandRange
実数区間 [a..b] に一様分布する擬似乱数を取得注意: C++ CRT 線形合同法擬似乱数生成器ラッパーを内部的に使用。 FRandRange
bool 型の擬似乱数を得る
注意: C++ CRT 線形合同法擬似乱数生成器ラッパーを内部的に使用。
RandBool
長さ 1擬似乱数ベクターを得る
注意: C++ CRT 線形合同法擬似乱数生成器ラッパーを内部的に使用。
VRand
中心方位と水平および垂直の弧度法による角度範囲に基づくコーン形状の方位を向くランダムなベクターを得る
注意: C++ CRT 線形合同法擬似乱数生成器ラッパーを内部的に使用。
VRandCone
直方体の稜線または内部に一様分布する擬似乱数の点を得る
注意: C++ CRT 線形合同法擬似乱数生成器ラッパーを内部的に使用。
RandPointInBox
面法線の明らかな面へ入射するベクターの反射ベクターを取得 GetReflectionVector
TestValue が 値域 [MinValue .. MaxValue) か判定 IsWithin
TestValue が 値域 [MinValue .. MaxValue] か判定 IsWithinInclusive
2つの浮動小数点数型の値について差が誤差許容値以内か判定する
デフォルトの誤差許容値は 1.0e-8f
IsNearlyEqual
浮動小数点数型の値と 0 との差が誤差許容値以内か判定する
デフォルトの誤差許容値は 1.0e-8f
IsNearlyZero
整数について2の指数倍か判定
実装詳細の都合、負の値は false0, 1true を返す。
IsPowerOfTwo
3つの値から最大値を選択 Max3
3つの値から最小値を選択 Min3
2乗 Square
制限された値域へ切り詰める Clamp
Grid の間隔に対し入力値を四捨五入的に吸着した値を取得 GridSnap
繰り上げ除算
(10,1) -> 10, (10,2) -> 5, (10,3) -> 4, (10, 4) -> 3, ..
DivideAndRoundUp
繰り下げ除算
(10,1) -> 10, (10,2) -> 5, (10,3) -> 3, (10, 4) -> 2, ..
DivideAndRoundDown
底2の対数
実装詳細で static const float LogToLog2 = 1.f/Loge(2.f) をキャッシュし return Loge(x)*LogToLog2 しているため cl.exe の std::log2 比で 1.37 倍程度高速。
Log2
正弦と余弦の同時計算
cl.exe では O3 でも個別に計算するより 1.13 倍程度高速。
SinCos
高速逆正弦(10進数で5桁まで安全な精度、O3条件下で2.2%程度Asinより高速) FastAsin
弧度法単位を度数法単位へ変換 RadiansToDegrees
度数法単位を弧度法単位へ変換 DegreesToRadians
弧度単位の角度を回転を考慮して制限された値域へ切り詰める ClampAngle
2つの与えられた度数法の角度の差を値域(-180..+180)で取得 FindDeltaAngleDgrees
2つの与えられた弧度法の角度の差を値域(-π..+π)で取得 FindDeltaAngleRadians
DEPRECATED -> FindDeltaAngleRadians に同じ。 FindDeltaAngle
弧度法の角度を [-π..+π] の範囲の表現に変換する UnwindRaidnas
度数法の角度を [-180..+180] の範囲の表現に変換する UnwindDegrees
2つの度数法の角度を与え、一方を180°を超える回転を伴わない角度へ反転する
この関数は四元数を使わずに回転を最適化したい場合用に用意されている
WindRelativeAnglesDegrees
度数法の角度を角度区間 [a..b] にクランプ。
但し負の入力や周回には恐らく意図しない応答となる。
FixedTurn
カルテシアン座標から極座標を得る CartesianToPolar
極座標からカルテシアン座標を得る PolarToCartesian
任意の軸方向の系で対象への向きから対象への相対的な方位角の余弦値と仰俯角の正弦値を計算
GetAzimuthAndElevation に比べ O3 で 1.34 倍速程度高速
GetDotDistance
任意の軸方向の系で対象への向きから対象への相対的な方位角(rad;右側が+)と仰俯角(rad;上側が+)を計算 GetAzimuthAndElevation
値を値域 [min .. max] から値域 [0 .. 1]写像
"Pct"は"Percentage"の略記と思われるが実際には"Percentage"ではなく単純な比(はみ出し有りの unorm )である。
GetRangePct
Lerp の FVector2D 版。 GetRangeValue
値を値域 [in.x .. in.y] から値域 [out.x .. out.y] へクランプ有りで写像 GetMappedRangeValueClamped
値を値域 [in.x .. in.y] から値域 [out.x .. out.y] へクランプ無しで写像 GetMappedRangeValueUnclamped
値域 [a..b] について比 Alpha となる値を取得
但しプラットフォームネイティブの整数型以上のビット幅の整数型の全域は扱えない。その必要があれば LerpStable を使う。
Lerp
Lerp の整数型全域に対して安全な実装
Lerp は実装詳細で b-a を計算するが、x64でこの計算は 32 bits 未満の a, b に対しては 32 bit 整数に拡張して行われるため、 int8a=-128, b=0, Alpha=1.0 を与えると b-a=+128 となり、結果的には (T)(a+Alpha*128) はオーバーフローして 0 となるから写像としては正しい結果が得られる。しかし、 int32a, b を同様に与える場合には b-a の時点でオーバーフローし 0 となるため (T)(a+Alpha*0)Alpha=1.0 でもaとなる。これはint64等でも同様に発生する。LerpStableはこの問題の発生しない実装。なお、LerpLerpStableの速度差は O0 でも O3 でもほとんど無いので特別な事情が無ければLerpStable` を使っておけば良い。
LerpStable
二次元の補間結果を得る BiLerp
3次 Hermite 補間した点の値を計算 CubicInterp
3次 Hermite 補間した点の1次微分値を計算 CubicInterpDerivative
3次 Hermite 補間した点の2次微分値を計算 CubicInterpSecondDerivative
底 Alpha 指数 Exp の指数関数で得られる値域 [0..1] を値域 [a..b]写像 InterpEaseIn
1 - InterpEaseIn InterpEaseOut
InterpEaseInInterpEaseOut を1:1で合成 InterpEaseInOut
値域 [0..1] を分解能 Step で分割した離散値へ入力値 Alpha 吸着し値域 [a..b]写像 `InterpStep
値域 [0..1] で表される比の入力値 Alpha を-cos(Alpha*π/2)+1 関数で補正し値域 [a..b]写像 InterpSinIn
1 - InterpSinInと等価 InterpSinOut
InterpSinInInterpSinOut を1:1で合成と等価 InterpSinInOut
値域 [0..1] で表される比の入力値 Alpha を-exp2(-10*Alpha)+1 関数で補正し値域 [a..b]写像 InterpExpoIn
1 - InterpExpoIn InterpExpoOut
InterpEaseInInterpEaseOut を1:1で合成 InterpExpoInOut
値域 [0..1] で表される比の入力値を円関数で補正し値域 [a..b]写像 `InterpCircularIn
1 - InterpCircularIn InterpCircularOut
InterpCircularInInterpCircularOut を1:1で合成 InterpCircularInOut
回転 FRotator において Lerp と基本的に同様、但し最短経路ではなく値域に応じて180°以上の経路を取る LerpRange
3次 Catmull-Rom スプライン補間 CubicCRSplineInterp
3次 Catmull-Rom スプライン補間、フェイルセーフ機能付き CubicCRSplineInterpSafe
InterpTo の 3次元ベクター FVector による正規化法線版 VInterpNormalRotationTo
InterpConstantTo の 3次元ベクター FVector VInterpConstantTo
InterpTo の 3次元ベクター FVector VInterpTo
InterpConstantTo の 2次元ベクター FVector2D Vector2DInterpConstantTo
InterpTo の 2次元ベクター FVector2D Vector2DInterpTo
InterpConstantTo の回転 FRotator RInterpConstantTo
InterpTo の回転 FRotator RInterpTo
float 用の区間 [a..b] にクランプされる速度倍率制御付き線形補間 FInterpConstantTo
事実上 FInterpConstantTo と同じ(実装詳細は異なる) FInterpTo
InterpTo の線形色 FLinearColor CInterpTo
周波数 (Hz) と位相シフト [0..1] と時刻 (sec) から振幅範囲 [0..1] の正弦波の振幅値を得る MakePulstingValue
線分と平面の交差判定 LinePlaneIntersection
遠近法変換行列、視野変換行列、視点、有効な球形の範囲からシザー領域の必要性の判定と領域の計算を行う ComputeProjectedSphereScissorRect
平面と軸平行直方体(AABB)の交差判定 PlaneAABBIntersection
球と軸平行直方体(AABB)との交差判定 SphereAABBIntersection
点と直方体の交差判定 PointBoxIntersection
線分と直方体の交差判定 LineBoxIntersection
掃引直方体と直方体の交差判定 LineExtentBoxIntersection
線分と球形状の交差判定 LineSphereIntersection
球とコーン形状の交差判定 SphereConeIntersection
三次元の2点を結ぶ線分上で他の任意の1点に最近接する点を得る
OnSegment2Dと同じ結果となるが、命名から推量するに線分と直線などの違いが設計段階ではあったのかもしれない。
ClosestPointOnLine
ClosestPointOnSegment
三次元の2点を結ぶ無限遠の直線上で他の任意の1点に最近接する点を得る ClosestPointOnInfiniteLine
3つのFPlane平面が交わるか判定し return を返し、交わる場合には交わる交点 I を与える IntersectPlanes3
2つのFPlane平面が交わるか判定し return を返し、交わる場合には交わる直線上の1点 I と直線の向き D を与える IntersectPlanes2
点と直線の距離 PointDistToLine
二次元の2点を結ぶ線分上で他の任意の1点に最近接する点を得る ClosestPointOnSegment2D
点と線分の距離
2乗次元の値を取得できれば事足りる場合は PointDistToSegmentSquared を使う方が速度的に有利
PointDistToSegment
点と線分の距離の2乗
PointDistToSegmentsqrt を要する分だけこちらの方が高速
PointDistToSegmentSquared
2つの線分上で互いに最接近となる2点の座標を得る
但し、線分の何れかまたは両方が長さ 0 の場合は正しい結果を得られない。その可能性がある場合は SegmentDistToSegmentSafe を使う。
SegmentDistToSegment
SegmentDistToSegment の線分の長さが 0 でも正しく計算可能な実装 SegmentDistToSegmentSafe
2点を通る直線が平面と交差する位置を直線の2点に対する比として取得
交差しない場合は -inf が返る
例: 直線 {(0,0,1),(0,0,-1)}FPlane(0,0,1,0) へ与えた場合、 0.5 が得られる。
GetTForSegmentPlaneIntersect
線分と平面が交差するか判定し、交差する場合はその座標も与える SegmentPlaneIntersection
線分と三角面が交差するか判定し、交差する場合はその座標も与える SegmentTriangleIntersection
3次元上の2つの線分がXY平面に投影された場合に交差するか判定し、交差する場合はその座標も与える SegmentIntersection2D
三次元の3点からなる三角形稜線上で他の任意の1点に最近接する点を得る ClosestPointOnTriangleToPoint
三次元の4点からなる四面体表面上で他の任意の1点に最近接する点を得る ClosestPointOnTetrahedronToPoint
球に対し1つの点と正規化された方位からなる半直線との交点を計算 SphereDistToLine
コーン形状に点が含まれるか判定 GetDistanceWithinConeSegment
配列で与える点群が同一の平面から一定の距離内にあるか判定 PointsAreCoplanar
偶数丸め RoundHalfToEven
浮動小数点数型の値を小数部の絶対値が 0.5 以上であれば 0 から遠ざかる方向、それ以外の場合は 0 へ近づく方向の整数へ丸める RoundHalfFromZero
浮動小数点数型の値を小数部の絶対値が 0.5 以下であれば 0 へ近づく方向、それ以外の場合は 0 から遠ざかる方向の整数へ丸める RoundHalfToZero
浮動小数点数型の値を 0 から遠ざかる方向の整数へ丸める RoundFromZero
0 方向への丸め RoundToZero
-∞ 方向への丸め RoundToNegativeInfinity
+∞ 方向への丸め RoundToPositiveInfinity
int32値に桁区切りのカンマを付けた文字列を返す
但しバグがあり正の999,999,999以下の値にしか正常な挙動を期待できない
https://goo.gl/sfv5zo
FormatIntToHumanReadable
4bytesを単位としてメモリー空間に対して書き込みと読み込みを行い成否を判定する MemoryTest
文字列の数式を評価
対応演算子: + - / % * @
対応数値文字: 0..9 と . で構成される実数文字列
括弧: ( )
L"1+2+3-4-5-6*7*8*9" -> -3027
Note: documentation bug
Eval
ComputeBaryCentric2D の簡易計算実装版
法線計算, 共線のcheckなどを端折っている
O3で1.59倍速程度に速い
GetBaryCentric2D
三次元の3点のからなる三角形に対し他の任意の1点の重心座標を得る ComputeBaryCentric2D
三次元の4点からなる四面体に対し他の任意の1点の重心座標を得る ComputeBaryCentric3D
値域 [0..1] で表される比 X を一般的なスムーズステップ関数を適用した値を [a..b]写像した値を取得
注意: 入力値について clamp 処理を実装していないため [0..1] を超える X に対する clmap された応答を期待してはならない
SmoothStep
任意の連続したメモリー領域から特定ビットを抽出 ExtractBoolFromBitfield
任意の連続したメモリー領域の特定ビットへ書き出し SetBoolInBitField
FVector で与えるXYZの拡大縮小と floatMagnitude から単一の拡大縮小倍率を float で得る ApplyScaleToFloat
float 型の入力値の値域 [0..1] を uint8 型の出力 [0..255]写像 Quantize8UnsinedByte
float 型の入力値の値域 [0..1] を int8 型の出力 [-128..+127]写像 Quantize8SinedByte
最大公約数 GreatestCommonDivisor
最小公倍数 LeastCommonMultiplier

UE4/C++er はこれらの実装が FMath にあり、必要に応じて独自に実装する事なく使用可能な事を把握しておくとよい。

はてなブログの記事あたりの容量制限のため続き §1.9.5 以降は次の記事でどうぞ→Real Unreal Engine C++ 2017-12 (part-4/5) - C++ ときどき ごはん、わりとてぃーぶれいく☆