Commit d619baf5 authored by Joachim Pouderoux's avatar Joachim Pouderoux
Browse files

OpenGLRenderWindow function to switch to offscreen buffers.

This new function switch to hardware off-screen rendering mode
with the current (on-screen) OpenGL context.
This allows to render on this buffer to perform a rendering pass
even if the window is hidden for instance.
parent c6dc4435
......@@ -572,6 +572,15 @@ public:
return 0;
}
// Description:
// Create and bind offscreen rendering buffers without destroying the current
// OpenGL context. This allows to temporary switch to offscreen rendering
// (ie. to make a screenshot even if the window is hidden).
// Return if the creation was successful (1) or not (0).
// Note: This function requires that the device supports OpenGL framebuffer extension.
// The function has no effect if OffScreenRendering is ON.
virtual int SetUseOffScreenBuffers(bool) { return 0; }
virtual bool GetUseOffScreenBuffers() { return false; }
protected:
vtkRenderWindow();
......
......@@ -17,6 +17,7 @@ vtk_add_test_cxx(${vtk-module}CxxTests render_window_tests
TestTDx.cxx
TestValuePainter.cxx,NO_VALID
TestValuePasses.cxx
TestRenderToImage.cxx
)
vtk_add_test_cxx(${vtk-module}CxxTests render_window_tests
TestScalarsToColorsPainterFieldDataCells,TestScalarsToColorsPainterFieldData.cxx -1
......
#include <vtkActor.h>
#include <vtkImageActor.h>
#include <vtkImageData.h>
#include <vtkImageMapper3D.h>
#include <vtkNew.h>
#include <vtkOpenGLRenderWindow.h>
#include <vtkPointData.h>
#include <vtkPolyData.h>
#include <vtkPolyDataMapper.h>
#include <vtkRegressionTestImage.h>
#include <vtkRenderer.h>
#include <vtkRenderWindow.h>
#include <vtkRenderWindowInteractor.h>
#include <vtkSphereSource.h>
#include <vtkTestUtilities.h>
#include <vtkUnsignedCharArray.h>
int TestRenderToImage(int argc, char* argv[])
{
vtkNew<vtkSphereSource> sphereSource;
sphereSource->SetCenter(0.0, 0.0, 0.0);
sphereSource->SetRadius(5.0);
sphereSource->Update();
// Visualize
vtkNew<vtkPolyDataMapper> mapper;
mapper->SetInputConnection(sphereSource->GetOutputPort());
vtkNew<vtkActor> actor;
actor->SetMapper(mapper.Get());
vtkNew<vtkRenderer> renderer;
vtkNew<vtkRenderWindow> renderWindow;
renderWindow->AddRenderer(renderer.Get());
renderWindow->SetMultiSamples(0);
vtkNew<vtkRenderWindowInteractor> renderWindowInteractor;
renderWindowInteractor->SetRenderWindow(renderWindow.Get());
renderWindow->Render();
// Render to the image
vtkOpenGLRenderWindow* glRenderWindow =
vtkOpenGLRenderWindow::SafeDownCast(renderWindow.Get());
if (!glRenderWindow->SetUseOffScreenBuffers(true))
{
// Hardware off screen buffer failed to be created.
// Turn debug mode on to write the errors on the output.
glRenderWindow->DebugOn();
glRenderWindow->SetUseOffScreenBuffers(true);
glRenderWindow->DebugOff();
std::cout << "Unable to create a hardware frame buffer, the graphic board "
"or driver can be too old:\n"
<< glRenderWindow->ReportCapabilities() << std::endl;
return EXIT_FAILURE;
}
renderWindow->Render();
// Create an (empty) image at the window size
int *size = renderWindow->GetSize();
vtkNew<vtkImageData> image;
image->SetDimensions(size[0], size[1], 1);
image->AllocateScalars(VTK_UNSIGNED_CHAR, 3);
renderWindow->GetPixelData(0, 0, size[0] - 1, size[1] - 1, 0,
vtkUnsignedCharArray::SafeDownCast(image->GetPointData()->GetScalars()));
glRenderWindow->SetUseOffScreenBuffers(false);
// Now add the actor
renderer->AddActor(actor.Get());
renderer->ResetCamera();
renderWindow->Render();
glRenderWindow->SetUseOffScreenBuffers(true);
renderWindow->Render();
// Capture the framebuffer to the image, again
renderWindow->GetPixelData(0, 0, size[0]-1, size[1]-1, 0,
vtkUnsignedCharArray::SafeDownCast(image->GetPointData()->GetScalars()));
glRenderWindow->SetUseOffScreenBuffers(false);
// Create a new image actor and remove the geometry one
vtkNew<vtkImageActor> imageActor;
imageActor->GetMapper()->SetInputData(image.Get());
renderer->RemoveActor(actor.Get());
renderer->AddActor(imageActor.Get());
// Background color white to distinguish image boundary
renderer->SetBackground(1, 1, 1);
renderWindow->Render();
renderer->ResetCamera();
renderWindow->Render();
int retVal = vtkRegressionTestImage(renderWindow.Get());
if (retVal == vtkRegressionTester::DO_INTERACTOR)
{
renderWindowInteractor->Start();
}
return !retVal;
}
......@@ -519,9 +519,7 @@ void vtkCocoaRenderWindow::SetSize(int x, int y)
if ((this->Size[0] != x) || (this->Size[1] != y) || (this->GetParentId()))
{
this->Modified();
this->Size[0] = x;
this->Size[1] = y;
this->Superclass::SetSize(x, y);
if (this->GetParentId() && this->GetWindowId() && this->Mapped)
{
// Set the NSView size, not the window size.
......
......@@ -314,9 +314,8 @@ void vtkOSOpenGLRenderWindow::SetSize(int width,int height)
{
if ((this->Size[0] != width)||(this->Size[1] != height))
{
this->Size[0] = width;
this->Size[1] = height;
this->ResizeOffScreenWindow(width,height);
this->Superclass::SetSize(width, height);
this->ResizeOffScreenWindow(width, height);
this->Modified();
}
}
......
This diff is collapsed.
......@@ -105,6 +105,11 @@ public:
// Returns 0 if not able to determine otherwise sets R G B and A into buffer.
int GetColorBufferSizes(int *rgba);
// Description:
// Set the size of the window in screen coordinates in pixels.
virtual void SetSize(int a[2]);
virtual void SetSize(int,int);
// Description:
// Initialize OpenGL for this window.
virtual void OpenGLInit();
......@@ -189,6 +194,16 @@ public:
// Useful for measurement only.
virtual void WaitForCompletion();
// Description:
// Create and bind offscreen rendering buffers without destroying the current
// OpenGL context. This allows to temporary switch to offscreen rendering
// (ie. to make a screenshot even if the window is hidden).
// Return if the creation was successful (1) or not (0).
// Note: This function requires that the device supports OpenGL framebuffer extension.
// The function has no effect if OffScreenRendering is ON.
virtual int SetUseOffScreenBuffers(bool offScreen);
virtual bool GetUseOffScreenBuffers();
protected:
vtkOpenGLRenderWindow();
~vtkOpenGLRenderWindow();
......@@ -211,12 +226,18 @@ protected:
// && (result implies OffScreenUseFrameBuffer)
int CreateHardwareOffScreenWindow(int width, int height);
int CreateHardwareOffScreenBuffers(int width, int height, bool bind = false);
void BindHardwareOffScreenBuffers();
// Description:
// Destroy an offscreen window based on OpenGL framebuffer extension.
// \pre initialized: OffScreenUseFrameBuffer
// \post destroyed: !OffScreenUseFrameBuffer
void DestroyHardwareOffScreenWindow();
void UnbindHardwareOffScreenBuffers();
void DestroyHardwareOffScreenBuffers();
// Description:
// Flag telling if a framebuffer-based offscreen is currently in use.
int OffScreenUseFrameBuffer;
......@@ -227,6 +248,8 @@ protected:
unsigned int TextureObjects[4]; // really GLuint
unsigned int FrameBufferObject; // really GLuint
unsigned int DepthRenderBufferObject; // really GLuint
int HardwareBufferSize[2];
bool HardwareOffScreenBuffersBind;
// Description:
// Create a not-off-screen window.
......
......@@ -323,9 +323,7 @@ void vtkWin32OpenGLRenderWindow::SetSize(int x, int y)
static int resizing = 0;
if ((this->Size[0] != x) || (this->Size[1] != y))
{
this->Modified();
this->Size[0] = x;
this->Size[1] = y;
this->Superclass::SetSize(x, y);
if (this->Interactor)
{
......
......@@ -1198,8 +1198,7 @@ void vtkXOpenGLRenderWindow::SetSize(int width,int height)
{
if ((this->Size[0] != width)||(this->Size[1] != height))
{
this->Size[0] = width;
this->Size[1] = height;
this->Superclass::SetSize(width, height);
if (this->Interactor)
{
......
......@@ -19,6 +19,7 @@ vtk_add_test_cxx(${vtk-module}CxxTests tests
TestSobelGradientMagnitudePass.cxx
TestEDLPass.cxx
TestCoincident.cxx
TestRenderToImage.cxx
)
vtk_test_cxx_executable(${vtk-module}CxxTests tests RENDERING_FACTORY)
#include <vtkActor.h>
#include <vtkImageActor.h>
#include <vtkImageData.h>
#include <vtkImageMapper3D.h>
#include <vtkNew.h>
#include <vtkOpenGLRenderWindow.h>
#include <vtkPointData.h>
#include <vtkPolyData.h>
#include <vtkPolyDataMapper.h>
#include <vtkRegressionTestImage.h>
#include <vtkRenderer.h>
#include <vtkRenderWindow.h>
#include <vtkRenderWindowInteractor.h>
#include <vtkSphereSource.h>
#include <vtkTestUtilities.h>
#include <vtkUnsignedCharArray.h>
int TestRenderToImage(int argc, char* argv[])
{
vtkNew<vtkSphereSource> sphereSource;
sphereSource->SetCenter(0.0, 0.0, 0.0);
sphereSource->SetRadius(5.0);
sphereSource->Update();
// Visualize
vtkNew<vtkPolyDataMapper> mapper;
mapper->SetInputConnection(sphereSource->GetOutputPort());
vtkNew<vtkActor> actor;
actor->SetMapper(mapper.Get());
vtkNew<vtkRenderer> renderer;
vtkNew<vtkRenderWindow> renderWindow;
renderWindow->AddRenderer(renderer.Get());
renderWindow->SetMultiSamples(0);
vtkNew<vtkRenderWindowInteractor> renderWindowInteractor;
renderWindowInteractor->SetRenderWindow(renderWindow.Get());
renderWindow->Render();
// Render to the image
vtkOpenGLRenderWindow* glRenderWindow =
vtkOpenGLRenderWindow::SafeDownCast(renderWindow.Get());
if (!glRenderWindow->SetUseOffScreenBuffers(true))
{
// Hardware off screen buffer failed to be created.
// Turn debug mode on to write the errors on the output.
glRenderWindow->DebugOn();
glRenderWindow->SetUseOffScreenBuffers(true);
glRenderWindow->DebugOff();
std::cout << "Unable to create a hardware frame buffer, the graphic board "
"or driver can be too old:\n"
<< glRenderWindow->ReportCapabilities() << std::endl;
return EXIT_FAILURE;
}
renderWindow->Render();
// Create an (empty) image at the window size
int *size = renderWindow->GetSize();
vtkNew<vtkImageData> image;
image->SetDimensions(size[0], size[1], 1);
image->AllocateScalars(VTK_UNSIGNED_CHAR, 3);
renderWindow->GetPixelData(0, 0, size[0] - 1, size[1] - 1, 0,
vtkUnsignedCharArray::SafeDownCast(image->GetPointData()->GetScalars()));
glRenderWindow->SetUseOffScreenBuffers(false);
// Now add the actor
renderer->AddActor(actor.Get());
renderer->ResetCamera();
renderWindow->Render();
glRenderWindow->SetUseOffScreenBuffers(true);
renderWindow->Render();
// Capture the framebuffer to the image, again
renderWindow->GetPixelData(0, 0, size[0]-1, size[1]-1, 0,
vtkUnsignedCharArray::SafeDownCast(image->GetPointData()->GetScalars()));
glRenderWindow->SetUseOffScreenBuffers(false);
// Create a new image actor and remove the geometry one
vtkNew<vtkImageActor> imageActor;
imageActor->GetMapper()->SetInputData(image.Get());
renderer->RemoveActor(actor.Get());
renderer->AddActor(imageActor.Get());
// Background color white to distinguish image boundary
renderer->SetBackground(1, 1, 1);
renderWindow->Render();
renderer->ResetCamera();
renderWindow->Render();
int retVal = vtkRegressionTestImage(renderWindow.Get());
if (retVal == vtkRegressionTester::DO_INTERACTOR)
{
renderWindowInteractor->Start();
}
return !retVal;
}
......@@ -515,9 +515,7 @@ void vtkCocoaRenderWindow::SetSize(int x, int y)
if ((this->Size[0] != x) || (this->Size[1] != y) || (this->GetParentId()))
{
this->Modified();
this->Size[0] = x;
this->Size[1] = y;
this->Superclass::SetSize(x, y);
if (this->GetParentId() && this->GetWindowId() && this->Mapped)
{
// Set the NSView size, not the window size.
......
......@@ -337,9 +337,8 @@ void vtkOSOpenGLRenderWindow::SetSize(int width,int height)
{
if ((this->Size[0] != width)||(this->Size[1] != height))
{
this->Size[0] = width;
this->Size[1] = height;
this->ResizeOffScreenWindow(width,height);
this->Superclass::SetSize(width, height);
this->ResizeOffScreenWindow(width, height);
this->Modified();
}
}
......
......@@ -53,6 +53,8 @@ static int vtkOpenGLRenderWindowGlobalMaximumNumberOfMultiSamples = 0;
static int vtkOpenGLRenderWindowGlobalMaximumNumberOfMultiSamples = 8;
#endif
const char* defaultWindowName = "Visualization Toolkit - OpenGL";
// ----------------------------------------------------------------------------
void vtkOpenGLRenderWindow::SetGlobalMaximumNumberOfMultiSamples(int val)
{
......@@ -104,10 +106,14 @@ vtkOpenGLRenderWindow::vtkOpenGLRenderWindow()
this->MultiSamples = vtkOpenGLRenderWindowGlobalMaximumNumberOfMultiSamples;
delete [] this->WindowName;
this->WindowName = new char[strlen("Visualization Toolkit - OpenGL")+1];
strcpy( this->WindowName, "Visualization Toolkit - OpenGL" );
this->WindowName = new char[strlen(defaultWindowName) + 1];
strcpy(this->WindowName, defaultWindowName);
this->OffScreenUseFrameBuffer = 0;
this->FrameBufferObject = 0;
this->HardwareBufferSize[0] = 0;
this->HardwareBufferSize[1] = 0;
this->HardwareOffScreenBuffersBind = false;
this->BackLeftBuffer = static_cast<unsigned int>(GL_BACK_LEFT);
this->BackRightBuffer = static_cast<unsigned int>(GL_BACK_RIGHT);
......@@ -325,6 +331,30 @@ void vtkOpenGLRenderWindow::StereoUpdate(void)
}
}
void vtkOpenGLRenderWindow::SetSize(int a[2])
{
this->SetSize(a[0], a[1]);
}
void vtkOpenGLRenderWindow::SetSize(int x, int y)
{
if (this->Size[0] == x
&& this->Size[1] == y)
{
// Nothing should happend in the superclass but never knows...
this->Superclass::SetSize(x, y);
return;
}
this->Superclass::SetSize(x, y);
if (this->HardwareOffScreenBuffersBind)
{
// We activate the offscreen buffers again so they will be
// recreated at the new window size
this->SetUseOffScreenBuffers(true);
}
}
void vtkOpenGLRenderWindow::OpenGLInit()
{
OpenGLInitContext();
......@@ -1786,6 +1816,38 @@ int vtkOpenGLRenderWindow::CreateHardwareOffScreenWindow(int width, int height)
assert("pre: positive_height" && height>0);
assert("pre: not_initialized" && !this->OffScreenUseFrameBuffer);
this->CreateAWindow();
this->MakeCurrent();
this->OpenGLInit();
int result = this->CreateHardwareOffScreenBuffers(width, height);
if (!result)
{
this->DestroyWindow();
}
else
{
this->BindHardwareOffScreenBuffers();
this->OffScreenUseFrameBuffer = 1;
}
// A=>B = !A || B
assert("post: valid_result" && (result==0 || result==1)
&& (!result || OffScreenUseFrameBuffer));
return result;
}
// ----------------------------------------------------------------------------
// Description:
// Create an offScreen rendering buffer based on OpenGL framebuffer extension.
// Return if the creation was successful or not.
int vtkOpenGLRenderWindow::CreateHardwareOffScreenBuffers(int width, int height,
bool bind)
{
assert("pre: positive_width" && width>0);
assert("pre: positive_height" && height>0);
// This implementation currently ignores multisampling configurations:
// the following code causes tests to fail, commenting it out
// if (this->MultiSamples > 1)
......@@ -1796,213 +1858,288 @@ int vtkOpenGLRenderWindow::CreateHardwareOffScreenWindow(int width, int height)
// return 0;
// }
// 1. create a regular OpenGLcontext (ie create a window)
this->CreateAWindow();
this->MakeCurrent();
int result = 0;
if (this->FrameBufferObject &&
this->HardwareBufferSize[0] == width &&
this->HardwareBufferSize[1] == height)
{
if (bind)
{
this->BindHardwareOffScreenBuffers();
}
return 1;
}
if (this->FrameBufferObject)
{
this->DestroyHardwareOffScreenBuffers();
}
this->FrameBufferObject = 0;
this->HardwareBufferSize[0] = 0;
this->HardwareBufferSize[1] = 0;
int glMajorVersion = 0;
glGetIntegerv(GL_MAJOR_VERSION, & glMajorVersion);
// test for FBO support
#if GL_ES_VERSION_2_0 != 1
if(!glewIsSupported("GL_EXT_framebuffer_object"))
if (glMajorVersion < 3 &&
!glewIsSupported("GL_EXT_framebuffer_object") &&
!glewIsSupported("GL_ARB_framebuffer_object"))
{
vtkDebugMacro( << " extension GL_EXT_framebuffer_object is not supported. "
"Hardware accelerated offscreen rendering is not available" );
this->DestroyWindow();
return 0;
}
else
#endif
{
// 2. regular framebuffer code
this->NumberOfFrameBuffers=1;
#ifdef GL_STEREO
GLboolean flag;
glGetBooleanv(GL_STEREO,&flag);
if(flag)
{
this->NumberOfFrameBuffers<<=1;
}
#endif
// Up to 2: stereo
GLuint textureObjects[2];
GLuint frameBufferObject;
GLuint depthRenderBufferObject;
glGenFramebuffers(1, &frameBufferObject); // color
glGenRenderbuffers(1, &depthRenderBufferObject); // depth
int i=0;
while(i<this->NumberOfFrameBuffers)
{
textureObjects[i]=0;
++i;
}
glGenTextures(this->NumberOfFrameBuffers,textureObjects);
// Color buffers
glBindFramebuffer(GL_FRAMEBUFFER,frameBufferObject);
int result = 0;
GLenum target;
target=GL_TEXTURE_2D;
this->NumberOfFrameBuffers = 1;
#ifdef GL_STEREO
GLboolean isStereo;
glGetBooleanv(GL_STEREO, &isStereo);
if (isStereo)
{
this->NumberOfFrameBuffers = 2;
}
#endif
i=0;
while(i<this->NumberOfFrameBuffers)
{
glBindTexture(target,textureObjects[i]);
glTexParameteri(target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glTexParameteri(target, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(target, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
// Up to 2 for stereo
GLuint textureObjects[2] = { 0, 0 };
GLuint frameBufferObject;
GLuint depthRenderBufferObject;
glGenFramebuffers(1, &frameBufferObject); // color
glGenRenderbuffers(1, &depthRenderBufferObject); // depth
glGenTextures(this->NumberOfFrameBuffers, textureObjects);
// Bind the color buffer
glBindFramebuffer(GL_FRAMEBUFFER, frameBufferObject);
GLenum target = GL_TEXTURE_2D;
for (int i = 0; i < this->NumberOfFrameBuffers; i++)
{
glBindTexture(target, textureObjects[i]);
glTexParameteri(target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glTexParameteri(target, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(target, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
#ifdef GL_RGBA8
glTexImage2D(target,0,GL_RGBA8,width,height,
0, GL_RGBA, GL_UNSIGNED_BYTE, NULL );
glTexImage2D(target,0,GL_RGBA8,width,height,
0, GL_RGBA, GL_UNSIGNED_BYTE, NULL);
#else
glTexImage2D(target,0,GL_RGBA,width,height,
0, GL_RGBA, GL_UNSIGNED_BYTE, NULL );
glTexImage2D(target,0,GL_RGBA,width,height,
0, GL_RGBA, GL_UNSIGNED_BYTE, NULL);
#endif
glFramebufferTexture2D(GL_FRAMEBUFFER,