// SPDX-FileCopyrightText: Copyright (c) Ken Martin, Will Schroeder, Bill Lorensen // SPDX-License-Identifier: BSD-3-Clause #include "vtkOpenGLSurfaceProbeVolumeMapper.h" #include "vtkCommand.h" #include "vtkExecutive.h" #include "vtkFloatArray.h" #include "vtkImageData.h" #include "vtkInformation.h" #include "vtkObjectFactory.h" #include "vtkOpenGLFramebufferObject.h" #include "vtkOpenGLRenderWindow.h" #include "vtkOpenGLResourceFreeCallback.h" #include "vtkOpenGLState.h" #include "vtkOpenGLTexture.h" #include "vtkOpenGLVertexBufferObject.h" #include "vtkPointData.h" #include "vtkPolyData.h" #include "vtkRenderer.h" #include "vtkScalarsToColors.h" #include "vtkShaderProgram.h" #include "vtkShaderProperty.h" #include "vtkTextureObject.h" #include "vtkVolumeTexture.h" #include VTK_ABI_NAMESPACE_BEGIN vtkStandardNewMacro(vtkOpenGLSurfaceProbeVolumeMapper); //------------------------------------------------------------------------------ vtkOpenGLSurfaceProbeVolumeMapper::vtkOpenGLSurfaceProbeVolumeMapper() { this->SetNumberOfInputPorts(3); } //------------------------------------------------------------------------------ vtkOpenGLSurfaceProbeVolumeMapper::~vtkOpenGLSurfaceProbeVolumeMapper() = default; //------------------------------------------------------------------------------ int vtkOpenGLSurfaceProbeVolumeMapper::FillInputPortInformation(int port, vtkInformation* info) { if (port == 0) { info->Set(vtkAlgorithm::INPUT_REQUIRED_DATA_TYPE(), "vtkPolyData"); return 1; } else if (port == 1) { info->Set(vtkAlgorithm::INPUT_REQUIRED_DATA_TYPE(), "vtkImageData"); return 1; } else if (port == 2) { info->Set(vtkAlgorithm::INPUT_IS_OPTIONAL(), 1); info->Set(vtkAlgorithm::INPUT_REQUIRED_DATA_TYPE(), "vtkPolyData"); return 1; } return 0; } //------------------------------------------------------------------------------ void vtkOpenGLSurfaceProbeVolumeMapper::SetProbeInputData(vtkPolyData* in) { this->SetInputDataObject(2, in); } //------------------------------------------------------------------------------ vtkPolyData* vtkOpenGLSurfaceProbeVolumeMapper::GetProbeInput() { if (this->GetNumberOfInputConnections(2) < 1) { return nullptr; } return vtkPolyData::SafeDownCast(this->GetExecutive()->GetInputData(2, 0)); } //------------------------------------------------------------------------------ void vtkOpenGLSurfaceProbeVolumeMapper::SetProbeInputConnection(vtkAlgorithmOutput* algOutput) { this->SetInputConnection(2, algOutput); } //------------------------------------------------------------------------------ void vtkOpenGLSurfaceProbeVolumeMapper::SetSourceData(vtkImageData* in) { this->SetInputDataObject(1, in); } //------------------------------------------------------------------------------ vtkImageData* vtkOpenGLSurfaceProbeVolumeMapper::GetSource() { if (this->GetNumberOfInputConnections(1) < 1) { return nullptr; } return vtkImageData::SafeDownCast(this->GetExecutive()->GetInputData(1, 0)); } //------------------------------------------------------------------------------ void vtkOpenGLSurfaceProbeVolumeMapper::SetSourceConnection(vtkAlgorithmOutput* algOutput) { this->SetInputConnection(1, algOutput); } //------------------------------------------------------------------------------ void vtkOpenGLSurfaceProbeVolumeMapper::RenderPiece(vtkRenderer* ren, vtkActor* actor) { // Make sure that we have been properly initialized. if (ren->GetRenderWindow()->CheckAbortStatus()) { return; } this->ResourceCallback->RegisterGraphicsResources( static_cast(ren->GetRenderWindow())); // The second input is used for probing if it exists. // The first input is always the one being rendered to avoid having to recompute bounds. vtkPolyData* secondInput = vtkPolyData::SafeDownCast(this->GetExecutive()->GetInputData(2, 0)); if (secondInput) { this->CurrentInput = secondInput; } else { this->CurrentInput = this->GetInput(); } // Source volume being probed vtkImageData* sourceInput = vtkImageData::SafeDownCast(this->GetExecutive()->GetInputData(1, 0)); if (this->CurrentInput == nullptr || sourceInput == nullptr) { vtkErrorMacro(<< "No input or source!"); return; } this->InvokeEvent(vtkCommand::StartEvent, nullptr); if (!this->Static) { this->GetInputAlgorithm()->Update(); // Update probed volume this->GetInputAlgorithm(1, 0)->Update(); // Update probe surface if (secondInput) { this->GetInputAlgorithm(2, 0)->Update(); } } this->InvokeEvent(vtkCommand::EndEvent, nullptr); // if there are no points then we are done if (!this->CurrentInput->GetPoints()) { return; } this->UpdateCameraShiftScale(ren, actor); this->RenderPieceStart(ren, actor); // 1. Position texture pass this->ReplaceShaderPositionPass(actor); // Render positions and normals into FBO textures this->ReplaceActiveFBO(ren); this->RenderPieceDraw(ren, actor); this->RestoreActiveFBO(ren); // Clear position pass shader replacements // WARNING: This has the side-effect of clearing the user's shader replacement. // To prevent this we should use ClearVertexShaderReplacements/ClearFragmentShaderReplacements // with the original strings used in ReplaceShaderPositionPass. actor->GetShaderProperty()->ClearAllVertexShaderReplacements(); actor->GetShaderProperty()->ClearAllFragmentShaderReplacements(); // 2. Probe pass // Replace input this->CurrentInput = this->GetInput(); if (this->CurrentInput == nullptr) { vtkErrorMacro(<< "No input!"); return; } // if there are no points then we are done if (!this->CurrentInput->GetPoints()) { return; } this->RenderPieceStart(ren, actor); this->ReplaceShaderProbePass(actor); this->RenderPieceDraw(ren, actor); // Deactivate textures used in probe pass this->PositionsTextureObject->Deactivate(); if (this->GetBlendMode() != BlendModes::NONE) { this->NormalsTextureObject->Deactivate(); } this->VolumeTexture->GetCurrentBlock()->TextureObject->Deactivate(); this->RenderPieceFinish(ren, actor); // Clear probe pass shader replacements // WARNING: This has the side-effect of clearing the user's shader replacement. // To prevent this we should use ClearVertexShaderReplacements/ClearFragmentShaderReplacements // with the original strings used in ReplaceShaderProbePass. actor->GetShaderProperty()->ClearAllVertexShaderReplacements(); actor->GetShaderProperty()->ClearAllFragmentShaderReplacements(); } //------------------------------------------------------------------------------ void vtkOpenGLSurfaceProbeVolumeMapper::UpdateShaders( vtkOpenGLHelper& cellBO, vtkRenderer* ren, vtkActor* act) { vtkOpenGLPolyDataMapper::UpdateShaders(cellBO, ren, act); // Update uniforms according to the current pass switch (this->CurrentPass) { case PassTypes::POSITION_TEXTURE: // Handle VBO shift and scale only when the actor matrix is identity. // Otherwise VBOShiftScale is already multiplied with the actor matrix in the base class. if (act->GetIsIdentity() && !this->VBOShiftScale->IsIdentity()) { cellBO.Program->SetUniformMatrix("MCWCMatrix", this->VBOShiftScale); } break; case PassTypes::PROBE: this->UpdateShadersProbePass(cellBO, ren); break; default: break; } } //------------------------------------------------------------------------------ void vtkOpenGLSurfaceProbeVolumeMapper::ReplaceShaderPositionPass(vtkActor* actor) { // Position implementation. // Expect TCoords in the [0, 1] range and normalize them to define the fragment position. // The vertex position is passed to the fragment shader to be written in the texture. std::string positionImpl = "gl_Position = vec4(tcoord.x * 2.0 - 1.0, tcoord.y * 2.0 - 1.0, 0.0, 1.0);\n"; if (!this->VBOShiftScale->IsIdentity() || !actor->GetIsIdentity()) { actor->GetShaderProperty()->AddVertexShaderReplacement("//VTK::PositionVC::Dec", true, "//VTK::PositionVC::Dec\n" "uniform mat4 MCWCMatrix;\n", true); positionImpl += "vertexVCVSOutput = MCWCMatrix * vertexMC;\n"; } else { positionImpl += "vertexVCVSOutput = vertexMC;\n"; } actor->GetShaderProperty()->AddVertexShaderReplacement( "//VTK::PositionVC::Impl", true, positionImpl, true); // TCoords attribute are always uploaded to the GPU when they exist in the superclass, but // tcoord is only defined in the shader when the actor is textured. Force the declaration here. actor->GetShaderProperty()->AddVertexShaderReplacement( "//VTK::TCoord::Dec", false, "in vec2 tcoord;", true); // Write vertex position in texture // Override gl_FragData set in Light::Impl and TCoord::Impl std::string tcoordsImpl = "gl_FragData[0] = vertexVCVSOutput;\n"; // Blending requires normals if (this->GetBlendMode() != BlendModes::NONE) { // Pass normals from the vertex to the fragment shader actor->GetShaderProperty()->AddVertexShaderReplacement( "//VTK::Normal::Impl", true, "normalVCVSOutput = normalMC;\n", true); // Write normals in additional render target tcoordsImpl += "gl_FragData[1] = vec4(normalVCVSOutput, 0.0);\n"; } actor->GetShaderProperty()->AddFragmentShaderReplacement( "//VTK::TCoord::Impl", true, tcoordsImpl, true); // Prevent OIT pass from overriding gl_FragData values actor->GetShaderProperty()->AddFragmentShaderReplacement( "//VTK::DepthPeeling::Impl", true, "", true); // Switch to positions/normals pass to update shaders uniforms accordingly this->CurrentPass = PassTypes::POSITION_TEXTURE; } //------------------------------------------------------------------------------ void vtkOpenGLSurfaceProbeVolumeMapper::ReplaceShaderProbePass(vtkActor* actor) { // Pass texture coordinates from vertex shader to fragment shader actor->GetShaderProperty()->AddVertexShaderReplacement("//VTK::TCoord::Dec", true, "in vec2 tcoord;\n" "out vec2 tcoordVCVSOutput;\n", true); actor->GetShaderProperty()->AddVertexShaderReplacement( "//VTK::TCoord::Impl", true, "tcoordVCVSOutput = tcoord;\n", true); // Textures and coloring declaration std::string tmapDec = "//VTK::TMap::Dec\n" // keep default replacement "uniform sampler2D positionTexture;\n" "uniform sampler3D in_volume;\n" "uniform mat4 in_inverseTextureDatasetMatrix;\n" "uniform mat4 in_cellToPoint;\n" // Coloring "uniform vec4 in_volume_scale;\n" "uniform vec4 in_volume_bias;\n" "uniform float in_window;\n" "uniform float in_level;\n"; // Blending requires normals and blending uniforms if (this->GetBlendMode() != BlendModes::NONE) { tmapDec += "uniform sampler2D normalTexture;\n" "uniform vec3 in_volume_spacing;\n" "uniform float blend_width;\n"; } actor->GetShaderProperty()->AddFragmentShaderReplacement("//VTK::TMap::Dec", true, tmapDec, true); actor->GetShaderProperty()->AddFragmentShaderReplacement("//VTK::TCoord::Dec", true, "//VTK::TCoord::Dec\n" "in vec2 tcoordVCVSOutput;\n" // Window/Level declaration "vec4 applyWindowLevel(vec4 color)\n" "{\n" " float l = in_level; \n" " float w = in_window;\n" " float s = w > 0.0 ? 0.5 : -0.5;\n" // handle negative window " color = clamp(color, l - s * w, l + s * w);\n" " return (color - (l - 0.5 * w)) / w;\n" "}\n", true); std::string tcoordsImpl = // Get the current fragment position on the probe surface "vec3 fragmentPos = texture2D(positionTexture, tcoordVCVSOutput).xyz;\n" // Background value when sampling outside volume. // WARNING: Initialization to 0 currently required for average blending "vec4 volumeValue = vec4(0, 0, 0, 0);\n" "int sampleCount = 0;\n"; // Keep track of the number of samples for blending if (this->GetBlendMode() == BlendModes::NONE) { tcoordsImpl += "vec3 texPos = (in_cellToPoint * in_inverseTextureDatasetMatrix * vec4(fragmentPos.xyz, " "1.0)).xyz;\n" "if ((all(lessThanEqual(texPos, vec3(1.0))) &&\n" " all(greaterThanEqual(texPos, vec3(0.0)))))\n" "{\n" " volumeValue = texture3D(in_volume, texPos) * in_volume_scale[0] + in_volume_bias[0];\n" " sampleCount++;\n" "}\n"; } else // Blend modes { tcoordsImpl += "float epsilon = 1e-7;\n" // Get the current fragment normal on the probe surface "vec3 fragmentNormal = texture2D(normalTexture, tcoordVCVSOutput).xyz;\n" "fragmentNormal = normalize(fragmentNormal);\n" // Use the half of the minimum spacing values as sampling step. "float spacing = 0.5 * min(min(in_volume_spacing[0], in_volume_spacing[1]), " "in_volume_spacing[2]);\n" "spacing = max(spacing, epsilon);\n" // Force positive spacing to avoid infinite loop below "float offset = -0.5 * (blend_width + epsilon);\n" "while(offset < 0.5 * (blend_width + epsilon))\n" "{" " vec3 pos = fragmentPos + offset * fragmentNormal;\n" " vec3 texPos = (in_cellToPoint * in_inverseTextureDatasetMatrix * vec4(pos.xyz, " "1.0)).xyz;\n" " if ((all(lessThanEqual(texPos, vec3(1.0))) && \n" " all(greaterThanEqual(texPos, vec3(0.0)))))\n" " {\n" " vec4 currentColor = texture3D(in_volume, texPos) * in_volume_scale[0] + " "in_volume_bias[0];\n"; switch (this->GetBlendMode()) { case BlendModes::MAX: tcoordsImpl += " volumeValue = max(currentColor, sampleCount > 0 ? volumeValue : vec4(0.0));\n"; break; case BlendModes::MIN: tcoordsImpl += " volumeValue = min(currentColor, sampleCount > 0 ? volumeValue : vec4(1.0));\n"; break; case BlendModes::AVERAGE: tcoordsImpl += " volumeValue += currentColor;\n"; break; default: break; } tcoordsImpl += " sampleCount++;\n" " }" " offset += spacing;" "}"; } if (this->GetBlendMode() == BlendModes::AVERAGE) { tcoordsImpl += "if (sampleCount > 0)\n" "{\n" " volumeValue = volumeValue / sampleCount;\n" "}\n"; } switch (this->GetSource()->GetNumberOfScalarComponents()) { case 1: // Grayscale volume source tcoordsImpl += "if (sampleCount > 0)\n" "{\n" " volumeValue = applyWindowLevel(volumeValue);\n" "}\n"; if (this->LookupTable) { // Scalar coloring with LUT tcoordsImpl += "vec2 volumeColorTCoord = vec2(volumeValue.r, 0.49);\n" "vec4 lutColor = texture(colortexture, volumeColorTCoord.st);\n" "lutColor.a *= opacityUniform;\n" "gl_FragData[0] = vec4(lutColor);\n"; } else { tcoordsImpl += "gl_FragData[0] = vec4(volumeValue.r, volumeValue.r, volumeValue.r, opacityUniform);\n"; } break; case 3: // RGB volume source tcoordsImpl += "gl_FragData[0] = vec4(volumeValue.rgb, opacityUniform);\n"; break; case 4: // RGBA volume source tcoordsImpl += "gl_FragData[0] = vec4(volumeValue.rgb, volumeValue.a * opacityUniform);\n"; break; default: vtkErrorMacro("Unsupported number of components in source. Use 1, 3 or 4-components images."); break; } actor->GetShaderProperty()->AddFragmentShaderReplacement( "//VTK::TCoord::Impl", true, tcoordsImpl, true); // Switch to probing pass to update shaders uniforms accordingly this->CurrentPass = PassTypes::PROBE; } //------------------------------------------------------------------------------ void vtkOpenGLSurfaceProbeVolumeMapper::UpdateShadersProbePass( vtkOpenGLHelper& cellBO, vtkRenderer* ren) { if (!this->VolumeTexture->GetLoadedScalars()) { vtkImageData* sourceInput = this->GetSource(); // The extent of the volume must start at (0,0,0) // see "void vtkGPUVolumeRayCastMapper::TransformInput" this->TransformedSource->ShallowCopy(sourceInput); // Get the current extents. int extents[6]; this->TransformedSource->GetExtent(extents); // Get the current origin and spacing. double origin[3], spacing[3]; this->TransformedSource->GetOrigin(origin); this->TransformedSource->GetSpacing(spacing); for (int cc = 0; cc < 3; cc++) { // Transform the origin and the extents. origin[cc] = origin[cc] + extents[2 * cc] * spacing[cc]; extents[2 * cc + 1] -= extents[2 * cc]; extents[2 * cc] = 0; } this->TransformedSource->SetOrigin(origin); this->TransformedSource->SetExtent(extents); // Always use scalar point data. // Mimic vtkAbstractVolumeMapper::GetScalars to handle array access and cell scalars if needed. vtkDataArray* scalars = this->TransformedSource->GetPointData()->GetScalars(); int isCellData = 0; // Load volume this->VolumeTexture->LoadVolume( ren, this->TransformedSource, scalars, isCellData, VTK_LINEAR_INTERPOLATION); } std::vector VolMatVec = {}; std::vector InvTexMatVec = {}; std::vector CellToPointVec = {}; VolMatVec.resize(16, 0); InvTexMatVec.resize(16, 0); CellToPointVec.resize(16, 0); vtkNew texToDataMat; texToDataMat->DeepCopy(this->VolumeTexture->GetCurrentBlock()->TextureToDataset.GetPointer()); texToDataMat->Transpose(); texToDataMat->Invert(); vtkNew cellToPointMat; cellToPointMat->DeepCopy(this->VolumeTexture->CellToPointMatrix.GetPointer()); cellToPointMat->Transpose(); for (int i = 0; i < 16; i++) { InvTexMatVec[i] = texToDataMat->Element[i / 4][i % 4]; CellToPointVec[i] = cellToPointMat->Element[i / 4][i % 4]; } cellBO.Program->SetUniformMatrix4x4("in_inverseTextureDatasetMatrix", InvTexMatVec.data()); cellBO.Program->SetUniformMatrix4x4("in_cellToPoint", CellToPointVec.data()); // LargeDataTypes have been already biased and scaled in vtkVolumeTexture std::array volume_scale = { 1.0f, 1.0f, 1.0f, 1.0f }; std::array volume_bias = { 0.0f, 0.0f, 0.0f, 0.0f }; int nbOfComponents = this->GetSource()->GetNumberOfScalarComponents(); if (!this->VolumeTexture->HandleLargeDataTypes && nbOfComponents < 3) { std::copy(std::begin(this->VolumeTexture->Scale), std::end(this->VolumeTexture->Scale), volume_scale.begin()); std::copy(std::begin(this->VolumeTexture->Bias), std::end(this->VolumeTexture->Bias), volume_bias.begin()); } cellBO.Program->SetUniform4f("in_volume_scale", volume_scale.data()); cellBO.Program->SetUniform4f("in_volume_bias", volume_bias.data()); if (this->GetBlendMode() != BlendModes::NONE) { cellBO.Program->SetUniform3f("in_volume_spacing", this->TransformedSource->GetSpacing()); cellBO.Program->SetUniformf("blend_width", this->GetBlendWidth()); } // Rescale window/level. float volumeRange[2] = { this->VolumeTexture->ScalarRange[0][0], this->VolumeTexture->ScalarRange[0][1] }; double* scalarRange = this->ScalarRange; if (this->UseLookupTableScalarRange && this->LookupTable) { scalarRange = this->GetLookupTable()->GetRange(); } double finalWindow = (scalarRange[1] - scalarRange[0]) / (volumeRange[1] - volumeRange[0]); double finalLevel = (0.5 * (scalarRange[1] + scalarRange[0]) - volumeRange[0]) / (volumeRange[1] - volumeRange[0]); cellBO.Program->SetUniformf("in_window", finalWindow); cellBO.Program->SetUniformf("in_level", finalLevel); // Handle single block. vtkTextureObject* volumeTextureObject = this->VolumeTexture->GetCurrentBlock()->TextureObject; volumeTextureObject->Activate(); cellBO.Program->SetUniformi("in_volume", volumeTextureObject->GetTextureUnit()); this->PositionsTextureObject->Activate(); cellBO.Program->SetUniformi("positionTexture", this->PositionsTextureObject->GetTextureUnit()); if (this->GetBlendMode() != BlendModes::NONE) { this->NormalsTextureObject->Activate(); cellBO.Program->SetUniformi("normalTexture", this->NormalsTextureObject->GetTextureUnit()); } } //------------------------------------------------------------------------------ bool vtkOpenGLSurfaceProbeVolumeMapper::HasTranslucentPolygonalGeometry() { vtkScalarsToColors* lut = this->GetLookupTable(); if (lut) { // Ensure that the lookup table is built lut->Build(); return lut->IsOpaque() == 0; } return false; } //------------------------------------------------------------------------------ vtkUnsignedCharArray* vtkOpenGLSurfaceProbeVolumeMapper::MapScalars( vtkDataSet* vtkNotUsed(input), double alpha, int& vtkNotUsed(cellFlag)) { // Do not map scalars if the LUT is not specified if (!this->LookupTable) { return nullptr; } // ColorCoordinates are not needed as we use the source's scalars directly in the shader code if (this->ColorCoordinates) { this->ColorCoordinates->UnRegister(this); this->ColorCoordinates = nullptr; } // Always use a texture map for coloring, Colors are not needed if (this->Colors) { this->Colors->UnRegister(this); this->Colors = nullptr; } this->LookupTable->Build(); // Use texture map for coloring if (this->ColorTextureMap == nullptr || this->GetMTime() > this->ColorTextureMap->GetMTime() || this->LookupTable->GetMTime() > this->ColorTextureMap->GetMTime() || this->LookupTable->GetAlpha() != alpha) { this->LookupTable->SetAlpha(alpha); if (this->ColorTextureMap) { this->ColorTextureMap->UnRegister(this); this->ColorTextureMap = nullptr; } auto colorMapImage = vtkMapper::BuildColorTextureImage(this->LookupTable, this->ColorMode); this->ColorTextureMap = colorMapImage; this->ColorTextureMap->Register(this); } return nullptr; } //------------------------------------------------------------------------------ void vtkOpenGLSurfaceProbeVolumeMapper::CreateTexture( vtkTextureObject* texture, vtkOpenGLRenderWindow* renWin) { if (texture->GetHandle() == 0) { texture->SetContext(renWin); texture->SetFormat(GL_RGBA); texture->SetInternalFormat(GL_RGBA32F); texture->SetDataType(GL_FLOAT); texture->SetWrapS(vtkTextureObject::ClampToEdge); texture->SetWrapT(vtkTextureObject::ClampToEdge); texture->SetMinificationFilter(vtkTextureObject::Linear); texture->SetMagnificationFilter(vtkTextureObject::Linear); texture->Allocate2D(renWin->GetSize()[0], renWin->GetSize()[1], 4, VTK_FLOAT); } else { texture->Resize(renWin->GetSize()[0], renWin->GetSize()[1]); } } //------------------------------------------------------------------------------ void vtkOpenGLSurfaceProbeVolumeMapper::ReplaceActiveFBO(vtkRenderer* ren) { vtkOpenGLRenderWindow* renWin = vtkOpenGLRenderWindow::SafeDownCast(ren->GetRenderWindow()); if (!renWin) { return; } // Save viewport. It must be queried from the current state as it might not // match vtkRenderer::GetTiledSizeAndOrigin when using the OIT render pass. renWin->GetState()->vtkglGetIntegerv(GL_VIEWPORT, this->SavedViewport); // Save scissor test and blend state this->SavedScissorTestState = renWin->GetState()->GetEnumState(GL_SCISSOR_TEST); this->SavedBlendState = renWin->GetState()->GetEnumState(GL_BLEND); // Use the entire render window to render textures, even when having multiple renderers renWin->GetState()->vtkglViewport(0, 0, renWin->GetSize()[0], renWin->GetSize()[1]); renWin->GetState()->vtkglDisable(GL_SCISSOR_TEST); renWin->GetState()->vtkglDisable(GL_BLEND); this->CreateTexture(this->PositionsTextureObject, renWin); this->PositionsTextureObject->Activate(); // Blending requires normals if (this->GetBlendMode() != BlendModes::NONE) { this->CreateTexture(this->NormalsTextureObject, renWin); this->NormalsTextureObject->Activate(); } renWin->GetState()->PushFramebufferBindings(); this->FBO->SetContext(renWin); this->FBO->Bind(GL_FRAMEBUFFER); this->FBO->AddColorAttachment(0U, this->PositionsTextureObject); if (this->GetBlendMode() != BlendModes::NONE) { this->FBO->AddColorAttachment(1U, this->NormalsTextureObject); } this->FBO->ActivateDrawBuffers(this->GetBlendMode() != BlendModes::NONE ? 2 : 1); this->FBO->CheckFrameBufferStatus(GL_FRAMEBUFFER); this->FBO->GetContext()->GetState()->vtkglClearColor(0.0, 0.0, 0.0, 0.0); this->FBO->GetContext()->GetState()->vtkglClear(GL_COLOR_BUFFER_BIT); } //------------------------------------------------------------------------------ void vtkOpenGLSurfaceProbeVolumeMapper::RestoreActiveFBO(vtkRenderer* ren) { vtkOpenGLRenderWindow* renWin = vtkOpenGLRenderWindow::SafeDownCast(ren->GetRenderWindow()); if (!renWin) { return; } this->FBO->RemoveColorAttachment(0U); if (this->GetBlendMode() != BlendModes::NONE) { this->FBO->RemoveColorAttachment(1U); } this->FBO->DeactivateDrawBuffers(); this->FBO->GetContext()->GetState()->PopFramebufferBindings(); this->PositionsTextureObject->Deactivate(); if (this->GetBlendMode() != BlendModes::NONE) { this->NormalsTextureObject->Deactivate(); } // Restore scissor test, blend and viewport state this->SavedScissorTestState ? renWin->GetState()->vtkglEnable(GL_SCISSOR_TEST) : renWin->GetState()->vtkglDisable(GL_SCISSOR_TEST); this->SavedBlendState ? renWin->GetState()->vtkglEnable(GL_BLEND) : renWin->GetState()->vtkglDisable(GL_BLEND); renWin->GetState()->vtkglViewport( this->SavedViewport[0], this->SavedViewport[1], this->SavedViewport[2], this->SavedViewport[3]); } VTK_ABI_NAMESPACE_END