Commit 674fd339 authored by Ken Martin's avatar Ken Martin Committed by Kitware Robot

Merge topic 'opengl_imageproc'

c3a6dfa9 add opengl gpu image processing support
Acked-by: Kitware Robot's avatarKitware Robot <kwrobot@kitware.com>
Merge-request: !1597
parents d40abfd3 c3a6dfa9
Pipeline #18411 running with stage
set(Module_SRCS
vtkOpenGLImageGradient.cxx
)
vtk_module_library(${vtk-module} ${Module_SRCS})
vtk_add_test_cxx(${vtk-module}CxxTests tests
TestOpenGLImageGradient.cxx
)
vtk_test_cxx_executable(${vtk-module}CxxTests tests RENDERING_FACTORY)
/*=========================================================================
Program: Visualization Toolkit
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 "vtkNew.h"
#include "vtkRenderWindowInteractor.h"
#include "vtkInteractorStyleImage.h"
#include "vtkRenderWindow.h"
#include "vtkRenderer.h"
#include "vtkCamera.h"
#include "vtkImageData.h"
#include "vtkImageSliceMapper.h"
#include "vtkImageProperty.h"
#include "vtkImageSlice.h"
#include "vtkImageReader2.h"
#include "vtkOpenGLImageGradient.h"
#include "vtkPointData.h"
#include "vtkDataArray.h"
#include "vtkTestUtilities.h"
#include "vtkRegressionTestImage.h"
int TestOpenGLImageGradient(int argc, char *argv[])
{
vtkNew<vtkRenderWindowInteractor> iren;
vtkNew<vtkInteractorStyleImage> style;
style->SetInteractionModeToImageSlicing();
vtkNew<vtkRenderWindow> renWin;
iren->SetRenderWindow(renWin.Get());
iren->SetInteractorStyle(style.Get());
char* fname =
vtkTestUtilities::ExpandDataFileName(argc, argv, "Data/headsq/quarter");
vtkNew<vtkImageReader2> reader;
reader->SetDataByteOrderToLittleEndian();
reader->SetDataExtent(0,63,0,63,1,93);
reader->SetDataSpacing(3.2, 3.2, 1.5);
reader->SetFilePrefix(fname);
delete [] fname;
vtkNew<vtkOpenGLImageGradient> filter;
// vtkNew<vtkImageGradient> filter;
filter->SetInputConnection(reader->GetOutputPort());
filter->Update();
// double *rnger = filter->GetOutput()->GetPointData()->GetScalars()->GetRange();
vtkNew<vtkImageSliceMapper> imageMapper;
imageMapper->SetInputConnection(filter->GetOutputPort());
imageMapper->SetOrientation(2);
imageMapper->SliceAtFocalPointOn();
vtkNew<vtkImageSlice> image;
image->SetMapper(imageMapper.Get());
double range[2] = { -100, 100 };
image->GetProperty()->SetColorWindow(range[1] - range[0]);
image->GetProperty()->SetColorLevel(0.5*(range[0] + range[1]));
image->GetProperty()->SetInterpolationTypeToNearest();
vtkNew<vtkRenderer> renderer;
renderer->AddViewProp(image.Get());
renderer->SetBackground(0.2,0.3,0.4);
renWin->AddRenderer(renderer.Get());
double *bounds = imageMapper->GetBounds();
double point[3];
point[0] = 0.5*(bounds[0] + bounds[1]);
point[1] = 0.5*(bounds[2] + bounds[3]);
point[2] = 0.5*(bounds[4] + bounds[5]);
vtkCamera *camera = renderer->GetActiveCamera();
camera->SetFocalPoint(point);
point[imageMapper->GetOrientation()] += 500.0;
camera->SetPosition(point);
if (imageMapper->GetOrientation() == 2)
{
camera->SetViewUp(0.0, 1.0, 0.0);
}
else
{
camera->SetViewUp(0.0, 0.0, -1.0);
}
camera->ParallelProjectionOn();
camera->SetParallelScale(0.8*128);
renWin->SetSize(512,512);
iren->Initialize();
renWin->Render();
int retVal = vtkRegressionTestImage( renWin.Get() );
if ( retVal == vtkRegressionTester::DO_INTERACTOR)
{
iren->Start();
}
return !retVal;
}
vtk_module(vtkImagingOpenGL2
TCL_NAME vtkImagingOpenGLII
BACKEND
OpenGL2
DEPENDS
vtkImagingGeneral
vtkRendering${VTK_RENDERING_BACKEND}
TEST_DEPENDS
vtkTestingRendering
vtkInteractionStyle
)
/*=========================================================================
Program: Visualization Toolkit
Module: vtkOpenGLImageGradient.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 "vtkOpenGLImageGradient.h"
#include "vtkOpenGLImageAlgorithmHelper.h"
#include "vtkCellData.h"
#include "vtkDataArray.h"
#include "vtkImageData.h"
#include "vtkInformation.h"
#include "vtkInformationVector.h"
#include "vtkObjectFactory.h"
#include "vtkPointData.h"
#include "vtkStreamingDemandDrivenPipeline.h"
#include "vtkShaderProgram.h"
#include <algorithm> // for std::nth_element
vtkStandardNewMacro(vtkOpenGLImageGradient);
//-----------------------------------------------------------------------------
// Construct an instance of vtkOpenGLImageGradient fitler.
vtkOpenGLImageGradient::vtkOpenGLImageGradient()
{
// for GPU we do not want threading
this->NumberOfThreads = 1;
this->EnableSMP = false;
this->Helper = vtkOpenGLImageAlgorithmHelper::New();
}
//-----------------------------------------------------------------------------
vtkOpenGLImageGradient::~vtkOpenGLImageGradient()
{
if (this->Helper)
{
this->Helper->Delete();
this->Helper = 0;
}
}
void vtkOpenGLImageGradient::SetRenderWindow(vtkRenderWindow *renWin)
{
this->Helper->SetRenderWindow(renWin);
}
//-----------------------------------------------------------------------------
void vtkOpenGLImageGradient::PrintSelf(ostream& os, vtkIndent indent)
{
this->Superclass::PrintSelf(os, indent);
os << indent << "Helper: ";
this->Helper->PrintSelf(os, indent);
}
// this is used as a callback by the helper to set shader parameters
// before running and to update them on each slice
class vtkOpenGLGradientCB : public vtkOpenGLImageAlgorithmCallback
{
public:
// initialize the spacing
virtual void InitializeShaderUniforms(vtkShaderProgram *program)
{
float sp[3];
sp[0] = this->Spacing[0];
sp[1] = this->Spacing[1];
sp[2] = this->Spacing[2];
program->SetUniform3f("spacing", sp);
}
// no uniforms change on a per slice basis so empty
virtual void UpdateShaderUniforms(
vtkShaderProgram * /* program */, int /* zExtent */) {};
double *Spacing;
};
//-----------------------------------------------------------------------------
// This method contains the first switch statement that calls the correct
// templated function for the input and output region types.
void vtkOpenGLImageGradient::ThreadedRequestData(
vtkInformation *vtkNotUsed(request),
vtkInformationVector **inputVector,
vtkInformationVector *vtkNotUsed(outputVector),
vtkImageData ***inData,
vtkImageData **outData,
int outExt[6], int vtkNotUsed(id))
{
void *inPtr;
vtkDataArray *inArray = this->GetInputArrayToProcess(0,inputVector);
outData[0]->GetPointData()->GetScalars()->SetName(inArray->GetName());
inPtr = inArray->GetVoidPointer(0);
// The ouptut scalar type must be double to store proper gradients.
if(outData[0]->GetScalarType() != VTK_DOUBLE)
{
vtkErrorMacro("Execute: output ScalarType is "
<< outData[0]->GetScalarType() << "but must be double.");
return;
}
// Gradient makes sense only with one input component. This is not
// a Jacobian filter.
if(inArray->GetNumberOfComponents() != 1)
{
vtkErrorMacro(
"Execute: input has more than one component. "
"The input to gradient should be a single component image. "
"Think about it. If you insist on using a color image then "
"run it though RGBToHSV then ExtractComponents to get the V "
"components. That's probably what you want anyhow.");
return;
}
vtkOpenGLGradientCB cb;
cb.Spacing = inData[0][0]->GetSpacing();
// build the fragement shader for 2D or 3D gradient
std::string fragShader =
"//VTK::System::Dec\n"
"varying vec2 tcoordVSOutput;\n"
"uniform sampler3D inputTex1;\n"
"uniform float zPos;\n"
"uniform vec3 spacing;\n"
"uniform float inputScale;\n"
"uniform float inputShift;\n"
"//VTK::Output::Dec\n"
"void main(void) {\n"
" float dx = textureOffset(inputTex1, vec3(tcoordVSOutput, zPos), ivec3(1,0,0)).r\n"
" - textureOffset(inputTex1, vec3(tcoordVSOutput, zPos), ivec3(-1,0,0)).r;\n"
" dx = inputScale*0.5*dx/spacing.x;\n"
" float dy = textureOffset(inputTex1, vec3(tcoordVSOutput, zPos), ivec3(0,1,0)).r\n"
" - textureOffset(inputTex1, vec3(tcoordVSOutput, zPos), ivec3(0,-1,0)).r;\n"
" dy = inputScale*0.5*dy/spacing.y;\n"
;
if (this->Dimensionality == 3)
{
fragShader +=
" float dz = textureOffset(inputTex1, vec3(tcoordVSOutput, zPos), ivec3(0,0,1)).r\n"
" - textureOffset(inputTex1, vec3(tcoordVSOutput, zPos), ivec3(0,0,-1)).r;\n"
" dz = inputScale*0.5*dz/spacing.z;\n";
" gl_FragData[0] = vec4(dx, dy, dz, 1.0);\n"
"}\n";
}
else
{
fragShader +=
" gl_FragData[0] = vec4(dx, dy, 0.0, 1.0);\n"
"}\n";
}
// call the helper to execte this code
this->Helper->Execute(&cb,
inData[0][0], inArray,
outData[0], outExt,
"//VTK::System::Dec\n"
"attribute vec4 vertexMC;\n"
"attribute vec2 tcoordMC;\n"
"varying vec2 tcoordVSOutput;\n"
"void main() {\n"
" tcoordVSOutput = tcoordMC;\n"
" gl_Position = vertexMC;\n"
"}\n",
fragShader.c_str(),
"");
}
/*=========================================================================
Program: Visualization Toolkit
Module: vtkOpenGLImageGradient.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.
=========================================================================*/
// .NAME vtkOpenGLImageGradient - Compute Gradient using the GPU
#ifndef vtkOpenGLImageGradient_h
#define vtkOpenGLImageGradient_h
#include "vtkImagingOpenGL2Module.h" // For export macro
#include "vtkImageGradient.h"
class vtkOpenGLImageAlgorithmHelper;
class vtkRenderWindow;
class VTKIMAGINGOPENGL2_EXPORT vtkOpenGLImageGradient : public vtkImageGradient
{
public:
static vtkOpenGLImageGradient *New();
vtkTypeMacro(vtkOpenGLImageGradient,vtkImageGradient);
// Description:
// Set the render window to get the OpenGL resources from
void SetRenderWindow(vtkRenderWindow *);
protected:
void PrintSelf(ostream& os, vtkIndent indent);
vtkOpenGLImageGradient();
~vtkOpenGLImageGradient();
vtkOpenGLImageAlgorithmHelper *Helper;
void ThreadedRequestData(vtkInformation *request,
vtkInformationVector **inputVector,
vtkInformationVector *outputVector,
vtkImageData ***inData, vtkImageData **outData,
int extent[6], int id);
private:
vtkOpenGLImageGradient(const vtkOpenGLImageGradient&); // Not implemented.
void operator=(const vtkOpenGLImageGradient&); // Not implemented.
};
#endif
......@@ -38,6 +38,7 @@ set(Module_SRCS
vtkOpenGLGlyph3DMapper.cxx
vtkOpenGLHardwareSelector.cxx
vtkOpenGLHelper.cxx
vtkOpenGLImageAlgorithmHelper.cxx
vtkOpenGLImageMapper.cxx
vtkOpenGLImageSliceMapper.cxx
vtkOpenGLIndexBufferObject.cxx
......
/*=========================================================================
Program: Visualization Toolkit
Module: vtkOpenGLImageAlgorithmHelper.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 "vtkOpenGLImageAlgorithmHelper.h"
#include "vtkObjectFactory.h"
#include "vtkTextureObject.h"
#include "vtkOpenGLRenderWindow.h"
#include "vtkDataArray.h"
#include "vtkImageData.h"
#include "vtkNew.h"
#include "vtkFrameBufferObject.h"
#include "vtkOpenGLShaderCache.h"
#include "vtk_glew.h"
#include "vtkPixelTransfer.h"
#include "vtkPointData.h"
#include "vtkPixelBufferObject.h"
#include "vtkShaderProgram.h"
#include "vtkOpenGLVertexArrayObject.h"
vtkStandardNewMacro(vtkOpenGLImageAlgorithmHelper);
// ----------------------------------------------------------------------------
vtkOpenGLImageAlgorithmHelper::vtkOpenGLImageAlgorithmHelper()
{
this->RenderWindow = 0;
}
// ----------------------------------------------------------------------------
vtkOpenGLImageAlgorithmHelper::~vtkOpenGLImageAlgorithmHelper()
{
this->SetRenderWindow(0);
}
void vtkOpenGLImageAlgorithmHelper::SetRenderWindow(vtkRenderWindow *renWin)
{
if (renWin == this->RenderWindow.Get())
{
return;
}
vtkOpenGLRenderWindow *orw = NULL;
if (renWin)
{
orw = vtkOpenGLRenderWindow::SafeDownCast(renWin);
}
this->RenderWindow = orw;
this->Modified();
}
void vtkOpenGLImageAlgorithmHelper::Execute(
vtkOpenGLImageAlgorithmCallback *cb,
vtkImageData *inImage, vtkDataArray *inArray,
vtkImageData *outImage, int outExt[6],
const char *vertexCode,
const char *fragmentCode,
const char *geometryCode
)
{
// make sure it is initialized
if (!this->RenderWindow)
{
this->SetRenderWindow(vtkRenderWindow::New());
this->RenderWindow->SetOffScreenRendering(true);
this->RenderWindow->UnRegister(this);
}
this->RenderWindow->Initialize();
// Is it a 2D or 3D image
int dims[3];
inImage->GetDimensions(dims);
int dimensions = 0;
for (int i = 0; i < 3; i ++)
{
if (dims[i] > 1)
{
dimensions++;
}
}
// no 1d or 2D supprt yet
if (dimensions < 3)
{
vtkErrorMacro("no 1D or 2D processing support yet");
return;
}
// send vector data to a texture
int inputExt[6];
inImage->GetExtent(inputExt);
void *inPtr = inArray->GetVoidPointer(0);
// could do shortcut here if the input volume is
// exactly what we want (updateExtent == wholeExtent)
// vtkIdType incX, incY, incZ;
// inImage->GetContinuousIncrements(inArray, extent, incX, incY, incZ);
// tmpImage->CopyAndCastFrom(inImage, inUpdateExtent)
vtkNew<vtkTextureObject> inputTex;
inputTex->SetContext(this->RenderWindow);
inputTex->Create3DFromRaw(
dims[0], dims[1], dims[2],
inArray->GetNumberOfComponents(),
inArray->GetDataType(), inPtr);
float shift = 0.0;
float scale = 1.0;
inputTex->GetShiftAndScale(shift, scale);
// now create the framebuffer for the output
int outDims[3];
outDims[0] = outExt[1] - outExt[0] + 1;
outDims[1] = outExt[3] - outExt[2] + 1;
outDims[2] = outExt[5] - outExt[4] + 1;
vtkNew<vtkTextureObject> outputTex;
outputTex->SetContext(this->RenderWindow);
vtkNew<vtkFrameBufferObject> fbo;
fbo->SetContext(this->RenderWindow);
outputTex->Create2D(outDims[0], outDims[1], 4, VTK_FLOAT, false);
fbo->SetNumberOfRenderTargets(1);
fbo->SetColorBuffer(0,outputTex.Get());
// because the same FBO can be used in another pass but with several color
// buffers, force this pass to use 1, to avoid side effects from the
// render of the previous frame.
fbo->SetActiveBuffer(0);
// fbo->SetDepthBufferNeeded(true);
fbo->SetDepthBufferNeeded(false);
fbo->StartNonOrtho(outDims[0], outDims[1], false);
glViewport(0, 0, outDims[0], outDims[1]);
glScissor(0, 0, outDims[0], outDims[1]);
glDisable(GL_DEPTH_TEST);
vtkShaderProgram *prog =
this->RenderWindow->GetShaderCache()->ReadyShaderProgram(
vertexCode, fragmentCode, geometryCode);
if (prog != this->Quad.Program)
{
this->Quad.Program = prog;
this->Quad.VAO->ShaderProgramChanged();
}
cb->InitializeShaderUniforms(prog);
inputTex->Activate();
int inputTexId = inputTex->GetTextureUnit();
this->Quad.Program->SetUniformi("inputTex1", inputTexId);
// shift and scale to get the data backing into its original units
this->Quad.Program->SetUniformf("inputShift", shift);
this->Quad.Program->SetUniformf("inputScale", scale);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
// for each zslice in the output
vtkPixelExtent outputPixelExt(outExt);
for (int i = outExt[4]; i <= outExt[5]; i++)
{
cb->UpdateShaderUniforms(prog, i);
this->Quad.Program->SetUniformf("zPos", (i - outExt[4] + 0.5) / (outDims[2]));
fbo->RenderQuad(
0, outDims[0] - 1,
0, outDims[1] - 1,
this->Quad.Program, this->Quad.VAO);
vtkPixelBufferObject *outPBO = outputTex->Download();
vtkPixelTransfer::Blit<float, double>(
outputPixelExt,
outputPixelExt,
outputPixelExt,
outputPixelExt,
4,
(float*)outPBO->MapPackedBuffer(),
outImage->GetPointData()->GetScalars()->GetNumberOfComponents(),
static_cast<double *>(outImage->GetScalarPointer(outExt[0], outExt[2], i)));
outPBO->UnmapPackedBuffer();
outPBO->Delete();
}
}
// ----------------------------------------------------------------------------
void vtkOpenGLImageAlgorithmHelper::PrintSelf(ostream& os, vtkIndent indent)
{
this->Superclass::PrintSelf(os,indent);
os << indent << "RenderWindow:";
if(this->RenderWindow != 0)
{
this->RenderWindow->PrintSelf(os,indent);
}
else
{
os << "(none)" <<endl;
}
}
/*=========================================================================
Program: Visualization Toolkit
Module: vtkOpenGLImageAlgorithmHelper.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.
=========================================================================*/
// .NAME vtkOpenGLImageAlgorithmHelper - Help image algorithms use the GPU
// .SECTION Description
// Designed to make it easier to accelerate an image algorithm on the GPU
#ifndef vtkOpenGLImageAlgorithmHelper_h
#define vtkOpenGLImageAlgorithmHelper_h
#include "vtkRenderingOpenGL2Module.h" // For export macro
#include "vtkObject.h"
#include "vtkOpenGLHelper.h" // used for ivars
#include "vtkSmartPointer.h" // for ivar
class vtkOpenGLRenderWindow;
class vtkRenderWindow;
class vtkImageData;
class vtkDataArray;
class vtkOpenGLImageAlgorithmCallback
{
public:
virtual void InitializeShaderUniforms(vtkShaderProgram * /* program */) {};
virtual void UpdateShaderUniforms(
vtkShaderProgram * /* program */, int /* zExtent */) {};
virtual ~vtkOpenGLImageAlgorithmCallback() {};
};
class VTKRENDERINGOPENGL2_EXPORT vtkOpenGLImageAlgorithmHelper : public vtkObject
{
public:
static vtkOpenGLImageAlgorithmHelper *New();
vtkTypeMacro(vtkOpenGLImageAlgorithmHelper,vtkObject);
void PrintSelf(ostream& os, vtkIndent indent);
void Execute(
vtkOpenGLImageAlgorithmCallback *cb,
vtkImageData *inImage, vtkDataArray *inData,
vtkImageData *outData, int outExt[6],
const char *vertexCode,
const char *fragmentCode,
const char *geometryCode
);
// Description:
// Set the render window to get the OpenGL resources from
void SetRenderWindow(vtkRenderWindow *renWin);
protected:
vtkOpenGLImageAlgorithmHelper();
virtual ~vtkOpenGLImageAlgorithmHelper();
vtkSmartPointer<vtkOpenGLRenderWindow> RenderWindow;
vtkOpenGLHelper Quad;
private:
vtkOpenGLImageAlgorithmHelper(const vtkOpenGLImageAlgorithmHelper&); // Not implemented.
void operator=(const vtkOpenGLImageAlgorithmHelper&); // Not implemented.
};
#endif
......@@ -744,6 +744,88 @@ void vtkTextureObject::SetInternalFormat(unsigned int glInternalFormat)
}
}
//----------------------------------------------------------------------------
static int vtkGetVTKType(GLenum gltype)
{
// DON'T DEAL with VTK_CHAR as this is platform dependent.
switch (gltype)
{
case GL_BYTE:
return VTK_SIGNED_CHAR;
case GL_UNSIGNED_BYTE:
return VTK_UNSIGNED_CHAR;
case GL_SHORT:
return VTK_SHORT;
case GL_UNSIGNED_SHORT:
return VTK_UNSIGNED_SHORT;
case GL_INT:
return VTK_INT;
case GL_UNSIGNED_INT:
return VTK_UNSIGNED_INT;
case GL_FLOAT:
return VTK_FLOAT;
}
return 0;
}
void vtkTextureObject::GetShiftAndScale(float &shift, float &scale)
{
shift = 1.0;
scale = 1.0;
// check to see if this is an int format
GLenum iresult = this->Context->GetDefaultTextureInternalFormat(
vtkGetVTKType(this->Type), this->Components, true, false);