CARLA
Loading...
Searching...
No Matches
CameraModelUtil.h
Go to the documentation of this file.
1// Copyright (c) 2026 Computer Vision Center (CVC) at the Universitat Autonoma
2// de Barcelona (UAB).
3//
4// This work is licensed under the terms of the MIT license.
5// For a copy, see <https://opensource.org/licenses/MIT>.
6
7#pragma once
8
9#include <util/ue-header-guard-begin.h>
10#include "CoreMinimal.h"
11#include "RHIDefinitions.h"
12#include "Containers/ArrayView.h"
13#include <util/ue-header-guard-end.h>
14
15#include "CameraModelUtil.generated.h"
16
17
18
19class FRDGBuilder;
20class FRDGTexture;
21class FRHISamplerState;
22class UTextureRenderTarget2D;
23
24
25
26UENUM(BlueprintType)
27enum class ECameraModel : uint8
28{
36 LUT1D,
37 FTheta, // Theta = sum c_i R^i (NVIDIA rig f-theta polynomial, R in pixels with fx = 1/width)
38 MaxEnum UMETA(Hidden),
39 Default = Perspective UMETA(Hidden)
40};
41
42
43
44// Shared lens-model descriptor: single source of truth for lens forward/inverse
45// mapping math, mirrored by the GPU core in Engine/Shaders/Private/LensModels.ush.
46//
47// Coordinate convention for all CameraModelUtil forward/inverse ray functions
48// below (camera space, matches the GPU core, NOT Unreal's native +X-forward
49// camera convention -- callers must convert):
50// +X = right, +Y = down, +Z = forward.
51//
52// Coeffs packing:
53// KannalaBrandt : Coeffs[0..3] = k1..k4
54// BrownConrady : Coeffs[0..2] = k1,k2,k3 (radial), Coeffs[3..4] = p1,p2 (tangential), OpenCV convention
55// LUT1D : Coeffs unused; LUT holds the samples instead
56//
57// LUT1D: LUT[i] is R(Theta) uniformly sampled for Theta in [0, ThetaMax],
58// i = 0 .. LUT.Num() - 1, assumed monotonically non-decreasing.
60{
62 float FocalX = 1.f, FocalY = 1.f; // normalized focal lengths
63 float CenterX = 0.5f, CenterY = 0.5f; // normalized principal point
64 TArray<float> Coeffs; // KB: k1..k4; BC: k1,k2,k3,p1,p2
65 TArray<float> LUT; // LUT1D samples theta->r
66 float ThetaMax = PI/2; // max half-FOV angle (radians)
67 float CAScaleR = 1.f, CAScaleB = 1.f; // per-channel radial scale for chromatic aberration
68};
69
70
71
72namespace CameraModelUtil
73{
75 {
76 TArrayView<const float> KannalaBrandtCoefficients;
77 // LUT1D: R(Theta) samples, uniform over [0, ThetaMax], in the same
78 // normalized units as the focal lengths (pixel = Center + Focal * R).
79 TArrayView<const float> LUT;
80 float ThetaMax = PI / 2;
81 float YFOVAngle;
83 // Explicit intrinsics in pixels (fx, fy, cx, cy), used when
84 // bExplicitIntrinsics is set; otherwise the projection uses
85 // (YFocalLength, YFocalLength, W/2, H/2).
86 FVector4f Intrinsics = FVector4f(1.f, 1.f, 0.f, 0.f);
87 // Written to pixels the lens maps no ray to (LUT1D beyond its range).
88 FVector4f InvalidColor = FVector4f(0.f, 0.f, 0.f, 1.f);
96 };
97
98
99
100 float ComputeAngle(
101 ECameraModel CameraModel,
102 float Distance,
103 TArrayView<const float> Coefficients);
104
105 float ComputeDistance(
106 ECameraModel CameraModel,
107 float Angle,
108 int32 ImageHeight,
109 TArrayView<const float> Coefficients);
110
111
112
113 namespace KannalaBrandt
114 {
116 float Theta,
117 TArrayView<const float> Coefficients);
118
120 float Theta,
121 TArrayView<const float> Coefficients);
122 }
123
124
125
126 // Which color channel a lens-model query is for; only affects models with
127 // chromatic-aberration support (FLensModelDescriptor::CAScaleR/CAScaleB).
128 enum class ELensColorChannel : uint8
129 {
130 Green, // no CA scale applied
131 Red,
132 Blue
133 };
134
135 // Forward map: camera-space direction (need not be normalized; +X right,
136 // +Y down, +Z forward -- see FLensModelDescriptor) -> normalized
137 // image-plane point (Descriptor.CenterX/Y +/- Descriptor.FocalX/Y * extent).
138 FVector2f ComputeLensImagePoint(
139 const FLensModelDescriptor& Descriptor,
140 FVector Direction,
142
143 // Inverse map: normalized image-plane point -> camera-space unit ray
144 // direction (+X right, +Y down, +Z forward). Uses Newton-Raphson for
145 // KannalaBrandt and BrownConrady, closed form for the other radial models,
146 // and binary-search interpolation for LUT1D.
148 const FLensModelDescriptor& Descriptor,
149 FVector2f ImagePoint,
151
152 // Convention adapters between the lens core's camera space (+X right,
153 // +Y down, +Z forward -- matches OpenCV, see FLensModelDescriptor) and
154 // Unreal's native camera space (+X forward, +Y right, +Z up). Pure axis
155 // relabeling: an isometry, and each is the other's exact inverse. Not a
156 // "rotation" in the proper sense -- the lens core's convention is
157 // right-handed and UE's is left-handed, so this necessarily flips
158 // handedness rather than preserving it.
159 FVector LensDirToUE(FVector LensDir);
160 FVector UEDirToLens(FVector UEDir);
161
162 namespace BrownConrady
163 {
164 // Normalized pinhole coords (Xn = X/Z, Yn = Y/Z) -> distorted coords.
165 // Coefficients: k1,k2,k3 (radial), p1,p2 (tangential), OpenCV convention.
166 FVector2f Distort(
167 float Xn,
168 float Yn,
169 TArrayView<const float> Coefficients);
170
171 // Inverse of Distort() via 2D Newton-Raphson with an analytic Jacobian.
172 FVector2f Undistort(
173 float Xd,
174 float Yd,
175 TArrayView<const float> Coefficients,
176 int32 Iterations = 10);
177 }
178
179 namespace FTheta
180 {
181 // R -> Theta: sum_{i} Coefficients[i] * R^i (up to 8 terms).
182 float Polynomial(float Distance, TArrayView<const float> Coefficients);
183 // Theta -> R by Newton-Raphson on Polynomial().
184 float SolveRadius(float Theta, TArrayView<const float> Coefficients, int32 Iterations = 32);
185 }
186 namespace LUT1D
187 {
188 // Theta -> R, linear interpolation over uniformly spaced samples.
189 float SampleForward(
190 float Theta,
191 TArrayView<const float> Samples,
192 float ThetaMax);
193
194 // R -> Theta, binary search + linear interpolation. Assumes Samples is
195 // monotonically non-decreasing.
196 float SampleInverse(
197 float Distance,
198 TArrayView<const float> Samples,
199 float ThetaMax);
200 }
201
202
203
205 FRDGBuilder& GraphBuilder,
206 FRDGTexture* Destination,
207 FRDGTexture** CubeTextures, // CubeTextures[6]
208 FRHISamplerState* Sampler,
209 const FDistortCubemapToImageOptions& Options);
210
212 FRDGBuilder& GraphBuilder,
213 UTextureRenderTarget2D* Destination,
214 UTextureRenderTarget2D** CubeRenderTargets, // CubeRenderTargets[6]
215 FRHISamplerState* Sampler,
216 const FDistortCubemapToImageOptions& Options);
217
218 FRHISamplerState* GetSampler(ESamplerFilter Filter);
219
220} // CameraModelUtil
ECameraModel
FVector2f Undistort(float Xd, float Yd, TArrayView< const float > Coefficients, int32 Iterations)
FVector2f Distort(float Xn, float Yn, TArrayView< const float > Coefficients)
float SolveRadius(float Theta, TArrayView< const float > Coefficients, int32 Iterations)
float Polynomial(float Distance, TArrayView< const float > Coefficients)
float ComputeCameraPolynomialDerivative(float Theta, TArrayView< const float > Coefficients)
float ComputeCameraPolynomial(float Theta, TArrayView< const float > Coefficients)
float SampleForward(float Theta, TArrayView< const float > Samples, float ThetaMax)
float SampleInverse(float Distance, TArrayView< const float > Samples, float ThetaMax)
float ComputeAngle(ECameraModel CameraModel, float Distance, TArrayView< const float > Coefficients)
float ComputeDistance(ECameraModel CameraModel, float Angle, int32 ImageHeight, TArrayView< const float > Coefficients)
FVector LensDirToUE(FVector LensDir)
FRHISamplerState * GetSampler(ESamplerFilter Filter)
FVector UEDirToLens(FVector UEDir)
void DistortCubemapToImage(FRDGBuilder &GraphBuilder, FRDGTexture *Destination, FRDGTexture **CubeTextures, FRHISamplerState *Sampler, const FDistortCubemapToImageOptions &Options)
FVector ComputeLensRayDirection(const FLensModelDescriptor &Descriptor, FVector2f ImagePoint, ELensColorChannel Channel)
FVector2f ComputeLensImagePoint(const FLensModelDescriptor &Descriptor, FVector Direction, ELensColorChannel Channel)
TArrayView< const float > KannalaBrandtCoefficients
TArray< float > LUT
TArray< float > Coeffs