/** * @file main.cpp * @author Supakorn "Jamie" Rassameemasmuang * Program for loading image and writing the irradiated image. */ #include "common.h" #include #ifdef _WIN32 // use vcpkg getopt package for this #undef _UNICODE #include #include #else #include #include #include #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 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 const& outSize, bool halve=false) { float roughness = step * i; auto [outWidth, outHeight] = outSize; std::vector 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 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 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 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(out_width, out_height), halve); } } } }