diff options
Diffstat (limited to 'Build/source/utils/asymptote/cudareflect/tinyexr/examples/cube2longlat/cube2longlat.cc')
-rw-r--r-- | Build/source/utils/asymptote/cudareflect/tinyexr/examples/cube2longlat/cube2longlat.cc | 470 |
1 files changed, 470 insertions, 0 deletions
diff --git a/Build/source/utils/asymptote/cudareflect/tinyexr/examples/cube2longlat/cube2longlat.cc b/Build/source/utils/asymptote/cudareflect/tinyexr/examples/cube2longlat/cube2longlat.cc new file mode 100644 index 00000000000..5a389c18cf7 --- /dev/null +++ b/Build/source/utils/asymptote/cudareflect/tinyexr/examples/cube2longlat/cube2longlat.cc @@ -0,0 +1,470 @@ +#include "tinyexr.h" + +#define STB_IMAGE_IMPLEMENTATION +#include "stb_image.h" + +#define STB_IMAGE_WRITE_IMPLEMENTATION +#include "stb_image_write.h" + +#include <array> +#include <cmath> +#include <iostream> +#include <string> +#include <vector> + +// From Filament. +static inline void RGBMtoLinear(const float rgbm[4], float linear[3]) { + linear[0] = rgbm[0] * rgbm[3] * 16.0f; + linear[1] = rgbm[1] * rgbm[3] * 16.0f; + linear[2] = rgbm[2] * rgbm[3] * 16.0f; + + // Gamma to linear space + linear[0] = linear[0] * linear[0]; + linear[1] = linear[1] * linear[1]; + linear[2] = linear[2] * linear[2]; +} + +static inline void LinearToRGBM(const float linear[3], float rgbm[4]) { + rgbm[0] = linear[0]; + rgbm[1] = linear[1]; + rgbm[2] = linear[2]; + rgbm[3] = 1.0f; + + // Linear to gamma space + rgbm[0] = rgbm[0] * rgbm[0]; + rgbm[1] = rgbm[1] * rgbm[1]; + rgbm[2] = rgbm[2] * rgbm[2]; + + // Set the range + rgbm[0] /= 16.0f; + rgbm[1] /= 16.0f; + rgbm[2] /= 16.0f; + + float maxComponent = + std::max(std::max(rgbm[0], rgbm[1]), std::max(rgbm[2], 1e-6f)); + // Don't let M go below 1 in the [0..16] range + rgbm[3] = std::max(1.0f / 16.0f, std::min(maxComponent, 1.0f)); + rgbm[3] = std::ceil(rgbm[3] * 255.0f) / 255.0f; + + // saturate([0.0, 1.0]) + rgbm[0] = std::max(0.0f, std::min(1.0f, rgbm[0] / rgbm[3])); + rgbm[1] = std::max(0.0f, std::min(1.0f, rgbm[1] / rgbm[3])); + rgbm[2] = std::max(0.0f, std::min(1.0f, rgbm[2] / rgbm[3])); +} + +static std::string GetFileExtension(const std::string& filename) { + if (filename.find_last_of(".") != std::string::npos) + return filename.substr(filename.find_last_of(".") + 1); + return ""; +} + +struct Image { + int width; + int height; + std::vector<float> data; +}; + +static bool LoadCubemaps(const std::array<std::string, 6> face_filenames, + std::array<Image, 6>* output) { + for (size_t i = 0; i < 6; i++) { + std::string ext = GetFileExtension(face_filenames[i]); + + Image image; + + if ((ext.compare("exr") == 0) || (ext.compare("EXR") == 0)) { + int width, height; + float* rgba; + const char* err; + + int ret = + LoadEXR(&rgba, &width, &height, face_filenames[i].c_str(), &err); + if (ret != 0) { + if (err) { + std::cerr << "EXR load error: " << err << std::endl; + } else { + std::cerr << "EXR load error: code " << ret << std::endl; + } + return false; + } + + image.width = width; + image.height = height; + image.data.resize(width * height * 3); + + // RGBA -> RGB + for (size_t j = 0; j < size_t(width * height); j++) { + image.data[3 * j + 0] = rgba[4 * j + 0]; + image.data[3 * j + 1] = rgba[4 * j + 1]; + image.data[3 * j + 2] = rgba[4 * j + 2]; + } + + free(rgba); + + (*output)[i] = std::move(image); + + } else if ((ext.compare("rgbm") == 0) || (ext.compare("RGBM") == 0)) { + int width, height; + int n; + + unsigned char* data = stbi_load(face_filenames[i].c_str(), &width, + &height, &n, STBI_default); + + if (!data) { + std::cerr << "Failed to load file: " << face_filenames[i] << std::endl; + return false; + } + + if ((n != 4)) { + std::cerr << "Not a RGBM encoded image: " << face_filenames[i] + << std::endl; + return false; + } + + image.width = width; + image.height = height; + image.data.resize(size_t(width * height)); + + for (size_t i = 0; i < size_t(width * height); i++) { + float rgbm[4]; + // [0, 1.0] + rgbm[0] = data[4 * i + 0] / 255.0f; + rgbm[1] = data[4 * i + 1] / 255.0f; + rgbm[2] = data[4 * i + 2] / 255.0f; + rgbm[3] = data[4 * i + 3] / 255.0f; + + float linear[3]; + RGBMtoLinear(rgbm, linear); + + image.data[3 * i + 0] = linear[0]; + image.data[3 * i + 1] = linear[1]; + image.data[3 * i + 2] = linear[2]; + } + + (*output)[i] = std::move(image); + + } else { + std::cerr << "Unknown file extension : " << ext << std::endl; + return false; + } + std::cout << "Loaded " << face_filenames[i] << std::endl; + } + + return true; +} + +void convert_xyz_to_cube_uv(float x, float y, float z, int* index, float* u, + float* v) { + float absX = fabs(x); + float absY = fabs(y); + float absZ = fabs(z); + + int isXPositive = x > 0.0f ? 1 : 0; + int isYPositive = y > 0.0f ? 1 : 0; + int isZPositive = z > 0.0f ? 1 : 0; + + float maxAxis, uc, vc; + + // POSITIVE X + if (isXPositive && absX >= absY && absX >= absZ) { + // u (0 to 1) goes from +z to -z + // v (0 to 1) goes from -y to +y + maxAxis = absX; + uc = -z; + vc = y; + *index = 0; + } + // NEGATIVE X + if (!isXPositive && absX >= absY && absX >= absZ) { + // u (0 to 1) goes from -z to +z + // v (0 to 1) goes from -y to +y + maxAxis = absX; + uc = z; + vc = y; + *index = 1; + } + // POSITIVE Y + if (isYPositive && absY >= absX && absY >= absZ) { + // u (0 to 1) goes from -x to +x + // v (0 to 1) goes from +z to -z + maxAxis = absY; + uc = x; + vc = -z; + *index = 2; + } + // NEGATIVE Y + if (!isYPositive && absY >= absX && absY >= absZ) { + // u (0 to 1) goes from -x to +x + // v (0 to 1) goes from -z to +z + maxAxis = absY; + uc = x; + vc = z; + *index = 3; + } + // POSITIVE Z + if (isZPositive && (absZ >= absX) && (absZ >= absY)) { + // u (0 to 1) goes from -x to +x + // v (0 to 1) goes from -y to +y + maxAxis = absZ; + uc = x; + vc = y; + *index = 4; + } + // NEGATIVE Z + if (!isZPositive && (absZ >= absX) && (absZ >= absY)) { + // u (0 to 1) goes from +x to -x + // v (0 to 1) goes from -y to +y + maxAxis = absZ; + uc = -x; + vc = y; + *index = 5; + } + + // Convert range from -1 to 1 to 0 to 1 + *u = 0.5f * (uc / maxAxis + 1.0f); + *v = 0.5f * (vc / maxAxis + 1.0f); +} + +// +// Simple bilinear texture filtering. +// +static void SampleTexture(float* rgba, float u, float v, int width, int height, + int channels, const float* texels) { + float sx = std::floor(u); + float sy = std::floor(v); + + // Wrap mode = repeat + float uu = u - sx; + float vv = v - sy; + + // clamp + uu = std::max(uu, 0.0f); + uu = std::min(uu, 1.0f); + vv = std::max(vv, 0.0f); + vv = std::min(vv, 1.0f); + + float px = (width - 1) * uu; + float py = (height - 1) * vv; + + int x0 = std::max(0, std::min((int)px, (width - 1))); + int y0 = std::max(0, std::min((int)py, (height - 1))); + int x1 = std::max(0, std::min((x0 + 1), (width - 1))); + int y1 = std::max(0, std::min((y0 + 1), (height - 1))); + + float dx = px - (float)x0; + float dy = py - (float)y0; + + float w[4]; + + w[0] = (1.0f - dx) * (1.0 - dy); + w[1] = (1.0f - dx) * (dy); + w[2] = (dx) * (1.0 - dy); + w[3] = (dx) * (dy); + + int i00 = channels * (y0 * width + x0); + int i01 = channels * (y0 * width + x1); + int i10 = channels * (y1 * width + x0); + int i11 = channels * (y1 * width + x1); + + for (int i = 0; i < channels; i++) { + rgba[i] = w[0] * texels[i00 + i] + w[1] * texels[i10 + i] + + w[2] * texels[i01 + i] + w[3] * texels[i11 + i]; + } +} + +static void SampleCubemap(const std::array<Image, 6>& cubemap_faces, + const float n[3], float col[3]) { + int face; + float u, v; + convert_xyz_to_cube_uv(n[0], n[1], n[2], &face, &u, &v); + + v = 1.0f - v; + + // std::cout << "face = " << face << std::endl; + + // TODO(syoyo): Do we better consider seams on the cubemap face border? + const Image& tex = cubemap_faces[face]; + + // std::cout << "n = " << n[0] << ", " << n[1] << ", " << n[2] << ", uv = " << + // u << ", " << v << std::endl; + + SampleTexture(col, u, v, tex.width, tex.height, /* RGB */ 3, tex.data.data()); + +// col[0] = u; +// col[1] = v; +// col[2] = 0.0f; +#if 0 + if (face == 0) { + col[0] = 1.0f; + col[1] = 0.0f; + col[2] = 0.0f; + } else if (face == 1) { + col[0] = 0.0f; + col[1] = 1.0f; + col[2] = 0.0f; + } else if (face == 2) { + col[0] = 0.0f; + col[1] = 0.0f; + col[2] = 1.0f; + } else if (face == 3) { + col[0] = 1.0f; + col[1] = 0.0f; + col[2] = 1.0f; + } else if (face == 4) { + col[0] = 0.0f; + col[1] = 1.0f; + col[2] = 1.0f; + } else if (face == 5) { + col[0] = 1.0f; + col[1] = 1.0f; + col[2] = 1.0f; + } +#endif +} + +static void CubemapToLonglat(const std::array<Image, 6>& cubemap_faces, + const float phi_offset, /* in angle */ + const int width, Image* longlat) { + int height = width / 2; + + longlat->width = width; + longlat->height = height; + longlat->data.resize(size_t(width * height * 3)); // RGB + + const float kPI = 3.141592f; + + for (size_t y = 0; y < size_t(height); y++) { + float theta = ((y + 0.5f) / float(height)) * kPI; // [0, pi] + for (size_t x = 0; x < size_t(width); x++) { + float phi = ((x + 0.5f) / float(width)) * 2.0f * kPI; // [0, 2 pi] + + phi += (phi_offset) * kPI / 180.0f; + + float n[3]; + + // Y-up + n[0] = std::sin(theta) * std::cos(phi); + n[1] = std::cos(theta); + n[2] = -std::sin(theta) * std::sin(phi); + + float col[3]; + SampleCubemap(cubemap_faces, n, col); + + longlat->data[3 * size_t(y * width + x) + 0] = col[0]; + longlat->data[3 * size_t(y * width + x) + 1] = col[1]; + longlat->data[3 * size_t(y * width + x) + 2] = col[2]; + } + } +} + +static unsigned char ftouc(const float f) { + int i(f * 255.0f); + i = std::max(0, std::min(255, i)); + return static_cast<unsigned char>(i); +} + +int main(int argc, char** argv) { + float phi_offset = 0.0f; + + if (argc < 9) { + printf( + "Usage: cube2longlat px.exr nx.exr py.exr ny.exr pz.exr nz.exr " + "output_width output.exr\n"); + exit(-1); + } + + std::array<std::string, 6> face_filenames; + + face_filenames[0] = argv[1]; + face_filenames[1] = argv[2]; + face_filenames[2] = argv[3]; + face_filenames[3] = argv[4]; + face_filenames[4] = argv[5]; + face_filenames[5] = argv[6]; + + int output_width = atoi(argv[7]); + + std::string output_filename = argv[8]; + + if (argc > 9) { + phi_offset = atof(argv[9]); + } + + std::array<Image, 6> cubemaps; + + if (!LoadCubemaps(face_filenames, &cubemaps)) { + std::cerr << "Failed to load cubemap faces." << std::endl; + return EXIT_FAILURE; + } + + Image longlat; + + CubemapToLonglat(cubemaps, phi_offset, output_width, &longlat); + + { + std::string ext = GetFileExtension(output_filename); + if ((ext.compare("exr") == 0) || (ext.compare("EXR") == 0)) { + const char *err; + int ret = SaveEXR(longlat.data.data(), longlat.width, longlat.height, + /* RGB */ 3, /* fp16 */ 0, output_filename.c_str(), &err); + if (ret != TINYEXR_SUCCESS) { + if (err) { + std::cout << "Failed to save image as EXR. msg = " << err << ", code = " << ret << std::endl; + FreeEXRErrorMessage(err); + } else { + std::cout << "Failed to save image as EXR. code = " << ret << std::endl; + } + return EXIT_FAILURE; + } + } else if ((ext.compare("rgbm") == 0) || (ext.compare("RGBM") == 0)) { + std::vector<unsigned char> rgbm_image; + + for (size_t j = 0; j < size_t(longlat.width * longlat.height); j++) { + float linear[3]; + linear[0] = longlat.data[3 * j + 0]; + linear[1] = longlat.data[3 * j + 1]; + linear[2] = longlat.data[3 * j + 2]; + + float rgbm[4]; + + LinearToRGBM(linear, rgbm); + + rgbm_image[4 * j + 0] = ftouc(rgbm[0]); + rgbm_image[4 * j + 1] = ftouc(rgbm[1]); + rgbm_image[4 * j + 2] = ftouc(rgbm[2]); + rgbm_image[4 * j + 3] = ftouc(rgbm[2]); + } + + // Save as PNG. + int ret = + stbi_write_png(output_filename.c_str(), longlat.width, longlat.height, + 4, rgbm_image.data(), longlat.width * 4); + + if (ret == 0) { + std::cerr << "Failed to save image as RGBM file : " << output_filename + << std::endl; + return EXIT_FAILURE; + } + + } else { + if ((ext.compare("hdr") == 0) || (ext.compare("HDR") == 0)) { + // ok + } else { + std::cout << "Unknown file extension. Interpret it as RGBE format : " + << ext << std::endl; + } + + int ret = stbi_write_hdr(output_filename.c_str(), longlat.width, + longlat.height, 3, longlat.data.data()); + + if (ret == 0) { + std::cerr << "Failed to save image as HDR file : " << output_filename + << std::endl; + return EXIT_FAILURE; + } + } + } + + std::cout << "Write " << output_filename << std::endl; + + return 0; +} |