From 8d5f01e58cc3c55681cc077216628c625fb333ba Mon Sep 17 00:00:00 2001 From: Michael Migliore <mcmigliore@gmail.com> Date: Wed, 5 Jun 2024 17:21:48 +0200 Subject: [PATCH] PBR Neutral tone mapper --- .../release/dev/pbr-neutral-tone-mapping.md | 4 ++++ .../Testing/Cxx/TestToneMappingPass.cxx | 22 ++++++++++--------- .../Baseline/TestToneMappingPass.png.sha512 | 2 +- Rendering/OpenGL2/vtkToneMappingPass.cxx | 19 ++++++++++++++++ Rendering/OpenGL2/vtkToneMappingPass.h | 5 +++-- 5 files changed, 39 insertions(+), 13 deletions(-) create mode 100644 Documentation/release/dev/pbr-neutral-tone-mapping.md diff --git a/Documentation/release/dev/pbr-neutral-tone-mapping.md b/Documentation/release/dev/pbr-neutral-tone-mapping.md new file mode 100644 index 00000000000..c0e85f5a760 --- /dev/null +++ b/Documentation/release/dev/pbr-neutral-tone-mapping.md @@ -0,0 +1,4 @@ +## PBR Neutral tone mapping + +A new alternative tone mapping method has been added to `vtkToneMappingPass`. +It's based on the reference implementation of Khronos PBR Neutral. diff --git a/Rendering/OpenGL2/Testing/Cxx/TestToneMappingPass.cxx b/Rendering/OpenGL2/Testing/Cxx/TestToneMappingPass.cxx index 164cc2d6a0e..04a375d1fee 100644 --- a/Rendering/OpenGL2/Testing/Cxx/TestToneMappingPass.cxx +++ b/Rendering/OpenGL2/Testing/Cxx/TestToneMappingPass.cxx @@ -14,6 +14,7 @@ #include "vtkOpaquePass.h" #include "vtkOpenGLRenderer.h" #include "vtkPolyDataMapper.h" +#include "vtkProperty.h" #include "vtkRenderPassCollection.h" #include "vtkRenderWindow.h" #include "vtkRenderWindowInteractor.h" @@ -24,7 +25,7 @@ int TestToneMappingPass(int argc, char* argv[]) { vtkNew<vtkRenderWindow> renWin; - renWin->SetSize(400, 800); + renWin->SetSize(900, 900); vtkNew<vtkRenderWindowInteractor> iren; iren->SetRenderWindow(renWin); @@ -33,8 +34,7 @@ int TestToneMappingPass(int argc, char* argv[]) sphere->SetThetaResolution(20); sphere->SetPhiResolution(20); - double y = 0.0; - for (int i = 0; i < 8; i++) + for (int i = 0; i < 9; i++) { vtkNew<vtkRenderer> renderer; @@ -84,19 +84,20 @@ int TestToneMappingPass(int argc, char* argv[]) toneMappingP->SetGenericFilmicUncharted2Presets(); toneMappingP->SetUseACES(false); break; + case 8: + toneMappingP->SetToneMappingType(vtkToneMappingPass::NeutralPBR); + break; } toneMappingP->SetDelegatePass(cameraP); vtkOpenGLRenderer::SafeDownCast(renderer)->SetPass(toneMappingP); - double x = 0.5 * (i & 1); - if (i) - { - y += 1 / 4.f * !(i & 1); - } + double oneThird = 1.0 / 3.0; + + double x = (i % 3) * oneThird; + double y = (i / 3) * oneThird; - renderer->SetViewport(x, y, x + 0.5, y + 1 / 4.f); - renderer->SetBackground(0.5, 0.5, 0.5); + renderer->SetViewport(x, y, x + oneThird, y + oneThird); renWin->AddRenderer(renderer); // add one light in front of the object @@ -141,6 +142,7 @@ int TestToneMappingPass(int argc, char* argv[]) vtkNew<vtkActor> actor; actor->SetMapper(mapper); + actor->GetProperty()->SetInterpolationToPBR(); renderer->AddActor(actor); renderer->ResetCamera(); diff --git a/Rendering/OpenGL2/Testing/Data/Baseline/TestToneMappingPass.png.sha512 b/Rendering/OpenGL2/Testing/Data/Baseline/TestToneMappingPass.png.sha512 index 741f700d08e..ab82260e07c 100644 --- a/Rendering/OpenGL2/Testing/Data/Baseline/TestToneMappingPass.png.sha512 +++ b/Rendering/OpenGL2/Testing/Data/Baseline/TestToneMappingPass.png.sha512 @@ -1 +1 @@ -4595f98960d268ccf75b17b50761f5559aab38e975f0c74fb61687ea1ca0ef8d1260661bb317d074dc8824f3d4e8aee0378d3dd538c038bcf06a59bdde042ad3 +af28d7481039a61d908c16db917bf1655c3b8931b2ab771b7f6dcd39825efc6c3ee5072b52301a4d6c1795597f1305eccee1556afff45e71b681eca50ce40418 diff --git a/Rendering/OpenGL2/vtkToneMappingPass.cxx b/Rendering/OpenGL2/vtkToneMappingPass.cxx index 9aa77c9adb7..d4f6c127abc 100644 --- a/Rendering/OpenGL2/vtkToneMappingPass.cxx +++ b/Rendering/OpenGL2/vtkToneMappingPass.cxx @@ -188,6 +188,25 @@ void vtkToneMappingPass::Render(const vtkRenderState* s) " toned = clamp(toned, vec3(0.f), vec3(1.f));\n" "//VTK::FSQ::Impl"); break; + case NeutralPBR: + // adapted from Khronos reference implementation: + // https://github.com/KhronosGroup/ToneMapping/blob/main/PBR_Neutral/pbrNeutral.glsl + vtkShaderProgram::Substitute(FSSource, "//VTK::FSQ::Impl", + " const float startCompression = 0.8 - 0.04;\n" + " const float desaturation = 0.15;\n" + " float x = min(color.r, min(color.g, color.b));\n" + " float offset = x < 0.08 ? x - 6.25 * x * x : 0.04;\n" + " vec3 toned = color - vec3(offset);\n" + " float peak = max(toned.r, max(toned.g, toned.b));\n" + " if (peak >= startCompression)\n" + " {\n" + " const float d = 1. - startCompression;\n" + " float newPeak = 1. - d * d / (peak + d - startCompression);\n" + " toned *= newPeak / peak;\n" + " float g = 1. - 1. / (desaturation * (peak - newPeak) + 1.);\n" + " toned = mix(toned, newPeak * vec3(1, 1, 1), g);\n" + " }\n" + "//VTK::FSQ::Impl"); } // Recorrect gamma and output diff --git a/Rendering/OpenGL2/vtkToneMappingPass.h b/Rendering/OpenGL2/vtkToneMappingPass.h index c0f1a5dcf37..67139b259ee 100644 --- a/Rendering/OpenGL2/vtkToneMappingPass.h +++ b/Rendering/OpenGL2/vtkToneMappingPass.h @@ -68,7 +68,8 @@ public: Clamp = 0, Reinhard = 1, Exponential = 2, - GenericFilmic = 3 + GenericFilmic = 3, + NeutralPBR = 4 }; ///@{ @@ -76,7 +77,7 @@ public: * Get/Set the tone mapping type. * Default is GenericFilmic */ - vtkSetClampMacro(ToneMappingType, int, 0, 3); + vtkSetClampMacro(ToneMappingType, int, 0, 4); vtkGetMacro(ToneMappingType, int); ///@} -- GitLab