Files
WickedEngine/WickedEngine/wiRenderPath3D_PathTracing.cpp
T
2021-12-04 16:48:12 +01:00

413 lines
11 KiB
C++

#include "wiRenderPath3D_PathTracing.h"
#include "wiRenderer.h"
#include "wiImage.h"
#include "wiHelper.h"
#include "wiTextureHelper.h"
#include "wiSprite.h"
#include "wiProfiler.h"
#include "wiScene.h"
#include "wiBacklog.h"
using namespace wi::graphics;
using namespace wi::scene;
namespace wi
{
#if __has_include("OpenImageDenoise/oidn.hpp")
#define OPEN_IMAGE_DENOISE
#include "OpenImageDenoise/oidn.hpp"
#pragma comment(lib,"OpenImageDenoise.lib")
#pragma comment(lib,"tbb.lib")
// Also provide OpenImageDenoise.dll and tbb.dll near the exe!
bool DenoiserCallback(void* userPtr, double n)
{
auto renderpath = (RenderPath3D_PathTracing*)userPtr;
if (renderpath->getProgress() < 1)
{
renderpath->denoiserProgress = 0;
return false;
}
renderpath->denoiserProgress = (float)n;
return true;
}
bool RenderPath3D_PathTracing::isDenoiserAvailable() const { return true; }
#else
bool RenderPath3D_PathTracing::isDenoiserAvailable() const { return false; }
#endif
void RenderPath3D_PathTracing::ResizeBuffers()
{
RenderPath2D::ResizeBuffers(); // we don't need to use any buffers from RenderPath3D, so skip those
GraphicsDevice* device = wi::graphics::GetDevice();
XMUINT2 internalResolution = GetInternalResolution();
{
TextureDesc desc;
desc.bind_flags = BindFlag::UNORDERED_ACCESS | BindFlag::SHADER_RESOURCE | BindFlag::RENDER_TARGET;
desc.format = Format::R32G32B32A32_FLOAT;
desc.width = internalResolution.x;
desc.height = internalResolution.y;
device->CreateTexture(&desc, nullptr, &traceResult);
device->SetName(&traceResult, "traceResult");
#ifdef OPEN_IMAGE_DENOISE
desc.bind_flags = BindFlag::UNORDERED_ACCESS;
desc.layout = ResourceState::UNORDERED_ACCESS;
device->CreateTexture(&desc, nullptr, &denoiserAlbedo);
device->SetName(&denoiserAlbedo, "denoiserAlbedo");
device->CreateTexture(&desc, nullptr, &denoiserNormal);
device->SetName(&denoiserNormal, "denoiserNormal");
#endif // OPEN_IMAGE_DENOISE
}
{
TextureDesc desc;
desc.bind_flags = BindFlag::SHADER_RESOURCE | BindFlag::UNORDERED_ACCESS;
desc.format = Format::R11G11B10_FLOAT;
desc.width = internalResolution.x;
desc.height = internalResolution.y;
device->CreateTexture(&desc, nullptr, &rtPostprocess);
device->SetName(&rtPostprocess, "rtPostprocess");
desc.width /= 4;
desc.height /= 4;
desc.bind_flags = BindFlag::UNORDERED_ACCESS | BindFlag::SHADER_RESOURCE;
device->CreateTexture(&desc, nullptr, &rtGUIBlurredBackground[0]);
device->SetName(&rtGUIBlurredBackground[0], "rtGUIBlurredBackground[0]");
desc.width /= 4;
desc.height /= 4;
device->CreateTexture(&desc, nullptr, &rtGUIBlurredBackground[1]);
device->SetName(&rtGUIBlurredBackground[1], "rtGUIBlurredBackground[1]");
device->CreateTexture(&desc, nullptr, &rtGUIBlurredBackground[2]);
device->SetName(&rtGUIBlurredBackground[2], "rtGUIBlurredBackground[2]");
}
{
RenderPassDesc desc;
desc.attachments.push_back(RenderPassAttachment::RenderTarget(&traceResult, RenderPassAttachment::LoadOp::CLEAR));
device->CreateRenderPass(&desc, &renderpass_debugbvh);
}
wi::renderer::CreateLuminanceResources(luminanceResources, internalResolution);
wi::renderer::CreateBloomResources(bloomResources, internalResolution);
// also reset accumulation buffer state:
sam = -1;
}
void RenderPath3D_PathTracing::Update(float dt)
{
setOcclusionCullingEnabled(false);
if (camera->IsDirty())
{
camera->SetDirty(false);
sam = -1;
}
else
{
for (size_t i = 0; i < scene->transforms.GetCount(); ++i)
{
const TransformComponent& transform = scene->transforms[i];
if (transform.IsDirty())
{
sam = -1;
break;
}
}
if (sam >= 0)
{
for (size_t i = 0; i < scene->materials.GetCount(); ++i)
{
const MaterialComponent& material = scene->materials[i];
if (material.IsDirty())
{
sam = -1;
break;
}
}
}
}
sam++;
if (sam > target)
{
sam = target;
}
if (target < sam)
{
resetProgress();
}
scene->SetAccelerationStructureUpdateRequested(sam == 0);
setSceneUpdateEnabled(sam == 0);
RenderPath3D::Update(dt);
#ifdef OPEN_IMAGE_DENOISE
if (sam == target)
{
if (!denoiserResult.IsValid() && !wi::jobsystem::IsBusy(denoiserContext))
{
//wi::helper::saveTextureToFile(denoiserAlbedo, "C:/PROJECTS/WickedEngine/Editor/_albedo.png");
//wi::helper::saveTextureToFile(denoiserNormal, "C:/PROJECTS/WickedEngine/Editor/_normal.png");
texturedata_src.clear();
texturedata_dst.clear();
texturedata_albedo.clear();
texturedata_normal.clear();
if (wi::helper::saveTextureToMemory(traceResult, texturedata_src))
{
wi::helper::saveTextureToMemory(denoiserAlbedo, texturedata_albedo);
wi::helper::saveTextureToMemory(denoiserNormal, texturedata_normal);
texturedata_dst.resize(texturedata_src.size());
wi::jobsystem::Execute(denoiserContext, [&](wi::jobsystem::JobArgs args) {
size_t width = (size_t)traceResult.desc.width;
size_t height = (size_t)traceResult.desc.height;
{
// https://github.com/OpenImageDenoise/oidn#c11-api-example
// Create an Intel Open Image Denoise device
static oidn::DeviceRef device = oidn::newDevice();
static bool init = false;
if (!init)
{
device.commit();
init = true;
}
// Create a denoising filter
oidn::FilterRef filter = device.newFilter("RT"); // generic ray tracing filter
filter.setImage("color", texturedata_src.data(), oidn::Format::Float3, width, height, 0, sizeof(XMFLOAT4));
if (!texturedata_albedo.empty())
{
filter.setImage("albedo", texturedata_albedo.data(), oidn::Format::Float3, width, height, 0, sizeof(XMFLOAT4)); // optional
}
if (!texturedata_normal.empty())
{
filter.setImage("normal", texturedata_normal.data(), oidn::Format::Float3, width, height, 0, sizeof(XMFLOAT4)); // optional
}
filter.setImage("output", texturedata_dst.data(), oidn::Format::Float3, width, height, 0, sizeof(XMFLOAT4));
filter.set("hdr", true); // image is HDR
//filter.set("cleanAux", true);
filter.commit();
denoiserProgress = 0;
filter.setProgressMonitorFunction(DenoiserCallback, this);
// Filter the image
filter.execute();
// Check for errors
const char* errorMessage;
auto error = device.getError(errorMessage);
if (error != oidn::Error::None && error != oidn::Error::Cancelled)
{
wi::backlog::post(std::string("[OpenImageDenoise error] ") + errorMessage);
}
}
GraphicsDevice* device = wi::graphics::GetDevice();
TextureDesc desc;
desc.width = (uint32_t)width;
desc.height = (uint32_t)height;
desc.bind_flags = BindFlag::SHADER_RESOURCE;
desc.format = Format::R32G32B32A32_FLOAT;
SubresourceData initdata;
initdata.data_ptr = texturedata_dst.data();
initdata.row_pitch = uint32_t(sizeof(XMFLOAT4) * width);
device->CreateTexture(&desc, &initdata, &denoiserResult);
});
}
}
}
else
{
denoiserResult = Texture();
denoiserProgress = 0;
}
#endif // OPEN_IMAGE_DENOISE
}
void RenderPath3D_PathTracing::Render() const
{
GraphicsDevice* device = wi::graphics::GetDevice();
wi::jobsystem::context ctx;
if (sam < target)
{
// Setup:
CommandList cmd = device->BeginCommandList();
wi::jobsystem::Execute(ctx, [this, cmd](wi::jobsystem::JobArgs args) {
wi::renderer::BindCameraCB(
*camera,
camera_previous,
camera_reflection,
cmd
);
wi::renderer::UpdateRenderData(visibility_main, frameCB, cmd);
wi::renderer::UpdateRenderDataAsync(visibility_main, frameCB, cmd);
if (scene->IsAccelerationStructureUpdateRequested())
{
wi::renderer::UpdateRaytracingAccelerationStructures(*scene, cmd);
}
});
// Main scene:
cmd = device->BeginCommandList();
wi::jobsystem::Execute(ctx, [this, cmd](wi::jobsystem::JobArgs args) {
GraphicsDevice* device = wi::graphics::GetDevice();
wi::renderer::BindCameraCB(
*camera,
*camera,
*camera,
cmd
);
wi::renderer::BindCommonResources(cmd);
if (wi::renderer::GetRaytraceDebugBVHVisualizerEnabled())
{
device->RenderPassBegin(&renderpass_debugbvh, cmd);
Viewport vp;
vp.width = (float)traceResult.GetDesc().width;
vp.height = (float)traceResult.GetDesc().height;
device->BindViewports(1, &vp, cmd);
wi::renderer::RayTraceSceneBVH(*scene, cmd);
device->RenderPassEnd(cmd);
}
else
{
auto range = wi::profiler::BeginRangeGPU("Traced Scene", cmd);
wi::renderer::RayTraceScene(
*scene,
traceResult,
sam,
cmd,
instanceInclusionMask_PathTrace,
denoiserAlbedo.IsValid() ? &denoiserAlbedo : nullptr,
denoiserNormal.IsValid() ? &denoiserNormal : nullptr
);
wi::profiler::EndRange(range); // Traced Scene
}
});
}
// Tonemap etc:
CommandList cmd = device->BeginCommandList();
wi::jobsystem::Execute(ctx, [this, cmd](wi::jobsystem::JobArgs args) {
GraphicsDevice* device = wi::graphics::GetDevice();
wi::renderer::BindCameraCB(
*camera,
*camera,
*camera,
cmd
);
wi::renderer::BindCommonResources(cmd);
Texture srcTex = denoiserResult.IsValid() && !wi::jobsystem::IsBusy(denoiserContext) ? denoiserResult : traceResult;
if (getEyeAdaptionEnabled())
{
wi::renderer::ComputeLuminance(
luminanceResources,
srcTex,
cmd,
getEyeAdaptionRate(),
getEyeAdaptionKey()
);
}
if (getBloomEnabled())
{
wi::renderer::ComputeBloom(
bloomResources,
srcTex,
cmd,
getBloomThreshold(),
getExposure(),
getEyeAdaptionEnabled() ? &luminanceResources.luminance : nullptr
);
}
wi::renderer::Postprocess_Tonemap(
srcTex,
rtPostprocess,
cmd,
getExposure(),
getDitherEnabled(),
getColorGradingEnabled() ? (scene->weather.colorGradingMap.IsValid() ? &scene->weather.colorGradingMap.GetTexture() : nullptr) : nullptr,
nullptr,
getEyeAdaptionEnabled() ? &luminanceResources.luminance : nullptr,
getBloomEnabled() ? &bloomResources.texture_bloom : nullptr,
colorspace
);
lastPostprocessRT = &rtPostprocess;
// GUI Background blurring:
{
auto range = wi::profiler::BeginRangeGPU("GUI Background Blur", cmd);
device->EventBegin("GUI Background Blur", cmd);
wi::renderer::Postprocess_Downsample4x(rtPostprocess, rtGUIBlurredBackground[0], cmd);
wi::renderer::Postprocess_Downsample4x(rtGUIBlurredBackground[0], rtGUIBlurredBackground[2], cmd);
wi::renderer::Postprocess_Blur_Gaussian(rtGUIBlurredBackground[2], rtGUIBlurredBackground[1], rtGUIBlurredBackground[2], cmd, -1, -1, true);
device->EventEnd(cmd);
wi::profiler::EndRange(range);
}
});
RenderPath2D::Render();
wi::jobsystem::Wait(ctx);
}
void RenderPath3D_PathTracing::Compose(CommandList cmd) const
{
GraphicsDevice* device = wi::graphics::GetDevice();
device->EventBegin("RenderPath3D_PathTracing::Compose", cmd);
wi::renderer::BindCommonResources(cmd);
wi::image::Params fx;
fx.enableFullScreen();
fx.blendFlag = wi::enums::BLENDMODE_OPAQUE;
fx.quality = wi::image::QUALITY_LINEAR;
wi::image::Draw(&rtPostprocess, fx, cmd);
device->EventEnd(cmd);
RenderPath2D::Compose(cmd);
}
}