vtk9/Rendering/LICOpenGL2/vtkLineIntegralConvolution2D.h

432 lines
17 KiB
C++

/*=========================================================================
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 vetor 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.
* <pre>
* noise texture
* |
* [ LIC ((CE) HPF LIC) (AA) (CE) ]
* | |
* vector field LIC'd image
* </pre>
* 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 <deque> // 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|<threshold the LIC fragment is not rendered. The default value is 0.0.
* For surface LIC MaskThreshold units are in the original vector space. For image LIC
* be aware that while the vector field is transformed to image space while the mask
* threshold is not. Therefore the mask threshold must be specified in image space
* units.
*/
vtkSetClampMacro(MaskThreshold, double, -1.0, VTK_FLOAT_MAX);
vtkGetMacro(MaskThreshold, double);
///@}
/**
* Compute the lic on the entire vector field texture.
*/
vtkTextureObject* Execute(vtkTextureObject* vectorTex, vtkTextureObject* noiseTex);
/**
* Compute the lic on the indicated subset of the vector field
* texture.
*/
vtkTextureObject* Execute(
const int extent[4], vtkTextureObject* vectorTex, vtkTextureObject* noiseTex);
/**
* Compute LIC over the desired subset of the input texture. The
* result is copied into the desired subset of the provided output
* texture.
* inputTexExtent : screen space extent of the input texture
* vectorExtent : part of the inpute extent that has valid vectors
* licExtent : part of the inpute extent to compute on
* outputTexExtent : screen space extent of the output texture
* outputExtent : part of the output texture to store the result
*/
vtkTextureObject* Execute(const vtkPixelExtent& inputTexExtent,
const std::deque<vtkPixelExtent>& vectorExtent, const std::deque<vtkPixelExtent>& 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<vtkOpenGLRenderWindow> 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