diff --git a/Documentation/release/dev/add-outline-glow.md b/Documentation/release/dev/add-outline-glow.md new file mode 100644 index 0000000000000000000000000000000000000000..56cb0cad21a168f468d6fe82b0e94b92f7ab2e51 --- /dev/null +++ b/Documentation/release/dev/add-outline-glow.md @@ -0,0 +1,5 @@ +## Added outline glow render pass + +`vtkOutlineGlowRenderPass` renders a scene as a glowing outline. Combined with +layered renderers this creates a very visible highlight without altering the +highlighted object. diff --git a/Documentation/release/dev/add-outline-glow.png b/Documentation/release/dev/add-outline-glow.png new file mode 100644 index 0000000000000000000000000000000000000000..618100da92e9f90f389c6139b32e7b4dce875eb3 Binary files /dev/null and b/Documentation/release/dev/add-outline-glow.png differ diff --git a/Rendering/OpenGL2/CMakeLists.txt b/Rendering/OpenGL2/CMakeLists.txt index e74d0d88e76360fc60ecf8c34984ff00a45bd319..6a25472dbb3366eae8b94fe6a7506ee533dfc090 100644 --- a/Rendering/OpenGL2/CMakeLists.txt +++ b/Rendering/OpenGL2/CMakeLists.txt @@ -67,6 +67,7 @@ set(classes vtkOpenGLVertexBufferObjectCache vtkOpenGLVertexBufferObjectGroup vtkOrderIndependentTranslucentPass + vtkOutlineGlowPass vtkOverlayPass vtkPanoramicProjectionPass vtkPixelBufferObject @@ -140,6 +141,8 @@ set(shader_files glsl/vtkGaussianBlurPassVS.glsl glsl/vtkGlyph3DVS.glsl glsl/vtkOrderIndependentTranslucentPassFinalFS.glsl + glsl/vtkOutlineGlowBlurPassFS.glsl + glsl/vtkOutlineGlowUpscalePassFS.glsl glsl/vtkPointFillPassFS.glsl glsl/vtkPointGaussianVS.glsl glsl/vtkPointGaussianGS.glsl diff --git a/Rendering/OpenGL2/Testing/Cxx/CMakeLists.txt b/Rendering/OpenGL2/Testing/Cxx/CMakeLists.txt index 86cede9c48ea2bcb102f592931f41b87fc20bc99..79b1422c04472bbe1b6dd8bc252c1f885f186854 100644 --- a/Rendering/OpenGL2/Testing/Cxx/CMakeLists.txt +++ b/Rendering/OpenGL2/Testing/Cxx/CMakeLists.txt @@ -47,6 +47,7 @@ vtk_add_test_cxx(vtkRenderingOpenGL2CxxTests tests TestMultiTexturing.cxx TestNormalMapping.cxx TestOffscreenRenderingResize.cxx + TestOutlineGlowPass.cxx TestOrderIndependentTranslucentPass.cxx TestPanoramicProjectionPass.cxx,NO_DATA TestPBREdgeTint.cxx diff --git a/Rendering/OpenGL2/Testing/Cxx/TestOutlineGlowPass.cxx b/Rendering/OpenGL2/Testing/Cxx/TestOutlineGlowPass.cxx new file mode 100644 index 0000000000000000000000000000000000000000..55537cf19cce9d984c1709fbd06965552f2d6acd --- /dev/null +++ b/Rendering/OpenGL2/Testing/Cxx/TestOutlineGlowPass.cxx @@ -0,0 +1,100 @@ +/*========================================================================= + + Program: Visualization Toolkit + Module: TestOutlineGlowPass.cxx + + Copyright (c) Ken Martin, Will Schroeder, Bill Lorensen + All rights reserved. + See Copyright.txt or http://www.kitware.com/Copyright.htm for details. + + This software is distributed WITHOUT ANY WARRANTY; without even + the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR + PURPOSE. See the above copyright notice for more information. + +=========================================================================*/ +// Test vtkOutlineGlowPass +// This test uses vtkOutlineGlowPass as intended with a layered renderer +// that draws he outline of a cone. The cone is rendered normally in the +// main renderer +// +// The command line arguments are: +// -I => run in interactive mode; unless this is used, the program will +// not allow interaction and exit + +#include "vtkActor.h" +#include "vtkCamera.h" +#include "vtkConeSource.h" +#include "vtkNew.h" +#include "vtkOpenGLRenderer.h" +#include "vtkOutlineGlowPass.h" +#include "vtkPolyDataMapper.h" +#include "vtkProperty.h" +#include "vtkRegressionTestImage.h" +#include "vtkRenderStepsPass.h" +#include "vtkRenderWindow.h" +#include "vtkRenderWindowInteractor.h" +#include "vtkTestUtilities.h" + +int TestOutlineGlowPass(int argc, char* argv[]) +{ + vtkNew iren; + vtkNew renWin; + renWin->SetMultiSamples(0); + + iren->SetRenderWindow(renWin); + + vtkNew renderer; + vtkNew rendererOutline; + rendererOutline->SetLayer(1); + renWin->SetNumberOfLayers(2); + renWin->AddRenderer(rendererOutline); + renWin->AddRenderer(renderer); + + // Create the render pass + vtkNew basicPasses; + vtkNew glowPass; + glowPass->SetDelegatePass(basicPasses); + + // Apply the render pass to the highlight renderer + rendererOutline->SetPass(glowPass); + + vtkNew coneSource; + + // Create mapper and actor for the main renderer + vtkNew coneMapperMain; + coneMapperMain->SetInputConnection(coneSource->GetOutputPort()); + + vtkNew coneActorMain; + coneActorMain->SetMapper(coneMapperMain); + + renderer->AddActor(coneActorMain); + + // Create mapper and actor for the outline + vtkNew coneMapperOutline; + coneMapperOutline->SetInputConnection(coneSource->GetOutputPort()); + + vtkNew coneActorOutline; + coneActorOutline->SetMapper(coneMapperOutline); + coneActorOutline->GetProperty()->SetColor(1.0, 0.0, 1.0); + coneActorOutline->GetProperty()->LightingOff(); + + rendererOutline->AddActor(coneActorOutline); + + renWin->SetSize(400, 400); + + int retVal; + renderer->ResetCamera(); + vtkCamera* camera = renderer->GetActiveCamera(); + camera->Azimuth(-40.0); + camera->Elevation(20.0); + renderer->ResetCamera(); + rendererOutline->SetActiveCamera(camera); + renWin->Render(); + + retVal = vtkRegressionTestImage(renWin); + if (retVal == vtkRegressionTester::DO_INTERACTOR) + { + iren->Start(); + } + return !retVal; +} diff --git a/Rendering/OpenGL2/Testing/Data/Baseline/TestOutlineGlowPass.png.sha512 b/Rendering/OpenGL2/Testing/Data/Baseline/TestOutlineGlowPass.png.sha512 new file mode 100644 index 0000000000000000000000000000000000000000..705b4d6ecba9ee4c0594a0ce65f4211a83ad9654 --- /dev/null +++ b/Rendering/OpenGL2/Testing/Data/Baseline/TestOutlineGlowPass.png.sha512 @@ -0,0 +1 @@ +80b438d7813cb2c914338099e9b9c25ec73099760c905f8eab2010e8f094c3ebb7ba4266ebdeae041ab8ed6633b3a40b083edb19973ddcb7c0d4a0547491d5bb diff --git a/Rendering/OpenGL2/glsl/vtkOutlineGlowBlurPassFS.glsl b/Rendering/OpenGL2/glsl/vtkOutlineGlowBlurPassFS.glsl new file mode 100644 index 0000000000000000000000000000000000000000..a84b1b3689b51ba38e27c1085a02d8b9f5f3bd49 --- /dev/null +++ b/Rendering/OpenGL2/glsl/vtkOutlineGlowBlurPassFS.glsl @@ -0,0 +1,37 @@ +//VTK::System::Dec + +// ============================================================================ +// +// Program: Visualization Toolkit +// Module: vtkOutlineGlowBlurPassFS.glsl +// +// Copyright (c) Ken Martin, Will Schroeder, Bill Lorensen +// All rights reserved. +// See Copyright.txt or http://www.kitware.com/Copyright.htm for details. +// +// This software is distributed WITHOUT ANY WARRANTY; without even +// the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR +// PURPOSE. See the above copyright notice for more information. +// +// ============================================================================ + +// Fragment shader used by the outline glow blur render pass. + +in vec2 tcoordVC; +uniform sampler2D source; + +uniform float coef[3]; +uniform float offsetx; +uniform float offsety; + +// the output of this shader +//VTK::Output::Dec + +void main(void) +{ + vec2 offset=vec2(offsetx,offsety); + + gl_FragData[0] = coef[0]*texture2D(source,tcoordVC-offset) + +coef[1]*texture2D(source,tcoordVC) + +coef[2]*texture2D(source,tcoordVC+offset); +} diff --git a/Rendering/OpenGL2/glsl/vtkOutlineGlowUpscalePassFS.glsl b/Rendering/OpenGL2/glsl/vtkOutlineGlowUpscalePassFS.glsl new file mode 100644 index 0000000000000000000000000000000000000000..60aad7bfb41f08a696ebf2c3254240262ec75e36 --- /dev/null +++ b/Rendering/OpenGL2/glsl/vtkOutlineGlowUpscalePassFS.glsl @@ -0,0 +1,44 @@ +//VTK::System::Dec + +// ============================================================================ +// +// Program: Visualization Toolkit +// Module: vtkOutlineGlowUpscalePassFS.glsl +// +// Copyright (c) Ken Martin, Will Schroeder, Bill Lorensen +// All rights reserved. +// See Copyright.txt or http://www.kitware.com/Copyright.htm for details. +// +// This software is distributed WITHOUT ANY WARRANTY; without even +// the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR +// PURPOSE. See the above copyright notice for more information. +// +// ============================================================================ + +// Fragment shader used by outline glow pass to combine the original scene and the blurred image to form an outline + +in vec2 tcoordVC; +uniform sampler2D scene; +uniform sampler2D source; + +uniform float outlineIntensity; + +// the output of this shader +//VTK::Output::Dec + +void main(void) +{ + vec4 color = texture2D(scene, tcoordVC); + if( length(color.rgb) > 0.0) + { + // If the pixel was filled in the original scene it not part of the outline + gl_FragData[0] = vec4(0.0, 0.0, 0.0, 0.0); + } + else + { + vec4 blurredColor = texture2D(source,tcoordVC); + float brightness = max(blurredColor.r, max(blurredColor.g, blurredColor.b)); + gl_FragData[0].rgb = blurredColor.rgb * outlineIntensity; + gl_FragData[0].a = brightness * outlineIntensity; + } +} diff --git a/Rendering/OpenGL2/vtkImageProcessingPass.cxx b/Rendering/OpenGL2/vtkImageProcessingPass.cxx index f381da865ca84f9c531857890a1da86953486fd0..239c8f47767e0c61d75efd7c2073885a3971206d 100644 --- a/Rendering/OpenGL2/vtkImageProcessingPass.cxx +++ b/Rendering/OpenGL2/vtkImageProcessingPass.cxx @@ -163,6 +163,13 @@ void vtkImageProcessingPass::RenderDelegate(const vtkRenderState* s, int width, fbo->AddDepthAttachment(); fbo->StartNonOrtho(newWidth, newHeight); + if (r->Transparent()) + { + // Clear is not called on tranparent renderers. But since this is a offscreen render target we + // want it cleared + ostate->vtkglClearColor(0.0f, 0.0f, 0.0f, 0.0f); + ostate->vtkglClear(GL_COLOR_BUFFER_BIT); + } ostate->vtkglViewport(0, 0, newWidth, newHeight); ostate->vtkglScissor(0, 0, newWidth, newHeight); diff --git a/Rendering/OpenGL2/vtkOutlineGlowPass.cxx b/Rendering/OpenGL2/vtkOutlineGlowPass.cxx new file mode 100644 index 0000000000000000000000000000000000000000..1c3e5e0e0263cb64b9c1788fb939b9be74bab979 --- /dev/null +++ b/Rendering/OpenGL2/vtkOutlineGlowPass.cxx @@ -0,0 +1,500 @@ +/*========================================================================= + + Program: Visualization Toolkit + Module: vtkOutlineGlowPass.cxx + + Copyright (c) Ken Martin, Will Schroeder, Bill Lorensen + All rights reserved. + See Copyright.txt or http://www.kitware.com/Copyright.htm for details. + + This software is distributed WITHOUT ANY WARRANTY; without even + the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR + PURPOSE. See the above copyright notice for more information. + +=========================================================================*/ + +#include "vtkOutlineGlowPass.h" +#include "vtkObjectFactory.h" +#include "vtkOpenGLError.h" +#include "vtkOpenGLFramebufferObject.h" +#include "vtkOpenGLRenderWindow.h" +#include "vtkOpenGLShaderCache.h" +#include "vtkOpenGLState.h" +#include "vtkOpenGLVertexArrayObject.h" +#include "vtkRenderState.h" +#include "vtkRenderer.h" +#include "vtkShaderProgram.h" +#include "vtkTextureObject.h" +#include + +#include "vtkOpenGLHelper.h" + +// to be able to dump intermediate passes into png files for debugging. +// only for vtkOutlineGlowPass developers. +// #define VTK_OUTLINE_GLOW_PASS_DEBUG + +#ifdef VTK_OUTLINE_GLOW_PASS_DEBUG +#include "vtkImageExtractComponents.h" +#include "vtkImageImport.h" +#include "vtkPNGWriter.h" +#include "vtkPixelBufferObject.h" +#endif + +// Shader includes +#include "vtkOutlineGlowBlurPassFS.h" +#include "vtkOutlineGlowUpscalePassFS.h" +#include "vtkTextureObjectVS.h" // a pass through shader + +vtkStandardNewMacro(vtkOutlineGlowPass); + +// ---------------------------------------------------------------------------- +vtkOutlineGlowPass::vtkOutlineGlowPass() +{ + this->FrameBufferObject = nullptr; + this->BlurPass1 = nullptr; + this->BlurPass2 = nullptr; + this->ScenePass = nullptr; + this->BlurProgram = nullptr; + this->UpscaleProgram = nullptr; +} + +// ---------------------------------------------------------------------------- +vtkOutlineGlowPass::~vtkOutlineGlowPass() +{ + if (this->FrameBufferObject != nullptr) + { + vtkErrorMacro(<< "FrameBufferObject should have been deleted in ReleaseGraphicsResources()."); + } + if (this->ScenePass != nullptr) + { + vtkErrorMacro(<< "ScenePass should have been deleted in ReleaseGraphicsResources()."); + } + if (this->BlurPass1 != nullptr) + { + vtkErrorMacro(<< "BlurPass1 should have been deleted in ReleaseGraphicsResources()."); + } + if (this->BlurPass2 != nullptr) + { + vtkErrorMacro(<< "BlurPass2 should have been deleted in ReleaseGraphicsResources()."); + } +} + +// ---------------------------------------------------------------------------- +void vtkOutlineGlowPass::PrintSelf(ostream& os, vtkIndent indent) +{ + this->Superclass::PrintSelf(os, indent); + + os << indent << "OutlineGlowPass:"; + os << indent << "OutlineIntensity: " << this->OutlineIntensity << endl; +} + +// ---------------------------------------------------------------------------- +// Description: +// Perform rendering according to a render state \p s. +// \pre s_exists: s!=0 +void vtkOutlineGlowPass::Render(const vtkRenderState* s) +{ + assert("pre: s_exists" && s != nullptr); + + vtkOpenGLClearErrorMacro(); + + this->NumberOfRenderedProps = 0; + + vtkRenderer* r = s->GetRenderer(); + vtkOpenGLRenderWindow* renWin = static_cast(r->GetRenderWindow()); + vtkOpenGLState* ostate = renWin->GetState(); + + if (this->DelegatePass != nullptr) + { + // 1. Create a new render state with an FBO. + + int width; + int height; + int size[2]; + s->GetWindowSize(size); + width = size[0]; + height = size[1]; + int halfWidth = std::ceil((double)width / 2.0); + int halfHeight = std::ceil((double)height / 2.0); + + // No extra pixels. Tex coord clamping takes care of the edges. + int widthHalfTarget = halfWidth; + int heightHalfTarget = halfHeight; + int widthFullTarget = width; + int heightFullTarget = height; + + if (this->ScenePass == nullptr) + { + this->ScenePass = vtkTextureObject::New(); + this->ScenePass->SetContext(renWin); + } + + if (this->FrameBufferObject == nullptr) + { + this->FrameBufferObject = vtkOpenGLFramebufferObject::New(); + this->FrameBufferObject->SetContext(renWin); + } + + // backup GL state + vtkOpenGLState::ScopedglEnableDisable bsaver(ostate, GL_BLEND); + vtkOpenGLState::ScopedglEnableDisable dsaver(ostate, GL_DEPTH_TEST); + + // 2. Render Scene to an offscreen render target + this->FrameBufferObject->SaveCurrentBindingsAndBuffers(); + + this->RenderDelegate(s, width, height, widthFullTarget, heightFullTarget, + this->FrameBufferObject, this->ScenePass); + +#ifdef VTK_OUTLINE_GLOW_PASS_DEBUG + // Save first pass in file for debugging. + vtkPixelBufferObject* pbo = this->BlurPass1->Download(); + + unsigned char* openglRawData = new unsigned char[4 * widthHalfTarget * heightHalfTarget]; + unsigned int dims[2]; + dims[0] = widthHalfTarget; + dims[1] = heightHalfTarget; + vtkIdType incs[2]; + incs[0] = 0; + incs[1] = 0; + bool status = pbo->Download2D(VTK_UNSIGNED_CHAR, openglRawData, dims, 4, incs); + assert("check" && status); + pbo->Delete(); + + // no pbo + this->BlurPass1->Bind(); + glGetTexImage(GL_TEXTURE_2D, 0, GL_RGBA, GL_UNSIGNED_BYTE, openglRawData); + this->BlurPass1->Deactivate(); + + vtkImageImport* importer = vtkImageImport::New(); + importer->CopyImportVoidPointer( + openglRawData, 4 * widthHalfTarget * heightHalfTarget * sizeof(unsigned char)); + importer->SetDataScalarTypeToUnsignedChar(); + importer->SetNumberOfScalarComponents(4); + importer->SetWholeExtent(0, widthHalfTarget - 1, 0, heightHalfTarget - 1, 0, 0); + importer->SetDataExtentToWholeExtent(); + delete[] openglRawData; + + vtkImageExtractComponents* rgbaToRgb = vtkImageExtractComponents::New(); + rgbaToRgb->SetInputConnection(importer->GetOutputPort()); + rgbaToRgb->SetComponents(0, 1, 2); + + vtkPNGWriter* writer = vtkPNGWriter::New(); + writer->SetFileName("BlurPass1.png"); + writer->SetInputConnection(rgbaToRgb->GetOutputPort()); + importer->Delete(); + rgbaToRgb->Delete(); + writer->Write(); + writer->Delete(); +#endif + + // 3. Render Scene to Buffer1 while applying horizontal blur + if (this->BlurPass1 == nullptr) + { + this->BlurPass1 = vtkTextureObject::New(); + this->BlurPass1->SetContext(this->FrameBufferObject->GetContext()); + } + + if (this->BlurPass1->GetWidth() != static_cast(widthHalfTarget) || + this->BlurPass1->GetHeight() != static_cast(heightHalfTarget)) + { + this->BlurPass1->Create2D(static_cast(widthHalfTarget), + static_cast(heightHalfTarget), 4, VTK_UNSIGNED_CHAR, false); + } + + this->FrameBufferObject->AddColorAttachment(0, this->BlurPass1); + this->FrameBufferObject->Start(widthHalfTarget, heightHalfTarget); + +#ifdef VTK_OUTLINE_GLOW_PASS_DEBUG + cout << "outline finish2" << endl; + glFinish(); +#endif + + // Use a blur shader, do it horizontally. this->ScenePass is the source + // (this->BlurPass1 is the fbo render target) + + // has something changed that would require us to recreate the shader? + if (!this->BlurProgram) + { + this->BlurProgram = new vtkOpenGLHelper; + // build the shader source code + std::string VSSource = vtkTextureObjectVS; + std::string FSSource = vtkOutlineGlowBlurPassFS; + std::string GSSource; + + // compile and bind it if needed + vtkShaderProgram* newShader = renWin->GetShaderCache()->ReadyShaderProgram( + VSSource.c_str(), FSSource.c_str(), GSSource.c_str()); + + // if the shader changed reinitialize the VAO + if (newShader != this->BlurProgram->Program) + { + this->BlurProgram->Program = newShader; + this->BlurProgram->VAO->ShaderProgramChanged(); // reset the VAO as the shader has changed + } + + this->BlurProgram->ShaderSourceTime.Modified(); + } + else + { + renWin->GetShaderCache()->ReadyShaderProgram(this->BlurProgram->Program); + } + + if (!this->BlurProgram->Program || this->BlurProgram->Program->GetCompiled() != true) + { + vtkErrorMacro("Couldn't build the shader program. At this point , it can be an error in a " + "shader or a driver bug."); + + // restore some state. + this->FrameBufferObject->UnBind(); + this->FrameBufferObject->RestorePreviousBindingsAndBuffers(); + return; + } + + this->ScenePass->Activate(); + int sourceId = this->ScenePass->GetTextureUnit(); + this->ScenePass->SetMinificationFilter(vtkTextureObject::Linear); + this->ScenePass->SetMagnificationFilter(vtkTextureObject::Linear); + // Clamp the texture coordinates so we don't get an outline at the opposite end of the screen + this->ScenePass->SetWrapS(vtkTextureObject::ClampToEdge); + this->ScenePass->SetWrapT(vtkTextureObject::ClampToEdge); + this->BlurProgram->Program->SetUniformi("source", sourceId); + float fvalues[3]; + + static float kernel[3] = { 5.0f, 6.0f, 5.0f }; + + int i = 0; + float sum = 0.0f; + while (i < 3) + { + sum += kernel[i]; + ++i; + } + + // kernel + i = 0; + while (i < 3) + { + fvalues[i] = kernel[i] / sum; + ++i; + } + this->BlurProgram->Program->SetUniform1fv("coef", 3, fvalues); + + // horizontal offset + fvalues[0] = static_cast(2.2 / widthHalfTarget); + this->BlurProgram->Program->SetUniformf("offsetx", fvalues[0]); + fvalues[0] = 0.0f; + this->BlurProgram->Program->SetUniformf("offsety", fvalues[0]); + +#ifdef VTK_OUTLINE_GLOW_PASS_DEBUG + cout << "outline finish3-" << endl; + glFinish(); +#endif + + ostate->vtkglDisable(GL_BLEND); + ostate->vtkglDisable(GL_DEPTH_TEST); + + this->FrameBufferObject->RenderQuad(0, widthHalfTarget - 1, 0, heightHalfTarget - 1, + this->BlurProgram->Program, this->BlurProgram->VAO); + +#ifdef VTK_OUTLINE_GLOW_PASS_DEBUG + cout << "outline finish3" << endl; + glFinish(); +#endif + + this->BlurPass1->Deactivate(); + +#ifdef VTK_OUTLINE_GLOW_PASS_DEBUG + + // Save second pass in file for debugging. + pbo = this->BlurPass2->Download(); + openglRawData = new unsigned char[4 * widthHalfTarget * heightHalfTarget]; + status = pbo->Download2D(VTK_UNSIGNED_CHAR, openglRawData, dims, 4, incs); + assert("check2" && status); + pbo->Delete(); + + // no pbo + this->BlurPass2->Bind(); + glGetTexImage(GL_TEXTURE_2D, 0, GL_RGBA, GL_UNSIGNED_BYTE, openglRawData); + this->BlurPass2->Deactivate(); + + importer = vtkImageImport::New(); + importer->CopyImportVoidPointer( + openglRawData, 4 * widthHalfTarget * heightHalfTarget * sizeof(unsigned char)); + importer->SetDataScalarTypeToUnsignedChar(); + importer->SetNumberOfScalarComponents(4); + importer->SetWholeExtent(0, widthHalfTarget - 1, 0, heightHalfTarget - 1, 0, 0); + importer->SetDataExtentToWholeExtent(); + delete[] openglRawData; + + rgbaToRgb = vtkImageExtractComponents::New(); + rgbaToRgb->SetInputConnection(importer->GetOutputPort()); + rgbaToRgb->SetComponents(0, 1, 2); + + writer = vtkPNGWriter::New(); + writer->SetFileName("BlurPass2.png"); + writer->SetInputConnection(rgbaToRgb->GetOutputPort()); + importer->Delete(); + rgbaToRgb->Delete(); + writer->Write(); + writer->Delete(); +#endif + + // 4. Render BlurrPass1 to BlurrPass2 while applying vertical blur + if (this->BlurPass2 == nullptr) + { + this->BlurPass2 = vtkTextureObject::New(); + this->BlurPass2->SetContext(this->FrameBufferObject->GetContext()); + } + + if (this->BlurPass2->GetWidth() != static_cast(widthHalfTarget) || + this->BlurPass2->GetHeight() != static_cast(heightHalfTarget)) + { + this->BlurPass2->Create2D(static_cast(widthHalfTarget), + static_cast(heightHalfTarget), 4, VTK_UNSIGNED_CHAR, false); + } + + this->FrameBufferObject->AddColorAttachment(0, this->BlurPass2); + this->FrameBufferObject->Start(widthHalfTarget, heightHalfTarget); + + // BlurrPass1 is the source + this->BlurPass1->Activate(); + sourceId = this->BlurPass1->GetTextureUnit(); + this->BlurPass1->SetMinificationFilter(vtkTextureObject::Linear); + this->BlurPass1->SetMagnificationFilter(vtkTextureObject::Linear); + // Clamp the texture coordinates so we don't get an outline at the opposite end of the screen + this->BlurPass1->SetWrapS(vtkTextureObject::ClampToEdge); + this->BlurPass1->SetWrapT(vtkTextureObject::ClampToEdge); + this->BlurProgram->Program->SetUniformi("source", sourceId); + + // Use the same blur shader, do it vertically. + + // vertical offset. + fvalues[0] = 0.0f; + this->BlurProgram->Program->SetUniformf("offsetx", fvalues[0]); + fvalues[0] = static_cast(2.2 / heightHalfTarget); + this->BlurProgram->Program->SetUniformf("offsety", fvalues[0]); + + ostate->vtkglDisable(GL_BLEND); + ostate->vtkglDisable(GL_DEPTH_TEST); + + this->FrameBufferObject->RenderQuad(0, widthHalfTarget - 1, 0, heightHalfTarget - 1, + this->BlurProgram->Program, this->BlurProgram->VAO); + + // 5. Render the blurred image back to the back buffer + this->FrameBufferObject->UnBind(); + this->FrameBufferObject->RestorePreviousBindingsAndBuffers(); + + // has something changed that would require us to recreate the shader? + if (!this->UpscaleProgram) + { + this->UpscaleProgram = new vtkOpenGLHelper; + // build the shader source code + std::string VSSource = vtkTextureObjectVS; + std::string FSSource = vtkOutlineGlowUpscalePassFS; + std::string GSSource; + + // compile and bind it if needed + vtkShaderProgram* newShader = renWin->GetShaderCache()->ReadyShaderProgram( + VSSource.c_str(), FSSource.c_str(), GSSource.c_str()); + + // if the shader changed reinitialize the VAO + if (newShader != this->UpscaleProgram->Program) + { + this->UpscaleProgram->Program = newShader; + this->UpscaleProgram->VAO + ->ShaderProgramChanged(); // reset the VAO as the shader has changed + } + + this->UpscaleProgram->ShaderSourceTime.Modified(); + } + else + { + renWin->GetShaderCache()->ReadyShaderProgram(this->UpscaleProgram->Program); + } + + // Set the textures. Scene contains the original unaltered scene in full resolution, + // source the blurred downsampled image. + this->ScenePass->Activate(); + sourceId = this->ScenePass->GetTextureUnit(); + this->UpscaleProgram->Program->SetUniformi("scene", sourceId); + this->BlurPass2->Activate(); + sourceId = this->BlurPass2->GetTextureUnit(); + this->UpscaleProgram->Program->SetUniformi("source", sourceId); + this->UpscaleProgram->Program->SetUniformf("outlineIntensity", this->OutlineIntensity); + + this->BlurPass2->SetMinificationFilter(vtkTextureObject::Linear); + this->BlurPass2->SetMagnificationFilter(vtkTextureObject::Linear); + + // If this is a transparent (layered) renderer enable blending + if (s->GetRenderer()->Transparent()) + { + ostate->vtkglEnable(GL_BLEND); + ostate->vtkglBlendEquationSeparate(GL_FUNC_ADD, GL_FUNC_ADD); + ostate->vtkglBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_ONE, GL_ZERO); + } + + this->BlurPass2->CopyToFrameBuffer(0, 0, widthHalfTarget - 1, heightHalfTarget - 1, 0, 0, + width - 1, height - 1, width, + height, // Not used, but only by calling this method + // directly will the correct texture coordinates be used + this->UpscaleProgram->Program, this->UpscaleProgram->VAO); + + this->BlurPass2->Deactivate(); + +#ifdef VTK_OUTLINE_GLOW_PASS_DEBUG + cout << "outline finish4" << endl; + glFinish(); +#endif + } + else + { + vtkWarningMacro(<< " no delegate."); + } + + vtkOpenGLCheckErrorMacro("failed after Render"); +} + +// ---------------------------------------------------------------------------- +// Description: +// Release graphics resources and ask components to release their own +// resources. +// \pre w_exists: w!=0 +void vtkOutlineGlowPass::ReleaseGraphicsResources(vtkWindow* w) +{ + assert("pre: w_exists" && w != nullptr); + + this->Superclass::ReleaseGraphicsResources(w); + + if (this->BlurProgram != nullptr) + { + this->BlurProgram->ReleaseGraphicsResources(w); + delete this->BlurProgram; + this->BlurProgram = nullptr; + } + if (this->UpscaleProgram != nullptr) + { + this->UpscaleProgram->ReleaseGraphicsResources(w); + delete this->UpscaleProgram; + this->UpscaleProgram = nullptr; + } + if (this->FrameBufferObject != nullptr) + { + this->FrameBufferObject->Delete(); + this->FrameBufferObject = nullptr; + } + if (this->ScenePass != nullptr) + { + this->ScenePass->Delete(); + this->ScenePass = nullptr; + } + if (this->BlurPass1 != nullptr) + { + this->BlurPass1->Delete(); + this->BlurPass1 = nullptr; + } + if (this->BlurPass2 != nullptr) + { + this->BlurPass2->Delete(); + this->BlurPass2 = nullptr; + } +} diff --git a/Rendering/OpenGL2/vtkOutlineGlowPass.h b/Rendering/OpenGL2/vtkOutlineGlowPass.h new file mode 100644 index 0000000000000000000000000000000000000000..e24b7afbef295dfc09e7f3731d373eaab74776a6 --- /dev/null +++ b/Rendering/OpenGL2/vtkOutlineGlowPass.h @@ -0,0 +1,118 @@ +/*========================================================================= + + Program: Visualization Toolkit + Module: vtkOutlineGlowPass.h + + Copyright (c) Ken Martin, Will Schroeder, Bill Lorensen + All rights reserved. + See Copyright.txt or http://www.kitware.com/Copyright.htm for details. + + This software is distributed WITHOUT ANY WARRANTY; without even + the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR + PURPOSE. See the above copyright notice for more information. + +=========================================================================*/ +/** + * @class vtkOutlineGlowPass + * @brief Renders a glowing outline using a image processing pass + * + * Create a glowing outline of the image rendered by the delegate. + * + * This render pass was designed to highlight parts of a scene by applying the render pass to a + * layered renderer on top of the main scene. For optimal results, actors that form the outline + * should be brightly colored with lighting disabled. The outline will have the color of the actors. + * There is only one outline around all objects rendered by the delegate. + * + * This pass expects an initialized depth buffer and color buffer. + * Initialized buffers means they have been cleared with farthest z-value and + * background color/gradient/transparent color. + * An opaque pass may have been performed right after the initialization. + * + * The delegate is used once. + * + * Its delegate is usually set to a vtkCameraPass or to a post-processing pass. + * + * This pass requires a OpenGL context that supports texture objects (TO), + * framebuffer objects (FBO) and GLSL. If not, it will emit an error message + * and will render its delegate and return. + * + * @par Implementation: + * The image is first rendered to a full size offscreen render target, then blurred twice on a half + * sized render target using Gaussian blur with an offset. The offset and the smaller render target + * increase the size of the outline without incurring the cost of a big Gaussian blur kernel. The + * implementation of the gaussian blur is similar to vtkGaussianBlurPass with the alterations + * described above. + * + * @sa + * vtkRenderPass vtkGaussianBlurPass + */ + +#ifndef vtkOutlineGlowPass_h +#define vtkOutlineGlowPass_h + +#include "vtkImageProcessingPass.h" +#include "vtkRenderingOpenGL2Module.h" // For export macro + +class vtkOpenGLFramebufferObject; +class vtkOpenGLHelper; +class vtkTextureObject; + +class VTKRENDERINGOPENGL2_EXPORT vtkOutlineGlowPass : public vtkImageProcessingPass +{ +public: + static vtkOutlineGlowPass* New(); + vtkTypeMacro(vtkOutlineGlowPass, vtkImageProcessingPass); + void PrintSelf(ostream& os, vtkIndent indent) override; + + /** + * Perform rendering according to a render state \p s. + * \pre s_exists: s!=0 + */ + void Render(const vtkRenderState* s) override; + + /** + * Release graphics resources and ask components to release their own + * resources. + * \pre w_exists: w!=0 + */ + void ReleaseGraphicsResources(vtkWindow* w) override; + + /** + * Get/Set the intensity of the outline. + * Default value is 3.0 which gives a bright outline with a fading edge + */ + vtkGetMacro(OutlineIntensity, float); + vtkSetMacro(OutlineIntensity, float); + +protected: + /** + * Default constructor. DelegatePass is set to NULL. + */ + vtkOutlineGlowPass(); + + /** + * Destructor. + */ + ~vtkOutlineGlowPass() override; + + /** + * Graphics resources. + */ + vtkOpenGLFramebufferObject* FrameBufferObject; + vtkTextureObject* ScenePass; // render target for the original scene + vtkTextureObject* BlurPass1; // render target for vertical blur + vtkTextureObject* BlurPass2; // render target for horizontal blur + + // Shader programs + vtkOpenGLHelper* BlurProgram; + vtkOpenGLHelper* UpscaleProgram; + + // Default value of 3.0 gives a bright outline with a fading edge + float OutlineIntensity = 3.0f; + +private: + vtkOutlineGlowPass(const vtkOutlineGlowPass&) = delete; + void operator=(const vtkOutlineGlowPass&) = delete; +}; + +#endif /* vtkOutlineGlowPass_h */