20 glGenTextures(1, &m_texture);
21 glBindTexture(GL_TEXTURE_CUBE_MAP, m_texture);
23 stbi_set_flip_vertically_on_load(
false);
25 for (
int i = 0; i < 6; ++i) {
27 unsigned char* pixels = stbi_load(facePaths[i].c_str(), &w, &h, &channels, 3);
29 SLEAK_ERROR(
"OpenGLCubemapTexture: Failed to load face {}: {}",
31 glDeleteTextures(1, &m_texture);
36 glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X + i,
39 GL_RGB, GL_UNSIGNED_BYTE, pixels);
42 m_width =
static_cast<uint32_t
>(w);
43 m_height =
static_cast<uint32_t
>(h);
46 stbi_image_free(pixels);
49 glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
50 glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
51 glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
52 glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
53 glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE);
55 glBindTexture(GL_TEXTURE_CUBE_MAP, 0);
62 stbi_set_flip_vertically_on_load(
false);
67 if (path.size() >= 4) {
68 const std::string ext = path.substr(path.size() - 4);
69 if (ext ==
".hdr" || ext ==
".HDR") isHDR =
true;
72 int panW = 0, panH = 0, channels = 0;
73 unsigned char* panoramaLDR =
nullptr;
74 float* panoramaHDR =
nullptr;
76 panoramaHDR = stbi_loadf(path.c_str(), &panW, &panH, &channels, 3);
78 SLEAK_ERROR(
"OpenGLCubemapTexture: Failed to load HDR panorama: {}", path);
82 panoramaLDR = stbi_load(path.c_str(), &panW, &panH, &channels, 3);
84 SLEAK_ERROR(
"OpenGLCubemapTexture: Failed to load panorama: {}", path);
89 glGenTextures(1, &m_texture);
90 glBindTexture(GL_TEXTURE_CUBE_MAP, m_texture);
95 std::vector<unsigned char> faceLDR;
96 std::vector<float> faceHDR;
97 if (isHDR) faceHDR.resize(faceSize * faceSize * 3);
98 else faceLDR.resize(faceSize * faceSize * 3);
100 auto sampleChannel = [&](
int x0,
int y0,
int x1,
int y1,
101 float fx,
float fy,
int c) ->
float {
103 float c00 = panoramaHDR[(y0 * panW + x0) * 3 + c];
104 float c10 = panoramaHDR[(y0 * panW + x1) * 3 + c];
105 float c01 = panoramaHDR[(y1 * panW + x0) * 3 + c];
106 float c11 = panoramaHDR[(y1 * panW + x1) * 3 + c];
107 return c00 * (1 - fx) * (1 - fy) + c10 * fx * (1 - fy)
108 + c01 * (1 - fx) * fy + c11 * fx * fy;
110 float c00 = panoramaLDR[(y0 * panW + x0) * 3 + c];
111 float c10 = panoramaLDR[(y0 * panW + x1) * 3 + c];
112 float c01 = panoramaLDR[(y1 * panW + x0) * 3 + c];
113 float c11 = panoramaLDR[(y1 * panW + x1) * 3 + c];
114 return c00 * (1 - fx) * (1 - fy) + c10 * fx * (1 - fy)
115 + c01 * (1 - fx) * fy + c11 * fx * fy;
121 for (
int face = 0; face < 6; ++face) {
122 for (uint32_t y = 0; y < faceSize; ++y) {
123 for (uint32_t x = 0; x < faceSize; ++x) {
124 float u = (2.0f * (x + 0.5f) / faceSize) - 1.0f;
125 float v = (2.0f * (y + 0.5f) / faceSize) - 1.0f;
129 case 0: dx = 1.0f; dy = -v; dz = -u;
break;
130 case 1: dx = -1.0f; dy = -v; dz = u;
break;
131 case 2: dx = u; dy = 1.0f; dz = v;
break;
132 case 3: dx = u; dy = -1.0f; dz = -v;
break;
133 case 4: dx = u; dy = -v; dz = 1.0f;
break;
134 case 5: dx = -u; dy = -v; dz = -1.0f;
break;
135 default: dx = dy = dz = 0.0f;
break;
138 float len = std::sqrt(dx * dx + dy * dy + dz * dz);
139 dx /= len; dy /= len; dz /= len;
141 float lon = std::atan2(dz, dx);
142 float lat = std::asin(std::clamp(dy, -1.0f, 1.0f));
144 float panU = 0.5f + lon / (2.0f * 3.14159265f);
145 float panV = 0.5f - lat / 3.14159265f;
147 float srcX = panU * (panW - 1);
148 float srcY = panV * (panH - 1);
149 int x0 =
static_cast<int>(srcX);
150 int y0 =
static_cast<int>(srcY);
151 int x1 = std::min(x0 + 1, panW - 1);
152 int y1 = std::min(y0 + 1, panH - 1);
153 x0 = std::clamp(x0, 0, panW - 1);
154 y0 = std::clamp(y0, 0, panH - 1);
155 float fx = srcX - x0;
156 float fy = srcY - y0;
158 size_t idx = (y * faceSize + x) * 3;
159 for (
int c = 0; c < 3; ++c) {
160 float val = sampleChannel(x0, y0, x1, y1, fx, fy, c);
162 faceHDR[idx + c] = val;
164 faceLDR[idx + c] =
static_cast<unsigned char>(
165 std::clamp(val, 0.0f, 255.0f));
172 glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X + face,
174 faceSize, faceSize, 0,
175 GL_RGB, GL_FLOAT, faceHDR.data());
177 glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X + face,
179 faceSize, faceSize, 0,
180 GL_RGB, GL_UNSIGNED_BYTE, faceLDR.data());
184 if (panoramaHDR) stbi_image_free(panoramaHDR);
185 if (panoramaLDR) stbi_image_free(panoramaLDR);
190 glGenerateMipmap(GL_TEXTURE_CUBE_MAP);
191 glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_FILTER,
192 GL_LINEAR_MIPMAP_LINEAR);
193 glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
194 glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
195 glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
196 glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE);
198 glBindTexture(GL_TEXTURE_CUBE_MAP, 0);
203 SLEAK_INFO(
"OpenGLCubemapTexture: Loaded {} panorama ({}x{} -> {} face)",
204 isHDR ?
"HDR" :
"LDR", panW, panH, faceSize);
209 float midR,
float midG,
float midB,
210 float botR,
float botG,
float botB,
211 uint32_t resolution) {
212 glGenTextures(1, &m_texture);
213 glBindTexture(GL_TEXTURE_CUBE_MAP, m_texture);
215 m_width = resolution;
216 m_height = resolution;
218 std::vector<unsigned char> faceData(resolution * resolution * 3);
220 for (
int face = 0; face < 6; ++face) {
221 for (uint32_t y = 0; y < resolution; ++y) {
223 float v = 1.0f - 2.0f *
static_cast<float>(y) / (resolution - 1);
228 r = midR + (topR - midR) * v;
229 g = midG + (topG - midG) * v;
230 b = midB + (topB - midB) * v;
234 r = midR + (botR - midR) * t;
235 g = midG + (botG - midG) * t;
236 b = midB + (botB - midB) * t;
239 for (uint32_t x = 0; x < resolution; ++x) {
240 size_t idx = (y * resolution + x) * 3;
241 faceData[idx + 0] =
static_cast<unsigned char>(r * 255.0f);
242 faceData[idx + 1] =
static_cast<unsigned char>(g * 255.0f);
243 faceData[idx + 2] =
static_cast<unsigned char>(b * 255.0f);
247 glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X + face,
249 resolution, resolution, 0,
250 GL_RGB, GL_UNSIGNED_BYTE, faceData.data());
253 glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
254 glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
255 glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
256 glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
257 glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE);
259 glBindTexture(GL_TEXTURE_CUBE_MAP, 0);
bool LoadCubemap(const std::array< std::string, 6 > &facePaths)
Load 6 face images: +X, -X, +Y, -Y, +Z, -Z.
bool LoadFromMemory(const void *data, uint32_t width, uint32_t height, TextureFormat format) override
Unused for cubemaps; always returns false, load via LoadCubemap/LoadEquirectangular instead.
bool LoadFromFile(const std::string &filePath) override
Unused for cubemaps; always returns false, load via LoadCubemap/LoadEquirectangular instead.
bool LoadEquirectangular(const std::string &path, uint32_t faceSize=512)
Load from a single equirectangular panorama and convert to cubemap.
bool LoadGradient(float topR, float topG, float topB, float midR, float midG, float midB, float botR, float botG, float botB, uint32_t resolution=64)
Generate a procedural gradient cubemap (top/mid/bottom colors).