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

Let's build cool stuff and have fun. :)

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

Let's build cool stuff and have fun. :)

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

 

Let's build cool stuff and have fun. :)

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

 

Let's build cool stuff and have fun. :)

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

 

Let's build cool stuff and have fun. :)

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

 

Let's build cool stuff and have fun. :)

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...