Josh Posted April 5 Posted April 5 This is meant to correct sky panoramas like these, so the half-black image doesn't darken your scene: https://ambientcg.com/list?type=hdri&technique=hdri-bracketed-panorama-horizon-clearing&sort=popular It would be better to perform a Guassian blur on the lower half, but this is a start. Works with HDR images! #include "Leadwerks.h" using namespace Leadwerks; int main(int argc, const char* argv[]) { WString file = "DaySkyHDRI011B_8K-HDR.exr"; auto pm = LoadPixmap(file); if (not pm) return 0; Vec4 color; int x, y; int hh = pm->size.y / 2; for (y = 0; y < hh; ++y) { for (x = 0; x < pm->size.x; ++x) { color = pm->Sample(iVec2(x, hh - y)); pm->WritePixel(x, hh + y, color); } } pm->Save(StripExt(file) + "_out." + ExtractExt(file)); return 0; } 2 Quote My job is to make tools you love, with the features you want, and performance you can't live without.
Josh Posted April 5 Author Posted April 5 Here is a version that blurs the lower half, with some help from AI. It takes almost three minutes. I would like a higher blur radius, still: #include "UltraEngine.h" #include <vector> #include <cmath> #define M_PI 3.14159265358979323846 using namespace Leadwerks; // Function to generate a 1D Gaussian kernel std::vector<float> Generate1DGaussianKernel(int radius, float sigma) { std::vector<float> kernel(2 * radius + 1); float sum = 0.0f; for (int i = -radius; i <= radius; ++i) { float value = exp(-(i * i) / (2 * sigma * sigma)) / (sqrt(2 * M_PI) * sigma); kernel[i + radius] = value; sum += value; } // Normalize the kernel for (int i = 0; i < kernel.size(); ++i) { kernel[i] /= sum; } return kernel; } // Function to apply a 1D Gaussian blur horizontally void ApplyHorizontalBlur(const std::shared_ptr<Pixmap>& pm, const std::vector<float>& kernel) { int radius = kernel.size() / 2; auto temp = pm->Copy()->As<Pixmap>(); // Temporary copy for reading original values //if (temp->format == TEXTURE_RGBA16) temp = temp->Convert(TEXTURE_RGBA32); int hh = pm->size.y / 2; for (int y = hh; y < pm->size.y; ++y) { Print(y); for (int x = 0; x < pm->size.x; ++x) { Vec4 color(0, 0, 0, 0); for (int k = -radius; k <= radius; ++k) { int nx = std::max(0, std::min(x + k, pm->size.x - 1)); // Clamp x to image boundaries color += temp->Sample(iVec2(nx, y)) * kernel[k + radius]; } pm->WritePixel(x, y, color); } } } // Function to apply a 1D Gaussian blur vertically void ApplyVerticalBlur(const std::shared_ptr<Pixmap>& pm, const std::vector<float>& kernel) { int radius = kernel.size() / 2; auto temp = pm->Copy()->As<Pixmap>(); // Temporary copy for reading original values //if (temp->format == TEXTURE_RGBA16) temp = temp->Convert(TEXTURE_RGBA32); int hh = pm->size.y / 2; for (int y = hh; y < pm->size.y; ++y) { Print(y); for (int x = 0; x < pm->size.x; ++x) { Vec4 color(0, 0, 0, 0); for (int k = -radius; k <= radius; ++k) { int ny = std::max(0, std::min(y + k, pm->size.y - 1)); // Clamp y to image boundaries color += temp->Sample(iVec2(x, ny)) * kernel[k + radius]; } pm->WritePixel(x, y, color); } } } int main(int argc, const char* argv[]) { WString file = "DaySkyHDRI011B_8K-HDR.exr"; auto pm = LoadPixmap(file); if (!pm) return 0; //if (pm->format == TEXTURE_RGBA16) Print("RGBA half-float format"); // Mirror the upper half to the lower half Vec4 color; int x, y; int hh = pm->size.y / 2; for (y = 0; y < hh; ++y) { for (x = 0; x < pm->size.x; ++x) { color = pm->Sample(iVec2(x, hh - y)); pm->WritePixel(x, hh + y, color); } } auto start = Millisecs(); // Generate a 1D Gaussian kernel int radius = 64; // Adjust the radius as needed float sigma = 20.0f; // Adjust the sigma as needed auto kernel = Generate1DGaussianKernel(radius, sigma); // Apply horizontal blur Print("-------------------------------------------------------"); Print("Blur X"); Print("-------------------------------------------------------"); ApplyHorizontalBlur(pm, kernel); // Apply vertical blur Print("-------------------------------------------------------"); Print("Blur Y"); Print("-------------------------------------------------------"); ApplyVerticalBlur(pm, kernel); Print("Time (s): " + String((Millisecs() - start) / 1000.0)); // Save the output image pm->Save(StripExt(file) + "_out." + ExtractExt(file)); return 0; } And here is the result. No more darkened reflections. Quote My job is to make tools you love, with the features you want, and performance you can't live without.
Josh Posted April 5 Author Posted April 5 This version will darken the lower half a bit, so your downwards-facing reflections are not as bright. It looks more natural: #include "UltraEngine.h" #include <vector> #include <cmath> #define M_PI 3.14159265358979323846 using namespace Leadwerks; // Function to generate a 1D Gaussian kernel std::vector<float> Generate1DGaussianKernel(int radius, float sigma) { std::vector<float> kernel(2 * radius + 1); float sum = 0.0f; for (int i = -radius; i <= radius; ++i) { float value = exp(-(i * i) / (2 * sigma * sigma)) / (sqrt(2 * M_PI) * sigma); kernel[i + radius] = value; sum += value; } // Normalize the kernel for (int i = 0; i < kernel.size(); ++i) { kernel[i] /= sum; } return kernel; } // Function to apply a 1D Gaussian blur horizontally void ApplyHorizontalBlur(const std::shared_ptr<Pixmap>& pm, const std::vector<float>& kernel) { int radius = kernel.size() / 2; auto temp = pm->Copy()->As<Pixmap>(); // Temporary copy for reading original values //if (temp->format == TEXTURE_RGBA16) temp = temp->Convert(TEXTURE_RGBA32); int hh = pm->size.y / 2; for (int y = hh; y < pm->size.y; ++y) { Print(y); for (int x = 0; x < pm->size.x; ++x) { Vec4 color(0, 0, 0, 0); for (int k = -radius; k <= radius; ++k) { int nx = std::max(0, std::min(x + k, pm->size.x - 1)); // Clamp x to image boundaries color += temp->Sample(iVec2(nx, y)) * kernel[k + radius]; } pm->WritePixel(x, y, color); } } } // Function to apply a 1D Gaussian blur vertically void ApplyVerticalBlur(const std::shared_ptr<Pixmap>& pm, const std::vector<float>& kernel, const float lightness) { int radius = kernel.size() / 2; auto temp = pm->Copy()->As<Pixmap>(); // Temporary copy for reading original values //if (temp->format == TEXTURE_RGBA16) temp = temp->Convert(TEXTURE_RGBA32); int hh = pm->size.y / 2; for (int y = hh; y < pm->size.y; ++y) { Print(y); for (int x = 0; x < pm->size.x; ++x) { Vec4 color(0, 0, 0, 0); for (int k = -radius; k <= radius; ++k) { int ny = std::max(0, std::min(y + k, pm->size.y - 1)); // Clamp y to image boundaries color += temp->Sample(iVec2(x, ny)) * kernel[k + radius]; } color.r *= lightness; color.g *= lightness; color.b *= lightness; pm->WritePixel(x, y, color); } } } int main(int argc, const char* argv[]) { WString file = "DaySkyHDRI011B_8K-HDR.exr"; auto pm = LoadPixmap(file); if (!pm) return 0; //if (pm->format == TEXTURE_RGBA16) Print("RGBA half-float format"); // Mirror the upper half to the lower half Vec4 color; int x, y; int hh = pm->size.y / 2; for (y = 0; y < hh; ++y) { for (x = 0; x < pm->size.x; ++x) { color = pm->Sample(iVec2(x, hh - y)); pm->WritePixel(x, hh + y, color); } } auto start = Millisecs(); // Generate a 1D Gaussian kernel int radius = 64; // Adjust the radius as needed float sigma = 20.0f; // Adjust the sigma as needed auto kernel = Generate1DGaussianKernel(radius, sigma); float lightness = 0.65f; // Apply horizontal blur Print("-------------------------------------------------------"); Print("Blur X"); Print("-------------------------------------------------------"); ApplyHorizontalBlur(pm, kernel); // Apply vertical blur Print("-------------------------------------------------------"); Print("Blur Y"); Print("-------------------------------------------------------"); ApplyVerticalBlur(pm, kernel, lightness); Print("Time (s): " + String((Millisecs() - start) / 1000.0)); // Save the output image pm->Save(StripExt(file) + "_out." + ExtractExt(file)); return 0; } Quote My job is to make tools you love, with the features you want, and performance you can't live without.
Josh Posted April 7 Author Posted April 7 Here is the result using this technique 1 Quote My job is to make tools you love, with the features you want, and performance you can't live without.
Josh Posted April 8 Author Posted April 8 This code will grab every sky-only HDRI ambientcg has and process it into a mirrored panorama, as well as a preview image. I'll let it run tonight since it will take a few hours: #include "UltraEngine.h" #include <vector> #include <cmath> #define M_PI 3.14159265358979323846 using namespace Leadwerks; // Function to generate a 1D Gaussian kernel std::vector<float> Generate1DGaussianKernel(int radius, float sigma) { std::vector<float> kernel(2 * radius + 1); float sum = 0.0f; for (int i = -radius; i <= radius; ++i) { float value = exp(-(i * i) / (2 * sigma * sigma)) / (sqrt(2 * M_PI) * sigma); kernel[i + radius] = value; sum += value; } // Normalize the kernel for (int i = 0; i < kernel.size(); ++i) { kernel[i] /= sum; } return kernel; } // Function to apply a 1D Gaussian blur horizontally void ApplyHorizontalBlur(const std::shared_ptr<Pixmap>& pm, const std::vector<float>& kernel) { int radius = kernel.size() / 2; auto temp = pm->Copy()->As<Pixmap>(); // Temporary copy for reading original values //if (temp->format == TEXTURE_RGBA16) temp = temp->Convert(TEXTURE_RGBA32); int hh = pm->size.y / 2; for (int y = hh; y < pm->size.y; ++y) { Print(y); for (int x = 0; x < pm->size.x; ++x) { Vec4 color(0, 0, 0, 0); for (int k = -radius; k <= radius; ++k) { int nx = std::max(0, std::min(x + k, pm->size.x - 1)); // Clamp x to image boundaries color += temp->Sample(iVec2(nx, y)) * kernel[k + radius]; } pm->WritePixel(x, y, color); } } } // Function to apply a 1D Gaussian blur vertically void ApplyVerticalBlur(const std::shared_ptr<Pixmap>& pm, const std::vector<float>& kernel, const float lightness) { int radius = kernel.size() / 2; auto temp = pm->Copy()->As<Pixmap>(); // Temporary copy for reading original values //if (temp->format == TEXTURE_RGBA16) temp = temp->Convert(TEXTURE_RGBA32); int hh = pm->size.y / 2; for (int y = hh; y < pm->size.y; ++y) { Print(y); for (int x = 0; x < pm->size.x; ++x) { Vec4 color(0, 0, 0, 0); for (int k = -radius; k <= radius; ++k) { int ny = std::max(0, std::min(y + k, pm->size.y - 1)); // Clamp y to image boundaries color += temp->Sample(iVec2(x, ny)) * kernel[k + radius]; } color.r *= lightness; color.g *= lightness; color.b *= lightness; pm->WritePixel(x, y, color); } } } int main(int argc, const char* argv[]) { CreateDir(AppDir() + "hdris"); ChangeDir(AppDir() + "/hdris"); //Download all files String s = FetchUrl("https://ambientcg.com/api/v2/full_json?type=hdri&limit=1000&technique=hdri-bracketed-panorama-horizon-clearing&include=downloadData"); auto t = ParseTable(s); for (auto& r : t["foundAssets"]) { for (auto& d : r.second["downloadFolders"]["default"]["downloadFiletypeCategories"]["exr"]["downloads"]) { if (std::string(d.second["attribute"]) == "8K-HDR") { String url = std::string(d.second["downloadLink"]); String path = std::string(d.second["fileName"]); path = path.Replace("_8K-HDR", ""); if (FileType(path) == 0) DownloadFile(url, path); break; } } } auto dir = LoadDir("."); for (auto file : dir) { if (ExtractExt(file).Lower() != "exr") continue; if (FileType("out/" + file) == 1) continue; auto pm = LoadPixmap(file); if (not pm) return 0; //if (pm->format == TEXTURE_RGBA16) Print("RGBA half-float format"); // Mirror the upper half to the lower half Vec4 color; int x, y; int hh = pm->size.y / 2; for (y = 0; y < hh; ++y) { for (x = 0; x < pm->size.x; ++x) { color = pm->Sample(iVec2(x, hh - y)); pm->WritePixel(x, hh + y, color); } } auto start = Millisecs(); // Generate a 1D Gaussian kernel int radius = 64; // Adjust the radius as needed float sigma = 20.0f; // Adjust the sigma as needed auto kernel = Generate1DGaussianKernel(radius, sigma); float lightness = 0.65f; // Apply horizontal blur Print("-------------------------------------------------------"); Print("Blur X"); Print("-------------------------------------------------------"); ApplyHorizontalBlur(pm, kernel); // Apply vertical blur Print("-------------------------------------------------------"); Print("Blur Y"); Print("-------------------------------------------------------"); ApplyVerticalBlur(pm, kernel, lightness); Print("Time (s): " + String((Millisecs() - start) / 1000.0)); CreateDir("out"); // Save the output image pm->Save("out/" + file); // Save preview auto pm2 = CreatePixmap(2048, 512, pm->format); pm->Blit(0, 0, pm->size.x, pm->size.y / 2, pm2, 0, 0, pm2->size.x, pm2->size.y); pm2 = pm2->LinearTosRgb(); pm2 = pm2->Convert(TEXTURE_RGBA); pm2->Save("out/" + StripExt(file) + ".jpg"); } return 0; } Quote My job is to make tools you love, with the features you want, and performance you can't live without.
Josh Posted Thursday at 03:26 PM Author Posted Thursday at 03:26 PM I found it was necessary to add tone mapping to the preview image before converting it to RGBA, otherwise all the bright skyboxes would appear almost pure white. #include "UltraEngine.h" #include <vector> #include <cmath> #define M_PI 3.14159265358979323846 using namespace Leadwerks; Vec4 aces(Vec4 x) { const float a = 2.51; const float b = 0.03; const float c = 2.43; const float d = 0.59; const float e = 0.14; x.x = Clamp((x.x * (a * x.x + b)) / (x.x * (c * x.x + d) + e), 0.0f, 1.0f); x.y = Clamp((x.y * (a * x.y + b)) / (x.y * (c * x.y + d) + e), 0.0f, 1.0f); x.z = Clamp((x.z * (a * x.z + b)) / (x.z * (c * x.z + d) + e), 0.0f, 1.0f); return x; } // Function to generate a 1D Gaussian kernel std::vector<float> Generate1DGaussianKernel(int radius, float sigma) { std::vector<float> kernel(2 * radius + 1); float sum = 0.0f; for (int i = -radius; i <= radius; ++i) { float value = exp(-(i * i) / (2 * sigma * sigma)) / (sqrt(2 * M_PI) * sigma); kernel[i + radius] = value; sum += value; } // Normalize the kernel for (int i = 0; i < kernel.size(); ++i) { kernel[i] /= sum; } return kernel; } // Function to apply a 1D Gaussian blur horizontally void ApplyHorizontalBlur(const std::shared_ptr<Pixmap>& pm, const std::vector<float>& kernel) { int radius = kernel.size() / 2; auto temp = pm->Copy()->As<Pixmap>(); // Temporary copy for reading original values //if (temp->format == TEXTURE_RGBA16) temp = temp->Convert(TEXTURE_RGBA32); int hh = pm->size.y / 2; for (int y = hh; y < pm->size.y; ++y) { //Print(y); for (int x = 0; x < pm->size.x; ++x) { Vec4 color(0, 0, 0, 0); for (int k = -radius; k <= radius; ++k) { int nx = std::max(0, std::min(x + k, pm->size.x - 1)); // Clamp x to image boundaries color += temp->Sample(iVec2(nx, y)) * kernel[k + radius]; } pm->WritePixel(x, y, color); } } } // Function to apply a 1D Gaussian blur vertically void ApplyVerticalBlur(const std::shared_ptr<Pixmap>& pm, const std::vector<float>& kernel, const float lightness) { int radius = kernel.size() / 2; auto temp = pm->Copy()->As<Pixmap>(); // Temporary copy for reading original values //if (temp->format == TEXTURE_RGBA16) temp = temp->Convert(TEXTURE_RGBA32); int hh = pm->size.y / 2; for (int y = hh; y < pm->size.y; ++y) { //Print(y); for (int x = 0; x < pm->size.x; ++x) { Vec4 color(0, 0, 0, 0); for (int k = -radius; k <= radius; ++k) { int ny = std::max(0, std::min(y + k, pm->size.y - 1)); // Clamp y to image boundaries color += temp->Sample(iVec2(x, ny)) * kernel[k + radius]; } color.r *= lightness; color.g *= lightness; color.b *= lightness; pm->WritePixel(x, y, color); } } } int main(int argc, const char* argv[]) { CreateDir(AppDir() + "hdris"); ChangeDir(AppDir() + "/hdris"); //Download all files String s = FetchUrl("https://ambientcg.com/api/v2/full_json?type=hdri&limit=1000&technique=hdri-bracketed-panorama-horizon-clearing&include=downloadData"); auto t = ParseTable(s); for (auto& r : t["foundAssets"]) { for (auto& d : r.second["downloadFolders"]["default"]["downloadFiletypeCategories"]["exr"]["downloads"]) { if (std::string(d.second["attribute"]) == "8K-HDR") { String url = std::string(d.second["downloadLink"]); String path = std::string(d.second["fileName"]); path = path.Replace("_8K-HDR", ""); if (FileType(path) == 0) DownloadFile(url, path); break; } } } auto dir = LoadDir("."); for (auto file : dir) { if (ExtractExt(file).Lower() != "exr") continue; if (FileType("out/" + file) == 0) { auto pm = LoadPixmap(file); if (not pm) return 0; //if (pm->format == TEXTURE_RGBA16) Print("RGBA half-float format"); // Mirror the upper half to the lower half Vec4 color; int x, y; int hh = pm->size.y / 2; for (y = 0; y < hh; ++y) { for (x = 0; x < pm->size.x; ++x) { color = pm->Sample(iVec2(x, hh - y)); pm->WritePixel(x, hh + y, color); } } auto start = Millisecs(); // Generate a 1D Gaussian kernel int radius = 64; // Adjust the radius as needed float sigma = 20.0f; // Adjust the sigma as needed auto kernel = Generate1DGaussianKernel(radius, sigma); float lightness = 0.65f; // Apply horizontal blur Print("-------------------------------------------------------"); Print("Blur X"); Print("-------------------------------------------------------"); ApplyHorizontalBlur(pm, kernel); // Apply vertical blur Print("-------------------------------------------------------"); Print("Blur Y"); Print("-------------------------------------------------------"); ApplyVerticalBlur(pm, kernel, lightness); Print("Time (s): " + String((Millisecs() - start) / 1000.0)); CreateDir("out"); // Save the output image pm->Save("out/" + file); } // Save preview auto pm = LoadPixmap("out/" + file); auto pm2 = CreatePixmap(2048, 512, pm->format); pm->Blit(0, 0, pm->size.x, pm->size.y / 2, pm2, 0, 0, pm2->size.x, pm2->size.y); pm2 = pm2->LinearTosRgb(); // Apply tone mapping or many skyboxes will be much too bright Vec4 c; for (int x = 0; x < pm2->size.x; ++x) { for (int y = 0; y < pm2->size.y; ++y) { c = pm2->Sample(iVec2(x, y)); c = aces(c); pm2->WritePixel(x, y, c); } } pm2 = pm2->Convert(TEXTURE_RGBA); pm2->Save("out/" + StripExt(file) + ".jpg"); } return 0; } Quote My job is to make tools you love, with the features you want, and performance you can't live without.
Josh Posted Thursday at 06:09 PM Author Posted Thursday at 06:09 PM And you actually want to perform the tone mapping BEFORE converting from linear to sRGB: #include "UltraEngine.h" #include <vector> #include <cmath> #define M_PI 3.14159265358979323846 using namespace Leadwerks; Vec4 aces(Vec4 x) { const float a = 2.51; const float b = 0.03; const float c = 2.43; const float d = 0.59; const float e = 0.14; x.x = Clamp((x.x * (a * x.x + b)) / (x.x * (c * x.x + d) + e), 0.0f, 1.0f); x.y = Clamp((x.y * (a * x.y + b)) / (x.y * (c * x.y + d) + e), 0.0f, 1.0f); x.z = Clamp((x.z * (a * x.z + b)) / (x.z * (c * x.z + d) + e), 0.0f, 1.0f); return x; } // Function to generate a 1D Gaussian kernel std::vector<float> Generate1DGaussianKernel(int radius, float sigma) { std::vector<float> kernel(2 * radius + 1); float sum = 0.0f; for (int i = -radius; i <= radius; ++i) { float value = exp(-(i * i) / (2 * sigma * sigma)) / (sqrt(2 * M_PI) * sigma); kernel[i + radius] = value; sum += value; } // Normalize the kernel for (int i = 0; i < kernel.size(); ++i) { kernel[i] /= sum; } return kernel; } // Function to apply a 1D Gaussian blur horizontally void ApplyHorizontalBlur(const std::shared_ptr<Pixmap>& pm, const std::vector<float>& kernel) { int radius = kernel.size() / 2; auto temp = pm->Copy()->As<Pixmap>(); // Temporary copy for reading original values //if (temp->format == TEXTURE_RGBA16) temp = temp->Convert(TEXTURE_RGBA32); int hh = pm->size.y / 2; for (int y = hh; y < pm->size.y; ++y) { //Print(y); for (int x = 0; x < pm->size.x; ++x) { Vec4 color(0, 0, 0, 0); for (int k = -radius; k <= radius; ++k) { int nx = std::max(0, std::min(x + k, pm->size.x - 1)); // Clamp x to image boundaries color += temp->Sample(iVec2(nx, y)) * kernel[k + radius]; } pm->WritePixel(x, y, color); } } } // Function to apply a 1D Gaussian blur vertically void ApplyVerticalBlur(const std::shared_ptr<Pixmap>& pm, const std::vector<float>& kernel, const float lightness) { int radius = kernel.size() / 2; auto temp = pm->Copy()->As<Pixmap>(); // Temporary copy for reading original values //if (temp->format == TEXTURE_RGBA16) temp = temp->Convert(TEXTURE_RGBA32); int hh = pm->size.y / 2; for (int y = hh; y < pm->size.y; ++y) { //Print(y); for (int x = 0; x < pm->size.x; ++x) { Vec4 color(0, 0, 0, 0); for (int k = -radius; k <= radius; ++k) { int ny = std::max(0, std::min(y + k, pm->size.y - 1)); // Clamp y to image boundaries color += temp->Sample(iVec2(x, ny)) * kernel[k + radius]; } color.r *= lightness; color.g *= lightness; color.b *= lightness; pm->WritePixel(x, y, color); } } } int main(int argc, const char* argv[]) { CreateDir(AppDir() + "hdris"); ChangeDir(AppDir() + "/hdris"); //Download all files String s = FetchUrl("https://ambientcg.com/api/v2/full_json?type=hdri&limit=1000&technique=hdri-bracketed-panorama-horizon-clearing&include=downloadData"); auto t = ParseTable(s); for (auto& r : t["foundAssets"]) { for (auto& d : r.second["downloadFolders"]["default"]["downloadFiletypeCategories"]["exr"]["downloads"]) { if (std::string(d.second["attribute"]) == "8K-HDR") { String url = std::string(d.second["downloadLink"]); String path = std::string(d.second["fileName"]); path = path.Replace("_8K-HDR", ""); if (FileType(path) == 0) DownloadFile(url, path); break; } } } auto dir = LoadDir("."); for (auto file : dir) { if (ExtractExt(file).Lower() != "exr") continue; if (FileType("out/" + file) == 0) { auto pm = LoadPixmap(file); if (not pm) return 0; //if (pm->format == TEXTURE_RGBA16) Print("RGBA half-float format"); // Mirror the upper half to the lower half Vec4 color; int x, y; int hh = pm->size.y / 2; for (y = 0; y < hh; ++y) { for (x = 0; x < pm->size.x; ++x) { color = pm->Sample(iVec2(x, hh - y)); pm->WritePixel(x, hh + y, color); } } auto start = Millisecs(); // Generate a 1D Gaussian kernel int radius = 64; // Adjust the radius as needed float sigma = 20.0f; // Adjust the sigma as needed auto kernel = Generate1DGaussianKernel(radius, sigma); float lightness = 0.65f; // Apply horizontal blur Print("-------------------------------------------------------"); Print("Blur X"); Print("-------------------------------------------------------"); ApplyHorizontalBlur(pm, kernel); // Apply vertical blur Print("-------------------------------------------------------"); Print("Blur Y"); Print("-------------------------------------------------------"); ApplyVerticalBlur(pm, kernel, lightness); Print("Time (s): " + String((Millisecs() - start) / 1000.0)); CreateDir("out"); // Save the output image pm->Save("out/" + file); } // Save preview auto pm = LoadPixmap("out/" + file); auto pm2 = CreatePixmap(2048, 512, pm->format); pm->Blit(0, 0, pm->size.x, pm->size.y / 2, pm2, 0, 0, pm2->size.x, pm2->size.y); // Apply tone mapping or many skyboxes will be much too bright Vec4 c; for (int x = 0; x < pm2->size.x; ++x) { for (int y = 0; y < pm2->size.y; ++y) { c = pm2->Sample(iVec2(x, y)); c = aces(c); pm2->WritePixel(x, y, c); } } pm2 = pm2->LinearTosRgb(); pm2 = pm2->Convert(TEXTURE_RGBA); pm2->Save("out/" + StripExt(file) + ".jpg"); } return 0; } Quote My job is to make tools you love, with the features you want, and performance you can't live without.
Recommended Posts
Join the conversation
You can post now and register later. If you have an account, sign in now to post with your account.
Note: Your post will require moderator approval before it will be visible.