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 にとって特に互換性のある標準機能は無く、さしあたりこうした機能群が UE4 の FMath
に存在するという予備知識が得られれば十分である事などを理由に、本項では関数の概要を紹介するに留める。
効果 | 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の指数倍か判定 実装詳細の都合、負の値は false 、 0 , 1 は true を返す。 |
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 整数に拡張して行われるため、 int8 で a=-128 , b=0 , Alpha=1.0 を与えると b-a=+128 となり、結果的には (T)(a+Alpha*128) はオーバーフローして 0 となるから写像としては正しい結果が得られる。しかし、 int32 の a , b を同様に与える場合には b-a の時点でオーバーフローし 0 となるため (T)(a+Alpha*0) は Alpha=1.0 でも aとなる。これは int64等でも同様に発生する。 LerpStableはこの問題の発生しない実装。なお、 Lerpと LerpStableの速度差は 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 |
InterpEaseIn とInterpEaseOut を1:1で合成 |
InterpEaseInOut |
値域 [0..1] を分解能 Step で分割した離散値へ入力値 Alpha 吸着し値域 [a..b] へ写像 |
`InterpStep |
値域 [0..1] で表される比の入力値 Alpha を-cos(Alpha*π/2)+1 関数で補正し値域 [a..b] へ写像 |
InterpSinIn |
1 - InterpSinIn と等価 |
InterpSinOut |
InterpSinIn とInterpSinOut を1:1で合成と等価 |
InterpSinInOut |
値域 [0..1] で表される比の入力値 Alpha を-exp2(-10*Alpha)+1 関数で補正し値域 [a..b] へ写像 |
InterpExpoIn |
1 - InterpExpoIn |
InterpExpoOut |
InterpEaseIn とInterpEaseOut を1:1で合成 |
InterpExpoInOut |
値域 [0..1] で表される比の入力値を円関数で補正し値域 [a..b] へ写像 |
`InterpCircularIn |
1 - InterpCircularIn |
InterpCircularOut |
InterpCircularIn とInterpCircularOut を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乗PointDistToSegment が sqrt を要する分だけこちらの方が高速 |
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の拡大縮小と float のMagnitude から単一の拡大縮小倍率を 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++ ときどき ごはん、わりとてぃーぶれいく☆)