/*========================================================================= Program: Visualization Toolkit Module: vtkOpenGLHAVSVolumeMapper.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. =========================================================================*/ /* Copyright 2005, 2006 by University of Utah. */ #include "vtkOpenGLHAVSVolumeMapper.h" #include "vtkCamera.h" #include "vtkColorTransferFunction.h" #include "vtkgl.h" #include "vtkInformation.h" #include "vtkObjectFactory.h" #include "vtkOpenGL.h" #include "vtkOpenGLExtensionManager.h" #include "vtkPiecewiseFunction.h" #include "vtkRenderer.h" #include "vtkRenderWindow.h" #include "vtkTimerLog.h" #include "vtkTransform.h" #include "vtkUnstructuredGrid.h" #include "vtkUnstructuredGridPartialPreIntegration.h" #include "vtkVolumeProperty.h" #include "vtkOpenGLRenderWindow.h" #include "vtkOpenGLError.h" #include "vtkHAVSVolumeMapper_kbufferVP.h" #include "vtkHAVSVolumeMapper_k2BeginFP.h" #include "vtkHAVSVolumeMapper_k2FP.h" #include "vtkHAVSVolumeMapper_k2EndFP.h" #include "vtkHAVSVolumeMapper_k6BeginFP.h" #include "vtkHAVSVolumeMapper_k6FP.h" #include "vtkHAVSVolumeMapper_k6EndFP.h" vtkStandardNewMacro(vtkOpenGLHAVSVolumeMapper); //---------------------------------------------------------------------------- // return the correct type of UnstructuredGridVolumeMapper vtkOpenGLHAVSVolumeMapper::vtkOpenGLHAVSVolumeMapper() { this->VBOVertexName = 0; this->VBOTexCoordName = 0; this->VBOVertexIndexName = 0; this->PsiTableTexture = 0; this->FramebufferObjectSize = 0; this->OrderedTriangles = 0; this->RenderWindow=0; } //---------------------------------------------------------------------------- // return the correct type of UnstructuredGridVolumeMapper vtkOpenGLHAVSVolumeMapper::~vtkOpenGLHAVSVolumeMapper() { if (!this->GPUDataStructures) { delete [] this->OrderedTriangles; } } //---------------------------------------------------------------------------- void vtkOpenGLHAVSVolumeMapper::CheckOpenGLError(const char * str) { int err = glGetError(); (void)str; if ( err != GL_NO_ERROR && this->GetDebug() ) { vtkDebugMacro( << "OpenGL Error: " << str ); } } //---------------------------------------------------------------------------- void vtkOpenGLHAVSVolumeMapper::ReleaseGraphicsResources(vtkWindow *renWin) { if (this->Initialized) { static_cast(renWin)->MakeCurrent(); vtkOpenGLClearErrorMacro(); this->DeleteShaders(); glDisable( vtkgl::VERTEX_PROGRAM_ARB ); glDisable( vtkgl::FRAGMENT_PROGRAM_ARB ); glDeleteTextures(1, reinterpret_cast(&this->TransferFunctionTexture)); int numBuffers = (this->KBufferState == VTK_KBUFFER_SIZE_2)? 2 : 4; for (int i = 0; i < numBuffers; i++) { glDeleteTextures(1,reinterpret_cast(&this->FramebufferTextures[i])); } vtkgl::DeleteFramebuffersEXT(1,reinterpret_cast(&this->FramebufferObject)); this->Initialized = false; if (this->GPUDataStructures) { vtkgl::DeleteBuffers(1, reinterpret_cast(&this->VBOVertexName)); vtkgl::DeleteBuffers(1, reinterpret_cast(&this->VBOTexCoordName)); vtkgl::DeleteBuffers(1,reinterpret_cast(&this->VBOVertexIndexName)); vtkgl::BindBuffer(vtkgl::ARRAY_BUFFER, 0); vtkgl::BindBuffer(vtkgl::ELEMENT_ARRAY_BUFFER, 0); } vtkOpenGLCheckErrorMacro("failed after ReleaseGraphicsResources"); } this->Superclass::ReleaseGraphicsResources(renWin); } //---------------------------------------------------------------------------- void vtkOpenGLHAVSVolumeMapper::Initialize(vtkRenderer *ren, vtkVolume *vol) { // Check for the required extensions only. if (!this->SupportedByHardware(ren)) { this->InitializationError = vtkHAVSVolumeMapper::UNSUPPORTED_EXTENSIONS; return; } vtkOpenGLExtensionManager *extensions= static_cast(ren->GetRenderWindow()) ->GetExtensionManager(); // Load required extensions // supports_GL_1_3=1 as checked by this->SupportedByHardware() // OpenGL 1.3 is required by GL_ARB_draw_buffers, GL_ARB_fragment_program // and GL_ARB_vertex_program. // CLAMP_TO_EGDE is core feature of OpenGL 1.2 and // multitexture is core feature of OpenGL 1.3. extensions->LoadExtension("GL_VERSION_1_3"); // multitexture // supports_draw_buffers as checked by this->SupportedByHardware() int supports_GL_2_0=extensions->ExtensionSupported( "GL_VERSION_2_0" ); if(supports_GL_2_0) { extensions->LoadExtension("GL_VERSION_2_0"); } else { extensions->LoadCorePromotedExtension( "GL_ARB_draw_buffers" ); } // supports_fragment_program && supports_vertex_program as checked // by this->SupportedByHardware() extensions->LoadExtension( "GL_ARB_fragment_program" ); extensions->LoadExtension( "GL_ARB_vertex_program" ); // supports_GL_EXT_framebuffer_object==1 as checked // by this->SupportedByHardware() extensions->LoadExtension("GL_EXT_framebuffer_object"); // GL_ARB_texture_float or GL_ATI_texture_float introduce new tokens but // no new function: don't need to call LoadExtension. // Optional extension. int supports_GL_1_5=extensions->ExtensionSupported( "GL_VERSION_1_5" ); int supports_vertex_buffer_object; if(supports_GL_1_5) { supports_vertex_buffer_object=1; } else { supports_vertex_buffer_object = extensions->ExtensionSupported( "GL_ARB_vertex_buffer_object" ); } if(supports_vertex_buffer_object) { if(supports_GL_1_5) { extensions->LoadExtension("GL_VERSION_1_5"); } else { extensions->LoadCorePromotedExtension( "GL_ARB_vertex_buffer_object" ); } } if (!supports_vertex_buffer_object) { this->SetGPUDataStructures(false); } this->UpdateProgress(0.0); // Initialize triangles and VBOs or Vertex Arrays this->InitializePrimitives(vol); this->UpdateProgress(0.4); // Initialize scalars and VBOs this->InitializeScalars(); this->UpdateProgress(0.5); // Initialize Level-of-Detail data structures this->InitializeLevelOfDetail(); this->UpdateProgress(0.7); // Initialize Lookup tables this->InitializeLookupTables(vol); this->UpdateProgress(0.8); // Initialize vertex and scalar storage this->InitializeGPUDataStructures(); this->UpdateProgress(0.9); // Initialize shaders this->InitializeShaders(); // Initialize FBOs this->InitializeFramebufferObject(); this->UpdateProgress(1.0); this->Initialized = 1; } //---------------------------------------------------------------------------- // Change GPU data structures state void vtkOpenGLHAVSVolumeMapper::SetGPUDataStructures(bool gpu) { if(this->GPUDataStructures!=gpu) { if(!this->GPUDataStructures) { delete [] this->OrderedTriangles; this->OrderedTriangles=0; } this->GPUDataStructures = gpu; if (this->Initialized) { this->InitializeGPUDataStructures(); } } } //---------------------------------------------------------------------------- // Store data structures on GPU if possible void vtkOpenGLHAVSVolumeMapper::InitializeGPUDataStructures() { vtkOpenGLClearErrorMacro(); if (this->GPUDataStructures) { if (VBOVertexName) { vtkgl::DeleteBuffers(1, reinterpret_cast(&this->VBOVertexName)); } if (VBOVertexIndexName) { vtkgl::DeleteBuffers(1,reinterpret_cast(&this->VBOVertexIndexName)); } if (VBOTexCoordName) { vtkgl::DeleteBuffers(1, reinterpret_cast(&this->VBOTexCoordName)); } // Build vertex array vtkgl::GenBuffers(1, reinterpret_cast(&this->VBOVertexName)); vtkgl::BindBuffer(vtkgl::ARRAY_BUFFER, this->VBOVertexName); vtkgl::BufferData(vtkgl::ARRAY_BUFFER, this->NumberOfVertices*3*sizeof(float), this->Vertices, vtkgl::STATIC_DRAW); // Build dynamic vertex index array vtkgl::GenBuffers(1, reinterpret_cast(&this->VBOVertexIndexName)); vtkgl::BindBuffer(vtkgl::ELEMENT_ARRAY_BUFFER, this->VBOVertexIndexName); vtkgl::BufferData(vtkgl::ELEMENT_ARRAY_BUFFER, this->NumberOfTriangles*3*sizeof(GLuint), 0, vtkgl::STREAM_DRAW); vtkgl::BindBuffer(vtkgl::ARRAY_BUFFER, 0); vtkgl::BindBuffer(vtkgl::ELEMENT_ARRAY_BUFFER, 0); // Build tex coord array vtkgl::GenBuffers(1, reinterpret_cast(&this->VBOTexCoordName)); vtkgl::BindBuffer(vtkgl::ARRAY_BUFFER, this->VBOTexCoordName); vtkgl::BufferData(vtkgl::ARRAY_BUFFER, this->NumberOfScalars*sizeof(float), this->Scalars, vtkgl::STATIC_DRAW); vtkgl::BindBuffer(vtkgl::ARRAY_BUFFER, 0); } else { delete [] this->OrderedTriangles; this->OrderedTriangles = new unsigned int[this->NumberOfTriangles*3]; } vtkOpenGLCheckErrorMacro("failed after InitializeGPUDataStructures"); } //---------------------------------------------------------------------------- // Vertex and Fragment shaders void vtkOpenGLHAVSVolumeMapper::InitializeShaders() { vtkOpenGLClearErrorMacro(); // Create vertex shader glEnable( vtkgl::VERTEX_PROGRAM_ARB ); vtkgl::GenProgramsARB(1, reinterpret_cast(&this->VertexProgram)); vtkgl::BindProgramARB(vtkgl::VERTEX_PROGRAM_ARB, this->VertexProgram); vtkgl::ProgramStringARB(vtkgl::VERTEX_PROGRAM_ARB, vtkgl::PROGRAM_FORMAT_ASCII_ARB, static_cast(strlen(vtkHAVSVolumeMapper_kbufferVP)), vtkHAVSVolumeMapper_kbufferVP); // Create fragment shaders glEnable( vtkgl::FRAGMENT_PROGRAM_ARB ); if (this->KBufferSize == VTK_KBUFFER_SIZE_2) { vtkgl::GenProgramsARB(1, reinterpret_cast(&this->FragmentProgramBegin)); vtkgl::BindProgramARB(vtkgl::FRAGMENT_PROGRAM_ARB, this->FragmentProgramBegin); vtkgl::ProgramStringARB(vtkgl::FRAGMENT_PROGRAM_ARB, vtkgl::PROGRAM_FORMAT_ASCII_ARB, static_cast(strlen(vtkHAVSVolumeMapper_k2BeginFP)), vtkHAVSVolumeMapper_k2BeginFP); vtkgl::GenProgramsARB(1, reinterpret_cast(&this->FragmentProgram)); vtkgl::BindProgramARB(vtkgl::FRAGMENT_PROGRAM_ARB, this->FragmentProgram); vtkgl::ProgramStringARB(vtkgl::FRAGMENT_PROGRAM_ARB, vtkgl::PROGRAM_FORMAT_ASCII_ARB, static_cast(strlen(vtkHAVSVolumeMapper_k2FP)), vtkHAVSVolumeMapper_k2FP); vtkgl::GenProgramsARB(1, reinterpret_cast(&this->FragmentProgramEnd)); vtkgl::BindProgramARB(vtkgl::FRAGMENT_PROGRAM_ARB, this->FragmentProgramEnd); vtkgl::ProgramStringARB(vtkgl::FRAGMENT_PROGRAM_ARB, vtkgl::PROGRAM_FORMAT_ASCII_ARB, static_cast(strlen(vtkHAVSVolumeMapper_k2EndFP)), vtkHAVSVolumeMapper_k2EndFP); } else { vtkgl::GenProgramsARB(1, reinterpret_cast(&this->FragmentProgramBegin)); vtkgl::BindProgramARB(vtkgl::FRAGMENT_PROGRAM_ARB, this->FragmentProgramBegin); vtkgl::ProgramStringARB(vtkgl::FRAGMENT_PROGRAM_ARB, vtkgl::PROGRAM_FORMAT_ASCII_ARB, static_cast(strlen(vtkHAVSVolumeMapper_k6BeginFP)), vtkHAVSVolumeMapper_k6BeginFP); vtkgl::GenProgramsARB(1, reinterpret_cast(&this->FragmentProgram)); vtkgl::BindProgramARB(vtkgl::FRAGMENT_PROGRAM_ARB, this->FragmentProgram); vtkgl::ProgramStringARB(vtkgl::FRAGMENT_PROGRAM_ARB, vtkgl::PROGRAM_FORMAT_ASCII_ARB, static_cast(strlen(vtkHAVSVolumeMapper_k6FP)), vtkHAVSVolumeMapper_k6FP); vtkgl::GenProgramsARB(1, reinterpret_cast(&this->FragmentProgramEnd)); vtkgl::BindProgramARB(vtkgl::FRAGMENT_PROGRAM_ARB, this->FragmentProgramEnd); vtkgl::ProgramStringARB(vtkgl::FRAGMENT_PROGRAM_ARB, vtkgl::PROGRAM_FORMAT_ASCII_ARB, static_cast(strlen(vtkHAVSVolumeMapper_k6EndFP)), vtkHAVSVolumeMapper_k6EndFP); } // Disable shaders vtkgl::BindProgramARB(vtkgl::VERTEX_PROGRAM_ARB, 0); vtkgl::BindProgramARB(vtkgl::FRAGMENT_PROGRAM_ARB, 0); glDisable( vtkgl::VERTEX_PROGRAM_ARB ); glDisable( vtkgl::FRAGMENT_PROGRAM_ARB ); vtkOpenGLCheckErrorMacro("failed after InitializeShaders"); } //---------------------------------------------------------------------------- void vtkOpenGLHAVSVolumeMapper::DeleteShaders() { vtkgl::DeleteProgramsARB(1, reinterpret_cast(&this->VertexProgram)); vtkgl::DeleteProgramsARB(1, reinterpret_cast(&this->FragmentProgramBegin)); vtkgl::DeleteProgramsARB(1, reinterpret_cast(&this->FragmentProgram)); vtkgl::DeleteProgramsARB(1, reinterpret_cast(&this->FragmentProgramEnd)); vtkOpenGLCheckErrorMacro("failed after DeleteShaders"); } //---------------------------------------------------------------------------- // Build the lookup tables used for partial pre-integration void vtkOpenGLHAVSVolumeMapper::InitializeLookupTables(vtkVolume *vol) { vtkOpenGLClearErrorMacro(); this->Superclass::InitializeLookupTables(vol); // Create a 1D texture for transfer function look up glGenTextures(1, reinterpret_cast(&this->TransferFunctionTexture)); glBindTexture(GL_TEXTURE_1D, this->TransferFunctionTexture); glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_WRAP_S, vtkgl::CLAMP_TO_EDGE); glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); glTexImage1D(GL_TEXTURE_1D, 0, GL_RGBA8, this->TransferFunctionSize,0, GL_RGBA,GL_FLOAT, this->TransferFunction); if (!this->PsiTableTexture) { vtkUnstructuredGridPartialPreIntegration *ppi = vtkUnstructuredGridPartialPreIntegration::New(); ppi->BuildPsiTable(); int tableSize = 0; float *psiTable = ppi->GetPsiTable(tableSize); // Create a 2D texture for the PSI lookup table glGenTextures(1, reinterpret_cast(&this->PsiTableTexture)); glBindTexture(GL_TEXTURE_2D, this->PsiTableTexture); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, vtkgl::CLAMP_TO_EDGE); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, vtkgl::CLAMP_TO_EDGE); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); glTexImage2D(GL_TEXTURE_2D, 0, GL_LUMINANCE8, tableSize, tableSize, 0, GL_LUMINANCE, GL_FLOAT, psiTable); ppi->Delete(); } vtkOpenGLCheckErrorMacro("failed after InitializeLookupTables"); } //---------------------------------------------------------------------------- // Initialize FBO and attach color and depth textures. void vtkOpenGLHAVSVolumeMapper::InitializeFramebufferObject() { vtkOpenGLClearErrorMacro(); GLint maxRB; glGetIntegerv(vtkgl::MAX_RENDERBUFFER_SIZE_EXT, &maxRB); int texSize = (maxRB > 1024)? 1024 : maxRB; int numBuffers = (this->KBufferState == VTK_KBUFFER_SIZE_2)? 2 : 4; if (!this->Initialized) { // Create FBO vtkgl::GenFramebuffersEXT(1, reinterpret_cast(&this->FramebufferObject)); vtkOpenGLCheckErrorMacro("creating FBO"); } else { glDeleteTextures(numBuffers, reinterpret_cast(this->FramebufferTextures)); vtkgl::DeleteRenderbuffersEXT(1, reinterpret_cast(&this->DepthTexture)); } numBuffers = (this->KBufferSize == VTK_KBUFFER_SIZE_2)? 2 : 4; // Create FBO textures glGenTextures(numBuffers, reinterpret_cast(this->FramebufferTextures)); for (int i = 0; i < numBuffers; i++) { glBindTexture(GL_TEXTURE_2D, this->FramebufferTextures[i]); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); glTexImage2D(GL_TEXTURE_2D, 0, vtkgl::RGBA32F_ARB, texSize, texSize, 0, GL_RGBA, GL_FLOAT, 0); } vtkOpenGLCheckErrorMacro("creating fbo textures"); // Bind framebuffer object GLint savedFrameBuffer; glGetIntegerv(vtkgl::FRAMEBUFFER_BINDING_EXT,&savedFrameBuffer); vtkgl::BindFramebufferEXT(vtkgl::FRAMEBUFFER_EXT, this->FramebufferObject); vtkOpenGLCheckErrorMacro("binding FBO"); // Generate depth buffer texture for framebuffer vtkgl::GenRenderbuffersEXT(1, reinterpret_cast(&this->DepthTexture)); // Attach texture to framebuffer color buffer vtkgl::FramebufferTexture2DEXT( vtkgl::FRAMEBUFFER_EXT, vtkgl::COLOR_ATTACHMENT0_EXT, GL_TEXTURE_2D, this->FramebufferTextures[0], 0 ); vtkgl::FramebufferTexture2DEXT( vtkgl::FRAMEBUFFER_EXT, vtkgl::COLOR_ATTACHMENT1_EXT, GL_TEXTURE_2D, this->FramebufferTextures[1], 0 ); if (numBuffers == 4) { vtkgl::FramebufferTexture2DEXT( vtkgl::FRAMEBUFFER_EXT, vtkgl::COLOR_ATTACHMENT2_EXT, GL_TEXTURE_2D, this->FramebufferTextures[2], 0 ); vtkgl::FramebufferTexture2DEXT( vtkgl::FRAMEBUFFER_EXT, vtkgl::COLOR_ATTACHMENT3_EXT, GL_TEXTURE_2D, this->FramebufferTextures[3], 0 ); } // Attach depth texture to framebuffer vtkgl::BindRenderbufferEXT(vtkgl::RENDERBUFFER_EXT, this->DepthTexture); vtkgl::RenderbufferStorageEXT(vtkgl::RENDERBUFFER_EXT, vtkgl::DEPTH_COMPONENT24, texSize, texSize); vtkgl::FramebufferRenderbufferEXT(vtkgl::FRAMEBUFFER_EXT, vtkgl::DEPTH_ATTACHMENT_EXT, vtkgl::RENDERBUFFER_EXT, this->DepthTexture); vtkOpenGLCheckErrorMacro("attach textures to FBO"); // Validate FBO after attaching textures if (vtkgl::CheckFramebufferStatusEXT(vtkgl::FRAMEBUFFER_EXT) != vtkgl::FRAMEBUFFER_COMPLETE_EXT && this->GetDebug()) { vtkDebugMacro( << "FBO incomplete" ); } // Disable FBO rendering vtkgl::BindFramebufferEXT(vtkgl::FRAMEBUFFER_EXT,savedFrameBuffer); this->FramebufferObjectSize = texSize; this->KBufferState = this->KBufferSize; vtkOpenGLCheckErrorMacro("failed after InitializeFramebufferObject"); } //---------------------------------------------------------------------------- void vtkOpenGLHAVSVolumeMapper::Render(vtkRenderer *ren, vtkVolume *vol) { ren->GetRenderWindow()->MakeCurrent(); this->RenderWindow=ren->GetRenderWindow(); if (!this->Initialized) { this->InitializationError = vtkHAVSVolumeMapper::NO_INIT_ERROR; this->Initialize(ren, vol); if (this->CheckInitializationError()) { return; } } if (ren->GetRenderWindow()->CheckAbortStatus()) { return; } // Check to see if we need to update the lookup table if ((this->ColorTransferFunctionMTime < vol->GetProperty()->GetRGBTransferFunction()->GetMTime()) ||(this->AlphaTransferFunctionMTime < vol->GetProperty()->GetScalarOpacity()->GetMTime()) ||(this->UnitDistance != vol->GetProperty()->GetScalarOpacityUnitDistance())) { this->InitializeLookupTables(vol); this->ColorTransferFunctionMTime.Modified(); this->AlphaTransferFunctionMTime.Modified(); } if (ren->GetRenderWindow()->CheckAbortStatus()) { return; } // Check to see if we need to update the scalars if ((this->ScalarsMTime < this->MTime) || (this->ScalarsMTime < this->GetInput()->GetMTime()) || (this->LastVolume != vol)) { this->InitializationError = vtkHAVSVolumeMapper::NO_INIT_ERROR; this->InitializeScalars(); this->InitializeGPUDataStructures(); if (this->CheckInitializationError()) return; this->ScalarsMTime.Modified(); } if (ren->GetRenderWindow()->CheckAbortStatus()) { return; } // Check to see if we need to update the geometry if ((this->UnstructuredGridMTime < this->GetInput()->GetMTime()) || (this->UnstructuredGridMTime < this->MTime)) { this->InitializationError = vtkHAVSVolumeMapper::NO_INIT_ERROR; this->InitializePrimitives(vol); this->InitializeLevelOfDetail(); this->InitializeGPUDataStructures(); if (this->CheckInitializationError()) return; this->UnstructuredGridMTime.Modified(); } if (ren->GetRenderWindow()->CheckAbortStatus()) { return; } this->Timer->StartTimer(); this->RenderHAVS(ren); this->LastVolume = vol; this->Timer->StopTimer(); this->TimeToDraw = static_cast(this->Timer->GetElapsedTime()); // Update level-of-detail this->UpdateLevelOfDetail(TimeToDraw); } //---------------------------------------------------------------------------- // The OpenGL rendering void vtkOpenGLHAVSVolumeMapper::RenderHAVS(vtkRenderer *ren) { vtkOpenGLClearErrorMacro(); glPushAttrib(GL_ENABLE_BIT | GL_CURRENT_BIT | GL_COLOR_BUFFER_BIT | GL_STENCIL_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_POLYGON_BIT | GL_TEXTURE_BIT | GL_LIGHTING_BIT | GL_TRANSFORM_BIT | GL_VIEWPORT_BIT); // Setup OpenGL state glShadeModel(GL_SMOOTH); glDisable(GL_DEPTH_TEST); glDisable(GL_CULL_FACE); glDisable(GL_LIGHTING); glDisable(GL_NORMALIZE); glDisable(GL_BLEND); glDisable(GL_SCISSOR_TEST); glDisable(GL_STENCIL_TEST); int screenWidth, screenHeight; ren->GetTiledSize(&screenWidth, &screenHeight); // Keep shaders up to date if (this->KBufferSize != this->KBufferState) { this->DeleteShaders(); this->InitializeShaders(); // Keep FBO up to date this->InitializeFramebufferObject(); } int vpWidth = screenWidth; int vpHeight = screenHeight; if (screenWidth > this->FramebufferObjectSize) { vpWidth = this->FramebufferObjectSize; } if (screenHeight > this->FramebufferObjectSize) { vpHeight = this->FramebufferObjectSize; } // Bind geometry arrays if (this->GPUDataStructures) { glEnableClientState(GL_VERTEX_ARRAY); vtkgl::BindBuffer(vtkgl::ARRAY_BUFFER, this->VBOVertexName); glVertexPointer(3, GL_FLOAT, 0, static_cast(NULL)); glEnableClientState(GL_TEXTURE_COORD_ARRAY); vtkgl::BindBuffer(vtkgl::ARRAY_BUFFER, this->VBOTexCoordName); glTexCoordPointer(1, GL_FLOAT, 0, static_cast(NULL)); vtkgl::BindBuffer(vtkgl::ELEMENT_ARRAY_BUFFER, this->VBOVertexIndexName); } else { glEnableClientState(GL_VERTEX_ARRAY); glVertexPointer(3, GL_FLOAT, 0, this->Vertices); glEnableClientState(GL_TEXTURE_COORD_ARRAY); glTexCoordPointer(1, GL_FLOAT, 0, this->Scalars); } if (ren->GetRenderWindow()->CheckAbortStatus()) { vtkOpenGLCheckErrorMacro("failed during Render"); return; } // Object-space sorting double *e = ren->GetActiveCamera()->GetPosition(); float eye[3]; eye[0] = e[0]; eye[1] = e[1]; eye[2] = e[2]; if (this->GPUDataStructures) { this->OrderedTriangles = static_cast(vtkgl::MapBuffer(vtkgl::ELEMENT_ARRAY_BUFFER, vtkgl::WRITE_ONLY)); } this->PartialVisibilitySort(eye); if (this->GPUDataStructures) { vtkgl::UnmapBuffer(vtkgl::ELEMENT_ARRAY_BUFFER); } this->UpdateProgress(0.4); if (ren->GetRenderWindow()->CheckAbortStatus()) { vtkOpenGLCheckErrorMacro("failed during Render"); return; } // Get depth range from OpenGL state for correct z GLfloat depthRange[2]; glGetFloatv(GL_DEPTH_RANGE, depthRange); // Get the current z-buffer float *zbuffer = ren->GetRenderWindow()->GetZbufferData(0,0,screenWidth-1,screenHeight-1); // Enable FBO Rendering GLint savedFrameBuffer; glGetIntegerv(vtkgl::FRAMEBUFFER_BINDING_EXT,&savedFrameBuffer); vtkgl::BindFramebufferEXT(vtkgl::FRAMEBUFFER_EXT, this->FramebufferObject); // Setup z-buffer this->SetupFBOZBuffer(vpWidth, vpHeight, depthRange[0], depthRange[1], zbuffer); delete [] zbuffer; // Setup multiple render targets this->SetupFBOMRT(); // Draw Initialization pass this->DrawFBOInit(vpWidth, vpHeight, depthRange[0], depthRange[1]); // Draw Geometry pass this->DrawFBOGeometry(); this->UpdateProgress(0.9); // Draw Flushing pass this->DrawFBOFlush(vpWidth, vpHeight, depthRange[0], depthRange[1]); // Disable FBO rendering vtkgl::BindFramebufferEXT(vtkgl::FRAMEBUFFER_EXT, savedFrameBuffer); glPopAttrib(); // Blend Result into framebuffer this->DrawBlend(vpWidth, vpHeight, depthRange[0], depthRange[1]); this->UpdateProgress(1.0); vtkOpenGLCheckErrorMacro("failed after Render"); } //---------------------------------------------------------------------------- // Draw the current z-buffer into the FBO z-buffer for correct compositing // with existing geometry or widgets. void vtkOpenGLHAVSVolumeMapper::SetupFBOZBuffer(int screenWidth, int screenHeight, float depthNear, float depthFar, float *zbuffer) { vtkOpenGLClearErrorMacro(); // Setup view for z-buffer copy glMatrixMode(GL_PROJECTION); glPushMatrix(); glLoadIdentity(); glOrtho(0.0, static_cast(screenWidth), 0.0, static_cast(screenHeight), static_cast(depthNear), static_cast(depthFar)); glMatrixMode(GL_MODELVIEW); glPushMatrix(); glLoadIdentity(); // Enable FBO z-buffer glEnable(GL_DEPTH_TEST); glClearDepth(depthFar); glClear(GL_DEPTH_BUFFER_BIT); glDepthFunc(GL_LESS); glRasterPos2i(0,0); glPixelStorei(GL_PACK_ALIGNMENT, 1); glDrawPixels(screenWidth, screenHeight, GL_DEPTH_COMPONENT, GL_FLOAT, zbuffer); glFlush(); // Make z-buffer Read only glDepthMask(GL_FALSE); // Reset view state after z-buffer copy glMatrixMode(GL_PROJECTION); glPopMatrix(); glMatrixMode(GL_MODELVIEW); glPopMatrix(); vtkOpenGLCheckErrorMacro("failed after SetupFBOZBuffer"); } //---------------------------------------------------------------------------- // Setup reading and writing into multiple textures from an FBO void vtkOpenGLHAVSVolumeMapper::SetupFBOMRT() { vtkOpenGLClearErrorMacro(); int numBuffers = (this->KBufferSize == VTK_KBUFFER_SIZE_2)? 2 : 4; GLenum buffers[4] = {vtkgl::COLOR_ATTACHMENT0_EXT, vtkgl::COLOR_ATTACHMENT1_EXT, vtkgl::COLOR_ATTACHMENT2_EXT, vtkgl::COLOR_ATTACHMENT3_EXT}; vtkgl::DrawBuffers(numBuffers, buffers); // Bind textures for reading glEnable(GL_TEXTURE_2D); vtkgl::ActiveTexture(vtkgl::TEXTURE0); glBindTexture(GL_TEXTURE_2D, this->FramebufferTextures[0]); vtkgl::ActiveTexture(vtkgl::TEXTURE1); glBindTexture(GL_TEXTURE_2D, this->FramebufferTextures[1]); if (numBuffers == 2) { // Bind lookup tables glEnable(GL_TEXTURE_2D); vtkgl::ActiveTexture(vtkgl::TEXTURE2); glBindTexture(GL_TEXTURE_2D, this->PsiTableTexture); glEnable(GL_TEXTURE_1D); vtkgl::ActiveTexture(vtkgl::TEXTURE3); glBindTexture(GL_TEXTURE_1D,this->TransferFunctionTexture); } else { // Bind lookup tables vtkgl::ActiveTexture(vtkgl::TEXTURE2); glBindTexture(GL_TEXTURE_2D, this->FramebufferTextures[2]); vtkgl::ActiveTexture(vtkgl::TEXTURE3); glBindTexture(GL_TEXTURE_2D, this->FramebufferTextures[3]); glEnable(GL_TEXTURE_2D); vtkgl::ActiveTexture(vtkgl::TEXTURE4); glBindTexture(GL_TEXTURE_2D, this->PsiTableTexture); glEnable(GL_TEXTURE_1D); vtkgl::ActiveTexture(vtkgl::TEXTURE5); glBindTexture(GL_TEXTURE_1D,this->TransferFunctionTexture); } vtkOpenGLCheckErrorMacro("SetupFBOMRT"); } //---------------------------------------------------------------------------- // Draw a screen-aligned plane with the init fragment shader enabled. The // init fragment shader clears the framebuffer to 0 and the k-buffers to -1. void vtkOpenGLHAVSVolumeMapper::DrawFBOInit(int screenWidth, int screenHeight, float depthNear, float depthFar) { vtkOpenGLClearErrorMacro(); // Bind initializing fragment shader glEnable(vtkgl::FRAGMENT_PROGRAM_ARB); vtkgl::BindProgramARB(vtkgl::FRAGMENT_PROGRAM_ARB, this->FragmentProgramBegin); // Setup ortho view glViewport(0,0,screenWidth,screenHeight); glMatrixMode(GL_PROJECTION); glPushMatrix(); glLoadIdentity(); glOrtho(0.0, static_cast(screenWidth), 0.0, static_cast(screenHeight), static_cast(depthNear), static_cast(depthFar)); glMatrixMode(GL_MODELVIEW); glPushMatrix(); glLoadIdentity(); // Draw a quad to initialize the k-buffer glBegin(GL_QUADS); glVertex3f(0.0, 0.0, 0.0); glVertex3f(static_cast(screenWidth), 0.0, 0.0); glVertex3f(static_cast(screenWidth), static_cast(screenHeight), 0.0); glVertex3f(0.0, static_cast(screenHeight), 0.0); glEnd(); vtkgl::BindProgramARB(vtkgl::FRAGMENT_PROGRAM_ARB, 0); glMatrixMode(GL_PROJECTION); glPopMatrix(); glMatrixMode(GL_MODELVIEW); glPopMatrix(); vtkOpenGLCheckErrorMacro("failed after DrawFBOInit"); } //---------------------------------------------------------------------------- // Draw the geometry using the k-buffer to sort and composite into the // framebuffer. void vtkOpenGLHAVSVolumeMapper::DrawFBOGeometry() { vtkOpenGLClearErrorMacro(); // Bind shaders glEnable(vtkgl::VERTEX_PROGRAM_ARB); vtkgl::BindProgramARB(vtkgl::VERTEX_PROGRAM_ARB, this->VertexProgram); vtkgl::BindProgramARB(vtkgl::FRAGMENT_PROGRAM_ARB, this->FragmentProgram); float scale = this->MaxEdgeLength; if (this->LevelOfDetail || !this->PartiallyRemoveNonConvexities) { scale = this->LevelOfDetailMaxEdgeLength; } float params[4] = {1.0f/this->FramebufferObjectSize, 1.0f/this->FramebufferObjectSize, scale, 0.0f}; vtkgl::ProgramLocalParameter4fvARB(vtkgl::FRAGMENT_PROGRAM_ARB, 0, params); // Draw geometry if (this->GPUDataStructures) { glDrawElements(GL_TRIANGLES, this->LevelOfDetailTriangleCount*3, GL_UNSIGNED_INT, static_cast(NULL)); } else { glDrawElements(GL_TRIANGLES, this->LevelOfDetailTriangleCount*3, GL_UNSIGNED_INT, this->OrderedTriangles); } vtkgl::BindProgramARB(vtkgl::FRAGMENT_PROGRAM_ARB, 0); vtkgl::BindProgramARB(vtkgl::VERTEX_PROGRAM_ARB, 0); glDisable( vtkgl::VERTEX_PROGRAM_ARB); vtkOpenGLCheckErrorMacro("failed after DrawFBOGeometry"); } //---------------------------------------------------------------------------- // Draw a k-1 screen-aligned planes to flush the valid entries from the // k-buffer. void vtkOpenGLHAVSVolumeMapper::DrawFBOFlush(int screenWidth, int screenHeight, float depthNear, float depthFar) { vtkOpenGLClearErrorMacro(); float scale = this->MaxEdgeLength; if (this->LevelOfDetail || !this->PartiallyRemoveNonConvexities) { scale = this->LevelOfDetailMaxEdgeLength; } float params[4] = {1.0f/this->FramebufferObjectSize, 1.0f/this->FramebufferObjectSize, scale, 0.0f}; // Bind fragment shader vtkgl::BindProgramARB(vtkgl::FRAGMENT_PROGRAM_ARB, this->FragmentProgramEnd); vtkgl::ProgramLocalParameter4fvARB(vtkgl::FRAGMENT_PROGRAM_ARB, 0, params); // Setup ortho view glMatrixMode(GL_PROJECTION); glPushMatrix(); glLoadIdentity(); glOrtho(0.0, static_cast(screenWidth), 0.0, static_cast(screenHeight), static_cast(depthNear), static_cast(depthFar)); glMatrixMode(GL_MODELVIEW); glPushMatrix(); glLoadIdentity(); // Draw k-1 quads to flush k-buffer int flushCount = (this->KBufferSize == VTK_KBUFFER_SIZE_2)? 1 : 5; for (int i = 0; i < flushCount; i++) { glBegin(GL_QUADS); glVertex3f(0.0, 0.0, 0.0); glVertex3f(0.0, static_cast(screenHeight), 0.0); glVertex3f(static_cast(screenWidth), static_cast(screenHeight), 0.0); glVertex3f(static_cast(screenWidth), 0.0, 0.0); glEnd(); } // Disable shader vtkgl::BindProgramARB(vtkgl::FRAGMENT_PROGRAM_ARB, 0); glDisable( vtkgl::FRAGMENT_PROGRAM_ARB ); // Reset view glMatrixMode(GL_PROJECTION); glPopMatrix(); glMatrixMode(GL_MODELVIEW); glPopMatrix(); // Disable Textures if (this->KBufferSize == VTK_KBUFFER_SIZE_2) { vtkgl::ActiveTexture(vtkgl::TEXTURE2); glDisable(GL_TEXTURE_2D); vtkgl::ActiveTexture(vtkgl::TEXTURE3); glDisable(GL_TEXTURE_1D); } else { vtkgl::ActiveTexture(vtkgl::TEXTURE4); glDisable(GL_TEXTURE_2D); vtkgl::ActiveTexture(vtkgl::TEXTURE5); glDisable(GL_TEXTURE_1D); } vtkgl::ActiveTexture(vtkgl::TEXTURE1); glDisable(GL_TEXTURE_2D); vtkgl::ActiveTexture(vtkgl::TEXTURE0); glDisable(GL_TEXTURE_2D); glDisable(GL_DEPTH_TEST); glFinish(); // Disable vertex arrays if (this->GPUDataStructures) { vtkgl::BindBuffer(vtkgl::ARRAY_BUFFER, 0); vtkgl::BindBuffer(vtkgl::ELEMENT_ARRAY_BUFFER, 0); glDisableClientState(GL_VERTEX_ARRAY); glDisableClientState(GL_TEXTURE_COORD_ARRAY); } else { glDisableClientState(GL_VERTEX_ARRAY); glDisableClientState(GL_TEXTURE_COORD_ARRAY); } vtkOpenGLCheckErrorMacro("failed after DrawFBOFlush"); } //---------------------------------------------------------------------------- // Blend the result from the off screen rendering into the framebuffer by // drawing a textured screen-aligned plane. This avoids expensive data // transfers between GPU and CPU. void vtkOpenGLHAVSVolumeMapper::DrawBlend(int screenWidth, int screenHeight, float depthNear, float depthFar) { vtkOpenGLClearErrorMacro(); // Setup draw buffer glDrawBuffer(GL_BACK); // Setup 2D view glMatrixMode(GL_PROJECTION); glPushMatrix(); glLoadIdentity(); glOrtho(0.0, screenWidth, 0.0, screenHeight, depthNear, depthFar); glMatrixMode(GL_MODELVIEW); glPushMatrix(); glLoadIdentity(); // Bind resulting texture vtkgl::ActiveTexture(vtkgl::TEXTURE0); glBindTexture(GL_TEXTURE_2D, this->FramebufferTextures[0]); glEnable(GL_TEXTURE_2D); glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE); float max_u =static_cast(screenWidth)/static_cast(this->FramebufferObjectSize); float max_v = static_cast(screenHeight)/static_cast(this->FramebufferObjectSize); if (max_u > 1.0) { max_u = 1.0; } if (max_v > 1.0) { max_v = 1.0; } // Setup blending. Use the same non-standard blending function as PT to get // similar images. glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA); // Draw textured screen-aligned plane glColor4f(0.0, 0.0, 0.0, 0.0); glBegin(GL_QUADS); glTexCoord2f(0.0, 0.0); glVertex2f(0.0, 0.0); glTexCoord2f(max_u, 0.0); glVertex2f(screenWidth, 0.0); glTexCoord2f(max_u, max_v); glVertex2f(screenWidth, screenHeight); glTexCoord2f(0.0, max_v); glVertex2f(0.0, screenHeight); glEnd(); // Reset view glMatrixMode(GL_PROJECTION); glPopMatrix(); glMatrixMode(GL_MODELVIEW); glPopMatrix(); // Restore OpenGL state glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); glBindTexture(GL_TEXTURE_2D, 0); glDisable(GL_TEXTURE_2D); vtkOpenGLCheckErrorMacro("failed after DrawBlend"); } //---------------------------------------------------------------------------- void vtkOpenGLHAVSVolumeMapper::PrintSelf(ostream& os, vtkIndent indent) { if(this->RenderWindow!=0) { vtkOpenGLExtensionManager *extensions= static_cast(this->RenderWindow.GetPointer()) ->GetExtensionManager(); if ( this->Initialized ) { os << indent << "Supports GL_VERSION_1_3 (edge_clamp (1.2) and" << " multitexture (1.3) minimal version required by" << " GL_ARB_draw_buffers): " << extensions->ExtensionSupported( "GL_VERSION_1_3" ); os << indent << "Supports GL_VERSION_2_0 (GL_ARB_draw_buffers is a core" << "feature): " << extensions->ExtensionSupported( "GL_VERSION_2_0" ); os << indent << "Supports GL_ARB_draw_buffers: " << extensions->ExtensionSupported( "GL_ARB_draw_buffers" ); os << indent << "Supports GL_EXT_framebuffer_object: " << extensions->ExtensionSupported( "GL_EXT_framebuffer_object" ) << endl; os << indent << "Supports GL_ARB_vertex_program: " << extensions->ExtensionSupported( "GL_ARB_vertex_program" ) << endl; os << indent << "Supports GL_ARB_fragment_program: " << extensions->ExtensionSupported( "GL_ARB_fragment_program" ) << endl; os << indent << "Supports GL_ARB_texture_float" << extensions->ExtensionSupported( "GL_ARB_texture_float" ) << endl; os << indent << "Supports GL_ATI_texture_float: " << extensions->ExtensionSupported( "GL_ATI_texture_float" ) << endl; os << indent << "Supports GL_VERSION_1_5 (for optional core feature VBO): " << extensions->ExtensionSupported( "GL_VERSION_1_5" ) <ExtensionSupported( "GL_ARB_vertex_buffer_object" ) <FramebufferObjectSize << endl; this->Superclass::PrintSelf(os,indent); } //---------------------------------------------------------------------------- // Check the OpenGL extension manager for GPU features necessary for the // HAVS algorithm. bool vtkOpenGLHAVSVolumeMapper::SupportedByHardware(vtkRenderer *r) { vtkOpenGLExtensionManager *extensions= static_cast(r->GetRenderWindow()) ->GetExtensionManager(); // os mesa notes: // 9.1.4 -- test fails with open gl errors (mesa bug) // 9.2.0 w/llvmpipe -- test passes if ( extensions->DriverIsMesa() && !(extensions->DriverGLRendererIsOSMesa() && extensions->DriverGLRendererHasToken("llvmpipe")) && !extensions->GetIgnoreDriverBugs("Mesa FBO bugs") ) { return false; } // Temporarily filter out the Macs, as this mapper makes the ATI driver crash // (RogueResearch2 on VTK, ATI Radeon X1600 OpenGL Engine 2.0 ATI-1.4.56) and // makes the Nvidia driver render some corrupted image (kamino on ParaView3 // dashboard NVIDIA GeForce 7300 GT 2.0 NVIDIA-1.4.56). // This mapper does not actually use texture3D but it is known that Macs // only support texture3d through OpenGL 1.2 API, not as an extension, so // this is a good way to filter them out. int iAmAMac=!extensions->ExtensionSupported("GL_EXT_texture3D"); // OpenGL 1.3 is required by GL_ARB_draw_buffers, GL_ARB_fragment_program // and GL_ARB_vertex_program // CLAMP_TO_EGDE is core feature of OpenGL 1.2 and // multitexture is core feature of OpenGL 1.3. int supports_GL_1_3=extensions->ExtensionSupported( "GL_VERSION_1_3" ); int supports_GL_2_0=extensions->ExtensionSupported( "GL_VERSION_2_0" ); int supports_draw_buffers; if(supports_GL_2_0) { supports_draw_buffers=1; } else { supports_draw_buffers= extensions->ExtensionSupported( "GL_ARB_draw_buffers" ); } int supports_GL_ARB_fragment_program = extensions->ExtensionSupported( "GL_ARB_fragment_program" ); int supports_GL_ARB_vertex_program = extensions->ExtensionSupported( "GL_ARB_vertex_program" ); int supports_GL_EXT_framebuffer_object = extensions->ExtensionSupported( "GL_EXT_framebuffer_object"); // GL_ARB_texture_float or GL_ATI_texture_float introduce new tokens but // no new function: don't need to call LoadExtension. int supports_GL_ARB_texture_float = extensions->ExtensionSupported( "GL_ARB_texture_float" ); int supports_GL_ATI_texture_float = extensions->ExtensionSupported( "GL_ATI_texture_float" ); return !iAmAMac && supports_GL_1_3 && supports_draw_buffers && supports_GL_ARB_fragment_program && supports_GL_ARB_vertex_program && supports_GL_EXT_framebuffer_object && ( supports_GL_ARB_texture_float || supports_GL_ATI_texture_float); } //---------------------------------------------------------------------------- int vtkOpenGLHAVSVolumeMapper::FillInputPortInformation(int port, vtkInformation* info) { if(!this->Superclass::FillInputPortInformation(port, info)) { return 0; } info->Set(vtkAlgorithm::INPUT_REQUIRED_DATA_TYPE(), "vtkUnstructuredGrid"); return 1; }