Hi guys, Just as an experiment im adding the nvidia ai denoiser to blender’s code, would really appreciate some help
Ok so I have created all the engine.py,ui.py,and properties.py files needed to add all AI denoiser options into the render settings tab,
the AI denoiser has pre defined states like 1,use only buaety render buffer,2 buety buffer and normal,3 buety,normal,albedo. 4, HDR on/off
Ive added code to blender_session.cpp
scene->film->use_aidenoise = get_boolean(crl, “use_pass_aidenoise”);
scene->film->use_hdr = get_boolean(crl, “ai_hdr”);
scene->film->use_albedo = get_boolean(crl, “ai_albedo”);
scene->film->use_normal = get_boolean(crl, “ai_normal”);
scene->film->ai_denoise_blend = get_float(crl, “ai_denoise_blend”);
Then added to blender_sync.cpp
MAP_PASS(“ai_result”, PASS_AIRESULT);
and
if (get_boolean(crp, "use_pass_aidenoise")) {
b_engine.add_pass("ai_result", 4, "RGBA", b_srlay.name().c_str());
Pass::add(PASS_AIRESULT, passes);
}
if (get_boolean(crp, "ai_normal")) {
b_engine.add_pass("Normal", 3, "XYZ", b_srlay.name().c_str());
Pass::add(PASS_NORMAL, passes);
}
if (get_boolean(crp, "ai_albedo")) {
b_engine.add_pass("DiffCol", 3, "RGB", b_srlay.name().c_str());
Pass::add(PASS_DIFFUSE_COLOR, passes);
Added to kernel_types.h
PASS_AIRESULT,
to the main passes list and the following handles
int use_aidenoise;
float ai_denoise_blend;
int ai_result;
int Normal;
int DiffCol;
Added to film.h
bool use_aidenoise;
bool use_hdr;
bool use_albedo;
bool use_normal;
float ai_denoise_blend;
Added to film.cpp
case PASS_AIRESULT:
pass.components = 4;
pass.exposure = true;
break;
and:
case PASS_AIRESULT:
kfilm->ai_result = kfilm->pass_stride;
break;
then under Film::Film()
use_aidenoise = false;
and paramter:
kfilm->use_aidenoise = use_aidenoise;
incase I need to add some other buffers for stats etc
Now Blender compiles fine everything works as expected, When I activate Ai denoise in the render settings I get the extra ai_result buffer
and is empty with checker background as I guess should because we havnt copied any data to the empty buffer yet. When I check the box for albedo,
normal, The buffers also then correctly appear in the render layer pases as they should.
Now what im woundering is where’s the best place to actualy feed the buffers combined,normal,albedo to the optix ai denoiser.
Blender_session.cpp seems the best place to me, as is already where the render start and finish setup is located as well as all the render/scene/camera change and update
params are and also the combined render pass buffer that ill want to read from to create the optix context denoiser input buffers & also viewport render buffers.
In some example code to use the optix ai denoiser it’s done in the following way:
// Get our pixel data
std::vector<float> beauty_pixels(b_width * b_height * beauty_roi.nchannels());
input_beauty->get_pixels(beauty_roi, OIIO::TypeDesc::FLOAT, &beauty_pixels[0]);
// Catch optix exceptions
try
{
// Create our optix context and image buffers
optix::Context optix_context = optix::Context::create();
optix::Buffer beauty_buffer = optix_context->createBuffer(RT_BUFFER_INPUT_OUTPUT, RT_FORMAT_FLOAT4, b_width, b_height);
optix::Buffer albedo_buffer = optix_context->createBuffer(RT_BUFFER_INPUT_OUTPUT, RT_FORMAT_FLOAT4, a_width, a_height);
optix::Buffer normal_buffer = optix_context->createBuffer(RT_BUFFER_INPUT_OUTPUT, RT_FORMAT_FLOAT4, n_width, n_height);
optix::Buffer out_buffer = optix_context->createBuffer(RT_BUFFER_INPUT_OUTPUT, RT_FORMAT_FLOAT4, b_width, b_height);
float* device_ptr = (float*)beauty_buffer->map();
unsigned int pixel_idx = 0;
for(unsigned int y=0; y<b_height; y++)
for(unsigned int x=0; x<b_width; x++)
{
memcpy(device_ptr, &beauty_pixels[pixel_idx], sizeof(float) * beauty_roi.nchannels());
device_ptr += 4;
pixel_idx += beauty_roi.nchannels();
}
beauty_buffer->unmap();
device_ptr = 0;
if (a_loaded)
{
std::vector<float> albedo_pixels(a_width * a_height * albedo_roi.nchannels());
input_albedo->get_pixels(albedo_roi, OIIO::TypeDesc::FLOAT, &albedo_pixels[0]);
device_ptr = (float*)albedo_buffer->map();
pixel_idx = 0;
for(unsigned int y=0; y<b_height; y++)
for(unsigned int x=0; x<b_width; x++)
{
memcpy(device_ptr, &albedo_pixels[pixel_idx], sizeof(float) * albedo_roi.nchannels());
device_ptr += 4;
pixel_idx += beauty_roi.nchannels();
}
albedo_buffer->unmap();
device_ptr = 0;
}
if (n_loaded)
{
std::vector<float> normal_pixels(n_width * n_height * normal_roi.nchannels());
input_normal->get_pixels(normal_roi, OIIO::TypeDesc::FLOAT, &normal_pixels[0]);
device_ptr = (float*)normal_buffer->map();
pixel_idx = 0;
for(unsigned int y=0; y<b_height; y++)
for(unsigned int x=0; x<b_width; x++)
{
memcpy(device_ptr, &normal_pixels[pixel_idx], sizeof(float) * normal_roi.nchannels());
device_ptr += 4;
pixel_idx += normal_roi.nchannels();
}
normal_buffer->unmap();
device_ptr = 0;
}
// Setup the optix denoiser post processing stage
optix::PostprocessingStage denoiserStage = optix_context->createBuiltinPostProcessingStage("DLDenoiser");
denoiserStage->declareVariable("input_buffer")->set(beauty_buffer);
denoiserStage->declareVariable("output_buffer")->set(out_buffer);
denoiserStage->declareVariable("blend")->setFloat(blend);
denoiserStage->declareVariable("hdr")->setUint(hdr);
denoiserStage->declareVariable("input_albedo_buffer")->set(albedo_buffer);
denoiserStage->declareVariable("input_normal_buffer")->set(normal_buffer);
// Add the denoiser to the new optix command list
optix::CommandList commandList= optix_context->createCommandList();
commandList->appendPostprocessingStage(denoiserStage, b_width, b_height);
commandList->finalize();
// Compile our context. I'm not sure if this is needed given there is no megakernal?
optix_context->validate();
optix_context->compile();
// Execute denoise
std::cout<<"Denoising..."<<std::endl;
commandList->execute();
std::cout<<"Denoising complete"<<std::endl;
// Copy denoised image back to the cpu
device_ptr = (float*)out_buffer->map();
pixel_idx = 0;
for(unsigned int y=0; y<b_height; y++)
for(unsigned int x=0; x<b_width; x++)
{
memcpy(&beauty_pixels[pixel_idx], device_ptr, sizeof(float) * beauty_roi.nchannels());
device_ptr += 4;
pixel_idx += beauty_roi.nchannels();
}
out_buffer->unmap();
device_ptr = 0;
// Remove our gpu buffers
beauty_buffer->destroy();
normal_buffer->destroy();
albedo_buffer->destroy();
out_buffer->destroy();
optix_context->destroy();
}
catch (std::exception e)
{
std::cerr<<"[OptiX]: "<<e.what()<<std::endl;
cleanup();
return EXIT_FAILURE;
}
Now this section is where I will add the buffers from blender normal,albedo,combined to the optix context:
// Setup the optix denoiser post processing stage
optix::PostprocessingStage denoiserStage = optix_context->createBuiltinPostProcessingStage("DLDenoiser");
denoiserStage->declareVariable("input_buffer")->set(beauty_buffer);
denoiserStage->declareVariable("output_buffer")->set(out_buffer);
denoiserStage->declareVariable("blend")->setFloat(blend);
denoiserStage->declareVariable("hdr")->setUint(hdr);
denoiserStage->declareVariable("input_albedo_buffer")->set(albedo_buffer);
denoiserStage->declareVariable("input_normal_buffer")->set(normal_buffer);
// Add the denoiser to the new optix command list
optix::CommandList commandList= optix_context->createCommandList();
commandList->appendPostprocessingStage(denoiserStage, b_width, b_height);
commandList->finalize();
// Compile our context. I'm not sure if this is needed given there is no megakernal?
optix_context->validate();
optix_context->compile();
// Execute denoise
std::cout<<"Denoising..."<<std::endl;
commandList->execute();
In Blender session.cpp this code is called for the combined buffer:
void BlenderSession::do_write_update_render_result(BL::RenderResult& b_rr,
BL::RenderLayer& b_rlay,
RenderTile& rtile,
bool do_update_only)
{
RenderBuffers *buffers = rtile.buffers;
/* copy data from device */
if(!buffers->copy_from_device())
return;
float exposure = scene->film->exposure;
vector<float> pixels(rtile.w*rtile.h*4);
/* Adjust absolute sample number to the range. */
int sample = rtile.sample;
const int range_start_sample = session->tile_manager.range_start_sample;
if(range_start_sample != -1) {
sample -= range_start_sample;
}
if(!do_update_only) {
/* copy each pass */
BL::RenderLayer::passes_iterator b_iter;
for(b_rlay.passes.begin(b_iter); b_iter != b_rlay.passes.end(); ++b_iter) {
BL::RenderPass b_pass(*b_iter);
/* find matching pass type */
PassType pass_type = BlenderSync::get_pass_type(b_pass);
int components = b_pass.channels();
bool read = false;
if(pass_type != PASS_NONE) {
/* copy pixels */
read = buffers->get_pass_rect(pass_type, exposure, sample, components, &pixels[0]);
}
else {
int denoising_offset = BlenderSync::get_denoising_pass(b_pass);
if(denoising_offset >= 0) {
read = buffers->get_denoising_pass_rect(denoising_offset, exposure, sample, components, &pixels[0]);
}
}
if(!read) {
memset(&pixels[0], 0, pixels.size()*sizeof(float));
}
b_pass.rect(&pixels[0]);
}
}
else {
/* copy combined pass */
BL::RenderPass b_combined_pass(b_rlay.passes.find_by_name("Combined", b_rview_name.c_str()));
if(buffers->get_pass_rect(PASS_COMBINED, exposure, sample, 4, &pixels[0]))
b_combined_pass.rect(&pixels[0]);
}
/* tag result as updated */
b_engine.update_result(b_rr);
}
Now this part is where im thinking to just add to the optix input buffer:
/* copy combined pass */
BL::RenderPass b_combined_pass(b_rlay.passes.find_by_name("Combined", b_rview_name.c_str()));
if(buffers->get_pass_rect(PASS_COMBINED, exposure, sample, 4, &pixels[0]))
b_combined_pass.rect(&pixels[0]);
}
so something like this with optix setup:
// Setup the optix denoiser post processing stage
optix::PostprocessingStage denoiserStage = optix_context->createBuiltinPostProcessingStage("DLDenoiser");
denoiserStage->declareVariable("input_buffer")->set(Combined);
denoiserStage->declareVariable("output_buffer")->set(ai_result);
denoiserStage->declareVariable("blend")->setFloat(blend);
denoiserStage->declareVariable("hdr")->setUint(hdr);
denoiserStage->declareVariable("input_albedo_buffer")->set(DiffCol);
denoiserStage->declareVariable("input_normal_buffer")->set(Normal);
Is this the best way to do this, and if not how, and if not in blender_session.cpp where? This is a fun little experiment that ive enjoyed playing with today
but would love some help/pointers from anyone. Once ive got the ai denoiser working in the combined render layer ill start looking at the realtime viewport.