diff --git a/projects/CUDA/zpc b/projects/CUDA/zpc index 2a59943ef8..efd865f356 160000 --- a/projects/CUDA/zpc +++ b/projects/CUDA/zpc @@ -1 +1 @@ -Subproject commit 2a59943ef879d126a2b34285b9f19e70094ed7e4 +Subproject commit efd865f35618a56f03277a3cc65bb848f7664b4a diff --git a/ui/zenoedit/dialog/zrecorddlg.cpp b/ui/zenoedit/dialog/zrecorddlg.cpp index 3be4f4bf56..a8c62535e7 100644 --- a/ui/zenoedit/dialog/zrecorddlg.cpp +++ b/ui/zenoedit/dialog/zrecorddlg.cpp @@ -62,6 +62,7 @@ bool ZRecordVideoDlg::getInfo(VideoRecInfo &info) { auto &ud = zeno::getSession().userData(); ud.set2("output_aov", m_ui->cbAOV->checkState() == Qt::Checked); + ud.set2("output_exr", m_ui->cbExportEXR->checkState() == Qt::Checked); auto &path = info.record_path; auto &fn = info.videoname; info.fps = m_ui->fps->text().toInt(); @@ -72,6 +73,7 @@ bool ZRecordVideoDlg::getInfo(VideoRecInfo &info) info.res[1] = m_ui->lineHeight->text().toFloat(); path = m_ui->linePath->text(); info.bExportVideo = m_ui->cbExportVideo->checkState() == Qt::Checked; + info.bExportEXR = m_ui->cbExportEXR->checkState() == Qt::Checked; info.needDenoise = m_ui->cbNeedDenoise->checkState() == Qt::Checked; info.bAutoRemoveCache = m_ui->cbRemoveAfterRender->checkState() == Qt::Checked; if (path.isEmpty()) diff --git a/ui/zenoedit/dialog/zrecorddlg.ui b/ui/zenoedit/dialog/zrecorddlg.ui index 0880cf8a83..cea81a9f7f 100644 --- a/ui/zenoedit/dialog/zrecorddlg.ui +++ b/ui/zenoedit/dialog/zrecorddlg.ui @@ -130,6 +130,16 @@ + + + + export exr + + + false + + + diff --git a/ui/zenoedit/recordmain.cpp b/ui/zenoedit/recordmain.cpp index 7264bd6d0a..f9b6a3d103 100644 --- a/ui/zenoedit/recordmain.cpp +++ b/ui/zenoedit/recordmain.cpp @@ -77,6 +77,7 @@ int record_main(const QCoreApplication& app) {"optix", "optix", "optix mode"}, {"video", "video", "export video"}, {"aov", "aov", "aov"}, + {"exr", "exr", "exr"}, {"needDenoise", "needDenoise", "needDenoise"}, {"videoname", "videoname", "export video's name"}, {"subzsg", "subgraphzsg", "subgraph zsg file path"}, @@ -147,8 +148,10 @@ int record_main(const QCoreApplication& app) param.isExportVideo = cmdParser.isSet("video") ? cmdParser.value("video").toInt() : 0; param.needDenoise = cmdParser.isSet("needDenoise") ? cmdParser.value("needDenoise").toInt() : 0; int enableAOV = cmdParser.isSet("aov") ? cmdParser.value("aov").toInt() : 0; + param.export_exr = cmdParser.isSet("exr") && cmdParser.value("exr").toInt() != 0; auto &ud = zeno::getSession().userData(); ud.set2("output_aov", enableAOV != 0); + ud.set2("output_exr", param.export_exr); param.videoName = cmdParser.isSet("videoname") ? cmdParser.value("videoname") : "output.mp4"; param.subZsg = cmdParser.isSet("subzsg") ? cmdParser.value("subzsg") : ""; #else diff --git a/ui/zenoedit/viewport/recordvideomgr.cpp b/ui/zenoedit/viewport/recordvideomgr.cpp index 4e1900a2fb..435d129ec9 100644 --- a/ui/zenoedit/viewport/recordvideomgr.cpp +++ b/ui/zenoedit/viewport/recordvideomgr.cpp @@ -94,6 +94,10 @@ void RecordVideoMgr::setRecordInfo(const VideoRecInfo& recInfo) void RecordVideoMgr::endRecToExportVideo() { + if (m_recordInfo.bExportEXR) { + emit recordFinished(m_recordInfo.record_path); + return; + } // denoising if (m_recordInfo.needDenoise) { QString dir_path = m_recordInfo.record_path + "/P/"; diff --git a/ui/zenoedit/viewport/recordvideomgr.h b/ui/zenoedit/viewport/recordvideomgr.h index 3ee330872f..d25bc08719 100644 --- a/ui/zenoedit/viewport/recordvideomgr.h +++ b/ui/zenoedit/viewport/recordvideomgr.h @@ -20,6 +20,7 @@ struct VideoRecInfo bool exitWhenRecordFinish = false; bool bRecordByCommandLine = false; bool bAutoRemoveCache = false; + bool bExportEXR = false; }; Q_DECLARE_METATYPE(VideoRecInfo); diff --git a/ui/zenoedit/zenomainwindow.cpp b/ui/zenoedit/zenomainwindow.cpp index 479e7bac89..c7673bf9bb 100644 --- a/ui/zenoedit/zenomainwindow.cpp +++ b/ui/zenoedit/zenomainwindow.cpp @@ -827,6 +827,7 @@ void ZenoMainWindow::optixRunRender(const ZENO_RECORD_RUN_INITPARAM& param, LAUN recInfo.videoname = param.videoName; recInfo.bExportVideo = param.isExportVideo; recInfo.needDenoise = param.needDenoise; + recInfo.bExportEXR = param.export_exr; recInfo.exitWhenRecordFinish = param.exitWhenRecordFinish; if (!param.sPixel.isEmpty()) @@ -918,6 +919,7 @@ void ZenoMainWindow::solidRunRender(const ZENO_RECORD_RUN_INITPARAM& param) recInfo.videoname = param.videoName; recInfo.bExportVideo = param.isExportVideo; recInfo.needDenoise = param.needDenoise; + recInfo.bExportEXR = param.export_exr; recInfo.exitWhenRecordFinish = param.exitWhenRecordFinish; recInfo.bRecordByCommandLine = true; diff --git a/ui/zenoedit/zenomainwindow.h b/ui/zenoedit/zenomainwindow.h index bdcd36ff4c..4a1b4f0354 100644 --- a/ui/zenoedit/zenomainwindow.h +++ b/ui/zenoedit/zenomainwindow.h @@ -18,6 +18,7 @@ struct ZENO_RECORD_RUN_INITPARAM { bool bOptix = false; //is optix view. bool isExportVideo = false; bool needDenoise = false; + bool export_exr = false; int iFrame = 0; int iSFrame = 0; int iSample = 0; diff --git a/zeno/src/nodes/ProcedrualSkyNode.cpp b/zeno/src/nodes/ProcedrualSkyNode.cpp index 1126823f72..4f4d84a34d 100644 --- a/zeno/src/nodes/ProcedrualSkyNode.cpp +++ b/zeno/src/nodes/ProcedrualSkyNode.cpp @@ -43,9 +43,9 @@ ZENDEFNODE(ProceduralSky, { struct HDRSky : INode { virtual void apply() override { auto prim = std::make_shared(); - auto path = get_input2("path"); - if (path.empty()) { - throw std::runtime_error("need hdr tex path"); + std::string path = ""; + if (has_input2("path")) { + path = get_input2("path"); } prim->userData().set2("isRealTimeObject", std::move(1)); prim->userData().set2("HDRSky", std::move(path)); diff --git a/zeno/src/nodes/prim/UVProjectFromPlane.cpp b/zeno/src/nodes/prim/UVProjectFromPlane.cpp index a6b77d0a95..f774b35151 100644 --- a/zeno/src/nodes/prim/UVProjectFromPlane.cpp +++ b/zeno/src/nodes/prim/UVProjectFromPlane.cpp @@ -517,7 +517,6 @@ struct WriteImageFile : INode { } else if(type == "exr"){ std::vector data2(w * h * n); - constexpr float gamma = 2.2f; for (int i = 0; i < w * h; i++) { data2[n * i + 0] = image->verts[i][0]; data2[n * i + 1] = image->verts[i][1]; @@ -532,39 +531,13 @@ struct WriteImageFile : INode { } } - // Create EXR header - EXRHeader header; - InitEXRHeader(&header); - - // Set image width, height, and number of channels - header.num_channels = n; - - // Create EXR image - EXRImage exrImage; - InitEXRImage(&exrImage); - - // Set image data - exrImage.num_channels = n; - exrImage.width = w; - exrImage.height = h; - exrImage.images = reinterpret_cast(&data2[0]); - - // Set image channel names (optional) - std::vector channelNames = {"R", "G", "B", "A"}; - header.channels = new EXRChannelInfo[n]; - for (int i = 0; i < n; ++i) { - strncpy(header.channels[i].name, channelNames[i].c_str(), 255); - header.channels[i].name[strlen(channelNames[i].c_str())] = '\0'; - header.channels[i].pixel_type = TINYEXR_PIXELTYPE_FLOAT; - } - const char* err; path += ".exr"; std::string native_path = std::filesystem::u8path(path).string(); - int ret = SaveEXR(data2.data(),w,h,n,0,native_path.c_str(),&err); + int ret = SaveEXR(data2.data(),w,h,n,1,native_path.c_str(),&err); if (ret != TINYEXR_SUCCESS) { - zeno::log_error("Error saving EXR file: %s\n", err); + zeno::log_error("Error saving EXR file: {}\n", err); FreeEXRErrorMessage(err); // free memory allocated by the library return; } diff --git a/zenovis/src/optx/RenderEngineOptx.cpp b/zenovis/src/optx/RenderEngineOptx.cpp index 8111bcd06d..cb0c4c373f 100644 --- a/zenovis/src/optx/RenderEngineOptx.cpp +++ b/zenovis/src/optx/RenderEngineOptx.cpp @@ -556,8 +556,10 @@ struct GraphicsManager { zeno::vec3f evnTex3DRotation = prim_in->userData().get2("evnTex3DRotation"); float evnTexStrength = prim_in->userData().get2("evnTexStrength"); bool enableHdr = prim_in->userData().get2("enable"); - OptixUtil::sky_tex = path; - OptixUtil::addTexture(path); + if (!path.empty()) { + OptixUtil::sky_tex = path; + OptixUtil::addTexture(path); + } xinxinoptix::update_hdr_sky(evnTexRotation, evnTex3DRotation, evnTexStrength); xinxinoptix::using_hdr_sky(enableHdr); } diff --git a/zenovis/xinxinoptix/PTKernel.cu b/zenovis/xinxinoptix/PTKernel.cu index 43bfa10f06..8fe81a2ddf 100644 --- a/zenovis/xinxinoptix/PTKernel.cu +++ b/zenovis/xinxinoptix/PTKernel.cu @@ -299,10 +299,11 @@ extern "C" __global__ void __raygen__rg() float3 out_color_t = accum_color_t; float3 out_color_b = accum_color_b; params.frame_buffer[ image_index ] = make_color ( out_color ); - params.frame_buffer_D[ image_index ] = make_color ( out_color_d ); - params.frame_buffer_S[ image_index ] = make_color ( out_color_s ); - params.frame_buffer_T[ image_index ] = make_color ( out_color_t ); - params.frame_buffer_B[ image_index ] = make_color ( out_color_b ); + params.frame_buffer_C[ image_index ] = accum_color; + params.frame_buffer_D[ image_index ] = accum_color_d; + params.frame_buffer_S[ image_index ] = accum_color_s; + params.frame_buffer_T[ image_index ] = accum_color_t; + params.frame_buffer_B[ image_index ] = accum_color_b; if (params.denoise) { params.albedo_buffer[ image_index ] = tmp_albedo; diff --git a/zenovis/xinxinoptix/optixPathTracer.cpp b/zenovis/xinxinoptix/optixPathTracer.cpp index 5e73d7b034..1efdeaa2ae 100644 --- a/zenovis/xinxinoptix/optixPathTracer.cpp +++ b/zenovis/xinxinoptix/optixPathTracer.cpp @@ -64,6 +64,7 @@ #include #include "tinyexr.h" +#include "zeno/utils/image_proc.h" #ifndef M_PI #define M_PI 3.14159265358979323846 @@ -154,10 +155,11 @@ typedef Record HitGroupRecord; //}; std::optional> output_buffer_o; -std::optional> output_buffer_diffuse; -std::optional> output_buffer_specular; -std::optional> output_buffer_transmit; -std::optional> output_buffer_background; +std::optional> output_buffer_color; +std::optional> output_buffer_diffuse; +std::optional> output_buffer_specular; +std::optional> output_buffer_transmit; +std::optional> output_buffer_background; using Vertex = float4; std::vector g_lightMesh; std::vector g_lightColor; @@ -427,6 +429,7 @@ static void handleResize( sutil::CUDAOutputBuffer& output_buffer, Params resize_dirty = false; output_buffer.resize( params.width, params.height ); + (*output_buffer_color).resize( params.width, params.height ); (*output_buffer_diffuse).resize( params.width, params.height ); (*output_buffer_specular).resize( params.width, params.height ); (*output_buffer_transmit).resize( params.width, params.height ); @@ -492,6 +495,7 @@ static void launchSubframe( sutil::CUDAOutputBuffer& output_buffer, Path // Launch uchar4* result_buffer_data = output_buffer.map(); state.params.frame_buffer = result_buffer_data; + state.params.frame_buffer_C = (*output_buffer_color ).map(); state.params.frame_buffer_D = (*output_buffer_diffuse ).map(); state.params.frame_buffer_S = (*output_buffer_specular ).map(); state.params.frame_buffer_T = (*output_buffer_transmit ).map(); @@ -528,6 +532,7 @@ static void launchSubframe( sutil::CUDAOutputBuffer& output_buffer, Path } } output_buffer.unmap(); + (*output_buffer_color ).unmap(); (*output_buffer_diffuse ).unmap(); (*output_buffer_specular ).unmap(); (*output_buffer_transmit ).unmap(); @@ -1608,6 +1613,14 @@ void optixinit( int argc, char* argv[] ) ); output_buffer_o->setStream( 0 ); } + if (!output_buffer_color) { + output_buffer_color.emplace( + output_buffer_type, + state.params.width, + state.params.height + ); + output_buffer_color->setStream( 0 ); + } if (!output_buffer_diffuse) { output_buffer_diffuse.emplace( output_buffer_type, @@ -3402,9 +3415,24 @@ void *optixgetimg_extra(std::string name) { else if (name == "background") { return output_buffer_background->getHostPointer(); } + else if (name == "color") { + return output_buffer_color->getHostPointer(); + } throw std::runtime_error("invalid optixgetimg_extra name: " + name); } - +static void save_exr(float3* ptr, int w, int h, std::string path) { + std::vector data(w * h); + std::copy_n(ptr, w * h, data.data()); + zeno::image_flip_vertical(data.data(), w, h); + const char *err = nullptr; + int ret = SaveEXR((float *) data.data(), w, h, 3, 1, path.c_str(), &err); + if (ret != TINYEXR_SUCCESS) { + if (err) { + zeno::log_error("failed to perform SaveEXR to {}: {}", path, err); + FreeEXRErrorMessage(err); + } + } +} void optixrender(int fbo, int samples, bool denoise, bool simpleRender) { bool imageRendered = false; samples = zeno::envconfig::getInt("SAMPLES", samples); @@ -3439,29 +3467,34 @@ void optixrender(int fbo, int samples, bool denoise, bool simpleRender) { auto w = (*output_buffer_o).width(); auto h = (*output_buffer_o).height(); stbi_flip_vertically_on_write(true); - stbi_write_jpg(path.c_str(), w, h, 4, p, 100); + if (zeno::getSession().userData().get2("output_exr", true)) { + auto exr_path = path.substr(0, path.size() - 4) + ".exr"; + save_exr((float3 *)optixgetimg_extra("color"), w, h, exr_path); + } + else { + stbi_write_jpg(path.c_str(), w, h, 4, p, 100); + if (denoise) { + const float* _albedo_buffer = reinterpret_cast(state.albedo_buffer_p.handle); + //SaveEXR(_albedo_buffer, w, h, 4, 0, (path+".albedo.exr").c_str(), nullptr); + auto a_path = path + ".albedo.pfm"; + write_pfm(a_path, w, h, _albedo_buffer); + + const float* _normal_buffer = reinterpret_cast(state.normal_buffer_p.handle); + //SaveEXR(_normal_buffer, w, h, 4, 0, (path+".normal.exr").c_str(), nullptr); + auto n_path = path + ".normal.pfm"; + write_pfm(n_path, w, h, _normal_buffer); + } + } zeno::log_info("optix: saving screenshot {}x{} to {}", w, h, path); ud.erase("optix_image_path"); - if (denoise) { - const float* _albedo_buffer = reinterpret_cast(state.albedo_buffer_p.handle); - //SaveEXR(_albedo_buffer, w, h, 4, 0, (path+".albedo.exr").c_str(), nullptr); - auto a_path = path + ".albedo.pfm"; - write_pfm(a_path, w, h, _albedo_buffer); - - const float* _normal_buffer = reinterpret_cast(state.normal_buffer_p.handle); - //SaveEXR(_normal_buffer, w, h, 4, 0, (path+".normal.exr").c_str(), nullptr); - auto n_path = path + ".normal.pfm"; - write_pfm(n_path, w, h, _normal_buffer); - } - // AOV if (zeno::getSession().userData().get2("output_aov", true)) { path = path.substr(0, path.size() - 4); - stbi_write_png((path + ".diffuse.png").c_str(), w, h, 4 , optixgetimg_extra("diffuse"), 0); - stbi_write_png((path + ".specular.png").c_str(), w, h, 4 , optixgetimg_extra("specular"), 0); - stbi_write_png((path + ".transmit.png").c_str(), w, h, 4 , optixgetimg_extra("transmit"), 0); - stbi_write_png((path + ".background.png").c_str(), w, h, 4 , optixgetimg_extra("background"), 0); + save_exr((float3 *)optixgetimg_extra("diffuse"), w, h, path + ".diffuse.exr"); + save_exr((float3 *)optixgetimg_extra("specular"), w, h, path + ".specular.exr"); + save_exr((float3 *)optixgetimg_extra("transmit"), w, h, path + ".transmit.exr"); + save_exr((float3 *)optixgetimg_extra("background"), w, h, path + ".background.exr"); } imageRendered = true; } diff --git a/zenovis/xinxinoptix/optixPathTracer.h b/zenovis/xinxinoptix/optixPathTracer.h index 8f0d750552..a3e3131c44 100644 --- a/zenovis/xinxinoptix/optixPathTracer.h +++ b/zenovis/xinxinoptix/optixPathTracer.h @@ -57,10 +57,11 @@ struct Params float4* accum_buffer_T; float4* accum_buffer_B; uchar4* frame_buffer; - uchar4* frame_buffer_D; - uchar4* frame_buffer_S; - uchar4* frame_buffer_T; - uchar4* frame_buffer_B; + float3* frame_buffer_C; + float3* frame_buffer_D; + float3* frame_buffer_S; + float3* frame_buffer_T; + float3* frame_buffer_B; float3* albedo_buffer; float3* normal_buffer;