CARLA
Loading...
Searching...
No Matches
SceneCaptureSensor_WideAngleLens.cpp
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
12
13#include <util/ue-header-guard-begin.h>
14#include "Engine/Engine.h"
15#include "RenderGraphBuilder.h"
16#include "RenderGraphUtils.h"
17#include "HAL/IConsoleManager.h"
18#include "Kismet/KismetSystemLibrary.h"
19#include <util/ue-header-guard-end.h>
20
21#include <cmath>
22
23// extern bool CARLA_API GCARLASelectiveRendering;
24// extern bool CARLA_API GCARLALightingOnly;
25
26static TAutoConsoleVariable<int32> CVarWideAngleSensorDumpAllFrames(
27 TEXT("Carla.WideAngleLens.DumpAllFrames"),
28 0,
29 TEXT("If enabled, saves all frames of all *_WideAngleLens sensors to disk.\n")
30 TEXT("0: Disabled\n")
31 TEXT("1: Enabled\n"));
32
33static TAutoConsoleVariable<FString> CVarWideAngleSensorDumpAllFramesPath(
34 TEXT("Carla.WideAngleLens.DumpAllFramesPath"),
35 TEXT(""),
36 TEXT("Sets the destination path when saving frames via \"Carla.WideAngleLens.DumpAllFrames\".\n"));
37
38static TAutoConsoleVariable<int32> CVarWideAngleSensorDumpAllFramesCubemap(
39 TEXT("Carla.WideAngleLens.DumpAllFrames.Cubemap"),
40 0,
41 TEXT("If enabled, saves each individual face of each *_WideAngleLens sensor.\n")
42 TEXT("0: Disabled\n")
43 TEXT("1: Enabled\n"));
44
45static TAutoConsoleVariable<int32> CVarWideAngleSensorSkipVFTR(
46 TEXT("Carla.WideAngleLens.SkipVFTR"),
47 0,
48 TEXT("If enabled, *_WideAngleLens sensors do not toggle r.VolumetricFog.TemporalReprojection when rendering.\n")
49 TEXT("0: Disabled\n")
50 TEXT("1: Enabled\n"));
51
53
54// =============================================================================
55// -- Local static methods -----------------------------------------------------
56// =============================================================================
57
58// Local namespace to avoid name collisions on unit builds.
60
61 static void SetCameraDefaultOverrides(USceneCaptureComponent2D_CARLA& CaptureComponent);
62
63} // namespace SceneCaptureSensorWideAngleLens_local_ns
64
65// =============================================================================
66// -- ASceneCaptureSensor_WideAngleLens ----------------------------------------
67// =============================================================================
68
70 Super(ObjectInitializer),
71 FaceCaptures(),
72 FaceRenderTargets(),
73 CaptureRenderTarget(),
74 TargetGamma(0.0F),
75 ImageWidth(1280U),
76 ImageHeight(1280U),
77 CameraModel(ECameraModel::Default),
78 KannalaBrandtCameraCoefficients
79 {
80 0.08309221636708493F,
81 0.01112126630599195F,
82 0.008587261043925865F,
83 0.0008542188930970716F
84 },
85 LensLUT(),
86 LensThetaMax(PI * 0.5F),
87 LensIntrinsics(0.0F, 0.0F, 0.5F, 0.5F),
88 YFOVAngle(PI * 0.5F),
89 XFOVAngle(VerticalToHorizontal(YFOVAngle)),
90 YFocalLength(
92 CameraModel,
93 YFOVAngle,
94 ImageHeight,
95 KannalaBrandtCameraCoefficients)),
96 LongitudeOffset(),
97 FOVFadeSize(),
98 CubemapRenderMask(0),
99 CubemapSampler(CameraModelUtil::GetSampler(SF_AnisotropicLinear)),
100 bUseRayTracing(true),
101 bEnablePostProcessingEffects(true),
102 bEnable16BitFormat(false),
103 bRenderPerspective(false),
104 bRenderEquirectangular(false),
105 bFOVMaskEnable(false),
106 bExplicitIntrinsics(false)
107{
108 FaceCaptures.SetNum(6);
109 FaceRenderTargets.SetNum(6);
110
111 // Computed in the constructor body, not the initializer list:
112 // ComputeCubemapRenderMask() reads bFOVMaskEnable / bRenderEquirectangular,
113 // which are declared after CubemapRenderMask and would still be
114 // uninitialized during initializer-list evaluation.
115 CubemapRenderMask = ComputeCubemapRenderMask();
116
117 PrimaryActorTick.bCanEverTick = true;
118 PrimaryActorTick.TickGroup = TG_PrePhysics;
119
120 using ProjectionMatrixType = std::conditional_t<
121 (bool)ERHIZBuffer::IsInverted,
122 FReversedZPerspectiveMatrix,
123 FPerspectiveMatrix>;
124
125 constexpr auto Deg2Rad = (PI / 180.0F);
126 constexpr auto FOV = 90.0F * Deg2Rad;
127 constexpr auto HalfFOV = FOV * 0.5F;
128
129 const auto SensorIndex = WIDE_ANGLE_LENS_SENSOR_COUNTER++;
130 const auto FaceIndexBase = SensorIndex * 6;
131 const auto ProjectionMatrix = ProjectionMatrixType(HalfFOV, 1.0F, 1.0F, GNearClippingPlane);
132
133 const FVector Forward[] =
134 {
135 FVector::ForwardVector,
136 -FVector::ForwardVector,
137 FVector::RightVector,
138 -FVector::RightVector,
139 FVector::UpVector,
140 -FVector::UpVector,
141 };
142
143 const FVector Right[] =
144 {
145 FVector::RightVector,
146 -FVector::RightVector,
147 -FVector::ForwardVector,
148 FVector::ForwardVector,
149 FVector::RightVector,
150 -FVector::RightVector,
151 };
152
153 CaptureRenderTarget = CreateDefaultSubobject<UTextureRenderTarget2D>(
154 FName(*FString::Printf(
155 TEXT("CaptureRenderTarget2D-WideLens-Final-d%d"),
156 SensorIndex)));
157
158 CaptureRenderTarget->CompressionSettings = TextureCompressionSettings::TC_Default;
159 CaptureRenderTarget->SRGB = false;
160 CaptureRenderTarget->bAutoGenerateMips = false;
161 CaptureRenderTarget->bGPUSharedFlag = true;
162 CaptureRenderTarget->AddressX = TextureAddress::TA_Clamp;
163 CaptureRenderTarget->AddressY = TextureAddress::TA_Clamp;
164
165 for (uint8 i = 0; i != 6; ++i)
166 {
167 const auto AbsIndex = FaceIndexBase + i;
168
169 auto& RenderTarget = FaceRenderTargets[i];
170 auto& FaceCapture = FaceCaptures[i];
171
172 RenderTarget = CreateDefaultSubobject<UTextureRenderTarget2D>(
173 FName(*FString::Printf(TEXT("CaptureRenderTarget2D-WideLens-Face-d%d"), AbsIndex)));
174 check(RenderTarget != nullptr);
175 RenderTarget->CompressionSettings = TextureCompressionSettings::TC_Default;
176 RenderTarget->SRGB = false;
177 RenderTarget->bAutoGenerateMips = false;
178 RenderTarget->bGPUSharedFlag = true;
179 RenderTarget->AddressX = TextureAddress::TA_Clamp;
180 RenderTarget->AddressY = TextureAddress::TA_Clamp;
181
182 FaceCapture = CreateDefaultSubobject<USceneCaptureComponent2D_CARLA>(
183 FName(*FString::Printf(TEXT("USceneCaptureComponent2D_CARLA-%d"), AbsIndex)));
184 check(FaceCapture != nullptr);
185 FaceCapture->SetupAttachment(RootComponent);
186 FaceCapture->SetRelativeRotation(FRotationMatrix::MakeFromXY(Forward[i], Right[i]).ToQuat());
187 FaceCapture->ViewActor = this;
188 FaceCapture->ProjectionType = ECameraProjectionMode::Perspective;
189 FaceCapture->PrimitiveRenderMode = ESceneCapturePrimitiveRenderMode::PRM_RenderScenePrimitives;
190 FaceCapture->bCaptureOnMovement = false;
191 FaceCapture->bCaptureEveryFrame = false;
192 FaceCapture->bAlwaysPersistRenderingState = true;
193 FaceCapture->bUseCustomProjectionMatrix = true;
194 FaceCapture->CustomProjectionMatrix = ProjectionMatrix;
195 // Propagate ray-tracing flag to this face capture.
196 FaceCapture->bUseRayTracingIfEnabled = ASceneCaptureSensor::ResolveCameraRayTracing(bUseRayTracing, false);
198 }
199}
200
202{
203 Super::Set(Description);
205}
206
207void ASceneCaptureSensor_WideAngleLens::SetImageSize(uint32 InWidth, uint32 InHeight)
208{
209 bool UpdateRenderMask = InWidth != ImageWidth || InHeight != ImageHeight;
210
211 ImageWidth = InWidth;
212 ImageHeight = InHeight;
213
214 if (UpdateRenderMask)
216}
217
219{
220 // Clamp to a sane minimum: negative values would wrap to enormous
221 // dimensions when cast to uint32.
223 static_cast<uint32>(FMath::Max(Width, 1)),
224 static_cast<uint32>(FMath::Max(Height, 1)));
225}
226
231
233{
234 bool UpdateRenderMask = NewCameraModel != CameraModel;
235
236 CameraModel = NewCameraModel;
237
238 if (UpdateRenderMask)
240}
241
246
251
256
257constexpr auto DegToRad = PI / 180.0F;
258constexpr auto RadToDeg = 180.0F / PI;
259
261{
262 NewFOV *= DegToRad;
263
264 bool UpdateRenderMask = NewFOV != YFOVAngle;
265
266 YFOVAngle = NewFOV;
268
269 // LUT1D and BrownConrady carry their own scale (the LUT samples / the
270 // explicit intrinsics); ComputeDistance has no closed form for them.
272 {
275 NewFOV,
278 }
279
280 if (UpdateRenderMask)
282}
283
284void ASceneCaptureSensor_WideAngleLens::SetLensLUT(const TArray<float>& Samples, float ThetaMaxRadians)
285{
286 LensLUT = Samples;
287 LensThetaMax = ThetaMaxRadians > 0.0F ? ThetaMaxRadians : PI * 0.5F;
289}
290
291void ASceneCaptureSensor_WideAngleLens::SetLensIntrinsics(float Fx, float Fy, float Cx, float Cy)
292{
293 bExplicitIntrinsics = Fx > 0.0F;
294 LensIntrinsics = FVector4f(Fx, Fy > 0.0F ? Fy : Fx, Cx, Cy);
296}
297
299{
300 TargetGamma = Gamma;
301}
302
307
309{
310 bool UpdateRenderMask = NewFocalLength != YFocalLength;
311
312 YFocalLength = NewFocalLength;
313
314 if (UpdateRenderMask)
316}
317
318void ASceneCaptureSensor_WideAngleLens::SetCameraCoefficients(TArrayView<const float> Coefficients)
319{
320 bool UpdateRenderMask = KannalaBrandtCameraCoefficients.Num() != Coefficients.Num();
321
322 for (uint32 i = 0; i != (uint32)Coefficients.Num() && !UpdateRenderMask; ++i)
323 UpdateRenderMask = UpdateRenderMask || KannalaBrandtCameraCoefficients[i] != Coefficients[i];
324
325 KannalaBrandtCameraCoefficients = TArray<float>(Coefficients);
326
327 if (UpdateRenderMask)
329}
330
331void ASceneCaptureSensor_WideAngleLens::SetCameraCoefficients(const TArray<float>& Coefficients)
332{
333 SetCameraCoefficients(TArrayView<const float>(Coefficients));
334}
335
340
345
350
355
357{
358 bool UpdateRenderMask = bRenderPerspective != bEnable;
359
360 bRenderPerspective = bEnable;
361
362 if (UpdateRenderMask)
364}
365
370
372{
373 bool UpdateRenderMask = bRenderEquirectangular != bEnable;
374
375 bRenderEquirectangular = bEnable;
376
377 if (UpdateRenderMask)
379}
380
385
387{
388 bool UpdateRenderMask = bFOVMaskEnable != bEnable;
389
390 bFOVMaskEnable = bEnable;
391
392 if (UpdateRenderMask)
394}
395
400
402{
403 FOVFadeSize = NewFOVFadeSize;
404}
405
410
412 float NewLatitudeOffset)
413{
414 LongitudeOffset = NewLatitudeOffset * DegToRad;
415}
416
418{
419 bUseRayTracing = Enable;
420 for (auto FaceCapture : FaceCaptures)
421 {
422 if (FaceCapture != nullptr)
423 FaceCapture->bUseRayTracingIfEnabled = ASceneCaptureSensor::ResolveCameraRayTracing(Enable, false);
424 }
425}
426
432
434{
435 const float R = hypotf(UV.X, UV.Y);
436 float Theta;
437 switch (CameraModel)
438 {
441 break;
444 break;
445 default:
447 break;
448 }
449 const float HalfPi = PI / 2.0f;
450 const float Phi = HalfPi - Theta;
451 const float Rho = atan2f(UV.Y, UV.X);
452
453 float PhiSin = 0.0F;
454 float PhiCos = 0.0F;
455 float RhoSin = 0.0F;
456 float RhoCos = 0.0F;
457
458 FMath::SinCos(&PhiSin, &PhiCos, Phi);
459 FMath::SinCos(&RhoSin, &RhoCos, Rho);
460
461 auto Direction = FVector(PhiSin, PhiCos * RhoCos, -PhiCos * RhoSin);
462 Direction.Normalize();
463
464 auto DirectionAbs = Direction.GetAbs();
465
466 if (DirectionAbs.X >= DirectionAbs.Y && DirectionAbs.X >= DirectionAbs.Z)
467 return Direction.X < 0 ? 1U : 0U;
468 else if (DirectionAbs.Y >= DirectionAbs.X && DirectionAbs.Y >= DirectionAbs.Z)
469 return Direction.Y < 0 ? 3U : 2U;
470 else
471 return Direction.Z < 0 ? 5U : 4U;
472}
473
475{
476 // Equirectangular projection samples the full sphere, so every cube face
477 // must be rendered regardless of the configured FOV.
479 return (1U << CubeFace_PosX) | (1U << CubeFace_NegX) |
480 (1U << CubeFace_PosY) | (1U << CubeFace_NegY) |
481 (1U << CubeFace_PosZ) | (1U << CubeFace_NegZ);
482
483 static const float Pi = PI;
484 static const float HalfPi = Pi / 2.0f;
485 static const float Sqrt2 = sqrtf(2.0f);
486
487 auto Mask = 1U << CubeFace_PosX; // Render front face by default.
488
489 // LUT1D: the table's acceptance angle bounds the field of view on both
490 // axes (the principal point may sit anywhere, so assume the full circle).
491 // Otherwise the fov attribute does, widened to the frame corners unless
492 // the FOV mask crops them.
493 const auto FOV = CameraModel == ECameraModel::LUT1D
494 ? FVector2D(2.0F * LensThetaMax, 2.0F * LensThetaMax)
495 : FVector2D(GetFOVAngleX(), GetFOVAngleY()) * (GetFOVMaskEnable() ? 1 : Sqrt2);
496
497 if (FOV.Y > HalfPi)
498 {
499 Mask |= 1U << CubeFace_PosZ;
500 Mask |= 1U << CubeFace_NegZ;
501 }
502
503 if (FOV.X > HalfPi)
504 {
505 Mask |= 1U << CubeFace_PosY;
506 Mask |= 1U << CubeFace_NegY;
507 }
508
509 if (FOV.X > Pi || FOV.Y > Pi)
510 {
511 Mask |= 1U << CubeFace_NegX;
512 }
513
514 return (uint8)Mask;
515}
516
518{
519 TRACE_CPUPROFILER_EVENT_SCOPE(ASceneCaptureSensor_WideAngleLens::CaptureSceneExtended);
520
521 bool SkipVFTR = CVarWideAngleSensorSkipVFTR.GetValueOnAnyThread() != 0;
522
523 // Capture the previous value so it can be restored afterwards, instead of
524 // unconditionally forcing the cvar back on and clobbering the project /
525 // scalability setting for every other view in the scene.
526 IConsoleVariable* VolumetricFogTRVar = nullptr;
527 int32 PreviousVolumetricFogTR = 1;
528
529 if (!SkipVFTR)
530 {
531 VolumetricFogTRVar = IConsoleManager::Get().FindConsoleVariable(
532 TEXT("r.VolumetricFog.TemporalReprojection"));
533 if (VolumetricFogTRVar != nullptr)
534 {
535 PreviousVolumetricFogTR = VolumetricFogTRVar->GetInt();
536 FlushRenderingCommands();
537 VolumetricFogTRVar->Set(TEXT("0"), ECVF_SetByCode);
538 }
539 }
540
541 // Show-only mode: refresh the actor list (rescans only when the world's
542 // actor count changed) and push it into every face capture.
544 {
545 ShowOnlyFilter.Refresh(GetWorld(), &GetEpisode());
546 for (auto FaceCapture : FaceCaptures)
547 if (FaceCapture != nullptr)
548 ShowOnlyFilter.ApplyTo(*FaceCapture);
549 }
550
551 // Same camera-cut warm-up as ASceneCaptureSensor::EnqueueRenderSceneImmediate, on every face,
552 // so a new fisheye does not start from a default exposure and adapt for seconds.
553 static IConsoleVariable* SpawnCameraCutsVar =
554 IConsoleManager::Get().FindConsoleVariable(TEXT("carla.Camera.SpawnCameraCuts"));
555 const bool bCameraCut = SpawnCameraCutsVar != nullptr && NumCaptures < SpawnCameraCutsVar->GetInt();
556 if (bCameraCut)
557 ++NumCaptures;
558
559 for (uint8 i = 0; i < 6; ++i)
560 if (CubemapRenderMask & (1U << i))
561 {
562 // Reset by the renderer after the capture.
563 if (bCameraCut)
564 FaceCaptures[i]->bCameraCutThisFrame = true;
565 FaceCaptures[i]->CaptureScene();
566 }
567
568 // Snapshot the data the render-thread pass needs so the lambda does not
569 // dereference `this` after the actor may have been destroyed. The render
570 // targets and sampler are still resolved through a TWeakObjectPtr so we
571 // skip the dispatch cleanly if the owning actor is gone.
572 TWeakObjectPtr<ASceneCaptureSensor_WideAngleLens> WeakSelf(this);
573 UTextureRenderTarget2D* RenderTargetsSnapshot[] =
574 {
581 };
582 UTextureRenderTarget2D* CaptureRenderTargetSnapshot = CaptureRenderTarget;
583 FRHISamplerState* CubemapSamplerSnapshot = CubemapSampler;
584
587 DistortedOptions.LUT = LensLUT;
588 DistortedOptions.ThetaMax = LensThetaMax;
589 DistortedOptions.bExplicitIntrinsics = bExplicitIntrinsics;
590 // Normalized viewport units -> pixels (x by width, y by height).
591 DistortedOptions.Intrinsics = FVector4f(
592 LensIntrinsics.X * static_cast<float>(ImageWidth),
593 LensIntrinsics.Y * static_cast<float>(ImageHeight),
594 LensIntrinsics.Z * static_cast<float>(ImageWidth),
595 LensIntrinsics.W * static_cast<float>(ImageHeight));
596 {
597 const FLinearColor Invalid = GetInvalidPixelColor();
598 DistortedOptions.InvalidColor = FVector4f(Invalid.R, Invalid.G, Invalid.B, Invalid.A);
599 }
600 DistortedOptions.YFOVAngle = YFOVAngle;
601 DistortedOptions.YFocalLength = YFocalLength;
602 DistortedOptions.LongitudeOffset = LongitudeOffset;
603 DistortedOptions.FOVFadeSize = FOVFadeSize;
604 DistortedOptions.CameraModel = CameraModel;
606 DistortedOptions.bFOVMaskEnable = bFOVMaskEnable;
607 DistortedOptions.bRenderPerspective = bRenderPerspective;
608
609 ENQUEUE_RENDER_COMMAND(WideAngleLensCommand)(
610 [WeakSelf,
611 RenderTargetsSnapshot,
612 CaptureRenderTargetSnapshot,
613 CubemapSamplerSnapshot,
614 DistortedOptions](FRHICommandListImmediate& RHICmdList)
615 {
616 TRACE_CPUPROFILER_EVENT_SCOPE(WideAngleLensCommand);
617
618 if (!WeakSelf.IsValid())
619 return;
620
621 FRDGBuilder GraphBuilder(RHICmdList);
622
623 UTextureRenderTarget2D* RenderTargets[] =
624 {
625 RenderTargetsSnapshot[0],
626 RenderTargetsSnapshot[1],
627 RenderTargetsSnapshot[2],
628 RenderTargetsSnapshot[3],
629 RenderTargetsSnapshot[4],
630 RenderTargetsSnapshot[5]
631 };
632
634 GraphBuilder,
635 CaptureRenderTargetSnapshot,
636 RenderTargets,
637 CubemapSamplerSnapshot,
638 DistortedOptions);
639
640 GraphBuilder.Execute();
641 });
642
643 if (CVarWideAngleSensorDumpAllFrames.GetValueOnAnyThread() == 1)
644 {
645 static thread_local auto FrameCounter = 0U;
646
647 const TCHAR* Names[(size_t)ECameraModel::MaxEnum] =
648 {
649 TEXT("Perspective"),
650 TEXT("Stereographic"),
651 TEXT("Equidistant"),
652 TEXT("Equisolid"),
653 TEXT("Orthographic"),
654 TEXT("KannalaBrandt"),
655 TEXT("BrownConrady"),
656 TEXT("LUT1D"),
657 TEXT("FTheta"),
658 };
659
660 auto CameraTypeName = Names[(uint8)CameraModel];
661 auto Path = CVarWideAngleSensorDumpAllFramesPath.GetValueOnAnyThread();
662
663 if (CVarWideAngleSensorDumpAllFramesCubemap.GetValueOnAnyThread())
664 {
665 for (uint8 FaceIndex = 0; FaceIndex != 6; ++FaceIndex)
666 {
668 *FaceRenderTargets[FaceIndex],
669 FString::Printf(
670 TEXT("%s/Frame-%s-%u-Face-%u.png"),
671 *Path,
672 CameraTypeName,
674 FaceIndex)).Wait();
675 }
676 }
677
680 FString::Printf(
681 TEXT("%s/Frame-%s-%u-Final.png"),
682 *Path,
683 CameraTypeName,
684 FrameCounter)).Wait();
685
686 ++FrameCounter;
687 }
688
689 if (VolumetricFogTRVar != nullptr)
690 {
691 FlushRenderingCommands();
692 VolumetricFogTRVar->Set(*FString::FromInt(PreviousVolumetricFogTR), ECVF_SetByCode);
693 }
694}
695
697{
698 const bool bInForceLinearGamma = !bEnablePostProcessingEffects;
699
700 const auto Format = bEnable16BitFormat ? PF_FloatRGBA : PF_B8G8R8A8;
701 const auto Side = FaceSize > 0 ?
702 static_cast<uint32>(FaceSize) :
703 std::max(GetImageWidth(), GetImageHeight());
704
705 CaptureRenderTarget->InitCustomFormat(
708 Format,
709 bInForceLinearGamma);
710
711 for (auto FaceRenderTarget : FaceRenderTargets)
712 {
713 FaceRenderTarget->InitCustomFormat(
714 Side, Side,
715 Format,
716 bInForceLinearGamma);
717 }
718
720 {
721 for (auto Face : FaceRenderTargets)
722 Face->TargetGamma = TargetGamma;
723 CaptureRenderTarget->TargetGamma = TargetGamma;
724 }
725
726 for (uint8 i = 0; i != 6; ++i)
727 {
728 FaceCaptures[i]->Deactivate();
729 FaceCaptures[i]->TextureTarget = FaceRenderTargets[i];
730 }
731
733
734 for (auto FaceCapture : FaceCaptures)
735 {
736 // Use PostProcessConfig for each face capture to honour the
737 // ue5-dev convention (mirrors ASceneCaptureSensor::BeginPlay).
738 auto PostProcessConfig = FPostProcessConfig(
739 FaceCapture->PostProcessSettings,
740 FaceCapture->ShowFlags);
741 PostProcessConfig.UpdateFromSceneCaptureComponent2D(*FaceCapture);
742 PostProcessConfig.EnablePostProcessingEffects(bEnablePostProcessingEffects);
743 FaceCapture->ShowFlags = PostProcessConfig.EngineShowFlags;
744 FaceCapture->PostProcessSettings = PostProcessConfig.PostProcessSettings;
745
746 // The assignment above replaces the settings the constructor put on
747 // this face, auto exposure included: each face would then adapt to its
748 // own view and the cube's faces meet at visible brightness seams.
750
751 FaceCapture->CaptureSource = ESceneCaptureSource::SCS_FinalColorLDR;
752 FaceCapture->UpdateContent();
753 FaceCapture->Activate();
754 }
755
756 // Raise the render-fence block timeout: a single wide-angle capture
757 // enqueues up to 6 face scene-captures plus the RDG composite pass, which
758 // can exceed the default timeout on heavy scenes and trip a false-positive
759 // render-fence fatal assert. This is a process-wide setting; CARLA already
760 // runs the render pipeline outside interactive-latency budgets, so a wider
761 // watchdog is harmless for other views.
762 UKismetSystemLibrary::ExecuteConsoleCommand(
763 GetWorld(),
764 FString("g.TimeoutForBlockOnRenderFence 300000"));
765
766 // This ensures the camera is always spawning the raindrops in case the
767 // weather was previously set to have rain.
768 auto Weather = GetEpisode().GetWeather();
769 if (Weather != nullptr)
770 Weather->NotifyWeather(this);
771
772 ReadbackPool = MakeShared<FRHIGPUReadbackPool, ESPMode::ThreadSafe>(
773 TEXT("WideAngleLensReadback"));
774
775 Super::BeginPlay();
776}
777
779{
780 Super::PrePhysTick(DeltaSeconds);
781
782 auto CaptureComponents = GetCaptureComponents2D();
783
784 // Add the view information every tick. It's only used for one tick and then
785 // removed by the streamer.
786 // FOVAngle is stored in degrees; convert to radians and use the
787 // half-angle tangent, guarding against a non-positive denominator.
788 const float HalfFOVTan = FMath::Tan(
789 0.5f * FMath::DegreesToRadians(CaptureComponents[0]->FOVAngle));
790 IStreamingManager::Get().AddViewInformation(
791 CaptureComponents[0]->GetComponentLocation(),
793 HalfFOVTan > 0.0f ? ImageWidth / HalfFOVTan : ImageWidth);
794}
795
796void ASceneCaptureSensor_WideAngleLens::PostPhysTick(UWorld* World, ELevelTick TickType, float DeltaTime)
797{
798 Super::PostPhysTick(World, TickType, DeltaTime);
799}
800
801void ASceneCaptureSensor_WideAngleLens::EndPlay(const EEndPlayReason::Type EndPlayReason)
802{
803 Super::EndPlay(EndPlayReason);
804 // The sensor counter is process-wide and only used to derive unique
805 // subobject names at construction. Resetting it here would let a
806 // surviving sibling sensor collide with the indices of the next spawned
807 // sensor.
808 FlushRenderingCommands();
809
810 // Release the GPU memory now rather than at the next garbage collection:
811 // six persistent face view states (Lumen scene, TSR history, shadow
812 // caches; several GB at 2048^2) plus the seven render targets. The
813 // UObjects stay alive until GC, but without their resources.
814 for (auto FaceCapture : FaceCaptures)
815 {
816 if (FaceCapture == nullptr)
817 continue;
818 FaceCapture->Deactivate();
819 FaceCapture->TextureTarget = nullptr;
820 FaceCapture->ReleaseViewStates();
821 }
822 for (auto FaceRenderTarget : FaceRenderTargets)
823 {
824 if (FaceRenderTarget != nullptr)
825 FaceRenderTarget->ReleaseResource();
826 }
827 if (CaptureRenderTarget != nullptr)
828 CaptureRenderTarget->ReleaseResource();
829 FlushRenderingCommands();
830 // In-flight readback tasks keep their own reference; the pool dies with the
831 // last of them (see ASceneCaptureSensor::EndPlay).
832 ReadbackPool.Reset();
833}
834
835TArrayView<USceneCaptureComponent2D_CARLA*> ASceneCaptureSensor_WideAngleLens::GetCaptureComponents2D()
836{
837 return FaceCaptures;
838}
839
841 float Value) const
842{
843 Value *= GetImageWidth();
844 Value /= GetImageHeight();
845 return Value;
846}
847
848
849
850// =============================================================================
851// -- Local static functions implementations -----------------------------------
852// =============================================================================
853
855
857 {
858 FPostProcessSettings& PostProcessSettings = CaptureComponent.PostProcessSettings;
859 PostProcessSettings.bOverride_VignetteIntensity = true;
860 PostProcessSettings.VignetteIntensity = 0;
861 PostProcessSettings.bOverride_DepthOfFieldVignetteSize = true;
862 PostProcessSettings.DepthOfFieldVignetteSize = 0;
863 // Every face must expose identically, or the faces meet at a visible
864 // brightness seam in the resampled lens image.
865 PostProcessSettings.bOverride_AutoExposureMethod = true;
866 PostProcessSettings.AutoExposureMethod = EAutoExposureMethod::AEM_Manual;
867 PostProcessSettings.bOverride_AutoExposureBias = true;
868 PostProcessSettings.AutoExposureBias = 0.0f;
869 PostProcessSettings.bOverride_AutoExposureMinBrightness = true;
870 PostProcessSettings.AutoExposureMinBrightness = 1.0f;
871 PostProcessSettings.bOverride_AutoExposureMaxBrightness = true;
872 PostProcessSettings.AutoExposureMaxBrightness = 1.0f;
873 }
874
875
876} // namespace SceneCaptureSensorWideAngleLens_local_ns
ECameraModel
static TAutoConsoleVariable< FString > CVarWideAngleSensorDumpAllFramesPath(TEXT("Carla.WideAngleLens.DumpAllFramesPath"), TEXT(""), TEXT("Sets the destination path when saving frames via \"Carla.WideAngleLens.DumpAllFrames\".\n"))
static TAutoConsoleVariable< int32 > CVarWideAngleSensorDumpAllFrames(TEXT("Carla.WideAngleLens.DumpAllFrames"), 0, TEXT("1: Enabled\n"))
constexpr auto DegToRad
static TAutoConsoleVariable< int32 > CVarWideAngleSensorSkipVFTR(TEXT("Carla.WideAngleLens.SkipVFTR"), 0, TEXT("1: Enabled\n"))
static auto WIDE_ANGLE_LENS_SENSOR_COUNTER
static TAutoConsoleVariable< int32 > CVarWideAngleSensorDumpAllFramesCubemap(TEXT("Carla.WideAngleLens.DumpAllFrames.Cubemap"), 0, TEXT("1: Enabled\n"))
constexpr auto RadToDeg
virtual void PrePhysTick(float DeltaSeconds) override
const TArray< float > & GetCameraCoefficients() const
virtual void SetUpSceneCaptureComponents(TArrayView< USceneCaptureComponent2D_CARLA * > SceneCaptures)
void SetCameraCoefficients(TArrayView< const float > Coefficients)
ASceneCaptureSensor_WideAngleLens(const FObjectInitializer &ObjectInitializer)
void SetLensLUT(const TArray< float > &Samples, float ThetaMaxRadians)
LUT1D camera model: R(Theta) samples, uniformly spaced over [0, ThetaMaxRadians], monotonically non-d...
void Set(const FActorDescription &ActorDescription) override
virtual void PostPhysTick(UWorld *World, ELevelTick TickType, float DeltaTime) override
TArray< USceneCaptureComponent2D_CARLA * > FaceCaptures
TArray< UTextureRenderTarget2D * > FaceRenderTargets
virtual void EndPlay(const EEndPlayReason::Type EndPlayReason) override
virtual FLinearColor GetInvalidPixelColor() const
Colour written to pixels the lens maps no ray to (LUT1D beyond its last sample).
TArrayView< USceneCaptureComponent2D_CARLA * > GetCaptureComponents2D()
void EnqueueRenderSceneImmediate()
Immediate enqueues render commands of the scene at the current time.
int32 NumCaptures
Captures rendered as camera cuts since BeginPlay (carla.Camera.SpawnCameraCuts).
void SetLensIntrinsics(float Fx, float Fy, float Cx, float Cy)
Explicit intrinsics in normalized viewport units, the same convention as sensor.camera....
FCarlaShowOnlyFilter ShowOnlyFilter
Show-only actor filter, applied to every face capture right before the faces are captured (see FCarla...
static bool ResolveCameraRayTracing(bool bSensorEnabled, bool bRequired)
Applies the carla.Camera.UseRayTracing tier policy to a camera's own ray-tracing preference.
void NotifyWeather(ASensor *Sensor=nullptr)
Notifing the weather to the blueprint's event.
Definition Weather.cpp:2650
static TFuture< bool > SavePixelsToDisk(UTextureRenderTarget2D &RenderTarget, const FString &FilePath)
Asynchronously save the pixels in RenderTarget to disk.
static void SetCamera(const FActorDescription &Description, ASceneCaptureSensor *Camera)
AWeather * GetWeather() const
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)
FRHISamplerState * GetSampler(ESamplerFilter Filter)
void DistortCubemapToImage(FRDGBuilder &GraphBuilder, FRDGTexture *Destination, FRDGTexture **CubeTextures, FRHISamplerState *Sampler, const FDistortCubemapToImageOptions &Options)
static void SetCameraDefaultOverrides(USceneCaptureComponent2D_CARLA &CaptureComponent)
TArrayView< const float > KannalaBrandtCoefficients
A description of a Carla Actor with all its variation.
void Refresh(UWorld *World, const UCarlaEpisode *Episode)
Rebuild the cached actor list if the world changed since the last call.
void ApplyTo(USceneCaptureComponent2D &Capture) const
Push the current list into Capture (or restore full-scene rendering when the filter is disabled).