CARLA
Loading...
Searching...
No Matches
SkyLightMap.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
8#include "Carla.h"
9
10#include <util/ue-header-guard-begin.h>
11#include "Engine/TextureCube.h"
12#include "Math/Float16Color.h"
13#include "PixelFormat.h"
14#include <util/ue-header-guard-end.h>
15
16#include <cmath>
17
18namespace
19{
20 // Direction of a cube face texel, Unreal/D3D convention. su, sv in [-1, 1]
21 // with sv growing downwards (texture rows). Mirrors GetCubemapVector in
22 // Engine/Shaders/Private/ReflectionEnvironmentShaders.usf.
23 FVector FaceDirection(int32 Face, float su, float sv)
24 {
25 switch (Face)
26 {
27 case 0: return FVector( 1.0f, -sv, -su);
28 case 1: return FVector(-1.0f, -sv, su);
29 case 2: return FVector( su, 1.0f, sv);
30 case 3: return FVector( su, -1.0f, -sv);
31 case 4: return FVector( su, -sv, 1.0f);
32 default: return FVector(-su, -sv, -1.0f);
33 }
34 }
35
36 // Bilinear fetch, wrapping in azimuth, clamping in elevation.
37 void SampleEquirect(const float* RGBA, int32 W, int32 H, float u, float v, float Out[4])
38 {
39 u -= 0.5f; v -= 0.5f;
40 const float fu = std::floor(u), fv = std::floor(v);
41 const float tu = u - fu, tv = v - fv;
42 int32 u0 = int32(fu), v0 = int32(fv);
43 int32 u1 = u0 + 1, v1 = v0 + 1;
44 u0 = ((u0 % W) + W) % W; u1 = ((u1 % W) + W) % W;
45 v0 = FMath::Clamp(v0, 0, H - 1); v1 = FMath::Clamp(v1, 0, H - 1);
46 const float* p00 = RGBA + 4 * (v0 * W + u0);
47 const float* p10 = RGBA + 4 * (v0 * W + u1);
48 const float* p01 = RGBA + 4 * (v1 * W + u0);
49 const float* p11 = RGBA + 4 * (v1 * W + u1);
50 for (int32 c = 0; c < 4; ++c)
51 {
52 Out[c] = (p00[c] * (1 - tu) + p10[c] * tu) * (1 - tv) + (p01[c] * (1 - tu) + p11[c] * tu) * tv;
53 }
54 }
55}
56
58 const float* RGBA, int32 Width, int32 Height, int32 FaceSize)
59{
60 if (RGBA == nullptr || Width < 2 || Height < 2 || FaceSize < 4)
61 {
62 UE_LOG(LogCarla, Error, TEXT("SkyLightMap: invalid panorama %dx%d face %d"), Width, Height, FaceSize);
63 return nullptr;
64 }
65
66 // Unique name on purpose: a fixed name makes NewObject overwrite the previous
67 // (still rooted, still assigned) cubemap in place, so the sky light sees the
68 // same pointer and never re-captures.
69 UTextureCube* Cube = UTextureCube::CreateTransient(FaceSize, FaceSize, PF_FloatRGBA,
70 MakeUniqueObjectName(GetTransientPackage(), UTextureCube::StaticClass(), TEXT("CarlaSkyLightMap")));
71 if (Cube == nullptr)
72 {
73 return nullptr;
74 }
75 Cube->SRGB = false;
76 Cube->CompressionSettings = TC_HDR;
77 Cube->NeverStream = true;
78 Cube->Filter = TF_Bilinear;
79
80 FTexture2DMipMap& Mip = Cube->GetPlatformData()->Mips[0];
81 FFloat16Color* Dst = static_cast<FFloat16Color*>(Mip.BulkData.Lock(LOCK_READ_WRITE));
82 const float TwoPi = 2.0f * PI;
83 for (int32 Face = 0; Face < 6; ++Face)
84 {
85 FFloat16Color* FaceDst = Dst + int64(Face) * FaceSize * FaceSize;
86 for (int32 j = 0; j < FaceSize; ++j)
87 {
88 const float sv = (j + 0.5f) / FaceSize * 2.0f - 1.0f;
89 for (int32 i = 0; i < FaceSize; ++i)
90 {
91 const float su = (i + 0.5f) / FaceSize * 2.0f - 1.0f;
92 const FVector d = FaceDirection(Face, su, sv).GetSafeNormal();
93 float phi = std::atan2(d.Y, d.X); // from +X toward +Y
94 if (phi < 0.0f) phi += TwoPi;
95 const float theta = std::asin(FMath::Clamp(float(d.Z), -1.0f, 1.0f));
96 const float u = phi / TwoPi * Width;
97 const float v = (0.5f - theta / PI) * Height;
98 float Px[4];
99 SampleEquirect(RGBA, Width, Height, u, v, Px);
100 FaceDst[j * FaceSize + i] = FFloat16Color(FLinearColor(Px[0], Px[1], Px[2], 1.0f));
101 }
102 }
103 }
104 Mip.BulkData.Unlock();
105 Cube->UpdateResource();
106 UE_LOG(LogCarla, Log, TEXT("SkyLightMap: built %dx%d cubemap from %dx%d panorama"), FaceSize, FaceSize, Width, Height);
107 return Cube;
108}
CARLA_API UTextureCube * CreateCubemapFromEquirect(const float *RGBA, int32 Width, int32 Height, int32 FaceSize=512)
Resamples the panorama into a transient 6-face HDR cubemap (PF_FloatRGBA, FaceSize x FaceSize per fac...