/**
* @file main.cpp
* @author Supakorn "Jamie" Rassameemasmuang <
[email protected]>
* Program for loading image and writing the irradiated image.
*/
#include "common.h"
#include <fstream>
#ifdef _WIN32
// use vcpkg getopt package for this
#undef _UNICODE
#include <getopt.h>
#include <Windows.h>
#else
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#endif
#include "kernel.h"
#include "ReflectanceMapper.cuh"
#include "EXRFiles.h"
size_t const MIN_WIDTH = 2;
size_t const MIN_HEIGHT = 2;
struct Args
{
char mode = 'a';
bool webgl = false;
char const* file_in = nullptr;
char const* directory = nullptr;
size_t count;
bool validate()
{
if (mode == 0)
return false;
if ((mode == 'a' || mode == 'b' || mode == 'i' || mode == 'r') && !file_in)
{
return false;
}
return true;
}
};
void usage()
{
std::cerr << "reflect [-a|-i|-r|-b] inputEXRFile [-d outputDirectory]" <<
std::endl;
std::cerr << "Options: " << std::endl;
std::cerr << "-h\t\t help" << std::endl;
std::cerr << "-d\t\t output directory" << std::endl;
std::cerr << "-a\t\t generate irradiance and reflectance images (default)"
<< std::endl;
std::cerr << "-i\t\t generate irradiance image diffuse.exr" << std::endl;
std::cerr << "-r\t\t generate reflectance images reflN.exr" << std::endl;
std::cerr << "-b\t\t generate brdf image refl.exr (independent of image)"
<< std::endl;
}
Args parseArgs(int argc, char* argv[])
{
Args arg;
int c;
while ((c = getopt(argc, argv, "abd:hir")) != -1)
{
switch (c)
{
case 'a':
arg.mode = 'a';
break;
case 'i':
arg.mode = 'i';
break;
case 'r':
arg.mode = 'r';
break;
case 'b':
arg.mode = 'b';
break;
/*
case 'c':
{
std::stringstream ss;
ss << optarg;
ss >> arg.count;
}
break;
*/
case 'd':
arg.directory = optarg;
break;
case 'h':
usage();
exit(0);
break;
default:
usage();
exit(1);
}
}
arg.file_in = argv[optind];
if (!arg.validate())
{
usage();
exit(1);
}
return arg;
}
struct image_t
{
float4* im;
int width, height;
int sz() const { return width * height; }
image_t(float4* im, int width, int height) :
im(im), width(std::move(width)), height(std::move(height)) {}
};
void irradiate_im(image_t& im, std::string const& prefix)
{
std::vector<float3> out_proc(im.sz());
std::stringstream out_name;
out_name << prefix;
std::cout << "Irradiating image..." << std::endl;
irradiate_ker(im.im, out_proc.data(), im.width, im.height);
out_name << "diffuse.exr";
std::string out_name_str(std::move(out_name).str());
OEXRFile ox(out_proc, im.width, im.height);
std::cout << "copying data back" << std::endl;
std::cout << "writing to: " << out_name_str << std::endl;
ox.write(out_name_str);
}
std::string generate_refl_file(std::string const& prefix, int const& i, std::string const suffix="")
{
std::stringstream out_name;
out_name << prefix << "refl" << i << suffix << ".exr";
return out_name.str();
}
void map_refl_im(image_t& im, std::string const& prefix, float const& step, int const& i, std::pair<size_t, size_t> const& outSize, bool halve=false)
{
float roughness = step * i;
auto [outWidth, outHeight] = outSize;
std::vector<float3> out_proc(outWidth * outHeight);
std::string out_name_str = generate_refl_file(prefix, i,
halve ? "w" : "");
std::cout << "Mapping reflectance map..." << std::endl;
map_reflectance_ker(im.im, out_proc.data(), im.width, im.height, roughness, outWidth, outHeight);
OEXRFile ox(out_proc, outWidth, outHeight);
std::cout << "copying data back" << std::endl;
std::cout << "writing to: " << out_name_str << std::endl;
ox.write(out_name_str);
}
std::string const INVALID_FILE_ATTRIB = "Intermediate directories do not exist";
void make_dir(std::string const& directory)
{
// different implementation for windows vs linux
#ifdef _WIN32
DWORD ret = CreateDirectoryA(directory.c_str(), nullptr);
if (ret == 0 && GetLastError() != ERROR_ALREADY_EXISTS)
{
std::cerr << INVALID_FILE_ATTRIB << std::endl;
exit(1);
}
#else
struct stat st;
if (stat(directory.c_str(), &st) == -1)
{
mkdir(directory.c_str(), 0755);
}
#endif
}
void copy_file(std::string const& in, std::string const& out)
{
std::ifstream ifs(in, std::ios::binary);
std::ofstream ofs(out, std::ios::binary);
ofs << ifs.rdbuf();
}
void generate_brdf_refl(
int res, std::string const& outdir, std::string const& outname="refl.exr")
{
std::vector<float2> out_proc(res * res);
std::string finalName = outdir + "/" + outname;
std::cout << "generating Fresnel/Roughness/cos_v data" << std::endl;
std::cout << "writing to " << finalName << std::endl;
generate_brdf_integrate_lut_ker(res, res, out_proc.data());
OEXRFile ox(out_proc, res, res);
ox.write(finalName);
}
inline float length(float3 v)
{
return sqrt(v.x*v.x+v.y*v.y+v.z*v.z);
}
int main(int argc, char* argv[])
{
Args args = parseArgs(argc, argv);
std::vector<float4> im_proc;
int width = 0;
int height = 0;
std::cout << "Loading file " << args.file_in << std::endl;
EXRFile im(args.file_in);
width = im.getWidth();
height = im.getHeight();
std::vector<float3> out_proc;
std::cout << "Image dimensions: " << width << "x" << height << std::endl;
for (int i = 0; i < height; ++i)
{
for (int j = 0; j < width; ++j)
{
// index is i*height+j <-> (i,j)
float3 frag3=im.getPixel3(j, i);
// Clamp oversaturated values.
float norm=0.02*length(frag3);
if(norm > 1.0) {
frag3.x /= norm;
frag3.y /= norm;
frag3.z /= norm;
}
out_proc.emplace_back(frag3);
im_proc.emplace_back(make_float4(frag3.x,frag3.y,frag3.z,1.0f));
}
// std::cout << "pushed row " << i << " into array" << std::endl;
}
std::cout << "finished converting to float3" << std::endl;
std::stringstream outss;
if (args.directory)
{
make_dir(args.directory);
}
else
{
args.directory = ".";
}
outss << args.directory << "/";
if(args.mode != 'b') {
std::ofstream readme(outss.str()+"README");
readme << "The image files in this directory were generated from " << args.file_in << std::endl;
readme.close();
}
std::string outprefix(outss.str());
image_t imt(im_proc.data(), width, height);
if (args.mode == 'b')
{
generate_brdf_refl(200, ".");
}
if (args.mode == 'i' || args.mode == 'a')
{
irradiate_im(imt, outprefix);
}
if (args.mode == 'r' || args.mode == 'a')
{
OEXRFile ox(out_proc, width, height);
std::string out_name_str = generate_refl_file(outprefix, 0);
std::cout << "writing to: " << out_name_str << std::endl;
ox.write(out_name_str);
for(size_t halve=0; halve < 2; ++halve) {
size_t count=halve ? 8 : 10;
float step = 1.0f / count;
unsigned int out_width = width;
unsigned int out_height = height;
for (size_t i = 1; i <= count; ++i)
{
if (halve && out_width >= MIN_WIDTH && out_height >= MIN_HEIGHT)
{
out_width = out_width >> 1;
out_height = out_height >> 1; // halve
}
map_refl_im(imt, outprefix, step, i, std::pair<size_t,size_t>(out_width, out_height), halve);
}
}
}
}