From 0983eaba378de9286dd2b4f5af5da7657985683b Mon Sep 17 00:00:00 2001 From: Alvaro Sanchez Date: Wed, 20 Jul 2016 14:26:07 -0400 Subject: [PATCH] Added vtkValuePass::FLOATING_POINT for point data. A field array of point data is uploaded as a vertex attribute and rendered to a float FBO member of the vtkValuePass. The legacy value rendering mode is still supported as INVERTIBLE_LUT. --- Rendering/Core/Testing/Cxx/CMakeLists.txt | 1 + .../Cxx/TestValuePassFloatingPoint.cxx | 283 ++++++++++++++++++ .../TestValuePassFloatingPoint.png.md5 | 1 + Rendering/Core/module.cmake | 3 +- Rendering/OpenGL2/CMakeLists.txt | 2 + Rendering/OpenGL2/glsl/vtkPolyDataFS.glsl | 3 + Rendering/OpenGL2/glsl/vtkPolyDataVS.glsl | 5 + Rendering/OpenGL2/vtkOpenGLPolyDataMapper.cxx | 80 +++-- Rendering/OpenGL2/vtkOpenGLPolyDataMapper.h | 3 + Rendering/OpenGL2/vtkValuePass.cxx | 203 ++++++++++++- Rendering/OpenGL2/vtkValuePass.h | 44 +++ Rendering/OpenGL2/vtkValuePassHelper.cxx | 191 ++++++++++++ Rendering/OpenGL2/vtkValuePassHelper.h | 54 ++++ 13 files changed, 849 insertions(+), 24 deletions(-) create mode 100644 Rendering/Core/Testing/Cxx/TestValuePassFloatingPoint.cxx create mode 100644 Rendering/Core/Testing/Data/Baseline/TestValuePassFloatingPoint.png.md5 create mode 100644 Rendering/OpenGL2/vtkValuePassHelper.cxx create mode 100644 Rendering/OpenGL2/vtkValuePassHelper.h diff --git a/Rendering/Core/Testing/Cxx/CMakeLists.txt b/Rendering/Core/Testing/Cxx/CMakeLists.txt index 51e5c94f44..2c9af5e1ac 100644 --- a/Rendering/Core/Testing/Cxx/CMakeLists.txt +++ b/Rendering/Core/Testing/Cxx/CMakeLists.txt @@ -12,6 +12,7 @@ if(VTK_RENDERING_BACKEND STREQUAL "OpenGL2") TestTranslucentLUTAlphaBlending.cxx TestTranslucentLUTTextureAlphaBlending.cxx TestAreaSelections.cxx + TestValuePassFloatingPoint.cxx ) else() set(extra_opengl2_tests diff --git a/Rendering/Core/Testing/Cxx/TestValuePassFloatingPoint.cxx b/Rendering/Core/Testing/Cxx/TestValuePassFloatingPoint.cxx new file mode 100644 index 0000000000..d099ac8e55 --- /dev/null +++ b/Rendering/Core/Testing/Cxx/TestValuePassFloatingPoint.cxx @@ -0,0 +1,283 @@ +/*========================================================================= + + Program: Visualization Toolkit + Module: TestValuePassFloatingPoint.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. + +=========================================================================*/ + +// Description: +// Tests vtkValuePass in FLOATING_POINT mode. The test generates a 3-component +// float array ("elevationVector") using the loaded polygonal data. Polygons +// are rendered with the ValuePass to its internal floating point frame-buffer. +// The rendered float image is then queried from the vtkValuePass and used to +// generate a color image using vtkLookupTable, the color image is rendered with +// an image actor on-screen. This is repeated for each component. + +#include "vtkRegressionTestImage.h" +#include "vtkTestUtilities.h" +#include "vtkActor.h" +#include "vtkCameraPass.h" +#include "vtkCellArray.h" +#include "vtkElevationFilter.h" +#include "vtkInformation.h" +#include "vtkOpenGLRenderer.h" +#include "vtkPLYReader.h" +#include "vtkPointData.h" +#include "vtkPointDataToCellData.h" +#include "vtkPolyData.h" +#include "vtkPolyDataMapper.h" +#include "vtkOpenGLPolyDataMapper.h" +#include "vtkRenderPassCollection.h" +#include "vtkRenderWindow.h" +#include "vtkRenderWindowInteractor.h" +#include "vtkSequencePass.h" +#include "vtkSmartPointer.h" +#include "vtkValuePass.h" + +#include "vtkCamera.h" +#include "vtkInteractorStyleTrackballCamera.h" +#include "vtkAbstractMapper.h" +#include "vtkXMLPolyDataReader.h" +#include "vtkFloatArray.h" +#include "vtkLookupTable.h" +#include "vtkColorSeries.h" +#include "vtkImageData.h" +#include "vtkImageActor.h" +#include "vtkImageMapper3D.h" +#include "vtkArrayCalculator.h" + + +void GenerateElevationArray(vtkSmartPointer reader) +{ + vtkPolyData* data = reader->GetOutput(); + double* bounds = data->GetBounds(); + + vtkSmartPointer elevation = + vtkSmartPointer::New(); + elevation->SetInputConnection(reader->GetOutputPort()); + + /// Use vtkElevation to generate an array per component. vtkElevation generates + /// a projected distance from each point in the dataset to the line, with respect to + /// the LowPoint ([0, 1] in this case. This is different from having the actual + /// coordinates of a given point. + for (int c = 0; c < 3; c++) + { + std::string name; + switch (c) + { + case 0: + name = "delta_x"; + elevation->SetLowPoint(bounds[0], 0.0, 0.0); + elevation->SetHighPoint(bounds[1], 0.0, 0.0); + break; + case 1: + name = "delta_y"; + elevation->SetLowPoint(0.0, bounds[2], 0.0); + elevation->SetHighPoint(0.0, bounds[3], 0.0); + break; + case 2: + name = "delta_z"; + elevation->SetLowPoint(0.0, 0.0, bounds[4]); + elevation->SetHighPoint(0.0, 0.0, bounds[5]); + break; + } + elevation->Update(); + + vtkPolyData* result = vtkPolyData::SafeDownCast(elevation->GetOutput()); + int outCellFlag; + /// Enums defined in vtkAbstractMapper + vtkDataArray* elevArray = vtkAbstractMapper::GetScalars(result, + VTK_SCALAR_MODE_USE_POINT_FIELD_DATA, VTK_GET_ARRAY_BY_NAME/*acc mode*/, + 0/*arr id*/, "Elevation"/*arr name*/, outCellFlag); + if (!elevArray) + { + std::cout << "->> Error: could not find array!" << std::endl; + return; + } + + elevArray->SetName(name.c_str()); + data->GetPointData()->AddArray(elevArray); + } + + /// Generate a 3-component vector array using the single components + /// form elevation. + vtkSmartPointer calc = vtkSmartPointer::New(); + calc->SetInputConnection(reader->GetOutputPort()); + calc->SetAttributeModeToUsePointData(); + calc->AddScalarArrayName("delta_x"); + calc->AddScalarArrayName("delta_y"); + calc->AddScalarArrayName("delta_z"); + calc->SetFunction("delta_x * iHat + delta_y * jHat + delta_z * kHat"); + calc->SetResultArrayName("elevationVector"); + calc->Update(); + + vtkPolyData* result = vtkPolyData::SafeDownCast(calc->GetOutput()); + int outCellFlag; + vtkDataArray* coordArray = vtkAbstractMapper::GetScalars(result, + VTK_SCALAR_MODE_USE_POINT_FIELD_DATA, VTK_GET_ARRAY_BY_NAME/*acc mode*/, + 0/*arr id*/, "elevationVector", outCellFlag); + if (!coordArray) + { + std::cout << "->> Error: could not find array!" << std::endl; + return; + } + + /// Include the elevation vector in the original data + data->GetPointData()->AddArray(coordArray); +} + + +/////////////////////////////////////////////////////////////////////////////// +int TestValuePassFloatingPoint(int argc, char *argv[]) +{ + // Load data + const char *fileName = + vtkTestUtilities::ExpandDataFileName(argc, argv, "Data/dragon.ply"); + vtkSmartPointer reader = + vtkSmartPointer::New(); + reader->SetFileName(fileName); + reader->Update(); + + // Prepare a 3-component array (data will be appended to reader's output) + GenerateElevationArray(reader); + + vtkSmartPointer mapper = + vtkSmartPointer::New(); + mapper->SetInputConnection(reader->GetOutputPort()); + mapper->ScalarVisibilityOn(); + + vtkSmartPointer actor = + vtkSmartPointer::New(); + actor->SetMapper(mapper); + + // Setup rendering and interaction + vtkSmartPointer interactor = + vtkSmartPointer::New(); + + vtkSmartPointer style = + vtkSmartPointer::New(); + interactor->SetInteractorStyle(style); + + vtkSmartPointer window = + vtkSmartPointer::New(); + window->SetSize(320, 320); + + vtkSmartPointer renderer = + vtkSmartPointer::New(); + + window->AddRenderer(renderer); + interactor->SetRenderWindow(window); + + renderer->AddActor(actor); + renderer->SetBackground(0.2, 0.2, 0.5); + + // Setup the value pass + //int const RenderingMode = vtkValuePass::INVERTIBLE_LUT; + int const RenderingMode = vtkValuePass::FLOATING_POINT; + int const comp = 0; + + vtkSmartPointer valuePass = + vtkSmartPointer::New(); + valuePass->SetRenderingMode(RenderingMode); + valuePass->SetInputComponentToProcess(comp); + //valuePass->SetScalarRange(bounds[0], bounds[1]); /*use the full range*/ + valuePass->SetInputArrayToProcess(VTK_SCALAR_MODE_USE_POINT_FIELD_DATA, + "elevationVector"); + + // 3. Add it to a sequence of passes + vtkSmartPointer passes = + vtkSmartPointer::New(); + passes->AddItem(valuePass); + + vtkSmartPointer sequence = + vtkSmartPointer::New(); + sequence->SetPasses(passes); + + vtkSmartPointer cameraPass = + vtkSmartPointer::New(); + cameraPass->SetDelegatePass(sequence); + + vtkOpenGLRenderer *glRenderer = + vtkOpenGLRenderer::SafeDownCast(renderer.GetPointer()); + + // Render the value pass + glRenderer->SetPass(cameraPass); + window->Render(); + + if (RenderingMode == vtkValuePass::FLOATING_POINT) + { + /// Prepare a lut to map the floating point values + vtkSmartPointer lut = vtkSmartPointer::New(); + lut->SetAlpha(1.0); + //lut->SetRange(elevRange[0], elevRange[1]); /*use the full range*/ + vtkSmartPointer series = vtkSmartPointer::New(); + series->SetColorScheme(vtkColorSeries::WARM); + //series->SetColorScheme(vtkColorSeries::BREWER_DIVERGING_SPECTRAL_11); + series->BuildLookupTable(lut, vtkColorSeries::ORDINAL); + + /// Render each component in a separate image + std::vector > colorImages; + for(int c = 0; c < 3; c++) + { + valuePass->SetInputComponentToProcess(c); + window->Render(); + + /// Get the result. + vtkFloatArray* result = valuePass->GetFloatImageData(renderer); + std::vector ext = valuePass->GetFloatImageExtents(renderer); + + /// Map the resulting float image to a color table + vtkUnsignedCharArray* colored = lut->MapScalars(result, VTK_COLOR_MODE_DEFAULT, + 0/* single comp*/); + + /// Create an image dataset to render in a quad. + vtkSmartPointer colorIm = vtkSmartPointer::New(); + colorIm->SetExtent(&(ext.front())); + colorIm->GetPointData()->SetScalars(colored); + colorImages.push_back(colorIm); + } + + // Render the image on-screen + renderer->RemoveActor(actor); + + vtkSmartPointer ia_x = vtkSmartPointer::New(); + ia_x->GetMapper()->SetInputData(colorImages.at(0)); + renderer->AddActor(ia_x); + + vtkSmartPointer ia_y = vtkSmartPointer::New(); + ia_y->RotateX(90); + ia_y->GetMapper()->SetInputData(colorImages.at(1)); + renderer->AddActor(ia_y); + + vtkSmartPointer ia_z = vtkSmartPointer::New(); + ia_z->RotateY(-90); + ia_z->GetMapper()->SetInputData(colorImages.at(2)); + renderer->AddActor(ia_z); + + vtkCamera* cam = renderer->GetActiveCamera(); + cam->SetPosition(2, 2, 2); + cam->SetFocalPoint(0, 0, 1); + renderer->ResetCamera(); + + // Use the default pass to render the colored image. + glRenderer->SetPass(NULL); + window->Render(); + } + + // initialize render loop + int retVal = vtkRegressionTestImage(window.GetPointer()); + if( retVal == vtkRegressionTester::DO_INTERACTOR) + { + interactor->Start(); + } + + return !retVal; +} diff --git a/Rendering/Core/Testing/Data/Baseline/TestValuePassFloatingPoint.png.md5 b/Rendering/Core/Testing/Data/Baseline/TestValuePassFloatingPoint.png.md5 new file mode 100644 index 0000000000..0ccfda72e3 --- /dev/null +++ b/Rendering/Core/Testing/Data/Baseline/TestValuePassFloatingPoint.png.md5 @@ -0,0 +1 @@ +c482c9a67076037525ea716826d91077 diff --git a/Rendering/Core/module.cmake b/Rendering/Core/module.cmake index d66c67d827..17b369e791 100644 --- a/Rendering/Core/module.cmake +++ b/Rendering/Core/module.cmake @@ -6,6 +6,7 @@ vtk_module(vtkRenderingCore vtkIOLegacy vtkIOParallel vtkIOXML + vtkIOPLY vtkTestingCore vtkTestingRendering vtkRendering${VTK_RENDERING_BACKEND} @@ -32,4 +33,4 @@ vtk_module(vtkRenderingCore vtkFiltersGeometry vtkFiltersSources vtksys - ) \ No newline at end of file + ) diff --git a/Rendering/OpenGL2/CMakeLists.txt b/Rendering/OpenGL2/CMakeLists.txt index d2fd4a3567..ad7be0c27f 100644 --- a/Rendering/OpenGL2/CMakeLists.txt +++ b/Rendering/OpenGL2/CMakeLists.txt @@ -77,6 +77,7 @@ set(Module_SRCS vtkTransformFeedback.cxx vtkTranslucentPass.cxx vtkValuePass.cxx + vtkValuePassHelper.cxx vtkVolumetricPass.cxx ) @@ -123,6 +124,7 @@ set_source_files_properties( vtkTextureObject vtkTextureUnitManager vtkTransformFeedback + vtkValuePassHelper WRAP_EXCLUDE) set_source_files_properties( diff --git a/Rendering/OpenGL2/glsl/vtkPolyDataFS.glsl b/Rendering/OpenGL2/glsl/vtkPolyDataFS.glsl index c3a3de53ad..14a02a5ed8 100644 --- a/Rendering/OpenGL2/glsl/vtkPolyDataFS.glsl +++ b/Rendering/OpenGL2/glsl/vtkPolyDataFS.glsl @@ -51,6 +51,9 @@ uniform int PrimitiveIDOffset; // handle coincident offsets //VTK::Coincident::Dec +// Value raster +//VTK::ValuePass::Dec + void main() { // VC position of this fragment. This should not branch/return/discard. diff --git a/Rendering/OpenGL2/glsl/vtkPolyDataVS.glsl b/Rendering/OpenGL2/glsl/vtkPolyDataVS.glsl index c12b879f55..572fcced35 100644 --- a/Rendering/OpenGL2/glsl/vtkPolyDataVS.glsl +++ b/Rendering/OpenGL2/glsl/vtkPolyDataVS.glsl @@ -41,6 +41,9 @@ attribute vec4 vertexMC; // Apple Bug //VTK::PrimID::Dec +// Value raster +//VTK::ValuePass::Dec + void main() { //VTK::Color::Impl @@ -55,5 +58,7 @@ void main() //VTK::PositionVC::Impl + //VTK::ValuePass::Impl + //VTK::Light::Impl } diff --git a/Rendering/OpenGL2/vtkOpenGLPolyDataMapper.cxx b/Rendering/OpenGL2/vtkOpenGLPolyDataMapper.cxx index ab962cf18f..6f4f62b1f1 100644 --- a/Rendering/OpenGL2/vtkOpenGLPolyDataMapper.cxx +++ b/Rendering/OpenGL2/vtkOpenGLPolyDataMapper.cxx @@ -51,7 +51,7 @@ #include "vtkTransform.h" #include "vtkUnsignedIntArray.h" #include "vtkValuePass.h" - +#include "vtkValuePassHelper.h" #include "vtkShadowMapPass.h" // Bring in our fragment lit shader symbols. @@ -115,6 +115,7 @@ vtkOpenGLPolyDataMapper::vtkOpenGLPolyDataMapper() this->TimerQuery = 0; this->ResourceCallback = new vtkOpenGLResourceFreeCallback(this, &vtkOpenGLPolyDataMapper::ReleaseGraphicsResources); + this->ValuePassHelper = vtkSmartPointer::New(); } //----------------------------------------------------------------------------- @@ -579,9 +580,15 @@ void vtkOpenGLPolyDataMapper::ReplaceShaderColor( // the following are always defined variables. We start // by assiging a default value from the uniform std::string colorImpl = - "vec3 ambientColor;\n" + " vec3 ambientColor;\n" " vec3 diffuseColor;\n" " float opacity;\n"; + + if (this->ValuePassHelper->GetRenderingMode() == vtkValuePass::FLOATING_POINT) + { + this->ValuePassHelper->UpdateShaders(VSSource, FSSource, colorImpl); + } + if (this->LastLightComplexity[this->LastBoundBO]) { colorImpl += @@ -793,6 +800,8 @@ void vtkOpenGLPolyDataMapper::ReplaceShaderLight( int lastLightComplexity = this->LastLightComplexity[this->LastBoundBO]; if (info && info->Has(vtkValuePass::RENDER_VALUES())) { + // Although vtkValuePass::FLOATING_POINT does not require this, it is for + // simplicity left unchanged (only required when using INVERTIBLE_LUT mode). lastLightComplexity = 0; } @@ -1478,6 +1487,16 @@ void vtkOpenGLPolyDataMapper::ReplaceShaderValues( //cout << "VS: " << shaders[vtkShader::Vertex]->GetSource() << endl; //cout << "GS: " << shaders[vtkShader::Geometry]->GetSource() << endl; //cout << "FS: " << shaders[vtkShader::Fragment]->GetSource() << endl; + +// std::string vertexShader = shaders[vtkShader::Vertex]->GetSource(); +// std::string fragmentShader = shaders[vtkShader::Fragment]->GetSource(); +// std::ofstream file("/home/alvaro/testShaders/PolyData_valuePass.frag"); +// file << fragmentShader; +// file.close(); +// +// file.open("/home/alvaro/testShaders/PolyData_valuePass.vert"); +// file << vertexShader; +// file.close(); } //----------------------------------------------------------------------------- @@ -1597,6 +1616,32 @@ void vtkOpenGLPolyDataMapper::UpdateShaders( this->BuildShaders(shaders, ren, actor); + +// vtkInformation *info = actor->GetPropertyKeys(); +// if (info && info->Has(vtkValuePass::RENDER_VALUES())) +// { +// // Load shaders +// //--------------- +// std::string path = "/home/alvaro/testShaders/"; +// std::string filename = "pv_values"; +// +// std::string vfilepath = path + filename + ".vert"; +// std::ifstream file(vfilepath.c_str()); +// std::string vert = std::string(std::istreambuf_iterator(file), +// std::istreambuf_iterator()); +// file.close(); +// +// std::string ffilepath = path + filename + ".frag"; +// file.open(ffilepath.c_str()); +// std::string frag = std::string(std::istreambuf_iterator(file), +// std::istreambuf_iterator()); +// file.close(); +// +// shaders[vtkShader::Vertex]->SetSource(vert); +// shaders[vtkShader::Fragment]->SetSource(frag); +// } + + // compile and bind the program if needed vtkShaderProgram *newShader = renWin->GetShaderCache()->ReadyShaderProgram(shaders); @@ -1694,6 +1739,12 @@ void vtkOpenGLPolyDataMapper::SetMapperShaderParameters(vtkOpenGLHelper &cellBO, vtkErrorMacro(<< "Error setting 'appleBugPrimID' in shader VAO."); } } + + if (this->ValuePassHelper->GetRenderingMode() == vtkValuePass::FLOATING_POINT) + { + this->ValuePassHelper->BindValueBuffer(cellBO); + } + cellBO.AttributeUpdateTime.Modified(); } @@ -2291,6 +2342,11 @@ void vtkOpenGLPolyDataMapper::RenderPieceStart(vtkRenderer* ren, vtkActor *actor this->CellNormalTexture->Activate(); } + if (this->ValuePassHelper->GetRenderingMode() == vtkValuePass::FLOATING_POINT) + { + this->ValuePassHelper->UploadValueData(actor, this->CurrentInput); + } + // If we are coloring by texture, then load the texture map. // Use Map as indicator, because texture hangs around. if (this->ColorTextureMap) @@ -2640,22 +2696,10 @@ void vtkOpenGLPolyDataMapper::ComputeBounds() //------------------------------------------------------------------------- void vtkOpenGLPolyDataMapper::UpdateBufferObjects(vtkRenderer *ren, vtkActor *act) { - // First check if the color mapping needs to be changed - vtkInformation *info = act->GetPropertyKeys(); - if (info && info->Has(vtkValuePass::RENDER_VALUES())) - { - this->UseInvertibleColorFor(this->CurrentInput, - info->Get(vtkValuePass::SCALAR_MODE()), - info->Get(vtkValuePass::ARRAY_MODE()), - info->Get(vtkValuePass::ARRAY_ID()), - info->Get(vtkValuePass::ARRAY_NAME()), - info->Get(vtkValuePass::ARRAY_COMPONENT()), - info->Get(vtkValuePass::SCALAR_RANGE())); - } - else - { - this->ClearInvertibleColor(); - } + // Checks for the pass's rendering mode and updates its configuration. + // Depending on the case, updates the mapper's color mapping or allocates + // a buffer. + this->ValuePassHelper->UpdateConfiguration(ren, act, this); // Rebuild buffers if needed if (this->GetNeedToRebuildBufferObjects(ren,act)) diff --git a/Rendering/OpenGL2/vtkOpenGLPolyDataMapper.h b/Rendering/OpenGL2/vtkOpenGLPolyDataMapper.h index df82a6f452..56f2398765 100644 --- a/Rendering/OpenGL2/vtkOpenGLPolyDataMapper.h +++ b/Rendering/OpenGL2/vtkOpenGLPolyDataMapper.h @@ -37,6 +37,7 @@ class vtkOpenGLVertexBufferObject; class vtkTextureObject; class vtkTransform; class vtkGenericOpenGLResourceFreeCallback; +class vtkValuePassHelper; class VTKRENDERINGOPENGL2_EXPORT vtkOpenGLPolyDataMapper : public vtkPolyDataMapper { @@ -182,6 +183,7 @@ public: * the VBO's shift+scale transform. */ void SetVBOShiftScaleMethod(int m); + protected: vtkOpenGLPolyDataMapper(); ~vtkOpenGLPolyDataMapper(); @@ -436,6 +438,7 @@ protected: char *FragmentShaderCode; char *GeometryShaderCode; unsigned int TimerQuery; + vtkSmartPointer ValuePassHelper; private: vtkOpenGLPolyDataMapper(const vtkOpenGLPolyDataMapper&) VTK_DELETE_FUNCTION; diff --git a/Rendering/OpenGL2/vtkValuePass.cxx b/Rendering/OpenGL2/vtkValuePass.cxx index 55012725af..5d785b4c20 100644 --- a/Rendering/OpenGL2/vtkValuePass.cxx +++ b/Rendering/OpenGL2/vtkValuePass.cxx @@ -25,9 +25,15 @@ #include "vtkRenderer.h" #include "vtkRenderState.h" #include "vtkSmartPointer.h" +#include "vtkRenderbuffer.h" +#include "vtkRenderWindow.h" +#include "vtkFloatArray.h" +#include "vtkOpenGLError.h" +#include "vtkOpenGLRenderWindow.h" +#include "vtkFrameBufferObject2.h" #include -#include + vtkStandardNewMacro(vtkValuePass); @@ -50,8 +56,10 @@ public: int Component; double ScalarRange[2]; bool ScalarRangeSet; + vtkSmartPointer ClearPass; vtkInternals() + : ClearPass(vtkSmartPointer::New()) { this->FieldAssociation = 0; this->FieldAttributeType = 0; @@ -66,8 +74,15 @@ public: // ---------------------------------------------------------------------------- vtkValuePass::vtkValuePass() +: RenderingMode(1) +, ValueRenderBO(NULL) +, ValueFrameBO(NULL) +, ValuePassResourcesAllocated(false) { this->Internals = new vtkInternals(); + + this->Size[0] = 0; + this->Size[1] = 0; } // ---------------------------------------------------------------------------- @@ -144,12 +159,17 @@ void vtkValuePass::Render(const vtkRenderState *s) { assert("pre: s_exists" && s!=0); - vtkSmartPointer clear = - vtkSmartPointer::New(); - clear->Render(s); + this->BeginPass(s->GetRenderer()); + + this->Internals->ClearPass->Render(s); this->NumberOfRenderedProps=0; this->RenderOpaqueGeometry(s); + + // vtkFrameBufferObject2 is not supported + //s->SetFrameBuffer(this->ValueFrameBO); + + this->EndPass(); } // ---------------------------------------------------------------------------- @@ -181,7 +201,7 @@ void vtkValuePass::RenderOpaqueGeometry(const vtkRenderState *s) { keys.TakeReference(vtkInformation::New()); } - keys->Set(vtkValuePass::RENDER_VALUES(), 1); + keys->Set(vtkValuePass::RENDER_VALUES(), this->RenderingMode); keys->Set(vtkValuePass::SCALAR_MODE(), this->Internals->FieldAssociation); keys->Set(vtkValuePass::ARRAY_MODE(), this->Internals->FieldNameSet); keys->Set(vtkValuePass::ARRAY_ID(), this->Internals->FieldAttributeType); @@ -221,3 +241,176 @@ void vtkValuePass::RenderOpaqueGeometry(const vtkRenderState *s) ++i; } } + +//------------------------------------------------------------------------------ +void vtkValuePass::BeginPass(vtkRenderer* ren) +{ + switch(this->RenderingMode) + { + case vtkValuePass::FLOATING_POINT: + // Allocate if necessary and bind frame buffer. + this->InitializeFloatingPointMode(ren); + this->ValueFrameBO->Bind(GL_DRAW_FRAMEBUFFER); + break; + + case vtkValuePass::INVERTIBLE_LUT: + default: + // Cleanup in case FLOATING_POINT was active. + this->ReleaseFloatingPointMode(ren); + break; + } +} + +//------------------------------------------------------------------------------ +void vtkValuePass::EndPass() +{ + switch(this->RenderingMode) + { + case vtkValuePass::FLOATING_POINT: + // Unbind the float FBO and glReadPixels to host side. + this->ValueFrameBO->UnBind(GL_DRAW_FRAMEBUFFER); + break; + + case vtkValuePass::INVERTIBLE_LUT: + default: + // Nothing to do in this mode. + break; + } +} + +//------------------------------------------------------------------------------ +void vtkValuePass::InitializeFloatingPointMode(vtkRenderer* ren) +{ + if (this->ValuePassResourcesAllocated) + return; + + vtkRenderWindow* renWin = ren->GetRenderWindow(); + if (!this->IsFloatFBOSupported(renWin)) + return; + + // Allocate FBO's Color attachment target + int* size = ren->GetSize(); + this->Size[0] = size[0]; + this->Size[1] = size[1]; + + this->ValueRenderBO = vtkRenderbuffer::New(); + this->ValueRenderBO->SetContext(renWin); + this->ValueRenderBO->CreateColorAttachment(this->Size[0], this->Size[1]); + + // Initialize the FBO into which the float value pass is rendered. + this->ValueFrameBO = vtkFrameBufferObject2::New(); + this->ValueFrameBO->SetContext(renWin); + this->ValueFrameBO->Bind(GL_FRAMEBUFFER); + this->ValueFrameBO->InitializeViewport(this->Size[0], this->Size[1]); + /* GL_COLOR_ATTACHMENT0 */ + this->ValueFrameBO->AddColorAttachment(GL_FRAMEBUFFER, 0, this->ValueRenderBO); + + // Verify FBO + if(!this->ValueFrameBO->CheckFrameBufferStatus(GL_FRAMEBUFFER)) + { + vtkErrorMacro("Failed to attach FBO."); + this->ReleaseFloatingPointMode(ren); + } + + this->ValueFrameBO->UnBind(GL_FRAMEBUFFER); + this->ValuePassResourcesAllocated = true; +} + +//----------------------------------------------------------------------------- +void vtkValuePass::ReleaseFloatingPointMode(vtkRenderer* ren) +{ + if (!this->ValuePassResourcesAllocated) + return; + + vtkRenderWindow* renWin = ren->GetRenderWindow(); + renWin->MakeCurrent(); + + // Cleanup FBO (grahpics resources cleaned internally) + this->ValueFrameBO->Delete(); + this->ValueFrameBO = NULL; + + this->ValueRenderBO->Delete(); + this->ValueRenderBO = NULL; + + this->ValuePassResourcesAllocated = false; +} + +//----------------------------------------------------------------------------- +bool vtkValuePass::IsFloatFBOSupported(vtkRenderWindow *renWin) +{ + vtkOpenGLRenderWindow *context = vtkOpenGLRenderWindow::SafeDownCast(renWin); + if (!context) + { + vtkErrorMacro(<< "Support for " << renWin->GetClassName() + << " not implemented"); + return false; + } + +#if GL_ES_VERSION_2_0 != 1 + bool contextSupport = vtkOpenGLRenderWindow::GetContextSupportsOpenGL32(); + if (!contextSupport) + { + vtkWarningMacro(<< "Context does not support OpenGL core profile 3.2. " + << " Will check extension support."); + } + + bool extSupport = glewIsSupported("GL_EXT_framebuffer_object") && + glewIsSupported("GL_ARB_texture_float"); + if (!extSupport) + { + vtkWarningMacro(<< "EXT_framebuffer_object or ARB_texture_float not" + << " supported."); + } + + return contextSupport || extSupport; +#else + return true; +#endif +} + +//------------------------------------------------------------------------------ +vtkFloatArray* vtkValuePass::GetFloatImageData(vtkRenderer* ren) +{ + vtkRenderWindow* renWin = ren->GetRenderWindow(); + renWin->MakeCurrent(); + + //Allocate output array. + vtkFloatArray* pixels = vtkFloatArray::New(); + pixels->SetNumberOfValues(this->Size[0] * this->Size[1] /* * numComponents */); + + // Prepare and bind value texture and FBO. + this->ValueFrameBO->Bind(GL_READ_FRAMEBUFFER); + + GLint originalReadBuff; + glGetIntegerv(GL_READ_BUFFER, &originalReadBuff); + glReadBuffer(GL_COLOR_ATTACHMENT0); + + // Calling pack alignment ensures any window size can be grabbed. + glPixelStorei(GL_PACK_ALIGNMENT, 1); + glClampColor(GL_CLAMP_READ_COLOR, GL_FALSE); + + glReadPixels(0, 0, this->Size[0], this->Size[1], GL_RED, GL_FLOAT, + pixels->GetVoidPointer(0)); + + glReadBuffer(originalReadBuff); + this->ValueFrameBO->UnBind(GL_READ_FRAMEBUFFER); + + vtkOpenGLCheckErrorMacro("Failed to read pixels from OpenGL buffer!"); + return pixels; +} + +//------------------------------------------------------------------------------- +std::vector vtkValuePass::GetFloatImageExtents(vtkRenderer* ren) +{ + vtkRenderWindow* renWin = ren->GetRenderWindow(); + renWin->MakeCurrent(); + int* size = ren->GetSize(); + + std::vector ext; + ext.reserve(6); + ext.push_back(0); ext.push_back(this->Size[0] - 1); + ext.push_back(0); ext.push_back(this->Size[1] - 1); + ext.push_back(0); ext.push_back(0); + + return ext; +} diff --git a/Rendering/OpenGL2/vtkValuePass.h b/Rendering/OpenGL2/vtkValuePass.h index 9886637685..50cbacf8d6 100644 --- a/Rendering/OpenGL2/vtkValuePass.h +++ b/Rendering/OpenGL2/vtkValuePass.h @@ -22,22 +22,36 @@ #ifndef vtkValuePass_h #define vtkValuePass_h +#include + #include "vtkRenderingOpenGL2Module.h" // For export macro #include "vtkDefaultPass.h" class vtkInformationDoubleVectorKey; class vtkInformationIntegerKey; class vtkInformationStringKey; +class vtkRenderer; +class vtkRenderWindow; +class vtkFrameBufferObject2; +class vtkRenderbuffer; +class vtkFloatArray; class VTKRENDERINGOPENGL2_EXPORT vtkValuePass : public vtkDefaultPass { public: + + enum Mode { + INVERTIBLE_LUT = 1, + FLOATING_POINT = 2 }; + static vtkValuePass *New(); vtkTypeMacro(vtkValuePass, vtkDefaultPass); void PrintSelf(ostream& os, vtkIndent indent); static vtkInformationIntegerKey *RENDER_VALUES(); + vtkSetMacro(RenderingMode, int); + vtkGetMacro(RenderingMode, int); void SetInputArrayToProcess(int fieldAssociation, const char *name); void SetInputArrayToProcess(int fieldAssociation, int fieldAttributeType); void SetInputComponentToProcess(int component); @@ -57,6 +71,10 @@ public: // \pre s_exists: s!=0 virtual void Render(const vtkRenderState *s); + /// \brief Interface to get the result of a render pass in FLOATING_POINT mode. + vtkFloatArray* GetFloatImageData(vtkRenderer* ren); + std::vector GetFloatImageExtents(vtkRenderer* ren); + protected: // Description: // Default constructor. @@ -71,12 +89,38 @@ public: // \pre s_exists: s!=0 virtual void RenderOpaqueGeometry(const vtkRenderState *s); + // Description: + // Manages graphics resources depending on the rendering mode. Binds internal + // FBO when FLOATING_POINT mode is enabled. + void BeginPass(vtkRenderer* ren); + + // Description: + // Unbinds internal FBO when FLOATING_POINT mode is enabled. + void EndPass(); + + /// \brief Member methods managing graphics resources required during FLOATING_POINT + /// mode. + bool IsFloatFBOSupported(vtkRenderWindow* renWin); + void InitializeFloatingPointMode(vtkRenderer* ren); + void ReleaseFloatingPointMode(vtkRenderer* ren); + +/////////////////////////////////////////////////////////////////////////////// + class vtkInternals; vtkInternals *Internals; + int RenderingMode; + + /// \brief FLOATING_POINT mode resources. FBO, attachments and other + /// control variables. + vtkFrameBufferObject2* ValueFrameBO; + vtkRenderbuffer* ValueRenderBO; + int Size[2]; + bool ValuePassResourcesAllocated; private: vtkValuePass(const vtkValuePass&) VTK_DELETE_FUNCTION; void operator=(const vtkValuePass&) VTK_DELETE_FUNCTION; + }; #endif diff --git a/Rendering/OpenGL2/vtkValuePassHelper.cxx b/Rendering/OpenGL2/vtkValuePassHelper.cxx new file mode 100644 index 0000000000..23363e1319 --- /dev/null +++ b/Rendering/OpenGL2/vtkValuePassHelper.cxx @@ -0,0 +1,191 @@ +#include "vtkValuePassHelper.h" +#include "vtkObjectFactory.h" +#include "vtkRenderer.h" +#include "vtkShaderProgram.h" +#include "vtkOpenGLError.h" +#include "vtkOpenGLRenderWindow.h" +#include "vtkOpenGLBufferObject.h" +#include "vtkOpenGLHelper.h" +#include "vtkOpenGLVertexArrayObject.h" +#include "vtkValuePass.h" +#include "vtkDataArray.h" +#include "vtkInformation.h" +#include "vtkMapper.h" + + +vtkStandardNewMacro(vtkValuePassHelper) + +// ---------------------------------------------------------------------------- +vtkValuePassHelper::vtkValuePassHelper() +: ValueBuffer(NULL) +, ValuePassArray(NULL) +, RenderingMode(-1) +{ +} + +//----------------------------------------------------------------------------- +vtkValuePassHelper::~vtkValuePassHelper() +{ + if (this->ValueBuffer) + this->ValueBuffer->Delete(); +} + +//----------------------------------------------------------------------------- +void vtkValuePassHelper::UploadValueData(vtkActor* actor, vtkDataSet* input) +{ + vtkInformation *info = actor->GetPropertyKeys(); + + // TODO: Check the Array number / name instead. info does not seem to update + // the timestamp. + //if (info->GetMTime() < this->ValueBufferTime) + { + int cellFlag = 0; + typedef vtkValuePass vp; + this->ValuePassArray = vtkAbstractMapper::GetScalars(input, + info->Get(vp::SCALAR_MODE()), info->Get(vp::ARRAY_MODE()), + info->Get(vp::ARRAY_ID()), info->Get(vp::ARRAY_NAME()), cellFlag); + if (!this->ValuePassArray) + { + vtkErrorMacro("Invalid data array from GetScalars()!"); + return; + } + + // Extract the current component value from the array. + vtkIdType const numTuples = this->ValuePassArray->GetNumberOfTuples(); + int const compIndex = info->Get(vp::ARRAY_COMPONENT()); + this->Buffer.clear(); + this->Buffer.reserve(numTuples); + + for (vtkIdType id = 0; id < numTuples; id++) + { + double* tuple = this->ValuePassArray->GetTuple(id); + float value = static_cast(tuple[compIndex]); + this->Buffer.push_back(value); + } + + // Upload array data + this->ValueBuffer->Upload(&(this->Buffer.front()), numTuples, + vtkOpenGLBufferObject::ArrayBuffer); + + //this->ValueBufferTime.Modified(); + } +} + +//----------------------------------------------------------------------------- +void vtkValuePassHelper::UpdateConfiguration(vtkRenderer* ren, vtkActor* act, + vtkMapper* mapper) +{ + this->RenderingMode = -1; + vtkInformation *info = act->GetPropertyKeys(); + if (info && info->Has(vtkValuePass::RENDER_VALUES())) + { + this->RenderingMode = info->Get(vtkValuePass::RENDER_VALUES()); + } + + // Configure the mapper's behavior if the ValuePass is active. + if (this->RenderingMode > 0) + { + switch (this->RenderingMode) + { + case vtkValuePass::FLOATING_POINT: + this->AllocateBuffer(ren); + break; + + case vtkValuePass::INVERTIBLE_LUT: + default: + { + vtkInformation* info = act->GetPropertyKeys(); + mapper->UseInvertibleColorFor(info->Get(vtkValuePass::SCALAR_MODE()), + info->Get(vtkValuePass::ARRAY_MODE()), + info->Get(vtkValuePass::ARRAY_ID()), + info->Get(vtkValuePass::ARRAY_NAME()), + info->Get(vtkValuePass::ARRAY_COMPONENT()), + info->Get(vtkValuePass::SCALAR_RANGE())); + } + break; + } + } + else + { + this->ReleaseBuffer(ren); + mapper->ClearInvertibleColor(); + } +} + +//----------------------------------------------------------------------------- +void vtkValuePassHelper::AllocateBuffer(vtkRenderer* ren) +{ + if (this->ValueBuffer) + return; + + this->ValueBuffer = vtkOpenGLBufferObject::New(); + this->ValueBuffer->SetType(vtkOpenGLBufferObject::ArrayBuffer); +} + +//----------------------------------------------------------------------------- +void vtkValuePassHelper::ReleaseBuffer(vtkRenderer* ren) +{ + if (!this->ValueBuffer) + return; + + vtkRenderWindow* renWin = ren->GetRenderWindow(); + vtkOpenGLRenderWindow* glWin = static_cast(renWin); + glWin->MakeCurrent(); + + // Cleanup GL buffer + if (this->ValueBuffer) + { + this->ValueBuffer->ReleaseGraphicsResources(); + this->ValueBuffer->Delete(); + this->ValueBuffer = NULL; + vtkOpenGLCheckErrorMacro("Failed to release ValueBuffer resources."); + } + + this->ValuePassArray = NULL; +} + +//----------------------------------------------------------------------------- +void vtkValuePassHelper::UpdateShaders(std::string & VSSource, std::string & FSSource, + std::string & required) +{ + // Pass the value pass attribute to the fragment shader. + vtkShaderProgram::Substitute(VSSource, "//VTK::ValuePass::Dec", + "attribute float dataAttribute;\n" + "varying float dataValue;\n"); + + vtkShaderProgram::Substitute(VSSource, "//VTK::ValuePass::Impl", + " dataValue = dataAttribute;\n"); + + // Render floating point values (variables in 'required' are a requirement in + // other sections of the fragment shader, so they are included for it to build + // correctly). + vtkShaderProgram::Substitute(FSSource, "//VTK::ValuePass::Dec", + "varying float dataValue;\n"); + + required += std::string( + " vec4 texColor = vec4(vec3(dataValue), 1.0);\n" + " gl_FragData[0] = texColor;\n" + " // Return right away since vtkValuePass::FLOATING_POINT mode is enabled\n" + " return;"); + vtkShaderProgram::Substitute(FSSource, "//VTK::Color::Impl", required); +} + +//----------------------------------------------------------------------------- +void vtkValuePassHelper::BindValueBuffer(vtkOpenGLHelper& cellBO) +{ + if (this->ValuePassArray) + { + if (cellBO.Program->IsAttributeUsed("dataAttribute")) + { + int const elementType = this->ValuePassArray->GetDataType(); + size_t const stride = sizeof(float); + + if (!cellBO.VAO->AddAttributeArray(cellBO.Program, this->ValueBuffer, + "dataAttribute", 0, stride, VTK_FLOAT, 1, false)) + { + vtkErrorMacro(<< "Error setting 'dataAttribute' in shader VAO."); + } + } + } + vtkOpenGLCheckErrorMacro("Failed in SetupValueBuffer!"); +} diff --git a/Rendering/OpenGL2/vtkValuePassHelper.h b/Rendering/OpenGL2/vtkValuePassHelper.h new file mode 100644 index 0000000000..b49b184e26 --- /dev/null +++ b/Rendering/OpenGL2/vtkValuePassHelper.h @@ -0,0 +1,54 @@ +#include + +#include "vtkObject.h" +#include "vtkRenderingOpenGL2Module.h" + + +class vtkOpenGLBufferObject; +//class vtkTimeStamp; +class vtkDataArray; +class vtkRenderer; +class vtkActor; +class vtkDataSet; +class vtkOpenGLHelper; +class vtkFloatArray; +class vtkMapper; + + +class VTKRENDERINGOPENGL2_EXPORT vtkValuePassHelper : public vtkObject +{ + friend class vtkOpenGLPolyDataMapper; + +public: + + static vtkValuePassHelper* New(); + +protected: + + vtkValuePassHelper(); + ~vtkValuePassHelper(); + + vtkGetMacro(RenderingMode, int); + + void UpdateConfiguration(vtkRenderer* ren, vtkActor* act, vtkMapper* mapper); + void UploadValueData(vtkActor* actor, vtkDataSet* input); + void UpdateShaders(std::string & VSSource, std::string & FSSource, + std::string & required); + void BindValueBuffer(vtkOpenGLHelper& cellBO); + +private: + + vtkValuePassHelper(const vtkValuePassHelper &); // Not implemented. + void operator=(const vtkValuePassHelper &); // Not implemented. + + void AllocateBuffer(vtkRenderer* ren); + void ReleaseBuffer(vtkRenderer* ren); + +//////////////////////////////////////////////////////////////////////////////// + + //vtkTimeStamp ValueBufferTime; + vtkOpenGLBufferObject* ValueBuffer; + vtkDataArray* ValuePassArray; + std::vector Buffer; + int RenderingMode; +}; -- 2.22.0