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;