/*========================================================================= Program: Visualization Toolkit Module: vtkLineIntegralConvolution2D.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 vtkLineIntegralConvolution2D * @brief GPU-based implementation of Line * Integral Convolution (LIC) * * * This class resorts to GLSL to implement GPU-based Line Integral Convolution * (LIC) for visualizing a 2D vector field that may be obtained by projecting * an original 3D vector field onto a surface (such that the resulting 2D * vector at each grid point on the surface is tangential to the local normal, * as done in vtkSurfaceLICPainter). * * As an image-based technique, 2D LIC works by (1) integrating a bidirectional * streamline from the center of each pixel (of the LIC output image), (2) * locating the pixels along / hit by this streamline as the correlated pixels * of the starting pixel (seed point / pixel), (3) indexing a (usually white) * noise texture (another input to LIC, in addition to the 2D vector field, * usually with the same size as that of the 2D vector field) to determine the * values (colors) of these pixels (the starting and the correlated pixels), * typically through bi-linear interpolation, and (4) performing convolution * (weighted averaging) on these values, by adopting a low-pass filter (such * as box, ramp, and Hanning kernels), to obtain the result value (color) that * is then assigned to the seed pixel. * * The GLSL-based GPU implementation herein maps the aforementioned pipeline to * fragment shaders and a box kernel is employed. Both the white noise and the * vector field are provided to the GPU as texture objects (supported by the * multi-texturing capability). In addition, there are four texture objects * (color buffers) allocated to constitute two pairs that work in a ping-pong * fashion, with one as the read buffers and the other as the write / render * targets. Maintained by a frame buffer object (GL_EXT_framebuffer_object), * each pair employs one buffer to store the current (dynamically updated) * position (by means of the texture coordinate that keeps being warped by the * underlying vector) of the (virtual) particle initially released from each * fragment while using the bother buffer to store the current (dynamically * updated too) accumulated texture value that each seed fragment (before the * 'mesh' is warped) collects. Given NumberOfSteps integration steps in each * direction, there are a total of (2 * NumberOfSteps + 1) fragments (including * the seed fragment) are convolved and each contributes 1 / (2 * NumberOfSteps * + 1) of the associated texture value to fulfill the box filter. * * One pass of LIC (basic LIC) tends to produce low-contrast / blurred images and * vtkLineIntegralConvolution2D provides an option for creating enhanced LIC * images. Enhanced LIC improves image quality by increasing inter-streamline * contrast while suppressing artifacts. It performs two passes of LIC, with a * 3x3 Laplacian high-pass filter in between that processes the output of pass * #1 LIC and forwards the result as the input 'noise' to pass #2 LIC. * * vtkLineIntegralConvolution2D applies masking to zero-vector fragments so * that un-filtered white noise areas are made totally transparent by class * vtkSurfaceLICPainter to show the underlying geometry surface. * * The convolution process tends to decrease both contrast and dynamic range, * sometimes leading to dull dark images. In order to counteract this, optional * contrast ehnancement stages have been added. These increase the dynamic range and * contrast and sharpen streaking patterns that emerge from the LIC process. * * Under some circumstances, typically depending on the contrast and dynamic * range and graininess of the noise texture, jagged or pixelated patterns emerge * in the LIC. These can be reduced by enabling the optional anti-aliasing pass. * * The internal pipeline is as follows, with optional stages denoted by () * nested optional stages depend on their parent stage. *
 *   noise texture
 *           |
 *           [ LIC ((CE) HPF LIC) (AA) (CE) ]
 *           |                              |
 *  vector field                       LIC'd image
 * 
* where LIC is the LIC stage, HPF is the high-pass filter stage, CE is the * contrast ehnacement stage, and AA is the antialias stage. * * @sa * vtkImageDataLIC2D vtkStructuredGridLIC2D */ #ifndef vtkLineIntegralConvolution2D_h #define vtkLineIntegralConvolution2D_h #include "vtkObject.h" #include "vtkRenderingLICOpenGL2Module.h" // for export macro #include "vtkWeakPointer.h" // for ren context #include // for deque class vtkOpenGLFramebufferObject; class vtkOpenGLHelper; class vtkOpenGLRenderWindow; class vtkPainterCommunicator; class vtkPixelExtent; class vtkRenderWindow; class vtkShaderProgram; class vtkTextureObject; class VTKRENDERINGLICOPENGL2_EXPORT vtkLineIntegralConvolution2D : public vtkObject { public: static vtkLineIntegralConvolution2D* New(); vtkTypeMacro(vtkLineIntegralConvolution2D, vtkObject); void PrintSelf(ostream& os, vtkIndent indent) override; /** * Returns if the context supports the required extensions. */ static bool IsSupported(vtkRenderWindow* renWin); ///@{ /** * Set/Get the rendering context. A reference is not explicitly held, * thus reference to the context must be held externally. */ void SetContext(vtkOpenGLRenderWindow* context); vtkOpenGLRenderWindow* GetContext(); ///@} ///@{ /** * EnhancedLIC mean compute the LIC twice with the second pass using * the edge-enhanced result of the first pass as a noise texture. Edge * enhancedment is made by a simple Laplace convolution. */ vtkSetClampMacro(EnhancedLIC, int, 0, 1); vtkGetMacro(EnhancedLIC, int); vtkBooleanMacro(EnhancedLIC, int); ///@} ///@{ /** * Enable/Disable contrast and dynamic range correction stages. Stage 1 is applied * on the input to the high-pass filter when the high-pass filter is enabled and * skipped otherwise. Stage 2, when enabled is the final stage in the internal * pipeline. Both stages are implemented by a histogram stretching of the gray scale * colors in the LIC'd image as follows: * c = (c-m)/(M-m) * where, c is the fragment color, m is the color value to map to 0, M is the * color value to map to 1. The default values of m and M are the min and max * over all fragments. * This increase the dynamic range and contrast in the LIC'd image, both of which * are naturally attenuated by the LI convolution process. * ENHANCE_CONTRAST_OFF -- don't enhance contrast * ENHANCE_CONTRAST_ON -- enhance high-pass input and final stage output * This feature is disabled by default. */ enum { ENHANCE_CONTRAST_OFF = 0, ENHANCE_CONTRAST_ON = 1 }; vtkSetClampMacro(EnhanceContrast, int, 0, 2); vtkGetMacro(EnhanceContrast, int); vtkBooleanMacro(EnhanceContrast, int); ///@} ///@{ /** * This feature is used to fine tune the contrast enhancement. Values are provided * indicating the fraction of the range to adjust m and M by during contrast enahncement * histogram stretching. M and m are the intensity/lightness values that map to 1 and 0. * (see EnhanceContrast for an explanation of the mapping procedure). m and M are computed * using the factors as follows: * m = min(C) - mFactor * (max(C) - min(C)) * M = max(C) - MFactor * (max(C) - min(C)) * the default values for mFactor and MFactor are 0 which result in * m = min(C), M = max(C), where C is all of the colors in the image. Adjusting * mFactor and MFactor above zero provide a means to control the saturation of * normalization. These settings only affect the final normalization, the * normalization that occurs on the input to the high-pass filter always uses * the min and max. */ vtkSetClampMacro(LowContrastEnhancementFactor, double, 0.0, 1.0); vtkGetMacro(LowContrastEnhancementFactor, double); vtkSetClampMacro(HighContrastEnhancementFactor, double, 0.0, 1.0); vtkGetMacro(HighContrastEnhancementFactor, double); ///@} ///@{ /** * Enable/Disable the anti-aliasing pass. This optional pass (disabled by * default) can be enabled to reduce jagged patterns in the final LIC image. * Values greater than 0 control the number of iterations, one is typically * sufficient. */ vtkSetClampMacro(AntiAlias, int, 0, VTK_INT_MAX); vtkGetMacro(AntiAlias, int); vtkBooleanMacro(AntiAlias, int); ///@} ///@{ /** * Number of streamline integration steps (initial value is 1). * In term of visual quality, the greater (within some range) the better. */ vtkSetClampMacro(NumberOfSteps, int, 0, VTK_INT_MAX); vtkGetMacro(NumberOfSteps, int); ///@} ///@{ /** * Get/Set the streamline integration step size (0.01 by default). This is * the length of each step in normalized image space i.e. in range [0, FLOAT_MAX]. * In term of visual quality, the smaller the better. The type for the * interface is double as VTK interface is, but GPU only supports float. * Thus it will be converted to float in the execution of the algorithm. */ vtkSetClampMacro(StepSize, double, 0.0, VTK_FLOAT_MAX); vtkGetMacro(StepSize, double); ///@} ///@{ /** * If VectorField has >= 3 components, we must choose which 2 components * form the (X, Y) components for the vector field. Must be in the range * [0, 3]. */ void SetComponentIds(int c0, int c1); void SetComponentIds(int c[2]) { this->SetComponentIds(c[0], c[1]); } vtkGetVector2Macro(ComponentIds, int); ///@} ///@{ /** * Set the max noise value for use during LIC integration normalization. * The integration normalization factor is the max noise value times the * number of steps taken. The default value is 1. */ vtkSetClampMacro(MaxNoiseValue, double, 0.0, 1.0); vtkGetMacro(MaxNoiseValue, double); ///@} ///@{ /** * This class performs LIC in the normalized image space. Hence, by default * it transforms the input vectors to the normalized image space (using the * GridSpacings and input vector field dimensions). Set this to 0 to disable * transformation if the vectors are already transformed. */ void SetTransformVectors(int val); vtkGetMacro(TransformVectors, int); ///@} /** * Set/Get the spacing in each dimension of the plane on which the vector * field is defined. This class performs LIC in the normalized image space * and hence generally it needs to transform the input vector field (given * in physical space) to the normalized image space. The Spacing is needed * to determine the transform. Default is (1.0, 1.0). It is possible to * disable vector transformation by setting TransformVectors to 0. * vtkSetVector2Macro(GridSpacings, double); * vtkGetVector2Macro(GridSpacings, double); */ ///@{ /** * Normalize vectors during integration. When set(the default) the input vector field * is normalized during integration, and each integration occurs over the same arclength. * When not set each integration occurs over an arc length proportional to the field * magnitude as is customary in traditional numerical methods. See, "Imaging Vector * Fields Using Line Integral Convolution" for an axample where normalization is used. * See, "Image Space Based Visualization of Unsteady Flow on Surfaces" for an example * of where no normalization is used. */ void SetNormalizeVectors(int val); vtkGetMacro(NormalizeVectors, int); ///@} ///@{ /** * The MaskThreshold controls blanking of the LIC texture. For fragments with * |V|& vectorExtent, const std::deque& licExtent, vtkTextureObject* vectorTex, vtkTextureObject* maskVectorTex, vtkTextureObject* noiseTex); /** * Convenience functions to ensure that the input textures are * configured correctly. */ static void SetVectorTexParameters(vtkTextureObject* vectors); static void SetNoiseTexParameters(vtkTextureObject* noise); /** * Set the communicator to use during parallel operation * The communicator will not be duplicated or reference * counted for performance reasons thus caller should * hold/manage reference to the communicator during use * of the LIC object. */ virtual void SetCommunicator(vtkPainterCommunicator*) {} virtual vtkPainterCommunicator* GetCommunicator(); /** * For parallel operation, find global min/max * min/max are in/out. */ virtual void GetGlobalMinMax(vtkPainterCommunicator*, float&, float&) {} /** * Methods used for parallel benchmarks. Use cmake to define * vtkLineIntegralConviolution2DTIME to enable benchmarks. * During each update timing information is stored, it can * be written to disk by calling WriteLog. */ virtual void WriteTimerLog(const char*) {} protected: vtkLineIntegralConvolution2D(); ~vtkLineIntegralConvolution2D() override; vtkPainterCommunicator* Comm; void SetVTShader(vtkShaderProgram* prog); void SetLIC0Shader(vtkShaderProgram* prog); void SetLICIShader(vtkShaderProgram* prog); void SetLICNShader(vtkShaderProgram* prog); void SetEEShader(vtkShaderProgram* prog); void SetCEShader(vtkShaderProgram* prog); void SetAAHShader(vtkShaderProgram* prog); void SetAAVShader(vtkShaderProgram* prog); void BuildShaders(); void RenderQuad(float computeBounds[4], vtkPixelExtent computeExtent); vtkTextureObject* AllocateBuffer(unsigned int texSize[2]); /** * Convenience functions to ensure that the input textures are * configured correctly. */ void SetNoise2TexParameters(vtkTextureObject* noise); /** * Methods used for parallel benchmarks. Use cmake to define * vtkSurfaceLICPainterTIME to enable benchmarks. During each * update timing information is stored, it can be written to * disk by calling WriteLog (defined in vtkSurfaceLICPainter). */ virtual void StartTimerEvent(const char*) {} virtual void EndTimerEvent(const char*) {} protected: vtkWeakPointer Context; vtkOpenGLFramebufferObject* FBO; int ShadersNeedBuild; vtkOpenGLHelper* FinalBlendProgram; vtkOpenGLHelper* IntermediateBlendProgram; vtkOpenGLHelper* VTShader; vtkOpenGLHelper* LIC0Shader; vtkOpenGLHelper* LICIShader; vtkOpenGLHelper* LICNShader; vtkOpenGLHelper* EEShader; vtkOpenGLHelper* CEShader; vtkOpenGLHelper* AAHShader; vtkOpenGLHelper* AAVShader; int NumberOfSteps; double StepSize; int EnhancedLIC; int EnhanceContrast; double LowContrastEnhancementFactor; double HighContrastEnhancementFactor; int AntiAlias; int NoiseTextureLookupCompatibilityMode; double MaskThreshold; int TransformVectors; int NormalizeVectors; int ComponentIds[2]; double MaxNoiseValue; private: vtkLineIntegralConvolution2D(const vtkLineIntegralConvolution2D&) = delete; void operator=(const vtkLineIntegralConvolution2D&) = delete; }; #endif