/*========================================================================= Program: Visualization Toolkit Module: vtkOpenGLGPUVolumeRayCastMapper.h Copyright (c) Ken Martin, Will Schroeder, Bill Lorensen All rights reserved. See Copyright.txt or http://www.kitware.com/Copyright.htm for details. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the above copyright notice for more information. =========================================================================*/ /** * @class vtkOpenGLGPUVolumeRayCastMapper * @brief OpenGL subclass that draws the * image to the screen * * This is the concrete implementation of a ray cast image display helper - * a helper class responsible for drawing the image to the screen. * * @sa * vtkGPUVolumeRayCastMapper * * @par Thanks: * Thanks to Michael Granseier for helping to debug this class with respect * to maximum memory issues (which must be specified as vtkIdType and not int). */ #ifndef vtkOpenGLGPUVolumeRayCastMapper_h #define vtkOpenGLGPUVolumeRayCastMapper_h #include "vtkRenderingVolumeOpenGLModule.h" // For export macro #include "vtkGPUVolumeRayCastMapper.h" class vtkVolume; class vtkRenderer; class vtkOpenGLExtensionManager; class vtkMatrix4x4; class vtkUnsupportedRequiredExtensionsStringStream; // Pimpl class vtkMapDataArrayTextureId; // Pimpl class vtkMapMaskTextureId; // Pimpl class vtkPolyData; class vtkClipConvexPolyData; class vtkClipPolyData; class vtkTessellatedBoxSource; class vtkOpacityTable; // internal class. class vtkRGBTable; // internal class. class vtkKWScalarField; // internal class. class vtkKWMask; // internal class. class vtkOpacityTables; // Pimpl class vtkDensifyPolyData; class vtkStdString; class vtkShaderProgram2; class vtkShader2; class VTKRENDERINGVOLUMEOPENGL_EXPORT vtkOpenGLGPUVolumeRayCastMapper : public vtkGPUVolumeRayCastMapper { public: static vtkOpenGLGPUVolumeRayCastMapper *New(); vtkTypeMacro(vtkOpenGLGPUVolumeRayCastMapper,vtkGPUVolumeRayCastMapper); void PrintSelf(ostream& os, vtkIndent indent) override; /** * Based on hardware and properties, we may or may not be able to * render using 3D texture mapping. This indicates if 3D texture * mapping is supported by the hardware, and if the other extensions * necessary to support the specific properties are available. */ int IsRenderSupported(vtkRenderWindow *window, vtkVolumeProperty *property) override; /** * Delete OpenGL objects. * \post done: this->OpenGLObjectsCreated==0 */ void ReleaseGraphicsResources(vtkWindow *window) override; /** * Return a string matching the OpenGL errorCode. * \post result_exists: result!=0 */ static const char *OpenGLErrorMessage(unsigned int errorCode); /** * Display headerMessage on the standard output and the last OpenGL error * message if any. * \pre headerMessage_exists: headerMessage!=0 */ static void PrintError(const char *headerMessage); protected: vtkOpenGLGPUVolumeRayCastMapper(); ~vtkOpenGLGPUVolumeRayCastMapper() override; // The render method called by the superclass void GPURender(vtkRenderer *ren, vtkVolume *vol) override; // Methods called by the AMR Volume Mapper. void PreRender(vtkRenderer *ren, vtkVolume *vol, double datasetBounds[6], double scalarRange[2], int numberOfScalarComponents, unsigned int numberOfLevels) override; // \pre input is up-to-date void RenderBlock(vtkRenderer *ren, vtkVolume *vol, unsigned int level) override; void PostRender(vtkRenderer *ren, int numberOfScalarComponents) override; /** * Return if the required OpenGL extension `extensionName' is supported. * If not, its name is added to the string of unsupported but required * extensions. * \pre extensions_exist: extensions!=0 * \pre extensionName_exists: extensionName!=0 */ int TestRequiredExtension(vtkOpenGLExtensionManager *extensions, const char *extensionName); /** * Attempt to load required and optional OpenGL extensions for the current * context window. Variable LoadExtensionsSucceeded is set if all required * extensions has been loaded. In addition, variable * Supports_GL_ARB_texture_float is set if this extension has been loaded. * \pre: window_exists: window!=0 */ void LoadExtensions(vtkRenderWindow *window); /** * Create OpenGL objects such as textures, buffers and fragment program Ids. * It only registers Ids, there is no actual initialization of textures or * fragment program. * \pre extensions_loaded: this->LoadExtensionsSucceeded * \post done: this->OpenGLObjectsCreated==1 */ void CreateOpenGLObjects(vtkRenderer *ren); /** * Allocate memory on the GPU for the framebuffers according to the size of * the window or reallocate if the size has changed. Return true if * allocation succeeded. * \pre ren_exists: ren!=0 * \pre opengl_objects_created: this->OpenGLObjectsCreated * \post right_size: LastSize[]=window size. */ int AllocateFrameBuffers(vtkRenderer *ren); /** * Load the scalar field (one or four component scalar field), cell or point * based for a given subextent of the whole extent (can be the whole extent) * as a 3D texture on the GPU. * Extents are expressed in point if the cell flag is false or in cells of * the cell flag is true. * It returns true if it succeeded, false if there is not enough memory on * the GPU. * If succeeded, it updates the LoadedExtent, LoadedBounds, LoadedCellFlag * and LoadedTime. It also succeed if the scalar field is already loaded * (ie since last load, input has not changed and cell flag has not changed * and requested texture extents are enclosed in the loaded extent). * \pre input_exists: input!=0 * \pre valid_point_extent: (this->CellFlag || * (textureExtent[0]CellFlag || * (textureExtent[0]<=textureExtent[1] && * textureExtent[2]<=textureExtent[3] && * textureExtent[4]<=textureExtent[5]))) */ int LoadScalarField(vtkImageData *input, vtkImageData *maskInput, int textureExtent[6], vtkVolume *volume); /** * Allocate memory and load color table on the GPU or * reload it if the transfer function changed. * \pre vol_exists: vol!=0 * \pre valid_numberOfScalarComponents: numberOfScalarComponents==1 || numberOfScalarComponents==4 */ int UpdateColorTransferFunction(vtkVolume *vol, int numberOfScalarComponents); /** * Allocate memory and load opacity table on the GPU or * reload it if the transfer functions changed. * \pre vol_exists: vol!=0 * \pre valid_numberOfScalarComponents: numberOfScalarComponents==1 || numberOfScalarComponents==4 */ int UpdateOpacityTransferFunction(vtkVolume *vol, int numberOfScalarComponents, unsigned int level); /** * Prepare rendering in the offscreen framebuffer. * \pre ren_exists: ren!=0 * \pre vol_exists: vol!=0 */ void SetupRender(vtkRenderer *ren, vtkVolume *vol); /** * Clip the bounding box with all clipping planes * and the near and far plane */ void ClipBoundingBox(vtkRenderer *ren, double worldBounds[6], vtkVolume *vol); /** * Render the bounding box. The flag indicates whether * or not tcoords are rendered too. Return abort status (true==abort). * \pre valid_currentBlock: currentBlock>=0 && currentBlock=0 * \post valid_result: result>=x */ int PowerOfTwoGreaterOrEqual(int x); /** * Display the status of the current framebuffer on the standard output. */ void CheckFrameBufferStatus(); /** * Create a string from a buffer id. The result has to be free by the caller. */ vtkStdString BufferToString(int buffer); /** * Display the buffers assigned for drawing and reading operations. */ void DisplayReadAndDrawBuffers(); /** * Display all the attachments of the current framebuffer object. */ void DisplayFrameBufferAttachments(); /** * Display a given attachment for the current framebuffer object. */ void DisplayFrameBufferAttachment(unsigned int uattachment); /** * Concatenate the header string, projection type code and method to the * final fragment code in this->FragmentCode. * \pre valid_raycastMethod: raycastMethod>= vtkOpenGLGPUVolumeRayCastMapperMethodMaximumIntensityProjection && raycastMethod<=vtkOpenGLGPUVolumeRayCastMapperMethodMinIPFourDependent */ void BuildProgram(vtkRenderWindow *w, int parallelProjection, int raycastMethod, int shadeMethod, int componentMethod); /** * Return the current OpenGL state about lighting. */ void GetLightingStatus(); /** * 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 * \post valid_new_reduction_range: this->ReductionFactor>0.0 && this->ReductionFactor<=1.0 */ void ComputeReductionFactor(double allocatedTime); /** * Render a subvolume. * \pre this->ProgramShader!=0 and is linked. */ void RenderWholeVolume(vtkRenderer *ren, vtkVolume *vol); /** * Render a subvolume. * \pre this->ProgramShader!=0 and is linked. */ void RenderRegions(vtkRenderer *ren, vtkVolume *vol); // Return abort status (true==abort) int RenderSubVolume(vtkRenderer *ren, double bounds[6], vtkVolume *vol); void LoadProjectionParameters(vtkRenderer *ren, vtkVolume *vol); /** * Compute and return the number of cropping regions */ void ComputeNumberOfCroppingRegions(); void GetTextureFormat(vtkImageData *input, unsigned int *internalFormat, unsigned int *format, unsigned int *type, int *componentSize); bool TestLoadingScalar(unsigned int internalFormat, unsigned int format, unsigned int type, int textureSize[3], int componentSize); void SlabsFromDatasetToIndex(double slabsDataSet[6], double slabsPoints[6]); void SlabsFromIndexToDataset(double slabsPoints[6], double slabsDataSet[6]); const char *GetEnabledString(unsigned char value); void GetOpenGLState(); void DebugDisplayBox(vtkPolyData *box); void UpdateNoiseTexture(); /** * Compute how each axis of a cell is projected on the viewport in pixel. * This requires to have information about the camera and about the volume. * It set the value of IgnoreSampleDistancePerPixel to true in case of * degenerated case (axes aligned with the view). */ double ComputeMinimalSampleDistancePerPixel(vtkRenderer *renderer, vtkVolume *volume); /** * Return how much the dataset has to be reduced in each dimension to * fit on the GPU. If the value is 1.0, there is no need to reduce the * dataset. * \pre the calling thread has a current OpenGL context. * \pre mapper_supported: IsRenderSupported(renderer->GetRenderWindow(),0) * The computation is based on hardware limits (3D texture indexable size) * and MaxMemoryInBytes. * \post valid_i_ratio: ratio[0]>0 && ratio[0]<=1.0 * \post valid_j_ratio: ratio[1]>0 && ratio[1]<=1.0 * \post valid_k_ratio: ratio[2]>0 && ratio[2]<=1.0 */ void GetReductionRatio(double ratio[3]) override; int NumberOfCroppingRegions; // World coordinates of each corner of the dataset. double BoundingBox[8][3]; // Used during the clipping process. vtkPolyData *PolyDataBoundingBox; vtkPlaneCollection *Planes; vtkPlane *NearPlane; vtkClipConvexPolyData *Clip; vtkMatrix4x4 *InvVolumeMatrix; vtkDensifyPolyData *Densify; int OpenGLObjectsCreated; int NumberOfFrameBuffers; unsigned int FrameBufferObject; unsigned int DepthRenderBufferObject; // 3D scalar texture +1D color+1D opacity+2D grabbed depth buffer // +1 2D colorbuffer. unsigned int TextureObjects[5]; // used in MIP Mode (2 needed for ping-pong technique) unsigned int MaxValueFrameBuffer; unsigned int MaxValueFrameBuffer2; int ReducedSize[2]; vtkPolyData *ClippedBoundingBox; int LastSize[2]; double ReductionFactor; // Supported extensions // List of unsupported required extensions. Pimpl. vtkUnsupportedRequiredExtensionsStringStream *UnsupportedRequiredExtensions; int LoadExtensionsSucceeded; int Supports_GL_ARB_texture_float; int SupportsPixelBufferObjects; vtkTimeStamp DataBufferTime; // Matrices used in internal computation. As a member variable, // only one memory allocation is performed. vtkMatrix4x4 *TempMatrix[3]; double TableRange[2]; // Final string to send to the GPU as the fragment program source code. // char *FragmentCode; // int FragmentCodeCapacity; int ErrorLine; int ErrorColumn; char *ErrorString; // Store the last projection an raycast method in order to not rebuild // the fragment code at every call. int LastParallelProjection; int LastRayCastMethod; int LastCroppingMode; int LastComponent; int LastShade; vtkImageData *SmallInput; vtkTimeStamp SmallInputBuildTime; /** * Build the fragment shader program that scale and bias a texture * for window/level purpose. */ void BuildScaleBiasProgram(vtkRenderWindow *w); #if 0 vtkIdType LoadedExtent[6]; double LoadedBounds[6]; vtkTimeStamp LoadedScalarTime; int LoadedCellFlag; // point data or cell data (or field data, not handled) ? #endif unsigned int SavedFrameBuffer; // some offscreen mode use a framebuffer too. vtkTessellatedBoxSource *BoxSource; float *NoiseTexture; int NoiseTextureSize; // size of one dimension. unsigned int NoiseTextureId; // GLuint bool IgnoreSampleDistancePerPixel; vtkMapDataArrayTextureId *ScalarsTextures; // need a list for AMR mode. vtkMapMaskTextureId *MaskTextures; // need a list for AMR mode. vtkRGBTable *RGBTable; vtkRGBTable *Mask1RGBTable; vtkRGBTable *Mask2RGBTable; vtkOpacityTables *OpacityTables; vtkKWScalarField *CurrentScalar; vtkKWMask *CurrentMask; float ActualSampleDistance; double LastProgressEventTime; // initial value is 0.0. Expressed in seconds. bool PreserveOrientation; vtkShaderProgram2 *Program; vtkShader2 *Main; vtkShader2 *Projection; vtkShader2 *Trace; vtkShader2 *CroppingShader; vtkShader2 *Component; vtkShader2 *Shade; // Internal Variable used to keep track of whether or render window's size // changed and therefore we need re-allocation. bool SizeChanged; vtkShaderProgram2 *ScaleBiasProgram; private: vtkOpenGLGPUVolumeRayCastMapper(const vtkOpenGLGPUVolumeRayCastMapper&) = delete; void operator=(const vtkOpenGLGPUVolumeRayCastMapper&) = delete; }; #endif