/*========================================================================= Program: Visualization Toolkit Module: vtkOpenGLGPUVolumeRayCastMapper.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 "vtkOpenGLGPUVolumeRayCastMapper.h" #include "vtkOpenGLTransferFunction2D.h" #include "vtkOpenGLVolumeGradientOpacityTable.h" #include "vtkOpenGLVolumeOpacityTable.h" #include "vtkOpenGLVolumeRGBTable.h" #include "vtkVolumeShaderComposer.h" #include "vtkVolumeStateRAII.h" // Include compiled shader code #include #include // VTK includes #include "vtkInformation.h" #include "vtkOpenGLActor.h" #include "vtkOpenGLResourceFreeCallback.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include // C/C++ includes #include #include #include #include #include vtkStandardNewMacro(vtkOpenGLGPUVolumeRayCastMapper); //---------------------------------------------------------------------------- class vtkOpenGLGPUVolumeRayCastMapper::vtkInternal { public: // Constructor //-------------------------------------------------------------------------- vtkInternal(vtkOpenGLGPUVolumeRayCastMapper* parent) { this->Parent = parent; this->ValidTransferFunction = false; this->LoadDepthTextureExtensionsSucceeded = false; this->CameraWasInsideInLastUpdate = false; this->CubeVBOId = 0; this->CubeVAOId = 0; this->CubeIndicesId = 0; this->NoiseTextureObject = nullptr; this->DepthTextureObject = nullptr; this->TextureWidth = 1024; this->ActualSampleDistance = 1.0; this->RGBTables = nullptr; this->OpacityTables = nullptr; this->Mask1RGBTable = nullptr; this->Mask2RGBTable = nullptr; this->GradientOpacityTables = nullptr; this->TransferFunctions2D = nullptr; this->CurrentMask = nullptr; this->Dimensions[0] = this->Dimensions[1] = this->Dimensions[2] = -1; this->TextureSize[0] = this->TextureSize[1] = this->TextureSize[2] = -1; this->WindowLowerLeft[0] = this->WindowLowerLeft[1] = 0; this->WindowSize[0] = this->WindowSize[1] = 0; this->LastDepthPassWindowSize[0] = this->LastDepthPassWindowSize[1] = 0; this->LastRenderToImageWindowSize[0] = 0; this->LastRenderToImageWindowSize[1] = 0; this->CurrentSelectionPass = vtkHardwareSelector::MIN_KNOWN_PASS - 1; this->CellScale[0] = this->CellScale[1] = this->CellScale[2] = 0.0; this->NoiseTextureData = nullptr; this->NumberOfLights = 0; this->LightComplexity = 0; this->Extents[0] = VTK_INT_MAX; this->Extents[1] = VTK_INT_MIN; this->Extents[2] = VTK_INT_MAX; this->Extents[3] = VTK_INT_MIN; this->Extents[4] = VTK_INT_MAX; this->Extents[5] = VTK_INT_MIN; this->CellToPointMatrix->Identity(); this->AdjustedTexMin[0] = this->AdjustedTexMin[1] = this->AdjustedTexMin[2] = 0.0f; this->AdjustedTexMin[3] = 1.0f; this->AdjustedTexMax[0] = this->AdjustedTexMax[1] = this->AdjustedTexMax[2] = 1.0f; this->AdjustedTexMax[3] = 1.0f; this->Scale.clear(); this->Bias.clear(); this->NeedToInitializeResources = false; this->ShaderCache = nullptr; this->FBO = nullptr; this->RTTDepthBufferTextureObject = nullptr; this->RTTDepthTextureObject = nullptr; this->RTTColorTextureObject = nullptr; this->RTTDepthTextureType = -1; this->DPFBO = nullptr; this->DPDepthBufferTextureObject = nullptr; this->DPColorTextureObject = nullptr; this->PreserveViewport = false; this->PreserveGLState = false; } // Destructor //-------------------------------------------------------------------------- ~vtkInternal() { delete[] this->NoiseTextureData; if (this->NoiseTextureObject) { this->NoiseTextureObject->Delete(); this->NoiseTextureObject = nullptr; } if (this->DepthTextureObject) { this->DepthTextureObject->Delete(); this->DepthTextureObject = nullptr; } if (this->FBO) { this->FBO->Delete(); this->FBO = nullptr; } if (this->RTTDepthBufferTextureObject) { this->RTTDepthBufferTextureObject->Delete(); this->RTTDepthBufferTextureObject = nullptr; } if (this->RTTDepthTextureObject) { this->RTTDepthTextureObject->Delete(); this->RTTDepthTextureObject = nullptr; } if (this->RTTColorTextureObject) { this->RTTColorTextureObject->Delete(); this->RTTColorTextureObject = nullptr; } if (this->ImageSampleFBO) { this->ImageSampleFBO->Delete(); this->ImageSampleFBO = nullptr; } for (auto& tex : this->ImageSampleTexture) { tex = nullptr; } this->ImageSampleTexture.clear(); this->ImageSampleTexNames.clear(); if (this->ImageSampleVBO) { this->ImageSampleVBO->Delete(); this->ImageSampleVBO = nullptr; } if (this->ImageSampleVAO) { this->ImageSampleVAO->Delete(); this->ImageSampleVAO = nullptr; } this->DeleteTransfer1D(); this->DeleteTransfer2D(); this->Scale.clear(); this->Bias.clear(); // Do not delete the shader programs - Let the cache clean them up. this->ImageSampleProg = nullptr; } // Helper methods //-------------------------------------------------------------------------- template static void ToFloat(const T& in1, const T& in2, float (&out)[2]); template static void ToFloat(const T& in1, const T& in2, const T& in3, float (&out)[3]); template static void ToFloat(T* in, float* out, int noOfComponents); template static void ToFloat(T (&in)[3], float (&out)[3]); template static void ToFloat(T (&in)[2], float (&out)[2]); template static void ToFloat(T& in, float& out); template static void ToFloat(T (&in)[4][2], float (&out)[4][2]); ///@{ /** * \brief Setup and clean-up 1D and 2D transfer functions. */ void InitializeTransferFunction(vtkRenderer* ren, vtkVolume* vol, int noOfComponents, int independentComponents); void UpdateTransferFunction(vtkRenderer* ren, vtkVolume* vol, int noOfComponents, int independentComponents); void ActivateTransferFunction(vtkShaderProgram* prog, vtkVolumeProperty* volProp, int numSamplers); void DeactivateTransferFunction(vtkVolumeProperty* volumeProperty, int numSamplers); void SetupTransferFunction1D(vtkRenderer* ren, int noOfComponents, int independentComponents); void ReleaseGraphicsTransfer1D(vtkWindow* window); void DeleteTransfer1D(); void SetupTransferFunction2D(vtkRenderer* ren, int noOfComponents, int independentComponents); void ReleaseGraphicsTransfer2D(vtkWindow* window); void DeleteTransfer2D(); ///@} bool LoadMask(vtkRenderer* ren, vtkImageData* input, vtkImageData* maskInput, vtkVolume* volume); bool LoadData(vtkRenderer* ren, vtkVolume* vol, vtkVolumeProperty* volProp, vtkImageData* input, vtkDataArray* scalars); void ComputeBounds(vtkImageData* input); // Update OpenGL volume information void UpdateVolume(vtkVolumeProperty* volumeProperty); // Update transfer color function based on the incoming inputs // and number of scalar components. int UpdateColorTransferFunction(vtkRenderer* ren, vtkVolume* vol, unsigned int component); // Scalar opacity int UpdateOpacityTransferFunction(vtkRenderer* ren, vtkVolume* vol, unsigned int component); int UpdateGradientOpacityTransferFunction(vtkRenderer* ren, vtkVolume* vol, unsigned int component); void UpdateTransferFunction2D(vtkRenderer* ren, vtkVolume* vol, unsigned int component); // Update noise texture (used to reduce rendering artifacts // specifically banding effects) void CreateNoiseTexture(vtkRenderer* ren); // Update depth texture (used for early termination of the ray) void CaptureDepthTexture(vtkRenderer* ren, vtkVolume* vol); // Test if camera is inside the volume geometry bool IsCameraInside(vtkRenderer* ren, vtkVolume* vol); // Compute transformation from cell texture-coordinates to point // texture-coords (CTP). Cell data maps correctly to OpenGL cells, point data // does not (VTK defines points at the cell corners). To set the point data in // the center of the OpenGL texels, a translation of 0.5 texels is applied, // and the range is rescaled to the point range. // // delta = TextureExtentsMax - TextureExtentsMin; // min = vec3(0.5) / delta; // max = (delta - vec3(0.5)) / delta; // range = max - min // // CTP = translation * Scale // CTP = range.x, 0, 0, min.x // 0, range.y, 0, min.y // 0, 0, range.z, min.z // 0, 0, 0, 1.0 void ComputeCellToPointMatrix(); // Update parameters for lighting that will be used in the shader. void SetLightingParameters(vtkRenderer* ren, vtkShaderProgram* prog, vtkVolume* vol); // Update the volume geometry void RenderVolumeGeometry(vtkRenderer* ren, vtkShaderProgram* prog, vtkVolume* vol); // Update cropping params to shader void SetCroppingRegions(vtkRenderer* ren, vtkShaderProgram* prog, vtkVolume* vol); // Update clipping params to shader void SetClippingPlanes(vtkRenderer* ren, vtkShaderProgram* prog, vtkVolume* vol); // Update the interval of sampling void UpdateSamplingDistance(vtkImageData* input, vtkRenderer* ren, vtkVolume* vol); // Check if the mapper should enter picking mode. void CheckPickingState(vtkRenderer* ren); // Look for property keys used to control the mapper's state. // This is necessary for some render passes which need to ensure // a specific OpenGL state when rendering through this mapper. void CheckPropertyKeys(vtkVolume* vol); // Configure the vtkHardwareSelector to begin a picking pass. void BeginPicking(vtkRenderer* ren); // Update the prop Id if hardware selection is enabled. void SetPickingId(vtkRenderer* ren); // Configure the vtkHardwareSelector to end a picking pass. void EndPicking(vtkRenderer* ren); // Load OpenGL extensiosn required to grab depth sampler buffer void LoadRequireDepthTextureExtensions(vtkRenderWindow* renWin); // Create GL buffers void CreateBufferObjects(); // Dispose / free GL buffers void DeleteBufferObjects(); // Convert vtkTextureObject to vtkImageData void ConvertTextureToImageData(vtkTextureObject* texture, vtkImageData* output); // Render to texture for final rendering void SetupRenderToTexture(vtkRenderer* ren); void ExitRenderToTexture(vtkRenderer* ren); // Render to texture for depth pass void SetupDepthPass(vtkRenderer* ren); void ExitDepthPass(vtkRenderer* ren); //@{ /** * Image XY-Sampling * Render to an internal framebuffer with lower resolution than the currently * bound one (hence casting less rays and improving performance). The rendered * image is subsequently rendered as a texture-mapped quad (linearly * interpolated) to the default (or previously attached) framebuffer. If a * vtkOpenGLRenderPass is attached, a variable number of render targets are * supported (as specified by the RenderPass). The render targets are assumed * to be ordered from GL_COLOR_ATTACHMENT0 to GL_COLOR_ATTACHMENT$N$, where * $N$ is the number of targets specified (targets of the previously bound * framebuffer as activated through ActivateDrawBuffers(int)). Without a * RenderPass attached, it relies on FramebufferObject to re-activate the * appropriate previous DrawBuffer. * * \sa vtkOpenGLRenderPass vtkOpenGLFramebufferObject */ void BeginImageSample(vtkRenderer* ren, vtkVolume* vol); bool InitializeImageSampleFBO(vtkRenderer* ren); void EndImageSample(vtkRenderer* ren); size_t GetNumImageSampleDrawBuffers(vtkVolume* vol); //@} void ReleaseRenderToTextureGraphicsResources(vtkWindow* win); void ReleaseImageSampleGraphicsResources(vtkWindow* win); void ReleaseDepthPassGraphicsResources(vtkWindow* win); // Private member variables //-------------------------------------------------------------------------- vtkOpenGLGPUVolumeRayCastMapper* Parent; bool ValidTransferFunction; bool LoadDepthTextureExtensionsSucceeded; bool CameraWasInsideInLastUpdate; GLuint CubeVBOId; GLuint CubeVAOId; GLuint CubeIndicesId; vtkTextureObject* NoiseTextureObject; vtkTextureObject* DepthTextureObject; int TextureWidth; std::vector Scale; std::vector Bias; float* NoiseTextureData; float ActualSampleDistance; int LastProjectionParallel; int Dimensions[3]; int TextureSize[3]; int WindowLowerLeft[2]; int WindowSize[2]; int LastDepthPassWindowSize[2]; int LastRenderToImageWindowSize[2]; double LoadedBounds[6]; int Extents[6]; double DatasetStepSize[3]; double CellScale[3]; double CellStep[3]; double CellSpacing[3]; int NumberOfLights; int LightComplexity; std::ostringstream ExtensionsStringStream; vtkOpenGLVolumeRGBTables* RGBTables; std::map RGBTablesMap; vtkOpenGLVolumeOpacityTables* OpacityTables; std::map OpacityTablesMap; vtkOpenGLVolumeRGBTable* Mask1RGBTable; vtkOpenGLVolumeRGBTable* Mask2RGBTable; vtkOpenGLVolumeGradientOpacityTables* GradientOpacityTables; std::map GradientOpacityTablesMap; vtkOpenGLTransferFunctions2D* TransferFunctions2D; std::map TransferFunctions2DMap; vtkTimeStamp Transfer2DTime; vtkTimeStamp ShaderBuildTime; vtkNew TextureToDataSetMat; vtkNew InverseTextureToDataSetMat; vtkNew InverseProjectionMat; vtkNew InverseModelViewMat; vtkNew InverseVolumeMat; vtkNew TextureToEyeTransposeInverse; vtkNew TempMatrix1; vtkNew CellToPointMatrix; float AdjustedTexMin[4]; float AdjustedTexMax[4]; vtkSmartPointer BBoxPolyData; vtkSmartPointer CurrentMask; vtkTimeStamp InitializationTime; vtkTimeStamp InputUpdateTime; vtkTimeStamp VolumeUpdateTime; vtkTimeStamp MaskUpdateTime; vtkTimeStamp ReleaseResourcesTime; vtkTimeStamp DepthPassTime; vtkTimeStamp DepthPassSetupTime; vtkTimeStamp SelectionStateTime; int CurrentSelectionPass; bool IsPicking; bool NeedToInitializeResources; bool PreserveViewport; bool PreserveGLState; vtkShaderProgram* ShaderProgram; vtkOpenGLShaderCache* ShaderCache; vtkOpenGLFramebufferObject* FBO; vtkTextureObject* RTTDepthBufferTextureObject; vtkTextureObject* RTTDepthTextureObject; vtkTextureObject* RTTColorTextureObject; int RTTDepthTextureType; vtkOpenGLFramebufferObject* DPFBO; vtkTextureObject* DPDepthBufferTextureObject; vtkTextureObject* DPColorTextureObject; vtkOpenGLFramebufferObject* ImageSampleFBO = nullptr; std::vector> ImageSampleTexture; std::vector ImageSampleTexNames; vtkShaderProgram* ImageSampleProg = nullptr; vtkOpenGLVertexArrayObject* ImageSampleVAO = nullptr; vtkOpenGLBufferObject* ImageSampleVBO = nullptr; size_t NumImageSampleDrawBuffers = 0; bool RebuildImageSampleProg = false; bool RenderPassAttached = false; vtkNew ContourFilter; vtkNew ContourMapper; vtkNew ContourActor; }; //---------------------------------------------------------------------------- template void vtkOpenGLGPUVolumeRayCastMapper::vtkInternal::ToFloat(const T& in1, const T& in2, float (&out)[2]) { out[0] = static_cast(in1); out[1] = static_cast(in2); } template void vtkOpenGLGPUVolumeRayCastMapper::vtkInternal::ToFloat(const T& in1, const T& in2, const T& in3, float (&out)[3]) { out[0] = static_cast(in1); out[1] = static_cast(in2); out[2] = static_cast(in3); } //---------------------------------------------------------------------------- template void vtkOpenGLGPUVolumeRayCastMapper::vtkInternal::ToFloat(T* in, float* out, int noOfComponents) { for (int i = 0; i < noOfComponents; ++i) { out[i] = static_cast(in[i]); } } //---------------------------------------------------------------------------- template void vtkOpenGLGPUVolumeRayCastMapper::vtkInternal::ToFloat(T (&in)[3], float (&out)[3]) { out[0] = static_cast(in[0]); out[1] = static_cast(in[1]); out[2] = static_cast(in[2]); } //---------------------------------------------------------------------------- template void vtkOpenGLGPUVolumeRayCastMapper::vtkInternal::ToFloat(T (&in)[2], float (&out)[2]) { out[0] = static_cast(in[0]); out[1] = static_cast(in[1]); } //---------------------------------------------------------------------------- template void vtkOpenGLGPUVolumeRayCastMapper::vtkInternal::ToFloat(T& in, float& out) { out = static_cast(in); } //---------------------------------------------------------------------------- template void vtkOpenGLGPUVolumeRayCastMapper::vtkInternal::ToFloat(T (&in)[4][2], float (&out)[4][2]) { out[0][0] = static_cast(in[0][0]); out[0][1] = static_cast(in[0][1]); out[1][0] = static_cast(in[1][0]); out[1][1] = static_cast(in[1][1]); out[2][0] = static_cast(in[2][0]); out[2][1] = static_cast(in[2][1]); out[3][0] = static_cast(in[3][0]); out[3][1] = static_cast(in[3][1]); } //---------------------------------------------------------------------------- void vtkOpenGLGPUVolumeRayCastMapper::vtkInternal::SetupTransferFunction1D( vtkRenderer* ren, int noOfComponents, int independentComponents) { this->ReleaseGraphicsTransfer1D(ren->GetRenderWindow()); this->DeleteTransfer1D(); // Check component mode (independent or dependent) noOfComponents = noOfComponents > 1 && independentComponents ? noOfComponents : 1; // Create RGB and opacity (scalar and gradient) lookup tables. We support up // to four components in independentComponents mode. this->RGBTables = new vtkOpenGLVolumeRGBTables(noOfComponents); this->OpacityTables = new vtkOpenGLVolumeOpacityTables(noOfComponents); this->GradientOpacityTables = new vtkOpenGLVolumeGradientOpacityTables(noOfComponents); this->OpacityTablesMap.clear(); this->RGBTablesMap.clear(); this->GradientOpacityTablesMap.clear(); if (this->Parent->MaskInput != nullptr && this->Parent->MaskType == LabelMapMaskType) { if (this->Mask1RGBTable == nullptr) { this->Mask1RGBTable = vtkOpenGLVolumeRGBTable::New(); } if (this->Mask2RGBTable == nullptr) { this->Mask2RGBTable = vtkOpenGLVolumeRGBTable::New(); } } std::ostringstream numeric; for (int i = 0; i < noOfComponents; ++i) { numeric << i; if (i > 0) { this->OpacityTablesMap[i] = std::string("in_opacityTransferFunc") + numeric.str(); this->RGBTablesMap[i] = std::string("in_colorTransferFunc") + numeric.str(); this->GradientOpacityTablesMap[i] = std::string("in_gradientTransferFunc") + numeric.str(); } else { this->OpacityTablesMap[i] = std::string("in_opacityTransferFunc"); this->RGBTablesMap[i] = std::string("in_colorTransferFunc"); this->GradientOpacityTablesMap[i] = std::string("in_gradientTransferFunc"); } numeric.str(""); numeric.clear(); } this->InitializationTime.Modified(); } //---------------------------------------------------------------------------- void vtkOpenGLGPUVolumeRayCastMapper::vtkInternal::SetupTransferFunction2D( vtkRenderer* ren, int noOfComponents, int independentComponents) { this->ReleaseGraphicsTransfer2D(ren->GetRenderWindow()); this->DeleteTransfer2D(); unsigned int const num = (noOfComponents > 1 && independentComponents) ? noOfComponents : 1; this->TransferFunctions2D = new vtkOpenGLTransferFunctions2D(num); std::ostringstream indexStream; const std::string baseName = "in_transfer2D"; for (unsigned int i = 0; i < num; i++) { if (i > 0) { indexStream << i; } this->TransferFunctions2DMap[0] = baseName + indexStream.str(); indexStream.str(""); indexStream.clear(); } this->Transfer2DTime.Modified(); } //---------------------------------------------------------------------------- void vtkOpenGLGPUVolumeRayCastMapper::vtkInternal::InitializeTransferFunction( vtkRenderer* ren, vtkVolume* vol, int noOfComponents, int independentComponents) { const int transferMode = vol->GetProperty()->GetTransferFunctionMode(); switch (transferMode) { case vtkVolumeProperty::TF_2D: this->SetupTransferFunction2D(ren, noOfComponents, independentComponents); break; case vtkVolumeProperty::TF_1D: default: this->SetupTransferFunction1D(ren, noOfComponents, independentComponents); } } //---------------------------------------------------------------------------- void vtkOpenGLGPUVolumeRayCastMapper::vtkInternal::UpdateTransferFunction( vtkRenderer* ren, vtkVolume* vol, int noOfComponents, int independentComponents) { int const transferMode = vol->GetProperty()->GetTransferFunctionMode(); switch (transferMode) { case vtkVolumeProperty::TF_1D: if (independentComponents) { for (int i = 0; i < noOfComponents; ++i) { this->UpdateOpacityTransferFunction(ren, vol, i); this->UpdateGradientOpacityTransferFunction(ren, vol, i); this->UpdateColorTransferFunction(ren, vol, i); } } else { if (noOfComponents == 2 || noOfComponents == 4) { this->UpdateOpacityTransferFunction(ren, vol, noOfComponents - 1); this->UpdateGradientOpacityTransferFunction( ren, vol, noOfComponents - 1); this->UpdateColorTransferFunction(ren, vol, 0); } } break; case vtkVolumeProperty::TF_2D: if (independentComponents) { for (int i = 0; i < noOfComponents; ++i) { this->UpdateTransferFunction2D(ren, vol, i); } } else { if (noOfComponents == 2 || noOfComponents == 4) { this->UpdateTransferFunction2D(ren, vol, 0); } } break; } } //---------------------------------------------------------------------------- void vtkOpenGLGPUVolumeRayCastMapper::vtkInternal::ActivateTransferFunction( vtkShaderProgram* prog, vtkVolumeProperty* volumeProperty, int numberOfSamplers) { int const transferMode = volumeProperty->GetTransferFunctionMode(); switch (transferMode) { case vtkVolumeProperty::TF_1D: for (int i = 0; i < numberOfSamplers; ++i) { this->OpacityTables->GetTable(i)->Activate(); prog->SetUniformi(this->OpacityTablesMap[i].c_str(), this->OpacityTables->GetTable(i)->GetTextureUnit()); if (this->Parent->BlendMode != vtkGPUVolumeRayCastMapper::ADDITIVE_BLEND) { this->RGBTables->GetTable(i)->Activate(); prog->SetUniformi(this->RGBTablesMap[i].c_str(), this->RGBTables->GetTable(i)->GetTextureUnit()); } if (this->GradientOpacityTables) { this->GradientOpacityTables->GetTable(i)->Activate(); prog->SetUniformi(this->GradientOpacityTablesMap[i].c_str(), this->GradientOpacityTables->GetTable(i)->GetTextureUnit()); } } break; case vtkVolumeProperty::TF_2D: for (int i = 0; i < numberOfSamplers; ++i) { vtkOpenGLTransferFunction2D* table = this->TransferFunctions2D->GetTable(i); table->Activate(); prog->SetUniformi( this->TransferFunctions2DMap[i].c_str(), table->GetTextureUnit()); } break; } } //----------------------------------------------------------------------------- void vtkOpenGLGPUVolumeRayCastMapper::vtkInternal::DeactivateTransferFunction( vtkVolumeProperty* volumeProperty, int numberOfSamplers) { int const transferMode = volumeProperty->GetTransferFunctionMode(); switch (transferMode) { case vtkVolumeProperty::TF_1D: for (int i = 0; i < numberOfSamplers; ++i) { this->OpacityTables->GetTable(i)->Deactivate(); if (this->Parent->BlendMode != vtkGPUVolumeRayCastMapper::ADDITIVE_BLEND) { this->RGBTables->GetTable(i)->Deactivate(); } if (this->GradientOpacityTables) { this->GradientOpacityTables->GetTable(i)->Deactivate(); } } break; case vtkVolumeProperty::TF_2D: for (int i = 0; i < numberOfSamplers; ++i) { this->TransferFunctions2D->GetTable(i)->Deactivate(); } break; } } //----------------------------------------------------------------------------- bool vtkOpenGLGPUVolumeRayCastMapper::vtkInternal::LoadMask(vtkRenderer* ren, vtkImageData* vtkNotUsed(input), vtkImageData* maskInput, vtkVolume* vtkNotUsed(volume)) { bool result = true; if (maskInput && (maskInput->GetMTime() > this->MaskUpdateTime)) { if (!this->CurrentMask) { this->CurrentMask = vtkSmartPointer::New(); this->CurrentMask->SetMapper(this->Parent); const auto& part = this->Parent->VolumeTexture->GetPartitions(); this->CurrentMask->SetPartitions(part[0], part[1], part[2]); } vtkDataArray* arr = this->Parent->GetScalars(maskInput, this->Parent->ScalarMode, this->Parent->ArrayAccessMode, this->Parent->ArrayId, this->Parent->ArrayName, this->Parent->CellFlag); result = this->CurrentMask->LoadVolume( ren, maskInput, arr, VTK_NEAREST_INTERPOLATION); this->MaskUpdateTime.Modified(); } return result; } //---------------------------------------------------------------------------- bool vtkOpenGLGPUVolumeRayCastMapper::vtkInternal::LoadData(vtkRenderer* ren, vtkVolume* vol, vtkVolumeProperty* volProp, vtkImageData* input, vtkDataArray* scalars) { // Update bounds, data, and geometry input->GetDimensions(this->Dimensions); bool success = this->Parent->VolumeTexture->LoadVolume( ren, input, scalars, volProp->GetInterpolationType()); this->ComputeBounds(input); this->ComputeCellToPointMatrix(); this->LoadMask(ren, input, this->Parent->MaskInput, vol); this->InputUpdateTime.Modified(); return success; } //---------------------------------------------------------------------------- void vtkOpenGLGPUVolumeRayCastMapper::vtkInternal::ReleaseGraphicsTransfer1D( vtkWindow* window) { if (this->RGBTables) { this->RGBTables->ReleaseGraphicsResources(window); } if (this->Mask1RGBTable) { this->Mask1RGBTable->ReleaseGraphicsResources(window); } if (this->Mask2RGBTable) { this->Mask2RGBTable->ReleaseGraphicsResources(window); } if (this->OpacityTables) { this->OpacityTables->ReleaseGraphicsResources(window); } if (this->GradientOpacityTables) { this->GradientOpacityTables->ReleaseGraphicsResources(window); } } //---------------------------------------------------------------------------- void vtkOpenGLGPUVolumeRayCastMapper::vtkInternal::DeleteTransfer1D() { delete this->RGBTables; this->RGBTables = nullptr; if (this->Mask1RGBTable) { this->Mask1RGBTable->Delete(); this->Mask1RGBTable = nullptr; } if (this->Mask2RGBTable) { this->Mask2RGBTable->Delete(); this->Mask2RGBTable = nullptr; } delete this->OpacityTables; this->OpacityTables = nullptr; delete this->GradientOpacityTables; this->GradientOpacityTables = nullptr; } //---------------------------------------------------------------------------- void vtkOpenGLGPUVolumeRayCastMapper::vtkInternal::ReleaseGraphicsTransfer2D( vtkWindow* window) { if (this->TransferFunctions2D) { this->TransferFunctions2D->ReleaseGraphicsResources(window); } } //---------------------------------------------------------------------------- void vtkOpenGLGPUVolumeRayCastMapper::vtkInternal::DeleteTransfer2D() { delete this->TransferFunctions2D; this->TransferFunctions2D = nullptr; } //---------------------------------------------------------------------------- void vtkOpenGLGPUVolumeRayCastMapper::vtkInternal::ComputeBounds( vtkImageData* input) { double origin[3]; input->GetSpacing(this->CellSpacing); input->GetOrigin(origin); input->GetExtent(this->Extents); int swapBounds[3]; swapBounds[0] = (this->CellSpacing[0] < 0); swapBounds[1] = (this->CellSpacing[1] < 0); swapBounds[2] = (this->CellSpacing[2] < 0); // Loaded data represents points if (!this->Parent->CellFlag) { // If spacing is negative, we may have to rethink the equation // between real point and texture coordinate... this->LoadedBounds[0] = origin[0] + static_cast(this->Extents[0 + swapBounds[0]]) * this->CellSpacing[0]; this->LoadedBounds[2] = origin[1] + static_cast(this->Extents[2 + swapBounds[1]]) * this->CellSpacing[1]; this->LoadedBounds[4] = origin[2] + static_cast(this->Extents[4 + swapBounds[2]]) * this->CellSpacing[2]; this->LoadedBounds[1] = origin[0] + static_cast(this->Extents[1 - swapBounds[0]]) * this->CellSpacing[0]; this->LoadedBounds[3] = origin[1] + static_cast(this->Extents[3 - swapBounds[1]]) * this->CellSpacing[1]; this->LoadedBounds[5] = origin[2] + static_cast(this->Extents[5 - swapBounds[2]]) * this->CellSpacing[2]; } // Loaded extents represent cells else { int i = 0; while (i < 3) { this->LoadedBounds[2 * i + swapBounds[i]] = origin[i] + (static_cast(this->Extents[2 * i])) * this->CellSpacing[i]; this->LoadedBounds[2 * i + 1 - swapBounds[i]] = origin[i] + (static_cast(this->Extents[2 * i + 1]) + 1.0) * this->CellSpacing[i]; i++; } } } //---------------------------------------------------------------------------- void vtkOpenGLGPUVolumeRayCastMapper::vtkInternal::UpdateVolume( vtkVolumeProperty* volumeProperty) { if (volumeProperty->GetMTime() > this->VolumeUpdateTime.GetMTime()) { int const newInterp = volumeProperty->GetInterpolationType(); this->Parent->VolumeTexture->UpdateInterpolationType(newInterp); } this->VolumeUpdateTime.Modified(); } //---------------------------------------------------------------------------- int vtkOpenGLGPUVolumeRayCastMapper::vtkInternal::UpdateColorTransferFunction( vtkRenderer* ren, vtkVolume* vol, unsigned int component) { // Volume property cannot be null. vtkVolumeProperty* volumeProperty = vol->GetProperty(); // Build the colormap in a 1D texture. // 1D RGB-texture=mapping from scalar values to color values // build the table. vtkColorTransferFunction* colorTransferFunction = volumeProperty->GetRGBTransferFunction(component); double componentRange[2]; if ((colorTransferFunction->GetSize() < 1) || (this->Parent->GetColorRangeType() == vtkGPUVolumeRayCastMapper::SCALAR)) { for (int i = 0; i < 2; ++i) { componentRange[i] = this->Parent->VolumeTexture->ScalarRange[component][i]; } } else { colorTransferFunction->GetRange(componentRange); } // Add points only if its not being added before if (colorTransferFunction->GetSize() < 1) { colorTransferFunction->AddRGBPoint(componentRange[0], 0.0, 0.0, 0.0); colorTransferFunction->AddRGBPoint(componentRange[1], 1.0, 1.0, 1.0); } int filterVal = volumeProperty->GetInterpolationType() == VTK_LINEAR_INTERPOLATION ? vtkTextureObject::Linear : vtkTextureObject::Nearest; this->RGBTables->GetTable(component)->Update( volumeProperty->GetRGBTransferFunction(component), componentRange, #if GL_ES_VERSION_3_0 != 1 filterVal, #else vtkTextureObject::Nearest, #endif vtkOpenGLRenderWindow::SafeDownCast(ren->GetRenderWindow())); if (this->Parent->MaskInput != nullptr && this->Parent->MaskType == LabelMapMaskType) { vtkColorTransferFunction* colorTransferFunc = volumeProperty->GetRGBTransferFunction(1); this->Mask1RGBTable->Update(colorTransferFunc, componentRange, vtkTextureObject::Nearest, vtkOpenGLRenderWindow::SafeDownCast(ren->GetRenderWindow())); colorTransferFunc = volumeProperty->GetRGBTransferFunction(2); this->Mask2RGBTable->Update(colorTransferFunc, componentRange, vtkTextureObject::Nearest, vtkOpenGLRenderWindow::SafeDownCast(ren->GetRenderWindow())); } return 0; } //---------------------------------------------------------------------------- int vtkOpenGLGPUVolumeRayCastMapper::vtkInternal::UpdateOpacityTransferFunction( vtkRenderer* ren, vtkVolume* vol, unsigned int component) { vtkVolumeProperty* volumeProperty = vol->GetProperty(); // Transfer function table index based on whether independent / dependent // components. If dependent, use the first scalar opacity transfer function unsigned int lookupTableIndex = volumeProperty->GetIndependentComponents() ? component : 0; vtkPiecewiseFunction* scalarOpacity = volumeProperty->GetScalarOpacity(lookupTableIndex); double componentRange[2]; if ((scalarOpacity->GetSize() < 1) || (this->Parent->GetScalarOpacityRangeType() == vtkGPUVolumeRayCastMapper::SCALAR)) { for (int i = 0; i < 2; ++i) { componentRange[i] = this->Parent->VolumeTexture->ScalarRange[component][i]; } } else { scalarOpacity->GetRange(componentRange); } if (scalarOpacity->GetSize() < 1) { scalarOpacity->AddPoint(componentRange[0], 0.0); scalarOpacity->AddPoint(componentRange[1], 0.5); } int filterVal = volumeProperty->GetInterpolationType() == VTK_LINEAR_INTERPOLATION ? vtkTextureObject::Linear : vtkTextureObject::Nearest; this->OpacityTables->GetTable(lookupTableIndex) ->Update(scalarOpacity, this->Parent->BlendMode, this->ActualSampleDistance, componentRange, volumeProperty->GetScalarOpacityUnitDistance(component), #if GL_ES_VERSION_3_0 != 1 filterVal, #else vtkTextureObject::Nearest, #endif vtkOpenGLRenderWindow::SafeDownCast(ren->GetRenderWindow())); return 0; } //------------------------------------------------------------------------------ void vtkOpenGLGPUVolumeRayCastMapper::vtkInternal::UpdateTransferFunction2D( vtkRenderer* ren, vtkVolume* vol, unsigned int component) { vtkVolumeProperty* prop = vol->GetProperty(); int const transferMode = prop->GetTransferFunctionMode(); if (transferMode != vtkVolumeProperty::TF_2D) { return; } // Use the first LUT when using dependent components unsigned int const lutIndex = prop->GetIndependentComponents() ? component : 0; vtkImageData* transfer2D = prop->GetTransferFunction2D(lutIndex); #if GL_ES_VERSION_3_0 != 1 int const interp = prop->GetInterpolationType() == VTK_LINEAR_INTERPOLATION ? vtkTextureObject::Linear : vtkTextureObject::Nearest; #else int const interp = vtkTextureObject::Nearest; #endif this->TransferFunctions2D->GetTable(lutIndex)->Update(transfer2D, interp, vtkOpenGLRenderWindow::SafeDownCast(ren->GetRenderWindow())); } //---------------------------------------------------------------------------- int vtkOpenGLGPUVolumeRayCastMapper::vtkInternal:: UpdateGradientOpacityTransferFunction(vtkRenderer* ren, vtkVolume* vol, unsigned int component) { vtkVolumeProperty* volumeProperty = vol->GetProperty(); // Transfer function table index based on whether independent / dependent // components. If dependent, use the first gradient opacity transfer function unsigned int lookupTableIndex = volumeProperty->GetIndependentComponents() ? component : 0; if (!volumeProperty->HasGradientOpacity(lookupTableIndex) || !this->GradientOpacityTables) { return 1; } vtkPiecewiseFunction* gradientOpacity = volumeProperty->GetGradientOpacity(lookupTableIndex); double componentRange[2]; if ((gradientOpacity->GetSize() < 1) || this->Parent->GetGradientOpacityRangeType() == vtkGPUVolumeRayCastMapper::SCALAR) { for (int i = 0; i < 2; ++i) { componentRange[i] = this->Parent->VolumeTexture->ScalarRange[component][i]; } } else { gradientOpacity->GetRange(componentRange); } if (gradientOpacity->GetSize() < 1) { gradientOpacity->AddPoint(componentRange[0], 0.0); gradientOpacity->AddPoint(componentRange[1], 0.5); } int filterVal = volumeProperty->GetInterpolationType() == VTK_LINEAR_INTERPOLATION ? vtkTextureObject::Linear : vtkTextureObject::Nearest; this->GradientOpacityTables->GetTable(lookupTableIndex) ->Update(gradientOpacity, this->ActualSampleDistance, componentRange, volumeProperty->GetScalarOpacityUnitDistance(component), #if GL_ES_VERSION_3_0 != 1 filterVal, #else vtkTextureObject::Nearest, #endif vtkOpenGLRenderWindow::SafeDownCast(ren->GetRenderWindow())); return 0; } //---------------------------------------------------------------------------- void vtkOpenGLGPUVolumeRayCastMapper::vtkInternal::CreateNoiseTexture( vtkRenderer* ren) { vtkOpenGLRenderWindow* glWindow = vtkOpenGLRenderWindow::SafeDownCast(ren->GetRenderWindow()); if (!this->NoiseTextureObject) { this->NoiseTextureObject = vtkTextureObject::New(); } this->NoiseTextureObject->SetContext(glWindow); bool updateSize = false; bool useUserSize = this->Parent->NoiseTextureSize[0] > 0 && this->Parent->NoiseTextureSize[1] > 0; if (useUserSize) { int const twidth = this->NoiseTextureObject->GetWidth(); int const theight = this->NoiseTextureObject->GetHeight(); updateSize = this->Parent->NoiseTextureSize[0] != twidth || this->Parent->NoiseTextureSize[1] != theight; } if (!this->NoiseTextureObject->GetHandle() || updateSize || this->NoiseTextureObject->GetMTime() < this->Parent->NoiseGenerator->GetMTime()) { int* winSize = ren->GetRenderWindow()->GetSize(); int sizeX = useUserSize ? this->Parent->NoiseTextureSize[0] : winSize[0]; int sizeY = useUserSize ? this->Parent->NoiseTextureSize[1] : winSize[1]; int const maxSize = vtkTextureObject::GetMaximumTextureSize(glWindow); if (sizeX > maxSize || sizeY > maxSize) { sizeX = vtkMath::Max(sizeX, maxSize); sizeY = vtkMath::Max(sizeY, maxSize); } // Allocate buffer. After controlling for the maximum supported size sizeX/Y // might have changed, so an additional check is needed. int const twidth = this->NoiseTextureObject->GetWidth(); int const theight = this->NoiseTextureObject->GetHeight(); bool sizeChanged = sizeX != twidth || sizeY != theight; if (sizeChanged || !this->NoiseTextureData) { delete[] this->NoiseTextureData; this->NoiseTextureData = nullptr; this->NoiseTextureData = new float[sizeX * sizeY]; } // Generate jitter noise if (!this->Parent->NoiseGenerator) { // Use default settings vtkPerlinNoise* perlinNoise = vtkPerlinNoise::New(); perlinNoise->SetPhase(0.0, 0.0, 0.0); perlinNoise->SetFrequency(sizeX, sizeY, 1.0); perlinNoise->SetAmplitude(0.5); /* [-n, n] */ this->Parent->NoiseGenerator = perlinNoise; } int const bufferSize = sizeX * sizeY; for (int i = 0; i < bufferSize; i++) { int const x = i % sizeX; int const y = i / sizeY; this->NoiseTextureData[i] = static_cast( this->Parent->NoiseGenerator->EvaluateFunction(x, y, 0.0) + 0.1); } // Prepare texture this->NoiseTextureObject->Create2DFromRaw( sizeX, sizeY, 1, VTK_FLOAT, this->NoiseTextureData); this->NoiseTextureObject->SetWrapS(vtkTextureObject::Repeat); this->NoiseTextureObject->SetWrapT(vtkTextureObject::Repeat); this->NoiseTextureObject->SetMagnificationFilter(vtkTextureObject::Nearest); this->NoiseTextureObject->SetMinificationFilter(vtkTextureObject::Nearest); this->NoiseTextureObject->SetBorderColor(0.0f, 0.0f, 0.0f, 0.0f); this->NoiseTextureObject->Modified(); } } //---------------------------------------------------------------------------- void vtkOpenGLGPUVolumeRayCastMapper::vtkInternal::CaptureDepthTexture( vtkRenderer* ren, vtkVolume* vtkNotUsed(vol)) { // Make sure our render window is the current OpenGL context ren->GetRenderWindow()->MakeCurrent(); // Load required extensions for grabbing depth sampler buffer if (!this->LoadDepthTextureExtensionsSucceeded) { this->LoadRequireDepthTextureExtensions(ren->GetRenderWindow()); } // If we can't load the necessary extensions, provide // feedback on why it failed. if (!this->LoadDepthTextureExtensionsSucceeded) { std::cerr << this->ExtensionsStringStream.str() << std::endl; return; } if (!this->DepthTextureObject) { this->DepthTextureObject = vtkTextureObject::New(); } this->DepthTextureObject->SetContext( vtkOpenGLRenderWindow::SafeDownCast(ren->GetRenderWindow())); if (!this->DepthTextureObject->GetHandle()) { // First set the parameters this->DepthTextureObject->SetWrapS(vtkTextureObject::ClampToEdge); this->DepthTextureObject->SetWrapT(vtkTextureObject::ClampToEdge); this->DepthTextureObject->SetMagnificationFilter(vtkTextureObject::Linear); this->DepthTextureObject->SetMinificationFilter(vtkTextureObject::Linear); this->DepthTextureObject->AllocateDepth( this->WindowSize[0], this->WindowSize[1], 4); } #if GL_ES_VERSION_3_0 != 1 // currently broken on ES this->DepthTextureObject->CopyFromFrameBuffer(this->WindowLowerLeft[0], this->WindowLowerLeft[1], 0, 0, this->WindowSize[0], this->WindowSize[1]); #endif } //---------------------------------------------------------------------------- void vtkOpenGLGPUVolumeRayCastMapper::vtkInternal::SetLightingParameters( vtkRenderer* ren, vtkShaderProgram* prog, vtkVolume* vol) { if (!ren || !prog || !vol) { return; } if (vol && !vol->GetProperty()->GetShade()) { return; } prog->SetUniformi("in_twoSidedLighting", ren->GetTwoSidedLighting()); // for lightkit case there are some parameters to set vtkCamera* cam = ren->GetActiveCamera(); vtkTransform* viewTF = cam->GetModelViewTransformObject(); // Bind some light settings int numberOfLights = 0; vtkLightCollection* lc = ren->GetLights(); vtkLight* light; vtkCollectionSimpleIterator sit; float lightAmbientColor[6][3]; float lightDiffuseColor[6][3]; float lightSpecularColor[6][3]; float lightDirection[6][3]; for (lc->InitTraversal(sit); (light = lc->GetNextLight(sit));) { float status = light->GetSwitch(); if (status > 0.0) { double* aColor = light->GetAmbientColor(); double* dColor = light->GetDiffuseColor(); double* sColor = light->GetDiffuseColor(); double intensity = light->GetIntensity(); lightAmbientColor[numberOfLights][0] = aColor[0] * intensity; lightAmbientColor[numberOfLights][1] = aColor[1] * intensity; lightAmbientColor[numberOfLights][2] = aColor[2] * intensity; lightDiffuseColor[numberOfLights][0] = dColor[0] * intensity; lightDiffuseColor[numberOfLights][1] = dColor[1] * intensity; lightDiffuseColor[numberOfLights][2] = dColor[2] * intensity; lightSpecularColor[numberOfLights][0] = sColor[0] * intensity; lightSpecularColor[numberOfLights][1] = sColor[1] * intensity; lightSpecularColor[numberOfLights][2] = sColor[2] * intensity; // Get required info from light double* lfp = light->GetTransformedFocalPoint(); double* lp = light->GetTransformedPosition(); double lightDir[3]; vtkMath::Subtract(lfp, lp, lightDir); vtkMath::Normalize(lightDir); double* tDir = viewTF->TransformNormal(lightDir); lightDirection[numberOfLights][0] = tDir[0]; lightDirection[numberOfLights][1] = tDir[1]; lightDirection[numberOfLights][2] = tDir[2]; numberOfLights++; } } prog->SetUniform3fv( "in_lightAmbientColor", numberOfLights, lightAmbientColor); prog->SetUniform3fv( "in_lightDiffuseColor", numberOfLights, lightDiffuseColor); prog->SetUniform3fv( "in_lightSpecularColor", numberOfLights, lightSpecularColor); prog->SetUniform3fv("in_lightDirection", numberOfLights, lightDirection); prog->SetUniformi("in_numberOfLights", numberOfLights); // we are done unless we have positional lights if (this->LightComplexity < 3) { return; } // if positional lights pass down more parameters float lightAttenuation[6][3]; float lightPosition[6][3]; float lightConeAngle[6]; float lightExponent[6]; int lightPositional[6]; numberOfLights = 0; for (lc->InitTraversal(sit); (light = lc->GetNextLight(sit));) { float status = light->GetSwitch(); if (status > 0.0) { double* attn = light->GetAttenuationValues(); lightAttenuation[numberOfLights][0] = attn[0]; lightAttenuation[numberOfLights][1] = attn[1]; lightAttenuation[numberOfLights][2] = attn[2]; lightExponent[numberOfLights] = light->GetExponent(); lightConeAngle[numberOfLights] = light->GetConeAngle(); double* lp = light->GetTransformedPosition(); double* tlp = viewTF->TransformPoint(lp); lightPosition[numberOfLights][0] = tlp[0]; lightPosition[numberOfLights][1] = tlp[1]; lightPosition[numberOfLights][2] = tlp[2]; lightPositional[numberOfLights] = light->GetPositional(); numberOfLights++; } } prog->SetUniform3fv("in_lightAttenuation", numberOfLights, lightAttenuation); prog->SetUniform1iv("in_lightPositional", numberOfLights, lightPositional); prog->SetUniform3fv("in_lightPosition", numberOfLights, lightPosition); prog->SetUniform1fv("in_lightExponent", numberOfLights, lightExponent); prog->SetUniform1fv("in_lightConeAngle", numberOfLights, lightConeAngle); } //----------------------------------------------------------------------------- void vtkOpenGLGPUVolumeRayCastMapper::vtkInternal::ComputeCellToPointMatrix() { this->CellToPointMatrix->Identity(); this->AdjustedTexMin[0] = this->AdjustedTexMin[1] = this->AdjustedTexMin[2] = 0.0f; this->AdjustedTexMin[3] = 1.0f; this->AdjustedTexMax[0] = this->AdjustedTexMax[1] = this->AdjustedTexMax[2] = 1.0f; this->AdjustedTexMax[3] = 1.0f; if (!this->Parent->CellFlag) // point data { // Extents are one minus the number of elements // so we have to add 1 to it to account for // number of elements in any cell or point image // data. float delta[3]; delta[0] = this->Extents[1] - this->Extents[0] + 1; delta[1] = this->Extents[3] - this->Extents[2] + 1; delta[2] = this->Extents[5] - this->Extents[4] + 1; float min[3]; min[0] = 0.5f / delta[0]; min[1] = 0.5f / delta[1]; min[2] = 0.5f / delta[2]; float range[3]; // max - min range[0] = (delta[0] - 0.5f) / delta[0] - min[0]; range[1] = (delta[1] - 0.5f) / delta[1] - min[1]; range[2] = (delta[2] - 0.5f) / delta[2] - min[2]; this->CellToPointMatrix->SetElement(0, 0, range[0]); // Scale diag this->CellToPointMatrix->SetElement(1, 1, range[1]); this->CellToPointMatrix->SetElement(2, 2, range[2]); this->CellToPointMatrix->SetElement(0, 3, min[0]); // t vector this->CellToPointMatrix->SetElement(1, 3, min[1]); this->CellToPointMatrix->SetElement(2, 3, min[2]); // Adjust limit coordinates for texture access. float const zeros[4] = { 0.0f, 0.0f, 0.0f, 1.0f }; // GL tex min float const ones[4] = { 1.0f, 1.0f, 1.0f, 1.0f }; // GL tex max this->CellToPointMatrix->MultiplyPoint(zeros, this->AdjustedTexMin); this->CellToPointMatrix->MultiplyPoint(ones, this->AdjustedTexMax); } } //---------------------------------------------------------------------------- bool vtkOpenGLGPUVolumeRayCastMapper::vtkInternal::IsCameraInside( vtkRenderer* ren, vtkVolume* vol) { this->TempMatrix1->DeepCopy(vol->GetMatrix()); this->TempMatrix1->Invert(); vtkCamera* cam = ren->GetActiveCamera(); double camWorldRange[2]; double camWorldPos[4]; double camFocalWorldPoint[4]; double camWorldDirection[4]; double camPos[4]; double camPlaneNormal[4]; cam->GetPosition(camWorldPos); camWorldPos[3] = 1.0; this->TempMatrix1->MultiplyPoint(camWorldPos, camPos); cam->GetFocalPoint(camFocalWorldPoint); camFocalWorldPoint[3] = 1.0; // The range (near/far) must also be transformed // into the local coordinate system. camWorldDirection[0] = camFocalWorldPoint[0] - camWorldPos[0]; camWorldDirection[1] = camFocalWorldPoint[1] - camWorldPos[1]; camWorldDirection[2] = camFocalWorldPoint[2] - camWorldPos[2]; camWorldDirection[3] = 0.0; // Compute the normalized near plane normal this->TempMatrix1->MultiplyPoint(camWorldDirection, camPlaneNormal); vtkMath::Normalize(camWorldDirection); vtkMath::Normalize(camPlaneNormal); double camNearWorldPoint[4]; double camNearPoint[4]; cam->GetClippingRange(camWorldRange); camNearWorldPoint[0] = camWorldPos[0] + camWorldRange[0] * camWorldDirection[0]; camNearWorldPoint[1] = camWorldPos[1] + camWorldRange[0] * camWorldDirection[1]; camNearWorldPoint[2] = camWorldPos[2] + camWorldRange[0] * camWorldDirection[2]; camNearWorldPoint[3] = 1.; this->TempMatrix1->MultiplyPoint(camNearWorldPoint, camNearPoint); int const result = vtkMath::PlaneIntersectsAABB( this->LoadedBounds, camPlaneNormal, camNearPoint); if (result == 0) { return true; } return false; } //---------------------------------------------------------------------------- void vtkOpenGLGPUVolumeRayCastMapper::vtkInternal::RenderVolumeGeometry( vtkRenderer* ren, vtkShaderProgram* prog, vtkVolume* vol) { if (this->NeedToInitializeResources || !this->BBoxPolyData || this->Parent->VolumeTexture->UploadTime > this->BBoxPolyData->GetMTime() || this->IsCameraInside(ren, vol) || this->CameraWasInsideInLastUpdate) { vtkNew boxSource; boxSource->SetBounds(this->LoadedBounds); boxSource->QuadsOn(); boxSource->SetLevel(0); vtkNew densityPolyData; if (this->IsCameraInside(ren, vol)) { // Normals should be transformed using the transpose of inverse // InverseVolumeMat this->TempMatrix1->DeepCopy(vol->GetMatrix()); this->TempMatrix1->Invert(); vtkCamera* cam = ren->GetActiveCamera(); double camWorldRange[2]; double camWorldPos[4]; double camFocalWorldPoint[4]; double camWorldDirection[4]; double camPos[4]; double camPlaneNormal[4]; cam->GetPosition(camWorldPos); camWorldPos[3] = 1.0; this->TempMatrix1->MultiplyPoint(camWorldPos, camPos); cam->GetFocalPoint(camFocalWorldPoint); camFocalWorldPoint[3] = 1.0; // The range (near/far) must also be transformed // into the local coordinate system. camWorldDirection[0] = camFocalWorldPoint[0] - camWorldPos[0]; camWorldDirection[1] = camFocalWorldPoint[1] - camWorldPos[1]; camWorldDirection[2] = camFocalWorldPoint[2] - camWorldPos[2]; camWorldDirection[3] = 0.0; // Compute the normalized near plane normal this->TempMatrix1->MultiplyPoint(camWorldDirection, camPlaneNormal); vtkMath::Normalize(camWorldDirection); vtkMath::Normalize(camPlaneNormal); double camNearWorldPoint[4]; double camFarWorldPoint[4]; double camNearPoint[4]; double camFarPoint[4]; cam->GetClippingRange(camWorldRange); camNearWorldPoint[0] = camWorldPos[0] + camWorldRange[0] * camWorldDirection[0]; camNearWorldPoint[1] = camWorldPos[1] + camWorldRange[0] * camWorldDirection[1]; camNearWorldPoint[2] = camWorldPos[2] + camWorldRange[0] * camWorldDirection[2]; camNearWorldPoint[3] = 1.; camFarWorldPoint[0] = camWorldPos[0] + camWorldRange[1] * camWorldDirection[0]; camFarWorldPoint[1] = camWorldPos[1] + camWorldRange[1] * camWorldDirection[1]; camFarWorldPoint[2] = camWorldPos[2] + camWorldRange[1] * camWorldDirection[2]; camFarWorldPoint[3] = 1.; this->TempMatrix1->MultiplyPoint(camNearWorldPoint, camNearPoint); this->TempMatrix1->MultiplyPoint(camFarWorldPoint, camFarPoint); vtkNew nearPlane; // We add an offset to the near plane to avoid hardware clipping of the // near plane due to floating-point precision. // camPlaneNormal is a unit vector, if the offset is larger than the // distance between near and far point, it will not work. Hence, we choose // a fraction of the near-far distance. However, care should be taken // to avoid hardware clipping in volumes with very small spacing where the // distance between near and far plane is also very small. In that case, // a minimum offset is chosen. This is chosen based on the typical // epsilon values on x86 systems. double offset = sqrt(vtkMath::Distance2BetweenPoints(camNearPoint, camFarPoint)) / 1000.0; // Minimum offset to avoid floating point precision issues for volumes // with very small spacing double minOffset = static_cast(std::numeric_limits::epsilon()) * 1000.0; offset = offset < minOffset ? minOffset : offset; camNearPoint[0] += camPlaneNormal[0] * offset; camNearPoint[1] += camPlaneNormal[1] * offset; camNearPoint[2] += camPlaneNormal[2] * offset; nearPlane->SetOrigin(camNearPoint); nearPlane->SetNormal(camPlaneNormal); vtkNew planes; planes->RemoveAllItems(); planes->AddItem(nearPlane); vtkNew clip; clip->SetInputConnection(boxSource->GetOutputPort()); clip->SetPlanes(planes); densityPolyData->SetInputConnection(clip->GetOutputPort()); this->CameraWasInsideInLastUpdate = true; } else { densityPolyData->SetInputConnection(boxSource->GetOutputPort()); this->CameraWasInsideInLastUpdate = false; } densityPolyData->SetNumberOfSubdivisions(2); densityPolyData->Update(); this->BBoxPolyData = vtkSmartPointer::New(); this->BBoxPolyData->ShallowCopy(densityPolyData->GetOutput()); vtkPoints* points = this->BBoxPolyData->GetPoints(); vtkCellArray* cells = this->BBoxPolyData->GetPolys(); vtkNew polys; polys->SetNumberOfComponents(3); vtkIdType npts; vtkIdType* pts; // See if the volume transform is orientation-preserving // and orient polygons accordingly vtkMatrix4x4* volMat = vol->GetMatrix(); double det = vtkMath::Determinant3x3(volMat->GetElement(0, 0), volMat->GetElement(0, 1), volMat->GetElement(0, 2), volMat->GetElement(1, 0), volMat->GetElement(1, 1), volMat->GetElement(1, 2), volMat->GetElement(2, 0), volMat->GetElement(2, 1), volMat->GetElement(2, 2)); bool preservesOrientation = det > 0.0; const vtkIdType indexMap[3] = { preservesOrientation ? 0 : 2, 1, preservesOrientation ? 2 : 0 }; while (cells->GetNextCell(npts, pts)) { polys->InsertNextTuple3( pts[indexMap[0]], pts[indexMap[1]], pts[indexMap[2]]); } // Dispose any previously created buffers this->DeleteBufferObjects(); // Now create new ones this->CreateBufferObjects(); // TODO: should really use the built in VAO class // which handles these apple issues internally #ifdef __APPLE__ if (vtkOpenGLRenderWindow::GetContextSupportsOpenGL32()) #endif { glBindVertexArray(this->CubeVAOId); } // Pass cube vertices to buffer object memory glBindBuffer(GL_ARRAY_BUFFER, this->CubeVBOId); glBufferData(GL_ARRAY_BUFFER, points->GetData()->GetDataSize() * points->GetData()->GetDataTypeSize(), points->GetData()->GetVoidPointer(0), GL_STATIC_DRAW); prog->EnableAttributeArray("in_vertexPos"); prog->UseAttributeArray( "in_vertexPos", 0, 0, VTK_FLOAT, 3, vtkShaderProgram::NoNormalize); glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, this->CubeIndicesId); glBufferData(GL_ELEMENT_ARRAY_BUFFER, polys->GetDataSize() * polys->GetDataTypeSize(), polys->GetVoidPointer(0), GL_STATIC_DRAW); } else { #ifdef __APPLE__ if (!vtkOpenGLRenderWindow::GetContextSupportsOpenGL32()) { glBindBuffer(GL_ARRAY_BUFFER, this->CubeVBOId); prog->EnableAttributeArray("in_vertexPos"); prog->UseAttributeArray( "in_vertexPos", 0, 0, VTK_FLOAT, 3, vtkShaderProgram::NoNormalize); glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, this->CubeIndicesId); } else #endif { glBindVertexArray(this->CubeVAOId); } } glDrawElements(GL_TRIANGLES, this->BBoxPolyData->GetNumberOfCells() * 3, GL_UNSIGNED_INT, nullptr); vtkOpenGLStaticCheckErrorMacro("Error after glDrawElements in" " RenderVolumeGeometry!"); #ifdef __APPLE__ if (!vtkOpenGLRenderWindow::GetContextSupportsOpenGL32()) { glBindBuffer(GL_ARRAY_BUFFER, 0); glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); } else #endif { glBindVertexArray(0); glBindBuffer(GL_ARRAY_BUFFER, 0); glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); } } //---------------------------------------------------------------------------- void vtkOpenGLGPUVolumeRayCastMapper::vtkInternal::SetCroppingRegions( vtkRenderer* vtkNotUsed(ren), vtkShaderProgram* prog, vtkVolume* vtkNotUsed(vol)) { if (this->Parent->GetCropping()) { int cropFlags = this->Parent->GetCroppingRegionFlags(); double croppingRegionPlanes[6]; this->Parent->GetCroppingRegionPlanes(croppingRegionPlanes); // Clamp it croppingRegionPlanes[0] = croppingRegionPlanes[0] < this->LoadedBounds[0] ? this->LoadedBounds[0] : croppingRegionPlanes[0]; croppingRegionPlanes[0] = croppingRegionPlanes[0] > this->LoadedBounds[1] ? this->LoadedBounds[1] : croppingRegionPlanes[0]; croppingRegionPlanes[1] = croppingRegionPlanes[1] < this->LoadedBounds[0] ? this->LoadedBounds[0] : croppingRegionPlanes[1]; croppingRegionPlanes[1] = croppingRegionPlanes[1] > this->LoadedBounds[1] ? this->LoadedBounds[1] : croppingRegionPlanes[1]; croppingRegionPlanes[2] = croppingRegionPlanes[2] < this->LoadedBounds[2] ? this->LoadedBounds[2] : croppingRegionPlanes[2]; croppingRegionPlanes[2] = croppingRegionPlanes[2] > this->LoadedBounds[3] ? this->LoadedBounds[3] : croppingRegionPlanes[2]; croppingRegionPlanes[3] = croppingRegionPlanes[3] < this->LoadedBounds[2] ? this->LoadedBounds[2] : croppingRegionPlanes[3]; croppingRegionPlanes[3] = croppingRegionPlanes[3] > this->LoadedBounds[3] ? this->LoadedBounds[3] : croppingRegionPlanes[3]; croppingRegionPlanes[4] = croppingRegionPlanes[4] < this->LoadedBounds[4] ? this->LoadedBounds[4] : croppingRegionPlanes[4]; croppingRegionPlanes[4] = croppingRegionPlanes[4] > this->LoadedBounds[5] ? this->LoadedBounds[5] : croppingRegionPlanes[4]; croppingRegionPlanes[5] = croppingRegionPlanes[5] < this->LoadedBounds[4] ? this->LoadedBounds[4] : croppingRegionPlanes[5]; croppingRegionPlanes[5] = croppingRegionPlanes[5] > this->LoadedBounds[5] ? this->LoadedBounds[5] : croppingRegionPlanes[5]; float cropPlanes[6] = { static_cast(croppingRegionPlanes[0]), static_cast(croppingRegionPlanes[1]), static_cast(croppingRegionPlanes[2]), static_cast(croppingRegionPlanes[3]), static_cast(croppingRegionPlanes[4]), static_cast(croppingRegionPlanes[5]) }; prog->SetUniform1fv("in_croppingPlanes", 6, cropPlanes); const int numberOfRegions = 32; int cropFlagsArray[numberOfRegions]; cropFlagsArray[0] = 0; int i = 1; while (cropFlags && i < 32) { cropFlagsArray[i] = cropFlags & 1; cropFlags = cropFlags >> 1; ++i; } for (; i < 32; ++i) { cropFlagsArray[i] = 0; } prog->SetUniform1iv("in_croppingFlags", numberOfRegions, cropFlagsArray); } } //---------------------------------------------------------------------------- void vtkOpenGLGPUVolumeRayCastMapper::vtkInternal::SetClippingPlanes( vtkRenderer* vtkNotUsed(ren), vtkShaderProgram* prog, vtkVolume* vtkNotUsed(vol)) { if (this->Parent->GetClippingPlanes()) { std::vector clippingPlanes; // Currently we don't have any clipping plane clippingPlanes.push_back(0); this->Parent->ClippingPlanes->InitTraversal(); vtkPlane* plane; while ((plane = this->Parent->ClippingPlanes->GetNextItem())) { // Planes are in world coordinates double planeOrigin[3], planeNormal[3]; plane->GetOrigin(planeOrigin); plane->GetNormal(planeNormal); clippingPlanes.push_back(planeOrigin[0]); clippingPlanes.push_back(planeOrigin[1]); clippingPlanes.push_back(planeOrigin[2]); clippingPlanes.push_back(planeNormal[0]); clippingPlanes.push_back(planeNormal[1]); clippingPlanes.push_back(planeNormal[2]); } clippingPlanes[0] = clippingPlanes.size() > 1 ? static_cast(clippingPlanes.size() - 1) : 0; prog->SetUniform1fv("in_clippingPlanes", static_cast(clippingPlanes.size()), &clippingPlanes[0]); } } // ----------------------------------------------------------------------------- void vtkOpenGLGPUVolumeRayCastMapper::vtkInternal::CheckPropertyKeys( vtkVolume* vol) { // Check the property keys to see if we should modify the blend/etc state: // Otherwise this breaks volume/translucent geo depth peeling. vtkInformation* volumeKeys = vol->GetPropertyKeys(); this->PreserveGLState = false; if (volumeKeys && volumeKeys->Has(vtkOpenGLActor::GLDepthMaskOverride())) { int override = volumeKeys->Get(vtkOpenGLActor::GLDepthMaskOverride()); if (override != 0 && override != 1) { this->PreserveGLState = true; } } // Some render passes (e.g. DualDepthPeeling) adjust the viewport for // intermediate passes so it is necessary to preserve it. This is a // temporary fix for vtkDualDepthPeelingPass to work when various viewports // are defined. The correct way of fixing this would be to avoid setting the // viewport within the mapper. It is enough for now to check for the // RenderPasses() vtkInfo given that vtkDualDepthPeelingPass is the only pass // currently supported by this mapper, the viewport will have to be adjusted // externally before adding support for other passes. vtkInformation* info = vol->GetPropertyKeys(); this->PreserveViewport = info && info->Has(vtkOpenGLRenderPass::RenderPasses()); } // ----------------------------------------------------------------------------- void vtkOpenGLGPUVolumeRayCastMapper::vtkInternal::CheckPickingState( vtkRenderer* ren) { vtkHardwareSelector* selector = ren->GetSelector(); bool selectorPicking = selector != nullptr; if (selector) { // this mapper currently only supports cell picking selectorPicking &= selector->GetFieldAssociation() == vtkDataObject::FIELD_ASSOCIATION_CELLS; } this->IsPicking = selectorPicking || ren->GetRenderWindow()->GetIsPicking(); if (this->IsPicking) { // rebuild the shader on every pass this->SelectionStateTime.Modified(); this->CurrentSelectionPass = selector ? selector->GetCurrentPass() : vtkHardwareSelector::ACTOR_PASS; } else if (this->CurrentSelectionPass != vtkHardwareSelector::MIN_KNOWN_PASS - 1) { // return to the regular rendering state this->SelectionStateTime.Modified(); this->CurrentSelectionPass = vtkHardwareSelector::MIN_KNOWN_PASS - 1; } } // ----------------------------------------------------------------------------- void vtkOpenGLGPUVolumeRayCastMapper::vtkInternal::BeginPicking( vtkRenderer* ren) { vtkHardwareSelector* selector = ren->GetSelector(); if (selector && this->IsPicking) { selector->BeginRenderProp(); if (this->CurrentSelectionPass >= vtkHardwareSelector::ID_LOW24) { selector->RenderAttributeId(0); } } } //------------------------------------------------------------------------------ void vtkOpenGLGPUVolumeRayCastMapper::vtkInternal::SetPickingId( vtkRenderer* ren) { float propIdColor[3] = { 0.0, 0.0, 0.0 }; vtkHardwareSelector* selector = ren->GetSelector(); if (selector && this->IsPicking) { // query the selector for the appropriate id selector->GetPropColorValue(propIdColor); } else // RenderWindow is picking { unsigned int const idx = ren->GetCurrentPickId(); vtkHardwareSelector::Convert(idx, propIdColor); } this->ShaderProgram->SetUniform3f("in_propId", propIdColor); } // --------------------------------------------------------------------------- void vtkOpenGLGPUVolumeRayCastMapper::vtkInternal::EndPicking(vtkRenderer* ren) { vtkHardwareSelector* selector = ren->GetSelector(); if (selector && this->IsPicking) { if (this->CurrentSelectionPass >= vtkHardwareSelector::ID_LOW24) { // tell the selector the maximum number of cells that the mapper could render unsigned int const numVoxels = (this->Extents[1] - this->Extents[0] + 1) * (this->Extents[3] - this->Extents[2] + 1) * (this->Extents[5] - this->Extents[4] + 1); selector->RenderAttributeId(numVoxels); } selector->EndRenderProp(); } } //---------------------------------------------------------------------------- void vtkOpenGLGPUVolumeRayCastMapper::vtkInternal::UpdateSamplingDistance( vtkImageData* input, vtkRenderer* vtkNotUsed(ren), vtkVolume* vol) { if (!this->Parent->AutoAdjustSampleDistances) { if (this->Parent->LockSampleDistanceToInputSpacing) { float const d = static_cast(this->Parent->SpacingAdjustedSampleDistance( this->CellSpacing, this->Extents)); float const sample = this->Parent->SampleDistance; // ActualSampleDistance will grow proportionally to numVoxels^(1/3) (see // vtkVolumeMapper.cxx). Until it reaches 1/2 average voxel size when // number of voxels is 1E6. this->ActualSampleDistance = (sample / d < 0.999f || sample / d > 1.001f) ? d : this->Parent->SampleDistance; return; } this->ActualSampleDistance = this->Parent->SampleDistance; } else { input->GetSpacing(this->CellSpacing); vtkMatrix4x4* worldToDataset = vol->GetMatrix(); double minWorldSpacing = VTK_DOUBLE_MAX; int i = 0; while (i < 3) { double tmp = worldToDataset->GetElement(0, i); double tmp2 = tmp * tmp; tmp = worldToDataset->GetElement(1, i); tmp2 += tmp * tmp; tmp = worldToDataset->GetElement(2, i); tmp2 += tmp * tmp; // We use fabs() in case the spacing is negative. double worldSpacing = fabs(this->CellSpacing[i] * sqrt(tmp2)); if (worldSpacing < minWorldSpacing) { minWorldSpacing = worldSpacing; } ++i; } // minWorldSpacing is the optimal sample distance in world space. // To go faster (reduceFactor<1.0), we multiply this distance // by 1/reduceFactor. this->ActualSampleDistance = static_cast(minWorldSpacing); if (this->Parent->ReductionFactor < 1.0 && this->Parent->ReductionFactor != 0.0) { this->ActualSampleDistance /= static_cast(this->Parent->ReductionFactor); } } } //---------------------------------------------------------------------------- void vtkOpenGLGPUVolumeRayCastMapper::vtkInternal:: LoadRequireDepthTextureExtensions(vtkRenderWindow* vtkNotUsed(renWin)) { // Reset the message stream for extensions if (vtkOpenGLRenderWindow::GetContextSupportsOpenGL32()) { this->LoadDepthTextureExtensionsSucceeded = true; return; } this->ExtensionsStringStream.str(""); this->ExtensionsStringStream.clear(); #if GL_ES_VERSION_3_0 != 1 // Check for float texture support. This extension became core // in 3.0 if (!glewIsSupported("GL_ARB_texture_float")) { this->ExtensionsStringStream << "Required extension " << " GL_ARB_texture_float is not supported"; return; } #endif // NOTE: Support for depth sampler texture made into the core since version // 1.4 and therefore we are no longer checking for it. this->LoadDepthTextureExtensionsSucceeded = true; } //---------------------------------------------------------------------------- void vtkOpenGLGPUVolumeRayCastMapper::vtkInternal::CreateBufferObjects() { #ifdef __APPLE__ if (vtkOpenGLRenderWindow::GetContextSupportsOpenGL32()) #endif { glGenVertexArrays(1, &this->CubeVAOId); } glGenBuffers(1, &this->CubeVBOId); glGenBuffers(1, &this->CubeIndicesId); } //---------------------------------------------------------------------------- void vtkOpenGLGPUVolumeRayCastMapper::vtkInternal::DeleteBufferObjects() { if (this->CubeVBOId) { glBindBuffer(GL_ARRAY_BUFFER, this->CubeVBOId); glDeleteBuffers(1, &this->CubeVBOId); this->CubeVBOId = 0; } if (this->CubeIndicesId) { glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, this->CubeIndicesId); glDeleteBuffers(1, &this->CubeIndicesId); this->CubeIndicesId = 0; } if (this->CubeVAOId) { #ifdef __APPLE__ if (vtkOpenGLRenderWindow::GetContextSupportsOpenGL32()) #endif { glDeleteVertexArrays(1, &this->CubeVAOId); } this->CubeVAOId = 0; } } //---------------------------------------------------------------------------- void vtkOpenGLGPUVolumeRayCastMapper::vtkInternal::ConvertTextureToImageData( vtkTextureObject* texture, vtkImageData* output) { if (!texture) { return; } unsigned int tw = texture->GetWidth(); unsigned int th = texture->GetHeight(); unsigned int tnc = texture->GetComponents(); int tt = texture->GetVTKDataType(); vtkPixelExtent texExt(0U, tw - 1U, 0U, th - 1U); int dataExt[6] = { 0, 0, 0, 0, 0, 0 }; texExt.GetData(dataExt); double dataOrigin[6] = { 0, 0, 0, 0, 0, 0 }; vtkImageData* id = vtkImageData::New(); id->SetOrigin(dataOrigin); id->SetDimensions(tw, th, 1); id->SetExtent(dataExt); id->AllocateScalars(tt, tnc); vtkPixelBufferObject* pbo = texture->Download(); vtkPixelTransfer::Blit(texExt, texExt, texExt, texExt, tnc, tt, pbo->MapPackedBuffer(), tnc, tt, id->GetScalarPointer(0, 0, 0)); pbo->UnmapPackedBuffer(); pbo->Delete(); if (!output) { output = vtkImageData::New(); } output->DeepCopy(id); id->Delete(); } //---------------------------------------------------------------------------- void vtkOpenGLGPUVolumeRayCastMapper::vtkInternal::BeginImageSample( vtkRenderer* ren, vtkVolume* vol) { const auto numBuffers = this->GetNumImageSampleDrawBuffers(vol); if (numBuffers != this->NumImageSampleDrawBuffers) { if (numBuffers > this->NumImageSampleDrawBuffers) { this->ReleaseImageSampleGraphicsResources(ren->GetRenderWindow()); } this->NumImageSampleDrawBuffers = numBuffers; this->RebuildImageSampleProg = true; } float const xySampleDist = this->Parent->ImageSampleDistance; if (xySampleDist != 1.f && this->InitializeImageSampleFBO(ren)) { this->ImageSampleFBO->SaveCurrentBindingsAndBuffers(GL_DRAW_FRAMEBUFFER); this->ImageSampleFBO->DeactivateDrawBuffers(); this->ImageSampleFBO->Bind(GL_DRAW_FRAMEBUFFER); this->ImageSampleFBO->ActivateDrawBuffers( static_cast(this->NumImageSampleDrawBuffers)); glClearColor(0.0, 0.0, 0.0, 0.0); glClear(GL_COLOR_BUFFER_BIT); } } //---------------------------------------------------------------------------- bool vtkOpenGLGPUVolumeRayCastMapper::vtkInternal::InitializeImageSampleFBO( vtkRenderer* ren) { // Set the FBO viewport size. These are used in the shader to normalize the // fragment coordinate, the normalized coordinate is used to fetch the depth // buffer. this->WindowSize[0] /= this->Parent->ImageSampleDistance; this->WindowSize[1] /= this->Parent->ImageSampleDistance; this->WindowLowerLeft[0] = 0; this->WindowLowerLeft[1] = 0; // Set FBO viewport glViewport(this->WindowLowerLeft[0], this->WindowLowerLeft[1], this->WindowSize[0], this->WindowSize[1]); if (!this->ImageSampleFBO) { vtkOpenGLRenderWindow* win = vtkOpenGLRenderWindow::SafeDownCast(ren->GetRenderWindow()); this->ImageSampleTexture.reserve(this->NumImageSampleDrawBuffers); this->ImageSampleTexNames.reserve(this->NumImageSampleDrawBuffers); for (size_t i = 0; i < this->NumImageSampleDrawBuffers; i++) { auto tex = vtkSmartPointer::New(); tex->SetContext(win); tex->Create2D( this->WindowSize[0], this->WindowSize[1], 4, VTK_UNSIGNED_CHAR, false); tex->Activate(); tex->SetMinificationFilter(vtkTextureObject::Linear); tex->SetMagnificationFilter(vtkTextureObject::Linear); tex->SetWrapS(vtkTextureObject::ClampToEdge); tex->SetWrapT(vtkTextureObject::ClampToEdge); this->ImageSampleTexture.push_back(tex); std::stringstream ss; ss << i; const std::string name = "renderedTex_" + ss.str(); this->ImageSampleTexNames.push_back(name); } this->ImageSampleFBO = vtkOpenGLFramebufferObject::New(); this->ImageSampleFBO->SetContext(win); this->ImageSampleFBO->SaveCurrentBindingsAndBuffers(GL_FRAMEBUFFER); this->ImageSampleFBO->Bind(GL_FRAMEBUFFER); this->ImageSampleFBO->InitializeViewport( this->WindowSize[0], this->WindowSize[1]); auto num = static_cast(this->NumImageSampleDrawBuffers); for (unsigned int i = 0; i < num; i++) { this->ImageSampleFBO->AddColorAttachment( GL_FRAMEBUFFER, i, this->ImageSampleTexture[i]); } // Verify completeness const int complete = this->ImageSampleFBO->CheckFrameBufferStatus(GL_FRAMEBUFFER); for (auto& tex : this->ImageSampleTexture) { tex->Deactivate(); } this->ImageSampleFBO->RestorePreviousBindingsAndBuffers(GL_FRAMEBUFFER); if (!complete) { vtkGenericWarningMacro(<< "Failed to attach ImageSampleFBO!"); this->ReleaseImageSampleGraphicsResources(win); return false; } this->RebuildImageSampleProg = true; return true; } // Resize if necessary int lastSize[2]; this->ImageSampleFBO->GetLastSize(lastSize); if (lastSize[0] != this->WindowSize[0] || lastSize[1] != this->WindowSize[1]) { this->ImageSampleFBO->Resize(this->WindowSize[0], this->WindowSize[1]); } return true; } //---------------------------------------------------------------------------- void vtkOpenGLGPUVolumeRayCastMapper::vtkInternal::EndImageSample( vtkRenderer* ren) { if (this->Parent->ImageSampleDistance != 1.f) { this->ImageSampleFBO->DeactivateDrawBuffers(); this->ImageSampleFBO->RestorePreviousBindingsAndBuffers( GL_DRAW_FRAMEBUFFER); if (this->RenderPassAttached) { this->ImageSampleFBO->ActivateDrawBuffers( static_cast(this->NumImageSampleDrawBuffers)); } // Render the contents of ImageSampleFBO as a quad to intermix with the // rest of the scene. typedef vtkOpenGLRenderUtilities GLUtil; vtkOpenGLRenderWindow* win = static_cast(ren->GetRenderWindow()); if (this->RebuildImageSampleProg) { std::string frag = GLUtil::GetFullScreenQuadFragmentShaderTemplate(); vtkShaderProgram::Substitute(frag, "//VTK::FSQ::Decl", vtkvolume::ImageSampleDeclarationFrag( this->ImageSampleTexNames, this->NumImageSampleDrawBuffers)); vtkShaderProgram::Substitute(frag, "//VTK::FSQ::Impl", vtkvolume::ImageSampleImplementationFrag( this->ImageSampleTexNames, this->NumImageSampleDrawBuffers)); this->ImageSampleProg = win->GetShaderCache()->ReadyShaderProgram( GLUtil::GetFullScreenQuadVertexShader().c_str(), frag.c_str(), GLUtil::GetFullScreenQuadGeometryShader().c_str()); } else { win->GetShaderCache()->ReadyShaderProgram(this->ImageSampleProg); } if (!this->ImageSampleProg) { vtkGenericWarningMacro(<< "Failed to initialize ImageSampleProgram!"); return; } if (!this->ImageSampleVAO) { this->ImageSampleVBO = vtkOpenGLBufferObject::New(); this->ImageSampleVAO = vtkOpenGLVertexArrayObject::New(); GLUtil::PrepFullScreenVAO( this->ImageSampleVBO, this->ImageSampleVAO, this->ImageSampleProg); } // Adjust the GL viewport to VTK's defined viewport ren->GetTiledSizeAndOrigin(this->WindowSize, this->WindowSize + 1, this->WindowLowerLeft, this->WindowLowerLeft + 1); glViewport(this->WindowLowerLeft[0], this->WindowLowerLeft[1], this->WindowSize[0], this->WindowSize[1]); // Bind objects and draw glEnable(GL_BLEND); glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA); glDisable(GL_DEPTH_TEST); for (size_t i = 0; i < this->NumImageSampleDrawBuffers; i++) { this->ImageSampleTexture[i]->Activate(); this->ImageSampleProg->SetUniformi(this->ImageSampleTexNames[i].c_str(), this->ImageSampleTexture[i]->GetTextureUnit()); } this->ImageSampleVAO->Bind(); GLUtil::DrawFullScreenQuad(); this->ImageSampleVAO->Release(); vtkOpenGLStaticCheckErrorMacro("Error after DrawFullScreenQuad()!"); for (auto& tex : this->ImageSampleTexture) { tex->Deactivate(); } } } //------------------------------------------------------------------------------ size_t vtkOpenGLGPUVolumeRayCastMapper::vtkInternal::GetNumImageSampleDrawBuffers( vtkVolume* vol) { if (this->RenderPassAttached) { vtkInformation* info = vol->GetPropertyKeys(); const int num = info->Length(vtkOpenGLRenderPass::RenderPasses()); vtkObjectBase* rpBase = info->Get(vtkOpenGLRenderPass::RenderPasses(), num - 1); vtkOpenGLRenderPass* rp = static_cast(rpBase); return static_cast(rp->GetActiveDrawBuffers()); } return 1; } //---------------------------------------------------------------------------- void vtkOpenGLGPUVolumeRayCastMapper::vtkInternal::SetupRenderToTexture( vtkRenderer* ren) { if (this->Parent->RenderToImage && this->Parent->CurrentPass == RenderPass) { if (this->Parent->ImageSampleDistance != 1.f) { this->WindowSize[0] /= this->Parent->ImageSampleDistance; this->WindowSize[1] /= this->Parent->ImageSampleDistance; } if ((this->LastRenderToImageWindowSize[0] != this->WindowSize[0]) || (this->LastRenderToImageWindowSize[1] != this->WindowSize[1])) { this->LastRenderToImageWindowSize[0] = this->WindowSize[0]; this->LastRenderToImageWindowSize[1] = this->WindowSize[1]; this->ReleaseRenderToTextureGraphicsResources(ren->GetRenderWindow()); } if (!this->FBO) { this->FBO = vtkOpenGLFramebufferObject::New(); } this->FBO->SetContext( vtkOpenGLRenderWindow::SafeDownCast(ren->GetRenderWindow())); this->FBO->SaveCurrentBindingsAndBuffers(); this->FBO->Bind(GL_FRAMEBUFFER); this->FBO->InitializeViewport(this->WindowSize[0], this->WindowSize[1]); int depthImageScalarType = this->Parent->GetDepthImageScalarType(); bool initDepthTexture = true; // Re-instantiate the depth texture object if the scalar type requested has // changed from the last frame if (this->RTTDepthTextureObject && this->RTTDepthTextureType == depthImageScalarType) { initDepthTexture = false; } if (initDepthTexture) { if (this->RTTDepthTextureObject) { this->RTTDepthTextureObject->Delete(); this->RTTDepthTextureObject = nullptr; } this->RTTDepthTextureObject = vtkTextureObject::New(); this->RTTDepthTextureObject->SetContext( vtkOpenGLRenderWindow::SafeDownCast(ren->GetRenderWindow())); this->RTTDepthTextureObject->Create2D(this->WindowSize[0], this->WindowSize[1], 1, depthImageScalarType, false); this->RTTDepthTextureObject->Activate(); this->RTTDepthTextureObject->SetMinificationFilter( vtkTextureObject::Nearest); this->RTTDepthTextureObject->SetMagnificationFilter( vtkTextureObject::Nearest); this->RTTDepthTextureObject->SetAutoParameters(0); // Cache the value of the scalar type this->RTTDepthTextureType = depthImageScalarType; } if (!this->RTTColorTextureObject) { this->RTTColorTextureObject = vtkTextureObject::New(); this->RTTColorTextureObject->SetContext( vtkOpenGLRenderWindow::SafeDownCast(ren->GetRenderWindow())); this->RTTColorTextureObject->Create2D( this->WindowSize[0], this->WindowSize[1], 4, VTK_UNSIGNED_CHAR, false); this->RTTColorTextureObject->Activate(); this->RTTColorTextureObject->SetMinificationFilter( vtkTextureObject::Nearest); this->RTTColorTextureObject->SetMagnificationFilter( vtkTextureObject::Nearest); this->RTTColorTextureObject->SetAutoParameters(0); } if (!this->RTTDepthBufferTextureObject) { this->RTTDepthBufferTextureObject = vtkTextureObject::New(); this->RTTDepthBufferTextureObject->SetContext( vtkOpenGLRenderWindow::SafeDownCast(ren->GetRenderWindow())); this->RTTDepthBufferTextureObject->AllocateDepth( this->WindowSize[0], this->WindowSize[1], vtkTextureObject::Float32); this->RTTDepthBufferTextureObject->Activate(); this->RTTDepthBufferTextureObject->SetMinificationFilter( vtkTextureObject::Nearest); this->RTTDepthBufferTextureObject->SetMagnificationFilter( vtkTextureObject::Nearest); this->RTTDepthBufferTextureObject->SetAutoParameters(0); } this->FBO->Bind(GL_FRAMEBUFFER); this->FBO->AddDepthAttachment( GL_FRAMEBUFFER, this->RTTDepthBufferTextureObject); this->FBO->AddColorAttachment( GL_FRAMEBUFFER, 0U, this->RTTColorTextureObject); this->FBO->AddColorAttachment( GL_FRAMEBUFFER, 1U, this->RTTDepthTextureObject); this->FBO->ActivateDrawBuffers(2); this->FBO->CheckFrameBufferStatus(GL_FRAMEBUFFER); glClearColor(1.0, 1.0, 1.0, 0.0); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); } } //---------------------------------------------------------------------------- void vtkOpenGLGPUVolumeRayCastMapper::vtkInternal::ExitRenderToTexture( vtkRenderer* vtkNotUsed(ren)) { if (this->Parent->RenderToImage && this->Parent->CurrentPass == RenderPass) { this->FBO->RemoveTexDepthAttachment(GL_FRAMEBUFFER); this->FBO->RemoveTexColorAttachment(GL_FRAMEBUFFER, 0U); this->FBO->RemoveTexColorAttachment(GL_FRAMEBUFFER, 1U); this->FBO->DeactivateDrawBuffers(); this->FBO->RestorePreviousBindingsAndBuffers(); this->RTTDepthBufferTextureObject->Deactivate(); this->RTTColorTextureObject->Deactivate(); this->RTTDepthTextureObject->Deactivate(); } } //---------------------------------------------------------------------------- void vtkOpenGLGPUVolumeRayCastMapper::vtkInternal::SetupDepthPass( vtkRenderer* ren) { if (this->Parent->ImageSampleDistance != 1.f) { this->WindowSize[0] /= this->Parent->ImageSampleDistance; this->WindowSize[1] /= this->Parent->ImageSampleDistance; } if ((this->LastDepthPassWindowSize[0] != this->WindowSize[0]) || (this->LastDepthPassWindowSize[1] != this->WindowSize[1])) { this->LastDepthPassWindowSize[0] = this->WindowSize[0]; this->LastDepthPassWindowSize[1] = this->WindowSize[1]; this->ReleaseDepthPassGraphicsResources(ren->GetRenderWindow()); } if (!this->DPFBO) { this->DPFBO = vtkOpenGLFramebufferObject::New(); } this->DPFBO->SetContext( vtkOpenGLRenderWindow::SafeDownCast(ren->GetRenderWindow())); this->DPFBO->SaveCurrentBindingsAndBuffers(); this->DPFBO->Bind(GL_FRAMEBUFFER); this->DPFBO->InitializeViewport(this->WindowSize[0], this->WindowSize[1]); if (!this->DPDepthBufferTextureObject || !this->DPColorTextureObject) { this->DPDepthBufferTextureObject = vtkTextureObject::New(); this->DPDepthBufferTextureObject->SetContext( vtkOpenGLRenderWindow::SafeDownCast(ren->GetRenderWindow())); this->DPDepthBufferTextureObject->AllocateDepth( this->WindowSize[0], this->WindowSize[1], vtkTextureObject::Native); this->DPDepthBufferTextureObject->Activate(); this->DPDepthBufferTextureObject->SetMinificationFilter( vtkTextureObject::Nearest); this->DPDepthBufferTextureObject->SetMagnificationFilter( vtkTextureObject::Nearest); this->DPDepthBufferTextureObject->SetAutoParameters(0); this->DPDepthBufferTextureObject->Bind(); this->DPColorTextureObject = vtkTextureObject::New(); this->DPColorTextureObject->SetContext( vtkOpenGLRenderWindow::SafeDownCast(ren->GetRenderWindow())); this->DPColorTextureObject->Create2D( this->WindowSize[0], this->WindowSize[1], 4, VTK_UNSIGNED_CHAR, false); this->DPColorTextureObject->Activate(); this->DPColorTextureObject->SetMinificationFilter( vtkTextureObject::Nearest); this->DPColorTextureObject->SetMagnificationFilter( vtkTextureObject::Nearest); this->DPColorTextureObject->SetAutoParameters(0); this->DPFBO->AddDepthAttachment( GL_FRAMEBUFFER, this->DPDepthBufferTextureObject); this->DPFBO->AddColorAttachment( GL_FRAMEBUFFER, 0U, this->DPColorTextureObject); } this->DPFBO->ActivateDrawBuffers(1); this->DPFBO->CheckFrameBufferStatus(GL_FRAMEBUFFER); // Setup the contour polydata mapper to render to DPFBO this->ContourMapper->SetInputConnection(this->ContourFilter->GetOutputPort()); glClearColor(0.0, 0.0, 0.0, 0.0); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); glEnable(GL_DEPTH_TEST); } //---------------------------------------------------------------------------- void vtkOpenGLGPUVolumeRayCastMapper::vtkInternal::ExitDepthPass( vtkRenderer* vtkNotUsed(ren)) { this->DPFBO->DeactivateDrawBuffers(); this->DPFBO->RestorePreviousBindingsAndBuffers(); this->DPDepthBufferTextureObject->Deactivate(); this->DPColorTextureObject->Deactivate(); glDisable(GL_DEPTH_TEST); } //---------------------------------------------------------------------------- void vtkOpenGLGPUVolumeRayCastMapper::vtkInternal :: ReleaseRenderToTextureGraphicsResources(vtkWindow* win) { vtkOpenGLRenderWindow* rwin = vtkOpenGLRenderWindow::SafeDownCast(win); if (rwin) { if (this->FBO) { this->FBO->Delete(); this->FBO = nullptr; } if (this->RTTDepthBufferTextureObject) { this->RTTDepthBufferTextureObject->ReleaseGraphicsResources(win); this->RTTDepthBufferTextureObject->Delete(); this->RTTDepthBufferTextureObject = nullptr; } if (this->RTTDepthTextureObject) { this->RTTDepthTextureObject->ReleaseGraphicsResources(win); this->RTTDepthTextureObject->Delete(); this->RTTDepthTextureObject = nullptr; } if (this->RTTColorTextureObject) { this->RTTColorTextureObject->ReleaseGraphicsResources(win); this->RTTColorTextureObject->Delete(); this->RTTColorTextureObject = nullptr; } } } //---------------------------------------------------------------------------- void vtkOpenGLGPUVolumeRayCastMapper::vtkInternal :: ReleaseDepthPassGraphicsResources(vtkWindow* win) { vtkOpenGLRenderWindow* rwin = vtkOpenGLRenderWindow::SafeDownCast(win); if (rwin) { if (this->DPFBO) { this->DPFBO->Delete(); this->DPFBO = nullptr; } if (this->DPDepthBufferTextureObject) { this->DPDepthBufferTextureObject->ReleaseGraphicsResources(win); this->DPDepthBufferTextureObject->Delete(); this->DPDepthBufferTextureObject = nullptr; } if (this->DPColorTextureObject) { this->DPColorTextureObject->ReleaseGraphicsResources(win); this->DPColorTextureObject->Delete(); this->DPColorTextureObject = nullptr; } this->ContourMapper->ReleaseGraphicsResources(win); } } //---------------------------------------------------------------------------- void vtkOpenGLGPUVolumeRayCastMapper::vtkInternal :: ReleaseImageSampleGraphicsResources(vtkWindow* win) { vtkOpenGLRenderWindow* rwin = vtkOpenGLRenderWindow::SafeDownCast(win); if (rwin) { if (this->ImageSampleFBO) { this->ImageSampleFBO->Delete(); this->ImageSampleFBO = nullptr; } for (auto& tex : this->ImageSampleTexture) { tex->ReleaseGraphicsResources(win); tex = nullptr; } this->ImageSampleTexture.clear(); this->ImageSampleTexNames.clear(); if (this->ImageSampleVBO) { this->ImageSampleVBO->Delete(); this->ImageSampleVBO = nullptr; } if (this->ImageSampleVAO) { this->ImageSampleVAO->Delete(); this->ImageSampleVAO = nullptr; } // Do not delete the shader program - Let the cache clean it up. this->ImageSampleProg = nullptr; } } //---------------------------------------------------------------------------- vtkOpenGLGPUVolumeRayCastMapper::vtkOpenGLGPUVolumeRayCastMapper() : vtkGPUVolumeRayCastMapper() { this->Impl = new vtkInternal(this); this->ReductionFactor = 1.0; this->CurrentPass = RenderPass; this->NoiseTextureSize[0] = this->NoiseTextureSize[1] = -1; this->NoiseGenerator = nullptr; this->VertexShaderCode = nullptr; this->FragmentShaderCode = nullptr; this->ResourceCallback = new vtkOpenGLResourceFreeCallback( this, &vtkOpenGLGPUVolumeRayCastMapper::ReleaseGraphicsResources); this->VolumeTexture = vtkVolumeTexture::New(); this->VolumeTexture->SetMapper(this); } //---------------------------------------------------------------------------- vtkOpenGLGPUVolumeRayCastMapper::~vtkOpenGLGPUVolumeRayCastMapper() { if (this->ResourceCallback) { this->ResourceCallback->Release(); delete this->ResourceCallback; this->ResourceCallback = nullptr; } if (this->NoiseGenerator) { this->NoiseGenerator->Delete(); this->NoiseGenerator = nullptr; } delete this->Impl; this->Impl = nullptr; this->VolumeTexture->Delete(); this->VolumeTexture = nullptr; this->SetVertexShaderCode(nullptr); this->SetFragmentShaderCode(nullptr); } //---------------------------------------------------------------------------- void vtkOpenGLGPUVolumeRayCastMapper::PrintSelf(ostream& os, vtkIndent indent) { this->Superclass::PrintSelf(os, indent); os << indent << "ReductionFactor: " << this->ReductionFactor << "\n"; os << indent << "CurrentPass: " << this->CurrentPass << "\n"; } //---------------------------------------------------------------------------- vtkTextureObject* vtkOpenGLGPUVolumeRayCastMapper::GetDepthTexture() { return this->Impl->RTTDepthTextureObject; } //---------------------------------------------------------------------------- vtkTextureObject* vtkOpenGLGPUVolumeRayCastMapper::GetColorTexture() { return this->Impl->RTTColorTextureObject; } //---------------------------------------------------------------------------- void vtkOpenGLGPUVolumeRayCastMapper::GetDepthImage(vtkImageData* output) { return this->Impl->ConvertTextureToImageData( this->Impl->RTTDepthTextureObject, output); } //---------------------------------------------------------------------------- void vtkOpenGLGPUVolumeRayCastMapper::GetColorImage(vtkImageData* output) { return this->Impl->ConvertTextureToImageData( this->Impl->RTTColorTextureObject, output); } //---------------------------------------------------------------------------- void vtkOpenGLGPUVolumeRayCastMapper::ReleaseGraphicsResources( vtkWindow* window) { if (!this->ResourceCallback->IsReleasing()) { this->ResourceCallback->Release(); return; } this->Impl->DeleteBufferObjects(); if (this->VolumeTexture) { this->VolumeTexture->ReleaseGraphicsResources(window); } if (this->Impl->NoiseTextureObject) { this->Impl->NoiseTextureObject->ReleaseGraphicsResources(window); this->Impl->NoiseTextureObject->Delete(); this->Impl->NoiseTextureObject = nullptr; } if (this->Impl->DepthTextureObject) { this->Impl->DepthTextureObject->ReleaseGraphicsResources(window); this->Impl->DepthTextureObject->Delete(); this->Impl->DepthTextureObject = nullptr; } this->Impl->ReleaseRenderToTextureGraphicsResources(window); this->Impl->ReleaseDepthPassGraphicsResources(window); this->Impl->ReleaseImageSampleGraphicsResources(window); if (this->Impl->CurrentMask) { this->Impl->CurrentMask->ReleaseGraphicsResources(window); this->Impl->CurrentMask = nullptr; } this->Impl->ReleaseGraphicsTransfer1D(window); this->Impl->DeleteTransfer1D(); this->Impl->ReleaseGraphicsTransfer2D(window); this->Impl->DeleteTransfer2D(); this->Impl->ReleaseResourcesTime.Modified(); } //----------------------------------------------------------------------------- void vtkOpenGLGPUVolumeRayCastMapper::AddShaderReplacement( vtkShader::Type shaderType, // vertex, fragment, etc const std::string& originalValue, bool replaceFirst, // do this replacement before the default const std::string& replacementValue, bool replaceAll) { vtkShader::ReplacementSpec spec; spec.ShaderType = shaderType; spec.OriginalValue = originalValue; spec.ReplaceFirst = replaceFirst; vtkShader::ReplacementValue values; values.Replacement = replacementValue; values.ReplaceAll = replaceAll; this->UserShaderReplacements[spec] = values; } //----------------------------------------------------------------------------- void vtkOpenGLGPUVolumeRayCastMapper::ClearShaderReplacement( vtkShader::Type shaderType, // vertex, fragment, etc const std::string& originalValue, bool replaceFirst) { vtkShader::ReplacementSpec spec; spec.ShaderType = shaderType; spec.OriginalValue = originalValue; spec.ReplaceFirst = replaceFirst; typedef std::map::iterator RIter; RIter found = this->UserShaderReplacements.find(spec); if (found != this->UserShaderReplacements.end()) { this->UserShaderReplacements.erase(found); } } //----------------------------------------------------------------------------- void vtkOpenGLGPUVolumeRayCastMapper::GetShaderTemplate( std::map& shaders) { if (shaders[vtkShader::Vertex]) { if (this->VertexShaderCode && strcmp(this->VertexShaderCode, "") != 0) { shaders[vtkShader::Vertex]->SetSource(this->VertexShaderCode); } else { shaders[vtkShader::Vertex]->SetSource(raycastervs); } } if (shaders[vtkShader::Fragment]) { if (this->FragmentShaderCode && strcmp(this->FragmentShaderCode, "") != 0) { shaders[vtkShader::Fragment]->SetSource(this->FragmentShaderCode); } else { shaders[vtkShader::Fragment]->SetSource(raycasterfs); } } if (shaders[vtkShader::Geometry]) { shaders[vtkShader::Geometry]->SetSource(""); } } //----------------------------------------------------------------------------- void vtkOpenGLGPUVolumeRayCastMapper::ReplaceShaderBase( std::map& shaders, vtkRenderer* ren, vtkVolume* vol, int numComps) { vtkShader* vertexShader = shaders[vtkShader::Vertex]; vtkShader* fragmentShader = shaders[vtkShader::Fragment]; // Every volume should have a property (cannot be nullptr); vtkVolumeProperty* volumeProperty = vol->GetProperty(); int independentComponents = volumeProperty->GetIndependentComponents(); vtkShaderProgram::Substitute(vertexShader, "//VTK::ComputeClipPos::Impl", vtkvolume::ComputeClipPositionImplementation(ren, this, vol)); vtkShaderProgram::Substitute(vertexShader, "//VTK::ComputeTextureCoords::Impl", vtkvolume::ComputeTextureCoordinates(ren, this, vol)); vtkShaderProgram::Substitute(vertexShader, "//VTK::Base::Dec", vtkvolume::BaseDeclarationVertex(ren, this, vol)); vtkShaderProgram::Substitute(fragmentShader, "//VTK::CallWorker::Impl", vtkvolume::WorkerImplementation(ren, this, vol)); vtkShaderProgram::Substitute(fragmentShader, "//VTK::Base::Dec", vtkvolume::BaseDeclarationFragment(ren, this, vol, this->Impl->NumberOfLights, this->Impl->LightComplexity, vol->GetProperty()->HasGradientOpacity(), numComps, independentComponents)); vtkShaderProgram::Substitute(fragmentShader, "//VTK::Base::Init", vtkvolume::BaseInit(ren, this, vol, this->Impl->LightComplexity)); vtkShaderProgram::Substitute(fragmentShader, "//VTK::Base::Impl", vtkvolume::BaseImplementation(ren, this, vol)); vtkShaderProgram::Substitute( fragmentShader, "//VTK::Base::Exit", vtkvolume::BaseExit(ren, this, vol)); } //----------------------------------------------------------------------------- void vtkOpenGLGPUVolumeRayCastMapper::ReplaceShaderTermination( std::map& shaders, vtkRenderer* ren, vtkVolume* vol, int vtkNotUsed(numComps)) { vtkShader* vertexShader = shaders[vtkShader::Vertex]; vtkShader* fragmentShader = shaders[vtkShader::Fragment]; vtkShaderProgram::Substitute(vertexShader, "//VTK::Termination::Dec", vtkvolume::TerminationDeclarationVertex(ren, this, vol)); vtkShaderProgram::Substitute(fragmentShader, "//VTK::Termination::Dec", vtkvolume::TerminationDeclarationFragment(ren, this, vol)); vtkShaderProgram::Substitute(fragmentShader, "//VTK::Terminate::Init", vtkvolume::TerminationInit(ren, this, vol)); vtkShaderProgram::Substitute(fragmentShader, "//VTK::Terminate::Impl", vtkvolume::TerminationImplementation(ren, this, vol)); vtkShaderProgram::Substitute(fragmentShader, "//VTK::Terminate::Exit", vtkvolume::TerminationExit(ren, this, vol)); } //----------------------------------------------------------------------------- void vtkOpenGLGPUVolumeRayCastMapper::ReplaceShaderShading( std::map& shaders, vtkRenderer* ren, vtkVolume* vol, int numComps) { vtkShader* vertexShader = shaders[vtkShader::Vertex]; vtkShader* fragmentShader = shaders[vtkShader::Fragment]; // Every volume should have a property (cannot be NULL); vtkVolumeProperty* volumeProperty = vol->GetProperty(); int independentComponents = volumeProperty->GetIndependentComponents(); vtkShaderProgram::Substitute(vertexShader, "//VTK::Shading::Dec", vtkvolume::ShadingDeclarationVertex(ren, this, vol)); vtkShaderProgram::Substitute(fragmentShader, "//VTK::Shading::Dec", vtkvolume::ShadingDeclarationFragment(ren, this, vol)); vtkShaderProgram::Substitute(fragmentShader, "//VTK::Shading::Init", vtkvolume::ShadingInit(ren, this, vol)); vtkShaderProgram::Substitute(fragmentShader, "//VTK::Shading::Impl", vtkvolume::ShadingImplementation(ren, this, vol, this->MaskInput, this->Impl->CurrentMask, this->MaskType, numComps, independentComponents)); vtkShaderProgram::Substitute(fragmentShader, "//VTK::Shading::Exit", vtkvolume::ShadingExit(ren, this, vol, numComps, independentComponents)); } //----------------------------------------------------------------------------- void vtkOpenGLGPUVolumeRayCastMapper::ReplaceShaderCompute( std::map& shaders, vtkRenderer* ren, vtkVolume* vol, int numComps) { vtkShader* fragmentShader = shaders[vtkShader::Fragment]; // Every volume should have a property (cannot be NULL); vtkVolumeProperty* volumeProperty = vol->GetProperty(); int independentComponents = volumeProperty->GetIndependentComponents(); vtkShaderProgram::Substitute(fragmentShader, "//VTK::ComputeGradient::Dec", vtkvolume::ComputeGradientDeclaration(vol)); switch (volumeProperty->GetTransferFunctionMode()) { case vtkVolumeProperty::TF_1D: vtkShaderProgram::Substitute(fragmentShader, "//VTK::ComputeOpacity::Dec", vtkvolume::ComputeOpacityDeclaration(ren, this, vol, numComps, independentComponents, this->Impl->OpacityTablesMap)); vtkShaderProgram::Substitute(fragmentShader, "//VTK::ComputeGradientOpacity1D::Dec", vtkvolume::ComputeGradientOpacity1DDecl(vol, numComps, independentComponents, this->Impl->GradientOpacityTablesMap)); vtkShaderProgram::Substitute(fragmentShader, "//VTK::ComputeColor::Dec", vtkvolume::ComputeColorDeclaration(ren, this, vol, numComps, independentComponents, this->Impl->RGBTablesMap)); break; case vtkVolumeProperty::TF_2D: vtkShaderProgram::Substitute(fragmentShader, "//VTK::ComputeOpacity::Dec", vtkvolume::ComputeOpacity2DDeclaration(ren, this, vol, numComps, independentComponents, this->Impl->TransferFunctions2DMap)); vtkShaderProgram::Substitute(fragmentShader, "//VTK::ComputeColor::Dec", vtkvolume::ComputeColor2DDeclaration(ren, this, vol, numComps, independentComponents, this->Impl->TransferFunctions2DMap)); vtkShaderProgram::Substitute(fragmentShader, "//VTK::GradientCache::Dec", vtkvolume::GradientCacheDec(ren, vol, numComps, independentComponents)); vtkShaderProgram::Substitute(fragmentShader, "//VTK::PreComputeGradients::Impl", vtkvolume::PreComputeGradientsImpl( ren, vol, numComps, independentComponents)); break; } vtkShaderProgram::Substitute(fragmentShader, "//VTK::ComputeLighting::Dec", vtkvolume::ComputeLightingDeclaration(ren, this, vol, numComps, independentComponents, this->Impl->NumberOfLights, this->Impl->LightComplexity)); vtkShaderProgram::Substitute(fragmentShader, "//VTK::ComputeRayDirection::Dec", vtkvolume::ComputeRayDirectionDeclaration(ren, this, vol, numComps)); } //----------------------------------------------------------------------------- void vtkOpenGLGPUVolumeRayCastMapper::ReplaceShaderCropping( std::map& shaders, vtkRenderer* ren, vtkVolume* vol, int vtkNotUsed(numComps)) { vtkShader* vertexShader = shaders[vtkShader::Vertex]; vtkShader* fragmentShader = shaders[vtkShader::Fragment]; vtkShaderProgram::Substitute(vertexShader, "//VTK::Cropping::Dec", vtkvolume::CroppingDeclarationVertex(ren, this, vol)); vtkShaderProgram::Substitute(fragmentShader, "//VTK::Cropping::Dec", vtkvolume::CroppingDeclarationFragment(ren, this, vol)); vtkShaderProgram::Substitute(fragmentShader, "//VTK::Cropping::Init", vtkvolume::CroppingInit(ren, this, vol)); vtkShaderProgram::Substitute(fragmentShader, "//VTK::Cropping::Impl", vtkvolume::CroppingImplementation(ren, this, vol)); // true); vtkShaderProgram::Substitute(fragmentShader, "//VTK::Cropping::Exit", vtkvolume::CroppingExit(ren, this, vol)); } //----------------------------------------------------------------------------- void vtkOpenGLGPUVolumeRayCastMapper::ReplaceShaderClipping( std::map& shaders, vtkRenderer* ren, vtkVolume* vol, int vtkNotUsed(numComps)) { vtkShader* vertexShader = shaders[vtkShader::Vertex]; vtkShader* fragmentShader = shaders[vtkShader::Fragment]; vtkShaderProgram::Substitute(vertexShader, "//VTK::Clipping::Dec", vtkvolume::ClippingDeclarationVertex(ren, this, vol)); vtkShaderProgram::Substitute(fragmentShader, "//VTK::Clipping::Dec", vtkvolume::ClippingDeclarationFragment(ren, this, vol)); vtkShaderProgram::Substitute(fragmentShader, "//VTK::Clipping::Init", vtkvolume::ClippingInit(ren, this, vol)); vtkShaderProgram::Substitute(fragmentShader, "//VTK::Clipping::Impl", vtkvolume::ClippingImplementation(ren, this, vol)); vtkShaderProgram::Substitute(fragmentShader, "//VTK::Clipping::Exit", vtkvolume::ClippingExit(ren, this, vol)); } //----------------------------------------------------------------------------- void vtkOpenGLGPUVolumeRayCastMapper::ReplaceShaderMasking( std::map& shaders, vtkRenderer* ren, vtkVolume* vol, int numComps) { vtkShader* fragmentShader = shaders[vtkShader::Fragment]; vtkShaderProgram::Substitute(fragmentShader, "//VTK::BinaryMask::Dec", vtkvolume::BinaryMaskDeclaration(ren, this, vol, this->MaskInput, this->Impl->CurrentMask, this->MaskType)); vtkShaderProgram::Substitute(fragmentShader, "//VTK::BinaryMask::Impl", vtkvolume::BinaryMaskImplementation(ren, this, vol, this->MaskInput, this->Impl->CurrentMask, this->MaskType)); vtkShaderProgram::Substitute(fragmentShader, "//VTK::CompositeMask::Dec", vtkvolume::CompositeMaskDeclarationFragment(ren, this, vol, this->MaskInput, this->Impl->CurrentMask, this->MaskType)); vtkShaderProgram::Substitute(fragmentShader, "//VTK::CompositeMask::Impl", vtkvolume::CompositeMaskImplementation(ren, this, vol, this->MaskInput, this->Impl->CurrentMask, this->MaskType, numComps)); } //----------------------------------------------------------------------------- void vtkOpenGLGPUVolumeRayCastMapper::ReplaceShaderPicking( std::map& shaders, vtkRenderer* ren, vtkVolume* vol, int vtkNotUsed(numComps)) { vtkShader* fragmentShader = shaders[vtkShader::Fragment]; if (this->Impl->CurrentSelectionPass != (vtkHardwareSelector::MIN_KNOWN_PASS - 1)) { switch (this->Impl->CurrentSelectionPass) { case vtkHardwareSelector::ID_LOW24: vtkShaderProgram::Substitute(fragmentShader, "//VTK::Picking::Exit", vtkvolume::PickingIdLow24PassExit(ren, this, vol)); break; case vtkHardwareSelector::ID_MID24: vtkShaderProgram::Substitute(fragmentShader, "//VTK::Picking::Exit", vtkvolume::PickingIdMid24PassExit(ren, this, vol)); break; default: // ACTOR_PASS, PROCESS_PASS vtkShaderProgram::Substitute(fragmentShader, "//VTK::Picking::Dec", vtkvolume::PickingActorPassDeclaration(ren, this, vol)); vtkShaderProgram::Substitute(fragmentShader, "//VTK::Picking::Exit", vtkvolume::PickingActorPassExit(ren, this, vol)); break; } } } //----------------------------------------------------------------------------- void vtkOpenGLGPUVolumeRayCastMapper::ReplaceShaderRTT( std::map& shaders, vtkRenderer* ren, vtkVolume* vol, int vtkNotUsed(numComps)) { vtkShader* fragmentShader = shaders[vtkShader::Fragment]; if (this->RenderToImage) { vtkShaderProgram::Substitute(fragmentShader, "//VTK::RenderToImage::Dec", vtkvolume::RenderToImageDeclarationFragment(ren, this, vol)); vtkShaderProgram::Substitute(fragmentShader, "//VTK::RenderToImage::Init", vtkvolume::RenderToImageInit(ren, this, vol)); vtkShaderProgram::Substitute(fragmentShader, "//VTK::RenderToImage::Impl", vtkvolume::RenderToImageImplementation(ren, this, vol)); vtkShaderProgram::Substitute(fragmentShader, "//VTK::RenderToImage::Exit", vtkvolume::RenderToImageExit(ren, this, vol)); } } //----------------------------------------------------------------------------- void vtkOpenGLGPUVolumeRayCastMapper::ReplaceShaderValues( std::map& shaders, vtkRenderer* ren, vtkVolume* vol, int noOfComponents) { // Every volume should have a property (cannot be NULL); vtkVolumeProperty* volumeProperty = vol->GetProperty(); if (volumeProperty->GetShade()) { vtkLightCollection* lc = ren->GetLights(); vtkLight* light; this->Impl->NumberOfLights = 0; // Compute light complexity. vtkCollectionSimpleIterator sit; for (lc->InitTraversal(sit); (light = lc->GetNextLight(sit));) { float status = light->GetSwitch(); if (status > 0.0) { this->Impl->NumberOfLights++; if (this->Impl->LightComplexity == 0) { this->Impl->LightComplexity = 1; } } if (this->Impl->LightComplexity == 1 && (this->Impl->NumberOfLights > 1 || light->GetIntensity() != 1.0 || light->GetLightType() != VTK_LIGHT_TYPE_HEADLIGHT)) { this->Impl->LightComplexity = 2; } if (this->Impl->LightComplexity < 3 && (light->GetPositional())) { this->Impl->LightComplexity = 3; break; } } } // Render pass pre replacements //--------------------------------------------------------------------------- this->ReplaceShaderRenderPass(shaders, vol, true); // Base methods replacements //--------------------------------------------------------------------------- this->ReplaceShaderBase(shaders, ren, vol, noOfComponents); // Termination methods replacements //--------------------------------------------------------------------------- this->ReplaceShaderTermination(shaders, ren, vol, noOfComponents); // Shading methods replacements //--------------------------------------------------------------------------- this->ReplaceShaderShading(shaders, ren, vol, noOfComponents); // Compute methods replacements //--------------------------------------------------------------------------- this->ReplaceShaderCompute(shaders, ren, vol, noOfComponents); // Cropping methods replacements //--------------------------------------------------------------------------- this->ReplaceShaderCropping(shaders, ren, vol, noOfComponents); // Clipping methods replacements //--------------------------------------------------------------------------- this->ReplaceShaderClipping(shaders, ren, vol, noOfComponents); // Masking methods replacements //--------------------------------------------------------------------------- this->ReplaceShaderMasking(shaders, ren, vol, noOfComponents); // Picking replacements //--------------------------------------------------------------------------- this->ReplaceShaderPicking(shaders, ren, vol, noOfComponents); // Render to texture //--------------------------------------------------------------------------- this->ReplaceShaderRTT(shaders, ren, vol, noOfComponents); // Render pass post replacements //--------------------------------------------------------------------------- this->ReplaceShaderRenderPass(shaders, vol, false); } //----------------------------------------------------------------------------- void vtkOpenGLGPUVolumeRayCastMapper::BuildShader(vtkRenderer* ren, vtkVolume* vol, int noOfComponents) { std::map shaders; vtkShader* vertexShader = vtkShader::New(); vertexShader->SetType(vtkShader::Vertex); shaders[vtkShader::Vertex] = vertexShader; vtkShader* fragmentShader = vtkShader::New(); fragmentShader->SetType(vtkShader::Fragment); shaders[vtkShader::Fragment] = fragmentShader; vtkShader* geometryShader = vtkShader::New(); geometryShader->SetType(vtkShader::Geometry); shaders[vtkShader::Geometry] = geometryShader; this->GetShaderTemplate(shaders); typedef std::map::const_iterator RIter; // user specified pre replacements for (RIter i = this->UserShaderReplacements.begin(); i != this->UserShaderReplacements.end(); ++i) { if (i->first.ReplaceFirst) { std::string ssrc = shaders[i->first.ShaderType]->GetSource(); vtkShaderProgram::Substitute(ssrc, i->first.OriginalValue, i->second.Replacement, i->second.ReplaceAll); shaders[i->first.ShaderType]->SetSource(ssrc); } } this->ReplaceShaderValues(shaders, ren, vol, noOfComponents); // user specified post replacements for (RIter i = this->UserShaderReplacements.begin(); i != this->UserShaderReplacements.end(); ++i) { if (!i->first.ReplaceFirst) { std::string ssrc = shaders[i->first.ShaderType]->GetSource(); vtkShaderProgram::Substitute(ssrc, i->first.OriginalValue, i->second.Replacement, i->second.ReplaceAll); shaders[i->first.ShaderType]->SetSource(ssrc); } } // Now compile the shader //-------------------------------------------------------------------------- this->Impl->ShaderProgram = this->Impl->ShaderCache->ReadyShaderProgram(shaders); if (!this->Impl->ShaderProgram || !this->Impl->ShaderProgram->GetCompiled()) { vtkErrorMacro("Shader failed to compile"); } vertexShader->Delete(); fragmentShader->Delete(); geometryShader->Delete(); this->Impl->ShaderBuildTime.Modified(); this->InvokeEvent(vtkCommand::UpdateShaderEvent, this->Impl->ShaderProgram); } //----------------------------------------------------------------------------- // Update the reduction factor of the render viewport (this->ReductionFactor) // according to the time spent in seconds to render the previous frame // (this->TimeToDraw) and a time in seconds allocated to render the next // frame (allocatedTime). // \pre valid_current_reduction_range: this->ReductionFactor>0.0 && // this->ReductionFactor<=1.0 \pre positive_TimeToDraw: this->TimeToDraw>=0.0 // \pre positive_time: allocatedTime>0.0 // \post valid_new_reduction_range: this->ReductionFactor>0.0 && // this->ReductionFactor<=1.0 //----------------------------------------------------------------------------- void vtkOpenGLGPUVolumeRayCastMapper::ComputeReductionFactor( double allocatedTime) { if (!this->AutoAdjustSampleDistances) { this->ReductionFactor = 1.0 / this->ImageSampleDistance; return; } if (this->TimeToDraw) { double oldFactor = this->ReductionFactor; double timeToDraw; if (allocatedTime < 1.0) { timeToDraw = this->SmallTimeToDraw; if (timeToDraw == 0.0) { timeToDraw = this->BigTimeToDraw / 3.0; } } else { timeToDraw = this->BigTimeToDraw; } // This should be the case when rendering the volume very first time // 10.0 is an arbitrary value chosen which happen to a large number // in this context if (timeToDraw == 0.0) { timeToDraw = 10.0; } double fullTime = timeToDraw / this->ReductionFactor; double newFactor = allocatedTime / fullTime; // Compute average factor this->ReductionFactor = (newFactor + oldFactor) / 2.0; // Discretize reduction factor so that it doesn't cause // visual artifacts when used to reduce the sample distance this->ReductionFactor = (this->ReductionFactor > 1.0) ? 1.0 : (this->ReductionFactor); if (this->ReductionFactor < 0.20) { this->ReductionFactor = 0.10; } else if (this->ReductionFactor < 0.50) { this->ReductionFactor = 0.20; } else if (this->ReductionFactor < 1.0) { this->ReductionFactor = 0.50; } // Clamp it if (1.0 / this->ReductionFactor > this->MaximumImageSampleDistance) { this->ReductionFactor = 1.0 / this->MaximumImageSampleDistance; } if (1.0 / this->ReductionFactor < this->MinimumImageSampleDistance) { this->ReductionFactor = 1.0 / this->MinimumImageSampleDistance; } } } //---------------------------------------------------------------------------- bool vtkOpenGLGPUVolumeRayCastMapper::PreLoadData(vtkRenderer* ren, vtkVolume* vol) { if (!this->ValidateRender(ren, vol)) { return false; } vtkImageData* input = this->GetTransformedInput(); vtkVolumeProperty* volProp = vol->GetProperty(); vtkDataArray* arr = this->GetScalars(input, this->ScalarMode, this->ArrayAccessMode, this->ArrayId, this->ArrayName, this->CellFlag); return this->Impl->LoadData(ren, vol, volProp, input, arr); } //---------------------------------------------------------------------------- void vtkOpenGLGPUVolumeRayCastMapper::GPURender(vtkRenderer* ren, vtkVolume* vol) { vtkOpenGLClearErrorMacro(); this->ResourceCallback->RegisterGraphicsResources( static_cast(ren->GetRenderWindow())); this->Impl->TempMatrix1->Identity(); this->Impl->NeedToInitializeResources = (this->Impl->ReleaseResourcesTime.GetMTime() > this->Impl->InitializationTime.GetMTime()); // Make sure the context is current vtkOpenGLRenderWindow* renWin = vtkOpenGLRenderWindow::SafeDownCast(ren->GetRenderWindow()); renWin->MakeCurrent(); // Update in_volume first to make sure states are current vol->Update(); vtkImageData* input = this->GetTransformedInput(); // vtkVolume ensures the property will be valid vtkVolumeProperty* volumeProperty = vol->GetProperty(); vtkOpenGLCamera* cam = vtkOpenGLCamera::SafeDownCast(ren->GetActiveCamera()); // Check whether we have independent components or not int const independentComponents = volumeProperty->GetIndependentComponents(); this->Impl->CheckPropertyKeys(vol); // Get window size and corners if (!this->Impl->PreserveViewport) { ren->GetTiledSizeAndOrigin(this->Impl->WindowSize, this->Impl->WindowSize + 1, this->Impl->WindowLowerLeft, this->Impl->WindowLowerLeft + 1); } else { int vp[4]; glGetIntegerv(GL_VIEWPORT, vp); this->Impl->WindowLowerLeft[0] = vp[0]; this->Impl->WindowLowerLeft[1] = vp[1]; this->Impl->WindowSize[0] = vp[2]; this->Impl->WindowSize[1] = vp[3]; } vtkDataArray* scalars = this->GetScalars(input, this->ScalarMode, this->ArrayAccessMode, this->ArrayId, this->ArrayName, this->CellFlag); // Allocate important variables int const noOfComponents = scalars->GetNumberOfComponents(); this->Impl->Bias.resize(noOfComponents, 0.0); if (this->Impl->NeedToInitializeResources || (volumeProperty->GetMTime() > this->Impl->InitializationTime.GetMTime())) { this->Impl->InitializeTransferFunction( ren, vol, noOfComponents, independentComponents); } // Three dependent components are not supported if ((noOfComponents == 3) && !independentComponents) { vtkErrorMacro("Three dependent components are not supported"); } // Update the volume if needed if (this->Impl->NeedToInitializeResources || (input->GetMTime() > this->Impl->InputUpdateTime.GetMTime())) { this->Impl->LoadData(ren, vol, volumeProperty, input, scalars); } else { this->Impl->LoadMask(ren, input, this->MaskInput, vol); this->Impl->UpdateVolume(volumeProperty); } this->ComputeReductionFactor(vol->GetAllocatedRenderTime()); this->Impl->UpdateSamplingDistance(input, ren, vol); this->Impl->UpdateTransferFunction( ren, vol, noOfComponents, independentComponents); // Update noise sampler texture if (this->UseJittering) { this->Impl->CreateNoiseTexture(ren); } // Grab depth sampler buffer (to handle cases when we are rendering geometry // and in_volume together this->Impl->CaptureDepthTexture(ren, vol); this->Impl->ShaderCache = vtkOpenGLRenderWindow::SafeDownCast(ren->GetRenderWindow()) ->GetShaderCache(); this->Impl->CheckPickingState(ren); vtkMTimeType renderPassTime = this->GetRenderPassStageMTime(vol); if (this->UseDepthPass && this->GetBlendMode() == vtkVolumeMapper::COMPOSITE_BLEND) { this->CurrentPass = DepthPass; if (this->Impl->NeedToInitializeResources || volumeProperty->GetMTime() > this->Impl->DepthPassSetupTime.GetMTime() || this->GetMTime() > this->Impl->DepthPassSetupTime.GetMTime() || cam->GetParallelProjection() != this->Impl->LastProjectionParallel || this->Impl->SelectionStateTime.GetMTime() > this->Impl->ShaderBuildTime.GetMTime() || renderPassTime > this->Impl->ShaderBuildTime) { this->Impl->LastProjectionParallel = cam->GetParallelProjection(); this->Impl->ContourFilter->SetInputData(input); for (int i = 0; i < this->GetDepthPassContourValues()->GetNumberOfContours(); ++i) { this->Impl->ContourFilter->SetValue( i, this->DepthPassContourValues->GetValue(i)); } vtkNew newMatrix; newMatrix->DeepCopy(vol->GetMatrix()); this->Impl->SetupDepthPass(ren); this->Impl->ContourActor->Render(ren, this->Impl->ContourMapper); this->Impl->ExitDepthPass(ren); this->Impl->DepthPassSetupTime.Modified(); this->Impl->DepthPassTime.Modified(); this->CurrentPass = RenderPass; this->BuildShader(ren, vol, noOfComponents); } else if (cam->GetMTime() > this->Impl->DepthPassTime.GetMTime()) { this->Impl->SetupDepthPass(ren); this->Impl->ContourActor->Render(ren, this->Impl->ContourMapper); this->Impl->ExitDepthPass(ren); this->Impl->DepthPassTime.Modified(); this->CurrentPass = RenderPass; } // Configure picking begin (changes blending, so needs to be called before // vtkVolumeStateRAII) if (this->Impl->IsPicking) { this->Impl->BeginPicking(ren); } // Set OpenGL states vtkVolumeStateRAII glState(this->Impl->PreserveGLState); if (this->RenderToImage) { this->Impl->SetupRenderToTexture(ren); } if (!this->Impl->PreserveViewport) { // NOTE: This is a must call or else, multiple viewport // rendering would not work. We need this primarily because // FBO set it otherwise. // TODO The viewport should not be set within the mapper, // causes issues when vtkOpenGLRenderPass instances modify it too. glViewport(this->Impl->WindowLowerLeft[0], this->Impl->WindowLowerLeft[1], this->Impl->WindowSize[0], this->Impl->WindowSize[1]); } renWin->GetShaderCache()->ReadyShaderProgram(this->Impl->ShaderProgram); this->Impl->DPDepthBufferTextureObject->Activate(); this->Impl->ShaderProgram->SetUniformi("in_depthPassSampler", this->Impl->DPDepthBufferTextureObject->GetTextureUnit()); this->DoGPURender(ren, vol, cam, this->Impl->ShaderProgram, noOfComponents, independentComponents); this->Impl->DPDepthBufferTextureObject->Deactivate(); } else { // Configure picking begin (changes blending, so needs to be called before // vtkVolumeStateRAII) if (this->Impl->IsPicking) { this->Impl->BeginPicking(ren); } // Set OpenGL states vtkVolumeStateRAII glState(this->Impl->PreserveGLState); // Build shader now // First get the shader cache from the render window. This is important // to make sure that shader cache knows the state of various shader programs // in use. if (this->Impl->NeedToInitializeResources || volumeProperty->GetMTime() > this->Impl->ShaderBuildTime.GetMTime() || this->GetMTime() > this->Impl->ShaderBuildTime.GetMTime() || cam->GetParallelProjection() != this->Impl->LastProjectionParallel || this->Impl->SelectionStateTime.GetMTime() > this->Impl->ShaderBuildTime.GetMTime() || renderPassTime > this->Impl->ShaderBuildTime) { this->Impl->LastProjectionParallel = cam->GetParallelProjection(); this->BuildShader(ren, vol, noOfComponents); } else { // Bind the shader this->Impl->ShaderCache->ReadyShaderProgram(this->Impl->ShaderProgram); } if (this->RenderToImage) { this->Impl->SetupRenderToTexture(ren); this->DoGPURender(ren, vol, cam, this->Impl->ShaderProgram, noOfComponents, independentComponents); this->Impl->ExitRenderToTexture(ren); } else { this->Impl->BeginImageSample(ren, vol); this->DoGPURender(ren, vol, cam, this->Impl->ShaderProgram, noOfComponents, independentComponents); this->Impl->EndImageSample(ren); } } // Configure picking end if (this->Impl->IsPicking) { this->Impl->EndPicking(ren); } glFinish(); } //---------------------------------------------------------------------------- void vtkOpenGLGPUVolumeRayCastMapper::DoGPURender(vtkRenderer* ren, vtkVolume* vol, vtkOpenGLCamera* cam, vtkShaderProgram* prog, int noOfComponents, int independentComponents) { // if the shader didn't compile return if (!prog) { return; } // Cell spacing is required to be computed globally (full volume extents) // given that gradients are computed globally (not per block). float fvalue3[3]; /* temporary value container */ vtkInternal::ToFloat(this->Impl->CellSpacing, fvalue3); prog->SetUniform3fv("in_cellSpacing", 1, &fvalue3); this->SetShaderParametersRenderPass(vol); // Sort blocks in case the viewpoint changed, it immediately returns if there // is a single block. this->VolumeTexture->SortBlocksBackToFront(ren, vol->GetMatrix()); vtkVolumeTexture::VolumeBlock* block = this->VolumeTexture->GetNextBlock(); vtkVolumeTexture::VolumeBlock* maskBlock = nullptr; if (this->Impl->CurrentMask) { this->Impl->CurrentMask->SortBlocksBackToFront(ren, vol->GetMatrix()); maskBlock = this->Impl->CurrentMask->GetNextBlock(); } while (block != nullptr) { this->Impl->ComputeBounds(block->ImageData); // Cell step/scale are adjusted per block. // Step should be dependent on the bounds and not on the texture size // since we can have a non-uniform voxel size / spacing / aspect ratio. this->Impl->CellStep[0] = (1.0 / static_cast(this->Impl->Extents[1] - this->Impl->Extents[0])); this->Impl->CellStep[1] = (1.0 / static_cast(this->Impl->Extents[3] - this->Impl->Extents[2])); this->Impl->CellStep[2] = (1.0 / static_cast(this->Impl->Extents[5] - this->Impl->Extents[4])); this->Impl->CellScale[0] = (this->Impl->LoadedBounds[1] - this->Impl->LoadedBounds[0]) * 0.5; this->Impl->CellScale[1] = (this->Impl->LoadedBounds[3] - this->Impl->LoadedBounds[2]) * 0.5; this->Impl->CellScale[2] = (this->Impl->LoadedBounds[5] - this->Impl->LoadedBounds[4]) * 0.5; vtkInternal::ToFloat(this->Impl->CellStep, fvalue3); prog->SetUniform3fv("in_cellStep", 1, &fvalue3); vtkInternal::ToFloat(this->Impl->CellScale, fvalue3); prog->SetUniform3fv("in_cellScale", 1, &fvalue3); // Update sampling distance this->Impl->DatasetStepSize[0] = 1.0 / (this->Impl->LoadedBounds[1] - this->Impl->LoadedBounds[0]); this->Impl->DatasetStepSize[1] = 1.0 / (this->Impl->LoadedBounds[3] - this->Impl->LoadedBounds[2]); this->Impl->DatasetStepSize[2] = 1.0 / (this->Impl->LoadedBounds[5] - this->Impl->LoadedBounds[4]); // Compute texture to dataset matrix this->Impl->TextureToDataSetMat->Identity(); this->Impl->TextureToDataSetMat->SetElement( 0, 0, (1.0 / this->Impl->DatasetStepSize[0])); this->Impl->TextureToDataSetMat->SetElement( 1, 1, (1.0 / this->Impl->DatasetStepSize[1])); this->Impl->TextureToDataSetMat->SetElement( 2, 2, (1.0 / this->Impl->DatasetStepSize[2])); this->Impl->TextureToDataSetMat->SetElement(3, 3, 1.0); this->Impl->TextureToDataSetMat->SetElement( 0, 3, this->Impl->LoadedBounds[0]); this->Impl->TextureToDataSetMat->SetElement( 1, 3, this->Impl->LoadedBounds[2]); this->Impl->TextureToDataSetMat->SetElement( 2, 3, this->Impl->LoadedBounds[4]); // Activate/bind DepthTextureObject to a texture unit first as it was already // activated in CaptureDepthTexture. Certain APPLE implementations seem to be // sensitive to swaping the activation order (causing GL_INVALID_OPERATION after // the glDraw call). #if GL_ES_VERSION_3_0 != 1 // currently broken on ES this->Impl->DepthTextureObject->Activate(); prog->SetUniformi( "in_depthSampler", this->Impl->DepthTextureObject->GetTextureUnit()); #endif // Bind current volume texture block->TextureObject->Activate(); prog->SetUniformi("in_volume", block->TextureObject->GetTextureUnit()); // Temporary variables float fvalue2[2]; float fvalue4[4]; vtkVolumeProperty* volumeProperty = vol->GetProperty(); // Bind textures //-------------------------------------------------------------------------- // Opacity, color, and gradient opacity samplers / textures int const numberOfSamplers = (independentComponents ? noOfComponents : 1); this->Impl->ActivateTransferFunction( prog, volumeProperty, numberOfSamplers); if (this->Impl->NoiseTextureObject) { this->Impl->NoiseTextureObject->Activate(); prog->SetUniformi( "in_noiseSampler", this->Impl->NoiseTextureObject->GetTextureUnit()); } if (maskBlock) { maskBlock->TextureObject->Activate(); prog->SetUniformi("in_mask", maskBlock->TextureObject->GetTextureUnit()); } if (noOfComponents == 1 && this->BlendMode != vtkGPUVolumeRayCastMapper::ADDITIVE_BLEND) { if (this->MaskInput != nullptr && this->MaskType == LabelMapMaskType) { this->Impl->Mask1RGBTable->Activate(); prog->SetUniformi( "in_mask1", this->Impl->Mask1RGBTable->GetTextureUnit()); this->Impl->Mask2RGBTable->Activate(); prog->SetUniformi( "in_mask2", this->Impl->Mask2RGBTable->GetTextureUnit()); prog->SetUniformf("in_maskBlendFactor", this->MaskBlendFactor); } } // Bind light and material properties //-------------------------------------------------------------------------- this->Impl->SetLightingParameters(ren, prog, vol); float ambient[4][3]; float diffuse[4][3]; float specular[4][3]; float specularPower[4]; for (int i = 0; i < numberOfSamplers; ++i) { ambient[i][0] = ambient[i][1] = ambient[i][2] = volumeProperty->GetAmbient(i); diffuse[i][0] = diffuse[i][1] = diffuse[i][2] = volumeProperty->GetDiffuse(i); specular[i][0] = specular[i][1] = specular[i][2] = volumeProperty->GetSpecular(i); specularPower[i] = volumeProperty->GetSpecularPower(i); } prog->SetUniform3fv("in_ambient", numberOfSamplers, ambient); prog->SetUniform3fv("in_diffuse", numberOfSamplers, diffuse); prog->SetUniform3fv("in_specular", numberOfSamplers, specular); prog->SetUniform1fv("in_shininess", numberOfSamplers, specularPower); // Bind matrices //-------------------------------------------------------------------------- vtkMatrix4x4* glTransformMatrix; vtkMatrix4x4* modelViewMatrix; vtkMatrix3x3* normalMatrix; vtkMatrix4x4* projectionMatrix; cam->GetKeyMatrices( ren, modelViewMatrix, normalMatrix, projectionMatrix, glTransformMatrix); this->Impl->InverseProjectionMat->DeepCopy(projectionMatrix); this->Impl->InverseProjectionMat->Invert(); prog->SetUniformMatrix("in_projectionMatrix", projectionMatrix); prog->SetUniformMatrix("in_inverseProjectionMatrix", this->Impl->InverseProjectionMat.GetPointer()); this->Impl->InverseModelViewMat->DeepCopy(modelViewMatrix); this->Impl->InverseModelViewMat->Invert(); prog->SetUniformMatrix("in_modelViewMatrix", modelViewMatrix); prog->SetUniformMatrix("in_inverseModelViewMatrix", this->Impl->InverseModelViewMat.GetPointer()); this->Impl->TempMatrix1->DeepCopy(vol->GetMatrix()); this->Impl->TempMatrix1->Transpose(); this->Impl->InverseVolumeMat->DeepCopy( this->Impl->TempMatrix1.GetPointer()); this->Impl->InverseVolumeMat->Invert(); prog->SetUniformMatrix( "in_volumeMatrix", this->Impl->TempMatrix1.GetPointer()); prog->SetUniformMatrix( "in_inverseVolumeMatrix", this->Impl->InverseVolumeMat.GetPointer()); this->Impl->TempMatrix1->DeepCopy( this->Impl->TextureToDataSetMat.GetPointer()); vtkMatrix4x4::Multiply4x4(vol->GetMatrix(), this->Impl->TempMatrix1.GetPointer(), this->Impl->TextureToEyeTransposeInverse.GetPointer()); vtkMatrix4x4::Multiply4x4(modelViewMatrix, this->Impl->TextureToEyeTransposeInverse.GetPointer(), this->Impl->TextureToEyeTransposeInverse.GetPointer()); this->Impl->TempMatrix1->Transpose(); this->Impl->InverseTextureToDataSetMat->DeepCopy( this->Impl->TempMatrix1.GetPointer()); this->Impl->InverseTextureToDataSetMat->Invert(); prog->SetUniformMatrix( "in_textureDatasetMatrix", this->Impl->TempMatrix1.GetPointer()); prog->SetUniformMatrix("in_inverseTextureDatasetMatrix", this->Impl->InverseTextureToDataSetMat.GetPointer()); prog->SetUniformMatrix( "in_textureToEye", this->Impl->TextureToEyeTransposeInverse.GetPointer()); // Bind other misc parameters //-------------------------------------------------------------------------- if (cam->GetParallelProjection()) { double dir[4]; cam->GetDirectionOfProjection(dir); vtkInternal::ToFloat(dir[0], dir[1], dir[2], fvalue3); prog->SetUniform3fv("in_projectionDirection", 1, &fvalue3); } // Pass constant uniforms at initialization prog->SetUniformi("in_noOfComponents", noOfComponents); prog->SetUniformi("in_independentComponents", independentComponents); // LargeDataTypes have been already biased and scaled so in those cases 0s // and 1s are passed respectively. float tscale[4] = { 1.0, 1.0, 1.0, 1.0 }; float tbias[4] = { 0.0, 0.0, 0.0, 0.0 }; float(*scalePtr)[4] = &tscale; float(*biasPtr)[4] = &tbias; if (!this->VolumeTexture->HandleLargeDataTypes && (noOfComponents == 1 || noOfComponents == 2 || independentComponents)) { scalePtr = &this->VolumeTexture->Scale; biasPtr = &this->VolumeTexture->Bias; } prog->SetUniform4fv("in_volume_scale", 1, scalePtr); prog->SetUniform4fv("in_volume_bias", 1, biasPtr); prog->SetUniformf("in_sampleDistance", this->Impl->ActualSampleDistance); float scalarsRange[4][2]; vtkInternal::ToFloat(this->VolumeTexture->ScalarRange, scalarsRange); prog->SetUniform2fv("in_scalarsRange", 4, scalarsRange); vtkInternal::ToFloat(cam->GetPosition(), fvalue3, 3); prog->SetUniform3fv("in_cameraPos", 1, &fvalue3); vtkInternal::ToFloat(this->Impl->LoadedBounds[0], this->Impl->LoadedBounds[2], this->Impl->LoadedBounds[4], fvalue3); prog->SetUniform3fv("in_volumeExtentsMin", 1, &fvalue3); vtkInternal::ToFloat(this->Impl->LoadedBounds[1], this->Impl->LoadedBounds[3], this->Impl->LoadedBounds[5], fvalue3); prog->SetUniform3fv("in_volumeExtentsMax", 1, &fvalue3); vtkInternal::ToFloat(this->Impl->Extents[0], this->Impl->Extents[2], this->Impl->Extents[4], fvalue3); prog->SetUniform3fv("in_textureExtentsMin", 1, &fvalue3); vtkInternal::ToFloat(this->Impl->Extents[1], this->Impl->Extents[3], this->Impl->Extents[5], fvalue3); prog->SetUniform3fv("in_textureExtentsMax", 1, &fvalue3); // TODO Take consideration of reduction factor vtkInternal::ToFloat(this->Impl->WindowLowerLeft, fvalue2); prog->SetUniform2fv("in_windowLowerLeftCorner", 1, &fvalue2); vtkInternal::ToFloat(1.0 / this->Impl->WindowSize[0], 1.0 / this->Impl->WindowSize[1], fvalue2); prog->SetUniform2fv("in_inverseOriginalWindowSize", 1, &fvalue2); vtkInternal::ToFloat(1.0 / this->Impl->WindowSize[0], 1.0 / this->Impl->WindowSize[1], fvalue2); prog->SetUniform2fv("in_inverseWindowSize", 1, &fvalue2); prog->SetUniformi("in_useJittering", this->GetUseJittering()); prog->SetUniformi("in_cellFlag", this->CellFlag); vtkInternal::ToFloat(this->Impl->AdjustedTexMin[0], this->Impl->AdjustedTexMin[1], this->Impl->AdjustedTexMin[2], fvalue3); prog->SetUniform3fv("in_texMin", 1, &fvalue3); vtkInternal::ToFloat(this->Impl->AdjustedTexMax[0], this->Impl->AdjustedTexMax[1], this->Impl->AdjustedTexMax[2], fvalue3); prog->SetUniform3fv("in_texMax", 1, &fvalue3); this->Impl->TempMatrix1->DeepCopy( this->Impl->CellToPointMatrix.GetPointer()); this->Impl->TempMatrix1->Transpose(); prog->SetUniformMatrix( "in_cellToPoint", this->Impl->TempMatrix1.GetPointer()); prog->SetUniformi( "in_clampDepthToBackface", this->GetClampDepthToBackface()); // Bind cropping //-------------------------------------------------------------------------- this->Impl->SetCroppingRegions(ren, prog, vol); // Bind clipping //-------------------------------------------------------------------------- this->Impl->SetClippingPlanes(ren, prog, vol); // Bind the prop Id //-------------------------------------------------------------------------- if (this->Impl->CurrentSelectionPass < vtkHardwareSelector::ID_LOW24) { this->Impl->SetPickingId(ren); } // Set the scalar range to be considered for average ip blend //-------------------------------------------------------------------------- double avgRange[2]; this->GetAverageIPScalarRange(avgRange); if (avgRange[1] < avgRange[0]) { double tmp = avgRange[1]; avgRange[1] = avgRange[0]; avgRange[0] = tmp; } vtkInternal::ToFloat(avgRange[0], avgRange[1], fvalue2); prog->SetUniform2fv("in_averageIPRange", 1, &fvalue2); // Finally set the scale and bias for color correction //-------------------------------------------------------------------------- prog->SetUniformf("in_scale", 1.0 / this->FinalColorWindow); prog->SetUniformf( "in_bias", (0.5 - (this->FinalColorLevel / this->FinalColorWindow))); if (noOfComponents > 1 && independentComponents) { for (int i = 0; i < noOfComponents; ++i) { fvalue4[i] = static_cast(volumeProperty->GetComponentWeight(i)); } prog->SetUniform4fv("in_componentWeight", 1, &fvalue4); } // Render volume geometry to trigger render //-------------------------------------------------------------------------- this->Impl->RenderVolumeGeometry(ren, prog, vol); // Undo binds and de-activate buffers //-------------------------------------------------------------------------- block->TextureObject->Deactivate(); if (this->Impl->NoiseTextureObject) { this->Impl->NoiseTextureObject->Deactivate(); } #if GL_ES_VERSION_3_0 != 1 if (this->Impl->DepthTextureObject) { this->Impl->DepthTextureObject->Deactivate(); } #endif this->Impl->DeactivateTransferFunction(volumeProperty, numberOfSamplers); if (maskBlock) { maskBlock->TextureObject->Deactivate(); } if (noOfComponents == 1 && this->BlendMode != vtkGPUVolumeRayCastMapper::ADDITIVE_BLEND) { if (this->MaskInput != nullptr && this->MaskType == LabelMapMaskType) { this->Impl->Mask1RGBTable->Deactivate(); this->Impl->Mask2RGBTable->Deactivate(); } } vtkOpenGLCheckErrorMacro("failed after Render"); // Update next block to render //--------------------------------------------------------------------------- block = this->VolumeTexture->GetNextBlock(); if (this->Impl->CurrentMask) { maskBlock = this->Impl->CurrentMask->GetNextBlock(); } } } //---------------------------------------------------------------------------- vtkCxxSetObjectMacro(vtkOpenGLGPUVolumeRayCastMapper, NoiseGenerator, vtkImplicitFunction); //----------------------------------------------------------------------------- void vtkOpenGLGPUVolumeRayCastMapper::SetPartitions(unsigned short x, unsigned short y, unsigned short z) { this->VolumeTexture->SetPartitions(x, y, z); } //----------------------------------------------------------------------------- vtkMTimeType vtkOpenGLGPUVolumeRayCastMapper::GetRenderPassStageMTime( vtkVolume* vol) { vtkInformation* info = vol->GetPropertyKeys(); vtkMTimeType renderPassMTime = 0; int curRenderPasses = 0; this->Impl->RenderPassAttached = false; if (info && info->Has(vtkOpenGLRenderPass::RenderPasses())) { curRenderPasses = info->Length(vtkOpenGLRenderPass::RenderPasses()); this->Impl->RenderPassAttached = true; } int lastRenderPasses = 0; if (this->LastRenderPassInfo->Has(vtkOpenGLRenderPass::RenderPasses())) { lastRenderPasses = this->LastRenderPassInfo->Length(vtkOpenGLRenderPass::RenderPasses()); } // Determine the last time a render pass changed stages: if (curRenderPasses != lastRenderPasses) { // Number of passes changed, definitely need to update. // Fake the time to force an update: renderPassMTime = VTK_MTIME_MAX; } else { // Compare the current to the previous render passes: for (int i = 0; i < curRenderPasses; ++i) { vtkObjectBase* curRP = info->Get(vtkOpenGLRenderPass::RenderPasses(), i); vtkObjectBase* lastRP = this->LastRenderPassInfo->Get(vtkOpenGLRenderPass::RenderPasses(), i); if (curRP != lastRP) { // Render passes have changed. Force update: renderPassMTime = VTK_MTIME_MAX; break; } else { // Render passes have not changed -- check MTime. vtkOpenGLRenderPass* rp = static_cast(curRP); renderPassMTime = std::max(renderPassMTime, rp->GetShaderStageMTime()); } } } // Cache the current set of render passes for next time: if (info) { this->LastRenderPassInfo->CopyEntry( info, vtkOpenGLRenderPass::RenderPasses()); } else { this->LastRenderPassInfo->Clear(); } return renderPassMTime; } //----------------------------------------------------------------------------- void vtkOpenGLGPUVolumeRayCastMapper::ReplaceShaderRenderPass( std::map& shaders, vtkVolume* vol, bool prePass) { std::string vertShader = shaders[vtkShader::Vertex]->GetSource(); std::string geomShader = shaders[vtkShader::Geometry]->GetSource(); std::string fragShader = shaders[vtkShader::Fragment]->GetSource(); vtkInformation* info = vol->GetPropertyKeys(); if (info && info->Has(vtkOpenGLRenderPass::RenderPasses())) { int numRenderPasses = info->Length(vtkOpenGLRenderPass::RenderPasses()); for (int i = 0; i < numRenderPasses; ++i) { vtkObjectBase* rpBase = info->Get(vtkOpenGLRenderPass::RenderPasses(), i); vtkOpenGLRenderPass* rp = static_cast(rpBase); if (prePass) { if (!rp->PreReplaceShaderValues( vertShader, geomShader, fragShader, this, vol)) { vtkErrorMacro( "vtkOpenGLRenderPass::PreReplaceShaderValues failed for " << rp->GetClassName()); } } else { if (!rp->PostReplaceShaderValues( vertShader, geomShader, fragShader, this, vol)) { vtkErrorMacro( "vtkOpenGLRenderPass::PostReplaceShaderValues failed for " << rp->GetClassName()); } } } } shaders[vtkShader::Vertex]->SetSource(vertShader); shaders[vtkShader::Geometry]->SetSource(geomShader); shaders[vtkShader::Fragment]->SetSource(fragShader); } //------------------------------------------------------------------------------ void vtkOpenGLGPUVolumeRayCastMapper::SetShaderParametersRenderPass( vtkVolume* vol) { vtkInformation* info = vol->GetPropertyKeys(); if (info && info->Has(vtkOpenGLRenderPass::RenderPasses())) { int numRenderPasses = info->Length(vtkOpenGLRenderPass::RenderPasses()); for (int i = 0; i < numRenderPasses; ++i) { vtkObjectBase* rpBase = info->Get(vtkOpenGLRenderPass::RenderPasses(), i); vtkOpenGLRenderPass* rp = static_cast(rpBase); if (!rp->SetShaderParameters(this->Impl->ShaderProgram, this, vol)) { vtkErrorMacro("RenderPass::SetShaderParameters failed for renderpass: " << rp->GetClassName()); } } } }