Jump to content

Recommended Posts

Posted

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;
}

 

  • Like 2

My job is to make tools you love, with the features you want, and performance you can't live without.

Posted

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.

screenshot188.thumb.jpg.e038f80b7e1b84201ad35169b426c717.jpg

My job is to make tools you love, with the features you want, and performance you can't live without.

Posted

This version will darken the lower half a bit, so your downwards-facing reflections are not as bright. It looks more natural:

Untitled.thumb.jpg.d25c51e5c1d8683df318c84b9b09fb23.jpg

#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;
}

 

My job is to make tools you love, with the features you want, and performance you can't live without.

Posted

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;
}

 

My job is to make tools you love, with the features you want, and performance you can't live without.

Posted

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;
}

 

My job is to make tools you love, with the features you want, and performance you can't live without.

Posted

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;
}

 

My job is to make tools you love, with the features you want, and performance you can't live without.

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.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

×
×
  • Create New...